diff options
112 files changed, 1122 insertions, 812 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index dd16606bcd..63223e1183 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1138,12 +1138,6 @@ S: Orphaned F: hw/mips/mipssim.c F: hw/net/mipsnet.c -R4000 -R: Aurelien Jarno <aurelien@aurel32.net> -R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com> -S: Obsolete -F: hw/mips/r4k.c - Fuloong 2E M: Huacai Chen <chenhc@lemote.com> M: Philippe Mathieu-Daudé <f4bug@amsat.org> diff --git a/block/export/export.c b/block/export/export.c index c3478c6c97..bad6f21b1c 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -22,13 +22,13 @@ #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) +#ifdef CONFIG_VHOST_USER_BLK_SERVER #include "vhost-user-blk-server.h" #endif static const BlockExportDriver *blk_exp_drivers[] = { &blk_exp_nbd, -#if defined(CONFIG_LINUX) && defined(CONFIG_VHOST_USER) +#ifdef CONFIG_VHOST_USER_BLK_SERVER &blk_exp_vhost_user_blk, #endif }; diff --git a/block/export/meson.build b/block/export/meson.build index 9fb4fbf81d..19526435d8 100644 --- a/block/export/meson.build +++ b/block/export/meson.build @@ -1,2 +1,2 @@ blockdev_ss.add(files('export.c')) -blockdev_ss.add(when: ['CONFIG_LINUX', 'CONFIG_VHOST_USER'], if_true: files('vhost-user-blk-server.c')) +blockdev_ss.add(when: 'CONFIG_VHOST_USER_BLK_SERVER', 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 index 41f4933d6e..62672d1cb9 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -264,9 +264,11 @@ static uint64_t vu_blk_get_protocol_features(VuDev *dev) 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); + + g_return_val_if_fail(len <= sizeof(struct virtio_blk_config), -1); + memcpy(config, &vexp->blkcfg, len); return 0; } @@ -343,18 +345,18 @@ vu_blk_initialize_config(BlockDriverState *bs, 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; + config->capacity = cpu_to_le64(bdrv_getlength(bs) >> BDRV_SECTOR_BITS); + config->blk_size = cpu_to_le32(blk_size); + config->size_max = cpu_to_le32(0); + config->seg_max = cpu_to_le32(128 - 2); + config->min_io_size = cpu_to_le16(1); + config->opt_io_size = cpu_to_le32(1); + config->num_queues = cpu_to_le16(num_queues); + config->max_discard_sectors = cpu_to_le32(32768); + config->max_discard_seg = cpu_to_le32(1); + config->discard_sector_alignment = cpu_to_le32(config->blk_size >> 9); + config->max_write_zeroes_sectors = cpu_to_le32(32768); + config->max_write_zeroes_seg = cpu_to_le32(1); } static void vu_blk_exp_request_shutdown(BlockExport *exp) diff --git a/configure b/configure index 2c3c69f118..805f779150 100755 --- a/configure +++ b/configure @@ -329,6 +329,7 @@ vhost_crypto="" vhost_scsi="" vhost_vsock="" vhost_user="" +vhost_user_blk_server="" vhost_user_fs="" kvm="auto" hax="auto" @@ -1246,6 +1247,10 @@ for opt do ;; --enable-vhost-vsock) vhost_vsock="yes" ;; + --disable-vhost-user-blk-server) vhost_user_blk_server="no" + ;; + --enable-vhost-user-blk-server) vhost_user_blk_server="yes" + ;; --disable-vhost-user-fs) vhost_user_fs="no" ;; --enable-vhost-user-fs) vhost_user_fs="yes" @@ -1791,6 +1796,7 @@ disabled with --disable-FEATURE, default is enabled if available: vhost-crypto vhost-user-crypto backend support vhost-kernel vhost kernel backend support vhost-user vhost-user backend support + vhost-user-blk-server vhost-user-blk server support vhost-vdpa vhost-vdpa kernel backend support spice spice rbd rados block device (rbd) @@ -2382,6 +2388,12 @@ if test "$vhost_net" = ""; then test "$vhost_kernel" = "yes" && vhost_net=yes fi +# libvhost-user is Linux-only +test "$vhost_user_blk_server" = "" && vhost_user_blk_server=$linux +if test "$vhost_user_blk_server" = "yes" && test "$linux" = "no"; then + error_exit "--enable-vhost-user-blk-server is only available on Linux" +fi + ########################################## # pkg-config probe @@ -3499,7 +3511,7 @@ if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then # with pkg-config --static --libs data for gio-2.0 that is missing # -lblkid and will give a link error. write_c_skeleton - if compile_prog "" "gio_libs" ; then + if compile_prog "" "$gio_libs" ; then gio=yes else gio=no @@ -6275,6 +6287,9 @@ fi if test "$vhost_vdpa" = "yes" ; then echo "CONFIG_VHOST_VDPA=y" >> $config_host_mak fi +if test "$vhost_user_blk_server" = "yes" ; then + echo "CONFIG_VHOST_USER_BLK_SERVER=y" >> $config_host_mak +fi if test "$vhost_user_fs" = "yes" ; then echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak fi @@ -6961,6 +6976,10 @@ fi mv $cross config-meson.cross rm -rf meson-private meson-info meson-logs +unset staticpic +if ! version_ge "$($meson --version)" 0.56.0; then + staticpic=$(if test "$pie" = yes; then echo true; else echo false; fi) +fi NINJA=$ninja $meson setup \ --prefix "$prefix" \ --libdir "$libdir" \ @@ -6980,7 +6999,7 @@ NINJA=$ninja $meson setup \ -Dwerror=$(if test "$werror" = yes; then echo true; else echo false; fi) \ -Dstrip=$(if test "$strip_opt" = yes; then echo true; else echo false; fi) \ -Db_pie=$(if test "$pie" = yes; then echo true; else echo false; fi) \ - -Db_staticpic=$(if test "$pie" = yes; then echo true; else echo false; fi) \ + ${staticpic:+-Db_staticpic=$staticpic} \ -Db_coverage=$(if test "$gcov" = yes; then echo true; else echo false; fi) \ -Dmalloc=$malloc -Dmalloc_trim=$malloc_trim -Dsparse=$sparse \ -Dkvm=$kvm -Dhax=$hax -Dwhpx=$whpx -Dhvf=$hvf \ diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 3bbeae8587..a1539dbb69 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -392,7 +392,8 @@ struct VuDev { bool broken; uint16_t max_queues; - /* @read_msg: custom method to read vhost-user message + /* + * @read_msg: custom method to read vhost-user message * * Read data from vhost_user socket fd and fill up * the passed VhostUserMsg *vmsg struct. @@ -409,15 +410,19 @@ struct VuDev { * */ vu_read_msg_cb read_msg; - /* @set_watch: add or update the given fd to the watch set, - * call cb when condition is met */ + + /* + * @set_watch: add or update the given fd to the watch set, + * call cb when condition is met. + */ vu_set_watch_cb set_watch; /* @remove_watch: remove the given fd from the watch set */ vu_remove_watch_cb remove_watch; - /* @panic: encountered an unrecoverable error, you may try to - * re-initialize */ + /* + * @panic: encountered an unrecoverable error, you may try to re-initialize + */ vu_panic_cb panic; const VuDevIface *iface; diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 25eccd02b5..caad88637e 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -404,6 +404,8 @@ vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len) VugDev *gdev; VubDev *vdev_blk; + g_return_val_if_fail(len <= sizeof(struct virtio_blk_config), -1); + gdev = container_of(vu_dev, VugDev, parent); vdev_blk = container_of(gdev, VubDev, parent); memcpy(config, &vdev_blk->blkcfg, len); diff --git a/contrib/vhost-user-gpu/meson.build b/contrib/vhost-user-gpu/meson.build index 37ecca13ca..c487ca72c1 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: { 'libexecdir' : get_option('libexecdir') }, + configuration: { 'libexecdir' : get_option('prefix') / get_option('libexecdir') }, install_dir: qemu_datadir / 'vhost-user') endif diff --git a/default-configs/devices/mips-softmmu-common.mak b/default-configs/devices/mips-softmmu-common.mak index da29c6c0b2..ea78fe7275 100644 --- a/default-configs/devices/mips-softmmu-common.mak +++ b/default-configs/devices/mips-softmmu-common.mak @@ -33,7 +33,6 @@ CONFIG_MC146818RTC=y CONFIG_EMPTY_SLOT=y CONFIG_MIPS_CPS=y CONFIG_MIPS_ITU=y -CONFIG_R4K=y CONFIG_MALTA=y CONFIG_PCNET_PCI=y CONFIG_MIPSSIM=y diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index 6fcf8854b7..31f4dced2a 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -187,21 +187,23 @@ process for: 4) other data files, such as icons or desktop files -The source code is highly modularized, split across many files to -facilitate building of all of these components with as little duplicated -compilation as possible. The Meson "sourceset" functionality is used -to list the files and their dependency on various configuration -symbols. - All executables are built by default, except for some `contrib/` binaries that are known to fail to build on some platforms (for example 32-bit or big-endian platforms). Tests are also built by default, though that might change in the future. -Various subsystems that are common to both tools and emulators have -their own sourceset, for example `block_ss` for the block device subsystem, -`chardev_ss` for the character device subsystem, etc. These sourcesets -are then turned into static libraries as follows:: +The source code is highly modularized, split across many files to +facilitate building of all of these components with as little duplicated +compilation as possible. Using the Meson "sourceset" functionality, +`meson.build` files group the source files in rules that are +enabled according to the available system libraries and to various +configuration symbols. Sourcesets belong to one of four groups: + +Subsystem sourcesets: + Various subsystems that are common to both tools and emulators have + their own sourceset, for example `block_ss` for the block device subsystem, + `chardev_ss` for the character device subsystem, etc. These sourcesets + are then turned into static libraries as follows:: libchardev = static_library('chardev', chardev_ss.sources(), name_suffix: 'fa', @@ -209,61 +211,111 @@ are then turned into static libraries as follows:: chardev = declare_dependency(link_whole: libchardev) -As of Meson 0.55.1, the special `.fa` suffix should be used for everything -that is used with `link_whole`, to ensure that the link flags are placed -correctly in the command line. - -Files linked into emulator targets there can be split into two distinct groups -of files, those which are independent of the QEMU emulation target and -those which are dependent on the QEMU emulation target. - -In the target-independent set lives various general purpose helper code, -such as error handling infrastructure, standard data structures, -platform portability wrapper functions, etc. This code can be compiled -once only and the .o files linked into all output binaries. -Target-independent code lives in the `common_ss`, `softmmu_ss` and -`user_ss` sourcesets. `common_ss` is linked into all emulators, `softmmu_ss` -only in system emulators, `user_ss` only in user-mode emulators. - -In the target-dependent set lives CPU emulation, device emulation and -much glue code. This sometimes also has to be compiled multiple times, -once for each target being built. Target-dependent files are included -in the `specific_ss` sourceset. - -All binaries link with a static library `libqemuutil.a`, which is then -linked to all the binaries. `libqemuutil.a` is built from several -sourcesets; most of them however host generated code, and the only two -of general interest are `util_ss` and `stub_ss`. - -The separation between these two is purely for documentation purposes. -`util_ss` contains generic utility files. Even though this code is only -linked in some binaries, sometimes it requires hooks only in some of -these and depend on other functions that are not fully implemented by -all QEMU binaries. `stub_ss` links dummy stubs that will only be linked -into the binary if the real implementation is not present. In a way, -the stubs can be thought of as a portable implementation of the weak -symbols concept. + As of Meson 0.55.1, the special `.fa` suffix should be used for everything + that is used with `link_whole`, to ensure that the link flags are placed + correctly in the command line. + +Target-independent emulator sourcesets: + Various general purpose helper code is compiled only once and + the .o files are linked into all output binaries that need it. + This includes error handling infrastructure, standard data structures, + platform portability wrapper functions, etc. + + Target-independent code lives in the `common_ss`, `softmmu_ss` and + `user_ss` sourcesets. `common_ss` is linked into all emulators, + `softmmu_ss` only in system emulators, `user_ss` only in user-mode + emulators. + + Target-independent sourcesets must exercise particular care when using + `if_false` rules. The `if_false` rule will be used correctly when linking + emulator binaries; however, when *compiling* target-independent files + into .o files, Meson may need to pick *both* the `if_true` and + `if_false` sides to cater for targets that want either side. To + achieve that, you can add a special rule using the ``CONFIG_ALL`` + symbol:: + + # Some targets have CONFIG_ACPI, some don't, so this is not enough + softmmu_ss.add(when: 'CONFIG_ACPI`, if_true: files('acpi.c'), + if_false: files('acpi-stub.c')) + + # This is required as well: + softmmu_ss.add(when: 'CONFIG_ALL`, if_true: files('acpi-stub.c')) + +Target-dependent emulator sourcesets: + In the target-dependent set lives CPU emulation, some device emulation and + much glue code. This sometimes also has to be compiled multiple times, + once for each target being built. Target-dependent files are included + in the `specific_ss` sourceset. + + Each emulator also includes sources for files in the `hw/` and `target/` + subdirectories. The subdirectory used for each emulator comes + from the target's definition of ``TARGET_BASE_ARCH`` or (if missing) + ``TARGET_ARCH``, as found in `default-configs/targets/*.mak`. + + Each subdirectory in `hw/` adds one sourceset to the `hw_arch` dictionary, + for example:: + + arm_ss = ss.source_set() + arm_ss.add(files('boot.c'), fdt) + ... + hw_arch += {'arm': arm_ss} + + The sourceset is only used for system emulators. + + Each subdirectory in `target/` instead should add one sourceset to each + of the `target_arch` and `target_softmmu_arch`, which are used respectively + for all emulators and for system emulators only. For example:: + + arm_ss = ss.source_set() + arm_softmmu_ss = ss.source_set() + ... + target_arch += {'arm': arm_ss} + target_softmmu_arch += {'arm': arm_softmmu_ss} + +Utility sourcesets: + All binaries link with a static library `libqemuutil.a`. This library + is built from several sourcesets; most of them however host generated + code, and the only two of general interest are `util_ss` and `stub_ss`. + + The separation between these two is purely for documentation purposes. + `util_ss` contains generic utility files. Even though this code is only + linked in some binaries, sometimes it requires hooks only in some of + these and depend on other functions that are not fully implemented by + all QEMU binaries. `stub_ss` links dummy stubs that will only be linked + into the binary if the real implementation is not present. In a way, + the stubs can be thought of as a portable implementation of the weak + symbols concept. + The following files concur in the definition of which files are linked into each emulator: -`default-configs/*.mak` - The files under default-configs/ control what emulated hardware is built - into each QEMU system and userspace emulator targets. They merely contain - a list of config variable definitions like the machines that should be - included. For example, default-configs/aarch64-softmmu.mak has:: +`default-configs/devices/*.mak` + The files under `default-configs/devices/` control the boards and devices + that are built into each QEMU system emulation targets. They merely contain + a list of config variable definitions such as:: include arm-softmmu.mak CONFIG_XLNX_ZYNQMP_ARM=y CONFIG_XLNX_VERSAL=y `*/Kconfig` - These files are processed together with `default-configs/*.mak` and + These files are processed together with `default-configs/devices/*.mak` and describe the dependencies between various features, subsystems and - device models. They are described in kconfig.rst. + device models. They are described in :ref:`kconfig` + +`default-configs/targets/*.mak` + These files mostly define symbols that appear in the `*-config-target.h` + file for each emulator [#cfgtarget]_. However, the ``TARGET_ARCH`` + and ``TARGET_BASE_ARCH`` will also be used to select the `hw/` and + `target/` subdirectories that are compiled into each target. + +.. [#cfgtarget] This header is included by `qemu/osdep.h` when + compiling files from the target-specific sourcesets. -These files rarely need changing unless new devices / hardware need to -be enabled for a particular system/userspace emulation target +These files rarely need changing unless you are adding a completely +new target, or enabling new devices or hardware for a particular +system/userspace emulation target Support scripts diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index e5df72b342..336ba0e8e5 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -1,3 +1,5 @@ +.. _kconfig: + ================ QEMU and Kconfig ================ diff --git a/docs/meson.build b/docs/meson.build index 8c222f96bb..bf8204a08f 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -27,7 +27,8 @@ if sphinx_build.found() build_docs = (sphinx_build_test_out.returncode() == 0) if not build_docs - warning('@0@ exists but it is either too old or uses too old a Python version'.format(get_option('sphinx_build'))) + warning('@0@ is either too old or uses too old a Python version' + .format(sphinx_build.full_path())) if get_option('docs').enabled() error('Install a Python 3 version of python-sphinx') endif diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 32a0e620db..8c1dc7645d 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -328,12 +328,6 @@ The 'scsi-disk' device is deprecated. Users should use 'scsi-hd' or System emulator machines ------------------------ -mips ``r4k`` platform (since 5.0) -''''''''''''''''''''''''''''''''' - -This machine type is very old and unmaintained. Users should use the ``malta`` -machine type instead. - mips ``fulong2e`` machine (since 5.1) ''''''''''''''''''''''''''''''''''''' @@ -576,6 +570,12 @@ The version specific Spike machines have been removed in favour of the generic ``spike`` machine. If you need to specify an older version of the RISC-V spec you can use the ``-cpu rv64gcsu,priv_spec=v1.10.0`` command line argument. +mips ``r4k`` platform (removed in 5.2) +'''''''''''''''''''''''''''''''''''''' + +This machine type was very old and unmaintained. Users should use the ``malta`` +machine type instead. + Related binaries ---------------- diff --git a/hmp-commands.hx b/hmp-commands.hx index cd068389de..ff2d7aa8f3 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -254,6 +254,7 @@ ERST .help = "save screen from head 'head' of display device 'device' " "into PPM image 'filename'", .cmd = hmp_screendump, + .coroutine = true, }, SRST diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 3107637209..af52c1daac 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -162,13 +162,13 @@ static void local_mapped_file_attr(int dirfd, const char *name, memset(buf, 0, ATTR_MAX); while (fgets(buf, ATTR_MAX, fp)) { if (!strncmp(buf, "virtfs.uid", 10)) { - stbuf->st_uid = atoi(buf+11); + stbuf->st_uid = atoi(buf + 11); } else if (!strncmp(buf, "virtfs.gid", 10)) { - stbuf->st_gid = atoi(buf+11); + stbuf->st_gid = atoi(buf + 11); } else if (!strncmp(buf, "virtfs.mode", 11)) { - stbuf->st_mode = atoi(buf+12); + stbuf->st_mode = atoi(buf + 12); } else if (!strncmp(buf, "virtfs.rdev", 11)) { - stbuf->st_rdev = atoi(buf+12); + stbuf->st_rdev = atoi(buf + 12); } memset(buf, 0, ATTR_MAX); } @@ -823,7 +823,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, if (fd == -1) { goto out; } - credp->fc_mode = credp->fc_mode|S_IFREG; + credp->fc_mode = credp->fc_mode | S_IFREG; if (fs_ctx->export_flags & V9FS_SM_MAPPED) { /* Set cleint credentials in xattr */ err = local_set_xattrat(dirfd, name, credp); diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 741d222c3f..94df440fc7 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1091,7 +1091,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) } } - if (!(ret&~0777)) { + if (!(ret & ~0777)) { ret |= S_IFREG; } @@ -2776,7 +2776,7 @@ static void coroutine_fn v9fs_create(void *opaque) v9fs_path_unlock(s); } else { err = v9fs_co_open2(pdu, fidp, &name, -1, - omode_to_uflags(mode)|O_CREAT, perm, &stbuf); + omode_to_uflags(mode) | O_CREAT, perm, &stbuf); if (err < 0) { goto out; } @@ -3428,7 +3428,7 @@ static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) * compute bsize factor based on host file system block size * and client msize */ - bsize_factor = (s->msize - P9_IOHDRSZ)/stbuf->f_bsize; + bsize_factor = (s->msize - P9_IOHDRSZ) / stbuf->f_bsize; if (!bsize_factor) { bsize_factor = 1; } @@ -3440,9 +3440,9 @@ static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) * adjust(divide) the number of blocks, free blocks and available * blocks by bsize factor */ - f_blocks = stbuf->f_blocks/bsize_factor; - f_bfree = stbuf->f_bfree/bsize_factor; - f_bavail = stbuf->f_bavail/bsize_factor; + f_blocks = stbuf->f_blocks / bsize_factor; + f_bfree = stbuf->f_bfree / bsize_factor; + f_bavail = stbuf->f_bavail / bsize_factor; f_files = stbuf->f_files; f_ffree = stbuf->f_ffree; fsid_val = (unsigned int) stbuf->f_fsid.__val[0] | @@ -4185,6 +4185,6 @@ static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) error_report("Failed to get the resource limit"); exit(1); } - open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3); - open_fd_rc = rlim.rlim_cur/2; + open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); + open_fd_rc = rlim.rlim_cur / 2; } diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 3dd1b50b1a..32df81f360 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -143,8 +143,7 @@ typedef struct { */ QEMU_BUILD_BUG_ON(sizeof(P9MsgHeader) != 7); -struct V9fsPDU -{ +struct V9fsPDU { uint32_t size; uint16_t tag; uint8_t id; @@ -270,8 +269,7 @@ union V9fsFidOpenState { void *private; }; -struct V9fsFidState -{ +struct V9fsFidState { int fid_type; int32_t fid; V9fsPath path; @@ -338,8 +336,7 @@ typedef struct { uint64_t path; } QpfEntry; -struct V9fsState -{ +struct V9fsState { QLIST_HEAD(, V9fsPDU) free_list; QLIST_HEAD(, V9fsPDU) active_list; V9fsFidState *fid_list; diff --git a/hw/9pfs/Kconfig b/hw/9pfs/Kconfig index d3ebd73730..3ae5749661 100644 --- a/hw/9pfs/Kconfig +++ b/hw/9pfs/Kconfig @@ -2,12 +2,8 @@ config FSDEV_9P bool depends on VIRTFS -config 9PFS - bool - config VIRTIO_9P bool default y depends on VIRTFS && VIRTIO select FSDEV_9P - select 9PFS diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c index 55991916ec..0b321b456e 100644 --- a/hw/9pfs/cofs.c +++ b/hw/9pfs/cofs.c @@ -23,7 +23,7 @@ static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf) ssize_t len, maxlen = PATH_MAX; buf->data = g_malloc(PATH_MAX); - for(;;) { + for (;;) { len = s->ops->readlink(&s->ctx, path, buf->data, maxlen); if (len < 0) { g_free(buf->data); diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index cc09426212..99be5d9119 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -15,6 +15,6 @@ fs_ss.add(files( 'coxattr.c', )) fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.c')) -softmmu_ss.add_all(when: 'CONFIG_9PFS', if_true: fs_ss) +softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) specific_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-device.c')) diff --git a/hw/acpi/core.c b/hw/acpi/core.c index ade9158cbf..2c0c83221f 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -558,7 +558,7 @@ static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) if (val & ACPI_BITMASK_SLEEP_ENABLE) { /* change suspend type */ uint16_t sus_typ = (val >> 10) & 7; - switch(sus_typ) { + switch (sus_typ) { case 0: /* soft power off */ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); break; diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 8f7cc16add..8ad5516142 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -556,7 +556,7 @@ static void nvdimm_dsm_func_read_fit(NVDIMMState *state, NvdimmDsmIn *in, fit = fit_buf->fit; - nvdimm_debug("Read FIT: offset %#x FIT size %#x Dirty %s.\n", + nvdimm_debug("Read FIT: offset 0x%x FIT size 0x%x Dirty %s.\n", read_fit->offset, fit->len, fit_buf->dirty ? "Yes" : "No"); if (read_fit->offset > fit->len) { @@ -664,7 +664,7 @@ static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr) label_size = nvdimm->label_size; mxfer = nvdimm_get_max_xfer_label_size(); - nvdimm_debug("label_size %#x, max_xfer %#x.\n", label_size, mxfer); + nvdimm_debug("label_size 0x%x, max_xfer 0x%x.\n", label_size, mxfer); label_size_out.func_ret_status = cpu_to_le32(NVDIMM_DSM_RET_STATUS_SUCCESS); label_size_out.label_size = cpu_to_le32(label_size); @@ -680,19 +680,19 @@ static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm, uint32_t ret = NVDIMM_DSM_RET_STATUS_INVALID; if (offset + length < offset) { - nvdimm_debug("offset %#x + length %#x is overflow.\n", offset, + nvdimm_debug("offset 0x%x + length 0x%x is overflow.\n", offset, length); return ret; } if (nvdimm->label_size < offset + length) { - nvdimm_debug("position %#x is beyond label data (len = %" PRIx64 ").\n", + nvdimm_debug("position 0x%x is beyond label data (len = %" PRIx64 ").\n", offset + length, nvdimm->label_size); return ret; } if (length > nvdimm_get_max_xfer_label_size()) { - nvdimm_debug("length (%#x) is larger than max_xfer (%#x).\n", + nvdimm_debug("length (0x%x) is larger than max_xfer (0x%x).\n", length, nvdimm_get_max_xfer_label_size()); return ret; } @@ -716,7 +716,7 @@ static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, get_label_data->offset = le32_to_cpu(get_label_data->offset); get_label_data->length = le32_to_cpu(get_label_data->length); - nvdimm_debug("Read Label Data: offset %#x length %#x.\n", + nvdimm_debug("Read Label Data: offset 0x%x length 0x%x.\n", get_label_data->offset, get_label_data->length); status = nvdimm_rw_label_data_check(nvdimm, get_label_data->offset, @@ -755,7 +755,7 @@ static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, set_label_data->offset = le32_to_cpu(set_label_data->offset); set_label_data->length = le32_to_cpu(set_label_data->length); - nvdimm_debug("Write Label Data: offset %#x length %#x.\n", + nvdimm_debug("Write Label Data: offset 0x%x length 0x%x.\n", set_label_data->offset, set_label_data->length); status = nvdimm_rw_label_data_check(nvdimm, set_label_data->offset, @@ -838,7 +838,7 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) NvdimmDsmIn *in; hwaddr dsm_mem_addr = val; - nvdimm_debug("dsm memory address %#" HWADDR_PRIx ".\n", dsm_mem_addr); + nvdimm_debug("dsm memory address 0x%" HWADDR_PRIx ".\n", dsm_mem_addr); /* * The DSM memory is mapped to guest address space so an evil guest @@ -852,11 +852,11 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) in->function = le32_to_cpu(in->function); in->handle = le32_to_cpu(in->handle); - nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision, + nvdimm_debug("Revision 0x%x Handler 0x%x Function 0x%x.\n", in->revision, in->handle, in->function); if (in->revision != 0x1 /* Currently we only support DSM Spec Rev1. */) { - nvdimm_debug("Revision %#x is not supported, expect %#x.\n", + nvdimm_debug("Revision 0x%x is not supported, expect 0x%x.\n", in->revision, 0x1); nvdimm_dsm_no_payload(NVDIMM_DSM_RET_STATUS_UNSUPPORT, dsm_mem_addr); goto exit; diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 32ae8b2c0a..17c32e0ffd 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -400,7 +400,7 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, s->io_len = ACPI_PCIHP_SIZE; s->io_base = ACPI_PCIHP_ADDR; - s->root= root_bus; + s->root = root_bus; s->legacy_piix = !bridges_enabled; memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s, diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index f67b29bbf3..2dd3d93ca0 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -131,7 +131,7 @@ static int vhost_user_blk_start(VirtIODevice *vdev) s->dev.acked_features = vdev->guest_features; - ret = vhost_dev_prepare_inflight(&s->dev); + ret = vhost_dev_prepare_inflight(&s->dev, vdev); if (ret < 0) { error_report("Error set inflight format: %d", -ret); goto err_guest_notifiers; diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index c465921b8f..b7c7b3ba94 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 23a8ae0cd8..4dc10ea795 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -75,8 +75,9 @@ void ati_2d_blt(ATIVGAState *s) dst_stride *= bpp; } uint8_t *end = s->vga.vram_ptr + s->vga.vram_size; - if (dst_bits >= end || dst_bits + dst_x + (dst_y + s->regs.dst_height) * - dst_stride >= end) { + if (dst_x > 0x3fff || dst_y > 0x3fff || dst_bits >= end + || dst_bits + dst_x + + (dst_y + s->regs.dst_height) * dst_stride >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } @@ -107,8 +108,9 @@ void ati_2d_blt(ATIVGAState *s) src_bits += s->regs.crtc_offset & 0x07ffffff; src_stride *= bpp; } - if (src_bits >= end || src_bits + src_x + - (src_y + s->regs.dst_height) * src_stride >= end) { + if (src_x > 0x3fff || src_y > 0x3fff || src_bits >= end + || src_bits + src_x + + (src_y + s->regs.dst_height) * src_stride >= end) { qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); return; } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 5e6c0023e0..17b514d1da 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1149,10 +1149,11 @@ void pc_basic_device_init(struct PCMachineState *pcms, error_report("couldn't create HPET device"); exit(1); } - /* For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7 - * and earlier, use IRQ2 for compat. Otherwise, use IRQ16~23, - * IRQ8 and IRQ2. - */ + /* + * For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7 and + * earlier, use IRQ2 for compat. Otherwise, use IRQ16~23, IRQ8 and + * IRQ2. + */ uint8_t compat = object_property_get_uint(OBJECT(hpet), HPET_INTCAP, NULL); if (!compat) { diff --git a/hw/intc/loongson_liointc.c b/hw/intc/loongson_liointc.c index 30fb375b72..fbbfb57ee9 100644 --- a/hw/intc/loongson_liointc.c +++ b/hw/intc/loongson_liointc.c @@ -130,7 +130,7 @@ liointc_read(void *opaque, hwaddr addr, unsigned int size) if (addr >= R_PERCORE_ISR(0) && addr < R_PERCORE_ISR(NUM_CORES)) { - int core = (addr - R_PERCORE_ISR(0)) / 4; + int core = (addr - R_PERCORE_ISR(0)) / 8; r = p->per_core_isr[core]; goto out; } @@ -173,7 +173,7 @@ liointc_write(void *opaque, hwaddr addr, if (addr >= R_PERCORE_ISR(0) && addr < R_PERCORE_ISR(NUM_CORES)) { - int core = (addr - R_PERCORE_ISR(0)) / 4; + int core = (addr - R_PERCORE_ISR(0)) / 8; p->per_core_isr[core] = value; goto out; } diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 04e5323140..087a18d04d 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "qapi/visitor.h" #include "qemu/range.h" @@ -312,10 +313,12 @@ void ich9_generate_smi(void) cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); } +/* Returns -1 on error, IRQ number on success */ static int ich9_lpc_sci_irq(ICH9LPCState *lpc) { - switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] & - ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK) { + uint8_t sel = lpc->d.config[ICH9_LPC_ACPI_CTRL] & + ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK; + switch (sel) { case ICH9_LPC_ACPI_CTRL_9: return 9; case ICH9_LPC_ACPI_CTRL_10: @@ -328,6 +331,8 @@ static int ich9_lpc_sci_irq(ICH9LPCState *lpc) return 21; default: /* reserved */ + qemu_log_mask(LOG_GUEST_ERROR, + "ICH9 LPC: SCI IRQ SEL #%u is reserved\n", sel); break; } return -1; @@ -459,7 +464,7 @@ ich9_lpc_pmbase_sci_update(ICH9LPCState *lpc) { uint32_t pm_io_base = pci_get_long(lpc->d.config + ICH9_LPC_PMBASE); uint8_t acpi_cntl = pci_get_long(lpc->d.config + ICH9_LPC_ACPI_CTRL); - uint8_t new_gsi; + int new_gsi; if (acpi_cntl & ICH9_LPC_ACPI_CTRL_ACPI_EN) { pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK; @@ -470,6 +475,9 @@ ich9_lpc_pmbase_sci_update(ICH9LPCState *lpc) ich9_pm_iospace_update(&lpc->pm, pm_io_base); new_gsi = ich9_lpc_sci_irq(lpc); + if (new_gsi == -1) { + return; + } if (lpc->sci_level && new_gsi != lpc->sci_gsi) { qemu_set_irq(lpc->pm.irq, 0); lpc->sci_gsi = new_gsi; diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 4bc9cf0917..cf0627fd01 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -119,9 +119,10 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, /* start of address space indicates the maximum alignment we expect */ if (!QEMU_IS_ALIGNED(range_lob(&as), align)) { - error_setg(errp, "the alignment (0x%" PRIx64 ") is not supported", - align); - return 0; + warn_report("the alignment (0x%" PRIx64 ") exceeds the expected" + " maximum alignment, memory will get fragmented and not" + " all 'maxmem' might be usable for memory devices.", + align); } memory_device_check_addable(ms, size, &err); @@ -151,7 +152,7 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, return 0; } } else { - if (range_init(&new, range_lob(&as), size)) { + if (range_init(&new, QEMU_ALIGN_UP(range_lob(&as), align), size)) { error_setg(errp, "can't add memory device, device too big"); return 0; } @@ -258,7 +259,7 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, { const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); Error *local_err = NULL; - uint64_t addr, align; + uint64_t addr, align = 0; MemoryRegion *mr; mr = mdc->get_memory_region(md, &local_err); @@ -266,7 +267,14 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, goto out; } - align = legacy_align ? *legacy_align : memory_region_get_alignment(mr); + if (legacy_align) { + align = *legacy_align; + } else { + if (mdc->get_min_alignment) { + align = mdc->get_min_alignment(md); + } + align = MAX(align, memory_region_get_alignment(mr)); + } addr = mdc->get_addr(md); addr = memory_device_get_free_addr(ms, !addr ? NULL : &addr, align, memory_region_size(mr), &local_err); diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 67d39c56a4..8be70122f4 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -1,16 +1,3 @@ -config R4K - bool - select ISA_BUS - select SERIAL_ISA - select I8259 - select I8254 - select MC146818RTC - imply VGA_ISA - imply NE2000_ISA - select IDE_ISA - select PCKBD - select PFLASH_CFI01 - config MALTA bool select ISA_SUPERIO diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 74c18edbb3..3356d7a681 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/mips/cps.c b/hw/mips/cps.c index c624821315..962b1b0b87 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/mips/meson.build b/hw/mips/meson.build index 46294b7382..bcdf96be69 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -6,6 +6,5 @@ mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c')) mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt]) mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c')) -mips_ss.add(when: 'CONFIG_R4K', if_true: files('r4k.c')) hw_arch += {'mips': mips_ss} diff --git a/hw/mips/r4k.c b/hw/mips/r4k.c deleted file mode 100644 index 3830854342..0000000000 --- a/hw/mips/r4k.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * QEMU/MIPS pseudo-board - * - * emulates a simple machine with ISA-like bus. - * ISA IO space mapped to the 0x14000000 (PHYS) and - * ISA memory at the 0x10000000 (PHYS, 16Mb in size). - * All peripherial devices are attached to this "bus" with - * the standard PC ISA addresses. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/clock.h" -#include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" -#include "hw/intc/i8259.h" -#include "hw/char/serial.h" -#include "hw/isa/isa.h" -#include "net/net.h" -#include "hw/net/ne2000-isa.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/block/flash.h" -#include "qemu/log.h" -#include "hw/mips/bios.h" -#include "hw/ide.h" -#include "hw/ide/internal.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/rtc/mc146818rtc.h" -#include "hw/input/i8042.h" -#include "hw/timer/i8254.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "qemu/error-report.h" - -#define MAX_IDE_BUS 2 - -static const int ide_iobase[2] = { 0x1f0, 0x170 }; -static const int ide_iobase2[2] = { 0x3f6, 0x376 }; -static const int ide_irq[2] = { 14, 15 }; - -static ISADevice *pit; /* PIT i8254 */ - -/* i8254 PIT is attached to the IRQ0 at PIC i8259 */ - -static struct _loaderparams { - int ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -static void mips_qemu_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - if ((addr & 0xffff) == 0 && val == 42) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } else if ((addr & 0xffff) == 4 && val == 42) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - } -} - -static uint64_t mips_qemu_read(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static const MemoryRegionOps mips_qemu_ops = { - .read = mips_qemu_read, - .write = mips_qemu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -typedef struct ResetData { - MIPSCPU *cpu; - uint64_t vector; -} ResetData; - -static int64_t load_kernel(void) -{ - const size_t params_size = 264; - int64_t entry, kernel_high, initrd_size; - long kernel_size; - ram_addr_t initrd_offset; - uint32_t *params_buf; - int big_endian; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - kernel_size = load_elf(loaderparams.kernel_filename, NULL, - cpu_mips_kseg0_to_phys, NULL, - (uint64_t *)&entry, NULL, - (uint64_t *)&kernel_high, NULL, big_endian, - EM_MIPS, 1, 0); - if (kernel_size < 0) { - error_report("could not load kernel '%s': %s", - loaderparams.kernel_filename, - load_elf_strerror(kernel_size)); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size(loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE); - if (initrd_offset + initrd_size > ram_size) { - error_report("memory too small for initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, - ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - error_report("could not load initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - } - - /* Store command line. */ - params_buf = g_malloc(params_size); - - params_buf[0] = tswap32(ram_size); - params_buf[1] = tswap32(0x12345678); - - if (initrd_size > 0) { - snprintf((char *)params_buf + 8, 256, - "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", - cpu_mips_phys_to_kseg0(NULL, initrd_offset), - initrd_size, loaderparams.kernel_cmdline); - } else { - snprintf((char *)params_buf + 8, 256, - "%s", loaderparams.kernel_cmdline); - } - - rom_add_blob_fixed("params", params_buf, params_size, - 16 * MiB - params_size); - - g_free(params_buf); - return entry; -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUMIPSState *env = &s->cpu->env; - - cpu_reset(CPU(s->cpu)); - env->active_tc.PC = s->vector; -} - -static const int sector_len = 32 * KiB; -static -void mips_r4k_init(MachineState *machine) -{ - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - char *filename; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *bios; - MemoryRegion *iomem = g_new(MemoryRegion, 1); - MemoryRegion *isa_io = g_new(MemoryRegion, 1); - MemoryRegion *isa_mem = g_new(MemoryRegion, 1); - int bios_size; - Clock *cpuclk; - MIPSCPU *cpu; - CPUMIPSState *env; - ResetData *reset_info; - int i; - qemu_irq *i8259; - ISABus *isa_bus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *dinfo; - int be; - - cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); - clock_set_hz(cpuclk, 200000000); /* 200 MHz */ - - /* init CPUs */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); - env = &cpu->env; - - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->vector = env->active_tc.PC; - qemu_register_reset(main_cpu_reset, reset_info); - - /* allocate RAM */ - if (machine->ram_size > 256 * MiB) { - error_report("Too much memory for this machine: %" PRId64 "MB," - " maximum 256MB", ram_size / MiB); - exit(1); - } - memory_region_add_subregion(address_space_mem, 0, machine->ram); - - memory_region_init_io(iomem, NULL, &mips_qemu_ops, - NULL, "mips-qemu", 0x10000); - - memory_region_add_subregion(address_space_mem, 0x1fbf0000, iomem); - - /* - * Try to load a BIOS image. If this fails, we continue regardless, - * but initialize the hardware ourselves. When a kernel gets - * preloaded we also initialize the hardware, since the BIOS wasn't - * run. - */ - - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - dinfo = drive_get(IF_PFLASH, 0, 0); - if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) { - bios = g_new(MemoryRegion, 1); - memory_region_init_rom(bios, NULL, "mips_r4k.bios", BIOS_SIZE, - &error_fatal); - memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios); - - load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); - } else if (dinfo != NULL) { - uint32_t mips_rom = 0x00400000; - if (!pflash_cfi01_register(0x1fc00000, "mips_r4k.bios", mips_rom, - blk_by_legacy_dinfo(dinfo), - sector_len, 4, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - } - } else if (!qtest_enabled()) { - /* not fatal */ - warn_report("could not load MIPS bios '%s'", bios_name); - } - g_free(filename); - - if (kernel_filename) { - loaderparams.ram_size = machine->ram_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - reset_info->vector = load_kernel(); - } - - /* Init CPU internal devices */ - cpu_mips_irq_init_cpu(cpu); - cpu_mips_clock_init(cpu); - - /* ISA bus: IO space at 0x14000000, mem space at 0x10000000 */ - memory_region_init_alias(isa_io, NULL, "isa-io", - get_system_io(), 0, 0x00010000); - memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000); - memory_region_add_subregion(get_system_memory(), 0x14000000, isa_io); - memory_region_add_subregion(get_system_memory(), 0x10000000, isa_mem); - isa_bus = isa_bus_new(NULL, isa_mem, get_system_io(), &error_abort); - - /* The PIC is attached to the MIPS CPU INT0 pin */ - i8259 = i8259_init(isa_bus, env->irq[2]); - isa_bus_irqs(isa_bus, i8259); - - mc146818_rtc_init(isa_bus, 2000, NULL); - - pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - - serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); - - isa_vga_init(isa_bus); - - if (nd_table[0].used) { - isa_ne2000_init(isa_bus, 0x300, 9, &nd_table[0]); - } - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) - isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], - hd[MAX_IDE_DEVS * i], - hd[MAX_IDE_DEVS * i + 1]); - - isa_create_simple(isa_bus, TYPE_I8042); -} - -static void mips_machine_init(MachineClass *mc) -{ - mc->deprecation_reason = "use malta machine type instead"; - mc->desc = "mips r4k platform"; - mc->init = mips_r4k_init; - mc->block_default_type = IF_IDE; -#ifdef TARGET_MIPS64 - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); -#else - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); -#endif - mc->default_ram_id = "mips_r4k.ram"; -} - -DEFINE_MACHINE("mips", mips_machine_init) diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 7c11fb3d44..4a94c87054 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index 3540985258..133399598f 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 3b321421b6..38d5901a45 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 227075103e..12a012d9dd 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1522,12 +1522,6 @@ int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp) int i; spapr->htab = qemu_memalign(size, size); - if (!spapr->htab) { - error_setg_errno(errp, errno, - "Could not allocate HPT of order %d", shift); - return -ENOMEM; - } - memset(spapr->htab, 0, size); spapr->htab_shift = shift; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 607740150f..1d8e8e6a88 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -360,7 +360,7 @@ static void *hpt_prepare_thread(void *opaque) SpaprPendingHpt *pending = opaque; size_t size = 1ULL << pending->shift; - pending->hpt = qemu_memalign(size, size); + pending->hpt = qemu_try_memalign(size, size); if (pending->hpt) { memset(pending->hpt, 0, size); pending->ret = H_SUCCESS; diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 8b30906e50..6a3d39793b 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -988,16 +988,18 @@ static int save_opt_one(void *opaque, if (ret < 0) { error_setg(errp, "Unable to read from %s: %s", value, strerror(errno)); + qemu_close(fd); return -1; } if (memchr(buf, '\0', ret)) { error_setg(errp, "NUL in OEM strings value in %s", value); + qemu_close(fd); return -1; } g_byte_array_append(data, (guint8 *)buf, ret); } - close(fd); + qemu_close(fd); *opt->dest = g_renew(char *, *opt->dest, (*opt->ndest) + 1); (*opt->dest)[*opt->ndest] = (char *)g_byte_array_free(data, FALSE); diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index b1622b7c7f..19e1933f04 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -20,85 +20,77 @@ #include "chardev/char-serial.h" #include "chardev/char-fe.h" #include "qom/object.h" +#include "trace.h" -//#define DEBUG_Serial - -#ifdef DEBUG_Serial -#define DPRINTF(fmt, ...) \ -do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif #define RECV_BUF (512 - (2 * 8)) /* Commands */ -#define FTDI_RESET 0 -#define FTDI_SET_MDM_CTRL 1 -#define FTDI_SET_FLOW_CTRL 2 -#define FTDI_SET_BAUD 3 -#define FTDI_SET_DATA 4 -#define FTDI_GET_MDM_ST 5 -#define FTDI_SET_EVENT_CHR 6 -#define FTDI_SET_ERROR_CHR 7 -#define FTDI_SET_LATENCY 9 -#define FTDI_GET_LATENCY 10 - -#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) -#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) +#define FTDI_RESET 0 +#define FTDI_SET_MDM_CTRL 1 +#define FTDI_SET_FLOW_CTRL 2 +#define FTDI_SET_BAUD 3 +#define FTDI_SET_DATA 4 +#define FTDI_GET_MDM_ST 5 +#define FTDI_SET_EVENT_CHR 6 +#define FTDI_SET_ERROR_CHR 7 +#define FTDI_SET_LATENCY 9 +#define FTDI_GET_LATENCY 10 /* RESET */ -#define FTDI_RESET_SIO 0 -#define FTDI_RESET_RX 1 -#define FTDI_RESET_TX 2 +#define FTDI_RESET_SIO 0 +#define FTDI_RESET_RX 1 +#define FTDI_RESET_TX 2 /* SET_MDM_CTRL */ -#define FTDI_DTR 1 -#define FTDI_SET_DTR (FTDI_DTR << 8) -#define FTDI_RTS 2 -#define FTDI_SET_RTS (FTDI_RTS << 8) +#define FTDI_DTR 1 +#define FTDI_SET_DTR (FTDI_DTR << 8) +#define FTDI_RTS 2 +#define FTDI_SET_RTS (FTDI_RTS << 8) /* SET_FLOW_CTRL */ -#define FTDI_RTS_CTS_HS 1 -#define FTDI_DTR_DSR_HS 2 -#define FTDI_XON_XOFF_HS 4 +#define FTDI_NO_HS 0 +#define FTDI_RTS_CTS_HS 1 +#define FTDI_DTR_DSR_HS 2 +#define FTDI_XON_XOFF_HS 4 /* SET_DATA */ -#define FTDI_PARITY (0x7 << 8) -#define FTDI_ODD (0x1 << 8) -#define FTDI_EVEN (0x2 << 8) -#define FTDI_MARK (0x3 << 8) -#define FTDI_SPACE (0x4 << 8) +#define FTDI_PARITY (0x7 << 8) +#define FTDI_ODD (0x1 << 8) +#define FTDI_EVEN (0x2 << 8) +#define FTDI_MARK (0x3 << 8) +#define FTDI_SPACE (0x4 << 8) -#define FTDI_STOP (0x3 << 11) -#define FTDI_STOP1 (0x0 << 11) -#define FTDI_STOP15 (0x1 << 11) -#define FTDI_STOP2 (0x2 << 11) +#define FTDI_STOP (0x3 << 11) +#define FTDI_STOP1 (0x0 << 11) +#define FTDI_STOP15 (0x1 << 11) +#define FTDI_STOP2 (0x2 << 11) /* GET_MDM_ST */ /* TODO: should be sent every 40ms */ -#define FTDI_CTS (1<<4) // CTS line status -#define FTDI_DSR (1<<5) // DSR line status -#define FTDI_RI (1<<6) // RI line status -#define FTDI_RLSD (1<<7) // Receive Line Signal Detect +#define FTDI_CTS (1 << 4) /* CTS line status */ +#define FTDI_DSR (1 << 5) /* DSR line status */ +#define FTDI_RI (1 << 6) /* RI line status */ +#define FTDI_RLSD (1 << 7) /* Receive Line Signal Detect */ /* Status */ -#define FTDI_DR (1<<0) // Data Ready -#define FTDI_OE (1<<1) // Overrun Err -#define FTDI_PE (1<<2) // Parity Err -#define FTDI_FE (1<<3) // Framing Err -#define FTDI_BI (1<<4) // Break Interrupt -#define FTDI_THRE (1<<5) // Transmitter Holding Register -#define FTDI_TEMT (1<<6) // Transmitter Empty -#define FTDI_FIFO (1<<7) // Error in FIFO +#define FTDI_DR (1 << 0) /* Data Ready */ +#define FTDI_OE (1 << 1) /* Overrun Err */ +#define FTDI_PE (1 << 2) /* Parity Err */ +#define FTDI_FE (1 << 3) /* Framing Err */ +#define FTDI_BI (1 << 4) /* Break Interrupt */ +#define FTDI_THRE (1 << 5) /* Transmitter Holding Register */ +#define FTDI_TEMT (1 << 6) /* Transmitter Empty */ +#define FTDI_FIFO (1 << 7) /* Error in FIFO */ struct USBSerialState { USBDevice dev; + USBEndpoint *intr; uint8_t recv_buf[RECV_BUF]; uint16_t recv_ptr; @@ -106,6 +98,10 @@ struct USBSerialState { uint8_t event_chr; uint8_t error_chr; uint8_t event_trigger; + bool always_plugged; + uint8_t flow_control; + uint8_t xon; + uint8_t xoff; QEMUSerialSetParams params; int latency; /* ms */ CharBackend cs; @@ -189,21 +185,44 @@ static const USBDesc desc_braille = { .str = desc_strings, }; +static void usb_serial_set_flow_control(USBSerialState *s, + uint8_t flow_control) +{ + USBDevice *dev = USB_DEVICE(s); + USBBus *bus = usb_bus_from_device(dev); + + /* TODO: ioctl */ + s->flow_control = flow_control; + trace_usb_serial_set_flow_control(bus->busnr, dev->addr, flow_control); +} + +static void usb_serial_set_xonxoff(USBSerialState *s, int xonxoff) +{ + USBDevice *dev = USB_DEVICE(s); + USBBus *bus = usb_bus_from_device(dev); + + s->xon = xonxoff & 0xff; + s->xoff = (xonxoff >> 8) & 0xff; + + trace_usb_serial_set_xonxoff(bus->busnr, dev->addr, s->xon, s->xoff); +} + static void usb_serial_reset(USBSerialState *s) { - /* TODO: Set flow control to none */ s->event_chr = 0x0d; s->event_trigger = 0; s->recv_ptr = 0; s->recv_used = 0; /* TODO: purge in char driver */ + usb_serial_set_flow_control(s, FTDI_NO_HS); } static void usb_serial_handle_reset(USBDevice *dev) { - USBSerialState *s = (USBSerialState *)dev; + USBSerialState *s = USB_SERIAL(dev); + USBBus *bus = usb_bus_from_device(dev); - DPRINTF("Reset\n"); + trace_usb_serial_reset(bus->busnr, dev->addr); usb_serial_reset(s); /* TODO: Reset char device, send BREAK? */ @@ -216,29 +235,36 @@ static uint8_t usb_get_modem_lines(USBSerialState *s) if (qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) { - return FTDI_CTS|FTDI_DSR|FTDI_RLSD; + return FTDI_CTS | FTDI_DSR | FTDI_RLSD; } ret = 0; - if (flags & CHR_TIOCM_CTS) + if (flags & CHR_TIOCM_CTS) { ret |= FTDI_CTS; - if (flags & CHR_TIOCM_DSR) + } + if (flags & CHR_TIOCM_DSR) { ret |= FTDI_DSR; - if (flags & CHR_TIOCM_RI) + } + if (flags & CHR_TIOCM_RI) { ret |= FTDI_RI; - if (flags & CHR_TIOCM_CAR) + } + if (flags & CHR_TIOCM_CAR) { ret |= FTDI_RLSD; + } return ret; } static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) + int request, int value, int index, + int length, uint8_t *data) { - USBSerialState *s = (USBSerialState *)dev; + USBSerialState *s = USB_SERIAL(dev); + USBBus *bus = usb_bus_from_device(dev); int ret; - DPRINTF("got control %x, value %x\n",request, value); + trace_usb_serial_handle_control(bus->busnr, dev->addr, request, value); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return; @@ -248,8 +274,8 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: break; - /* Class specific requests. */ - case DeviceOutVendor | FTDI_RESET: + /* Class specific requests. */ + case VendorDeviceOutRequest | FTDI_RESET: switch (value) { case FTDI_RESET_SIO: usb_serial_reset(s); @@ -264,96 +290,131 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, break; } break; - case DeviceOutVendor | FTDI_SET_MDM_CTRL: + case VendorDeviceOutRequest | FTDI_SET_MDM_CTRL: { static int flags; qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); if (value & FTDI_SET_RTS) { - if (value & FTDI_RTS) + if (value & FTDI_RTS) { flags |= CHR_TIOCM_RTS; - else + } else { flags &= ~CHR_TIOCM_RTS; + } } if (value & FTDI_SET_DTR) { - if (value & FTDI_DTR) + if (value & FTDI_DTR) { flags |= CHR_TIOCM_DTR; - else + } else { flags &= ~CHR_TIOCM_DTR; + } } qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); break; } - case DeviceOutVendor | FTDI_SET_FLOW_CTRL: - /* TODO: ioctl */ + case VendorDeviceOutRequest | FTDI_SET_FLOW_CTRL: { + uint8_t flow_control = index >> 8; + + usb_serial_set_flow_control(s, flow_control); + if (flow_control & FTDI_XON_XOFF_HS) { + usb_serial_set_xonxoff(s, value); + } break; - case DeviceOutVendor | FTDI_SET_BAUD: { + } + case VendorDeviceOutRequest | FTDI_SET_BAUD: { static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) | ((index & 1) << 2)]; int divisor = value & 0x3fff; /* chip special cases */ - if (divisor == 1 && subdivisor8 == 0) + if (divisor == 1 && subdivisor8 == 0) { subdivisor8 = 4; - if (divisor == 0 && subdivisor8 == 0) + } + if (divisor == 0 && subdivisor8 == 0) { divisor = 1; + } s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); + trace_usb_serial_set_baud(bus->busnr, dev->addr, s->params.speed); qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); break; } - case DeviceOutVendor | FTDI_SET_DATA: + case VendorDeviceOutRequest | FTDI_SET_DATA: + switch (value & 0xff) { + case 7: + s->params.data_bits = 7; + break; + case 8: + s->params.data_bits = 8; + break; + default: + /* + * According to a comment in Linux's ftdi_sio.c original FTDI + * chips fall back to 8 data bits for unsupported data_bits + */ + trace_usb_serial_unsupported_data_bits(bus->busnr, dev->addr, + value & 0xff); + s->params.data_bits = 8; + } + switch (value & FTDI_PARITY) { - case 0: - s->params.parity = 'N'; - break; - case FTDI_ODD: - s->params.parity = 'O'; - break; - case FTDI_EVEN: - s->params.parity = 'E'; - break; - default: - DPRINTF("unsupported parity %d\n", value & FTDI_PARITY); - goto fail; + case 0: + s->params.parity = 'N'; + break; + case FTDI_ODD: + s->params.parity = 'O'; + break; + case FTDI_EVEN: + s->params.parity = 'E'; + break; + default: + trace_usb_serial_unsupported_parity(bus->busnr, dev->addr, + value & FTDI_PARITY); + goto fail; } + switch (value & FTDI_STOP) { - case FTDI_STOP1: - s->params.stop_bits = 1; - break; - case FTDI_STOP2: - s->params.stop_bits = 2; - break; - default: - DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); - goto fail; + case FTDI_STOP1: + s->params.stop_bits = 1; + break; + case FTDI_STOP2: + s->params.stop_bits = 2; + break; + default: + trace_usb_serial_unsupported_stopbits(bus->busnr, dev->addr, + value & FTDI_STOP); + goto fail; } + + trace_usb_serial_set_data(bus->busnr, dev->addr, s->params.parity, + s->params.data_bits, s->params.stop_bits); qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); /* TODO: TX ON/OFF */ break; - case DeviceInVendor | FTDI_GET_MDM_ST: + case VendorDeviceRequest | FTDI_GET_MDM_ST: data[0] = usb_get_modem_lines(s) | 1; data[1] = FTDI_THRE | FTDI_TEMT; p->actual_length = 2; break; - case DeviceOutVendor | FTDI_SET_EVENT_CHR: + case VendorDeviceOutRequest | FTDI_SET_EVENT_CHR: /* TODO: handle it */ s->event_chr = value; break; - case DeviceOutVendor | FTDI_SET_ERROR_CHR: + case VendorDeviceOutRequest | FTDI_SET_ERROR_CHR: /* TODO: handle it */ s->error_chr = value; break; - case DeviceOutVendor | FTDI_SET_LATENCY: + case VendorDeviceOutRequest | FTDI_SET_LATENCY: s->latency = value; break; - case DeviceInVendor | FTDI_GET_LATENCY: + case VendorDeviceRequest | FTDI_GET_LATENCY: data[0] = s->latency; p->actual_length = 1; break; default: fail: - DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); + trace_usb_serial_unsupported_control(bus->busnr, dev->addr, request, + value); p->status = USB_RET_STALL; break; } @@ -416,32 +477,37 @@ static void usb_serial_token_in(USBSerialState *s, USBPacket *p) static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) { - USBSerialState *s = (USBSerialState *)dev; + USBSerialState *s = USB_SERIAL(dev); + USBBus *bus = usb_bus_from_device(dev); uint8_t devep = p->ep->nr; struct iovec *iov; int i; switch (p->pid) { case USB_TOKEN_OUT: - if (devep != 2) + if (devep != 2) { goto fail; + } for (i = 0; i < p->iov.niov; i++) { iov = p->iov.iov + i; - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len); } p->actual_length = p->iov.size; break; case USB_TOKEN_IN: - if (devep != 1) + if (devep != 1) { goto fail; + } usb_serial_token_in(s, p); break; default: - DPRINTF("Bad token\n"); + trace_usb_serial_bad_token(bus->busnr, dev->addr); fail: p->status = USB_RET_STALL; break; @@ -464,21 +530,24 @@ static void usb_serial_read(void *opaque, const uint8_t *buf, int size) int first_size, start; /* room in the buffer? */ - if (size > (RECV_BUF - s->recv_used)) + if (size > (RECV_BUF - s->recv_used)) { size = RECV_BUF - s->recv_used; + } start = s->recv_ptr + s->recv_used; if (start < RECV_BUF) { /* copy data to end of buffer */ first_size = RECV_BUF - start; - if (first_size > size) + if (first_size > size) { first_size = size; + } memcpy(s->recv_buf + start, buf, first_size); /* wrap around to front if needed */ - if (size > first_size) + if (size > first_size) { memcpy(s->recv_buf, buf + first_size, size - first_size); + } } else { start -= RECV_BUF; memcpy(s->recv_buf + start, buf, size); @@ -493,23 +562,23 @@ static void usb_serial_event(void *opaque, QEMUChrEvent event) USBSerialState *s = opaque; switch (event) { - case CHR_EVENT_BREAK: - s->event_trigger |= FTDI_BI; - break; - case CHR_EVENT_OPENED: - if (!s->dev.attached) { - usb_device_attach(&s->dev, &error_abort); - } - break; - case CHR_EVENT_CLOSED: - if (s->dev.attached) { - usb_device_detach(&s->dev); - } - break; - case CHR_EVENT_MUX_IN: - case CHR_EVENT_MUX_OUT: - /* Ignore */ - break; + case CHR_EVENT_BREAK: + s->event_trigger |= FTDI_BI; + break; + case CHR_EVENT_OPENED: + if (!s->always_plugged && !s->dev.attached) { + usb_device_attach(&s->dev, &error_abort); + } + break; + case CHR_EVENT_CLOSED: + if (!s->always_plugged && s->dev.attached) { + usb_device_detach(&s->dev); + } + break; + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -537,7 +606,8 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) usb_serial_event, NULL, s, NULL, true); usb_serial_handle_reset(dev); - if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { + if ((s->always_plugged || qemu_chr_fe_backend_open(&s->cs)) && + !dev->attached) { usb_device_attach(dev, &error_abort); } s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); @@ -549,8 +619,9 @@ static USBDevice *usb_braille_init(const char *unused) Chardev *cdrv; cdrv = qemu_chr_new("braille", "braille", NULL); - if (!cdrv) + if (!cdrv) { return NULL; + } dev = usb_new("usb-braille"); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); @@ -564,6 +635,7 @@ static const VMStateDescription vmstate_usb_serial = { static Property serial_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), + DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 72e4298780..a3292d4624 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -320,3 +320,16 @@ usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev % usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" usb_host_remote_wakeup_removed(int bus, int addr) "dev %d:%d" + +# dev-serial.c +usb_serial_reset(int bus, int addr) "dev %d:%u reset" +usb_serial_handle_control(int bus, int addr, int request, int value) "dev %d:%u got control 0x%x, value 0x%x" +usb_serial_unsupported_parity(int bus, int addr, int value) "dev %d:%u unsupported parity %d" +usb_serial_unsupported_stopbits(int bus, int addr, int value) "dev %d:%u unsupported stop bits %d" +usb_serial_unsupported_control(int bus, int addr, int request, int value) "dev %d:%u got unsupported/bogus control 0x%x, value 0x%x" +usb_serial_unsupported_data_bits(int bus, int addr, int value) "dev %d:%u unsupported data bits %d, falling back to 8" +usb_serial_bad_token(int bus, int addr) "dev %d:%u bad token" +usb_serial_set_baud(int bus, int addr, int baud) "dev %d:%u baud rate %d" +usb_serial_set_data(int bus, int addr, int parity, int data, int stop) "dev %d:%u parity %c, data bits %d, stop bits %d" +usb_serial_set_flow_control(int bus, int addr, int index) "dev %d:%u flow control %d" +usb_serial_set_xonxoff(int bus, int addr, uint8_t xon, uint8_t xoff) "dev %d:%u xon 0x%x xoff 0x%x" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index e18ea2cf91..c1fdbf17f2 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -789,6 +789,14 @@ static void vfio_listener_region_add(MemoryListener *listener, int128_get64(llend), iommu_idx); + ret = memory_region_iommu_set_page_size_mask(giommu->iommu, + container->pgsizes, + &err); + if (ret) { + g_free(giommu); + goto fail; + } + ret = memory_region_register_iommu_notifier(section->mr, &giommu->n, &err); if (ret) { @@ -942,6 +950,17 @@ static void vfio_listener_region_del(MemoryListener *listener, } if (try_unmap) { + if (int128_eq(llsize, int128_2_64())) { + /* The unmap ioctl doesn't accept a full 64-bit span. */ + llsize = int128_rshift(llsize, 1); + ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); + if (ret) { + error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%m)", + container, iova, int128_get64(llsize), ret); + } + iova += int128_get64(llsize); + } ret = vfio_dma_unmap(container, iova, int128_get64(llsize), NULL); if (ret) { error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index cf1e59de30..2060a144a2 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -106,6 +106,12 @@ virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d" virtio_iommu_translate_out(uint64_t virt_addr, uint64_t phys_addr, uint32_t sid) "0x%"PRIx64" -> 0x%"PRIx64 " for sid=%d" virtio_iommu_report_fault(uint8_t reason, uint32_t flags, uint32_t endpoint, uint64_t addr) "FAULT reason=%d flags=%d endpoint=%d address =0x%"PRIx64 virtio_iommu_fill_resv_property(uint32_t devid, uint8_t subtype, uint64_t start, uint64_t end) "dev= %d, type=%d start=0x%"PRIx64" end=0x%"PRIx64 +virtio_iommu_notify_map(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64" flags=%d" +virtio_iommu_notify_unmap(const char *name, uint64_t virt_start, uint64_t virt_end) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 +virtio_iommu_remap(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64 +virtio_iommu_set_page_size_mask(const char *name, uint64_t old, uint64_t new) "mr=%s old_mask=0x%"PRIx64" new_mask=0x%"PRIx64 +virtio_iommu_notify_flag_add(const char *name) "add notifier to mr %s" +virtio_iommu_notify_flag_del(const char *name) "del notifier from mr %s" # virtio-mem.c virtio_mem_send_response(uint16_t type) "type=%" PRIu16 diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 88c8ecc9e0..222bbcc62d 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -257,7 +257,7 @@ static int vhost_kernel_send_device_iotlb_msg(struct vhost_dev *dev, struct vhost_iotlb_msg *imsg) { if (dev->backend_cap & (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2)) { - struct vhost_msg_v2 msg; + struct vhost_msg_v2 msg = {}; msg.type = VHOST_IOTLB_MSG_V2; msg.iotlb = *imsg; @@ -267,7 +267,7 @@ static int vhost_kernel_send_device_iotlb_msg(struct vhost_dev *dev, return -EFAULT; } } else { - struct vhost_msg msg; + struct vhost_msg msg = {}; msg.type = VHOST_IOTLB_MSG; msg.iotlb = *imsg; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index f2482378c6..614ccc2bcb 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1645,15 +1645,17 @@ int vhost_dev_load_inflight(struct vhost_inflight *inflight, QEMUFile *f) return 0; } -int vhost_dev_prepare_inflight(struct vhost_dev *hdev) +int vhost_dev_prepare_inflight(struct vhost_dev *hdev, VirtIODevice *vdev) { int r; - + if (hdev->vhost_ops->vhost_get_inflight_fd == NULL || hdev->vhost_ops->vhost_set_inflight_fd == NULL) { return 0; } - + + hdev->vdev = vdev; + r = vhost_dev_set_features(hdev, hdev->log_enabled); if (r < 0) { VHOST_OPS_DEBUG("vhost_dev_prepare_inflight failed"); diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 21ec63b108..fc5c75d693 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -49,6 +49,7 @@ typedef struct VirtIOIOMMUDomain { typedef struct VirtIOIOMMUEndpoint { uint32_t id; VirtIOIOMMUDomain *domain; + IOMMUMemoryRegion *iommu_mr; QLIST_ENTRY(VirtIOIOMMUEndpoint) next; } VirtIOIOMMUEndpoint; @@ -101,7 +102,7 @@ static IOMMUMemoryRegion *virtio_iommu_mr(VirtIOIOMMU *s, uint32_t sid) bus_n = PCI_BUS_NUM(sid); iommu_pci_bus = iommu_find_iommu_pcibus(s, bus_n); if (iommu_pci_bus) { - devfn = sid & PCI_DEVFN_MAX; + devfn = sid & (PCI_DEVFN_MAX - 1); dev = iommu_pci_bus->pbdev[devfn]; if (dev) { return &dev->iommu_mr; @@ -124,11 +125,84 @@ static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data) } } +static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start, + hwaddr virt_end, hwaddr paddr, + uint32_t flags) +{ + IOMMUTLBEntry entry; + IOMMUAccessFlags perm = IOMMU_ACCESS_FLAG(flags & VIRTIO_IOMMU_MAP_F_READ, + flags & VIRTIO_IOMMU_MAP_F_WRITE); + + if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_MAP) || + (flags & VIRTIO_IOMMU_MAP_F_MMIO) || !perm) { + return; + } + + trace_virtio_iommu_notify_map(mr->parent_obj.name, virt_start, virt_end, + paddr, perm); + + entry.target_as = &address_space_memory; + entry.addr_mask = virt_end - virt_start; + entry.iova = virt_start; + entry.perm = perm; + entry.translated_addr = paddr; + + memory_region_notify_iommu(mr, 0, entry); +} + +static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start, + hwaddr virt_end) +{ + IOMMUTLBEntry entry; + + if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) { + return; + } + + trace_virtio_iommu_notify_unmap(mr->parent_obj.name, virt_start, virt_end); + + entry.target_as = &address_space_memory; + entry.addr_mask = virt_end - virt_start; + entry.iova = virt_start; + entry.perm = IOMMU_NONE; + entry.translated_addr = 0; + + memory_region_notify_iommu(mr, 0, entry); +} + +static gboolean virtio_iommu_notify_unmap_cb(gpointer key, gpointer value, + gpointer data) +{ + VirtIOIOMMUInterval *interval = (VirtIOIOMMUInterval *) key; + IOMMUMemoryRegion *mr = (IOMMUMemoryRegion *) data; + + virtio_iommu_notify_unmap(mr, interval->low, interval->high); + + return false; +} + +static gboolean virtio_iommu_notify_map_cb(gpointer key, gpointer value, + gpointer data) +{ + VirtIOIOMMUMapping *mapping = (VirtIOIOMMUMapping *) value; + VirtIOIOMMUInterval *interval = (VirtIOIOMMUInterval *) key; + IOMMUMemoryRegion *mr = (IOMMUMemoryRegion *) data; + + virtio_iommu_notify_map(mr, interval->low, interval->high, + mapping->phys_addr, mapping->flags); + + return false; +} + static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) { + VirtIOIOMMUDomain *domain = ep->domain; + if (!ep->domain) { return; } + g_tree_foreach(domain->mappings, virtio_iommu_notify_unmap_cb, + ep->iommu_mr); QLIST_REMOVE(ep, next); ep->domain = NULL; } @@ -137,16 +211,19 @@ static VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, uint32_t ep_id) { VirtIOIOMMUEndpoint *ep; + IOMMUMemoryRegion *mr; ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id)); if (ep) { return ep; } - if (!virtio_iommu_mr(s, ep_id)) { + mr = virtio_iommu_mr(s, ep_id); + if (!mr) { return NULL; } ep = g_malloc0(sizeof(*ep)); ep->id = ep_id; + ep->iommu_mr = mr; trace_virtio_iommu_get_endpoint(ep_id); g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep); return ep; @@ -268,6 +345,10 @@ static int virtio_iommu_attach(VirtIOIOMMU *s, ep->domain = domain; + /* Replay domain mappings on the associated memory region */ + g_tree_foreach(domain->mappings, virtio_iommu_notify_map_cb, + ep->iommu_mr); + return VIRTIO_IOMMU_S_OK; } @@ -311,6 +392,7 @@ static int virtio_iommu_map(VirtIOIOMMU *s, VirtIOIOMMUDomain *domain; VirtIOIOMMUInterval *interval; VirtIOIOMMUMapping *mapping; + VirtIOIOMMUEndpoint *ep; if (flags & ~VIRTIO_IOMMU_MAP_F_MASK) { return VIRTIO_IOMMU_S_INVAL; @@ -340,6 +422,11 @@ static int virtio_iommu_map(VirtIOIOMMU *s, g_tree_insert(domain->mappings, interval, mapping); + QLIST_FOREACH(ep, &domain->endpoint_list, next) { + virtio_iommu_notify_map(ep->iommu_mr, virt_start, virt_end, phys_start, + flags); + } + return VIRTIO_IOMMU_S_OK; } @@ -352,6 +439,7 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s, VirtIOIOMMUMapping *iter_val; VirtIOIOMMUInterval interval, *iter_key; VirtIOIOMMUDomain *domain; + VirtIOIOMMUEndpoint *ep; int ret = VIRTIO_IOMMU_S_OK; trace_virtio_iommu_unmap(domain_id, virt_start, virt_end); @@ -369,6 +457,10 @@ static int virtio_iommu_unmap(VirtIOIOMMU *s, uint64_t current_high = iter_key->high; if (interval.low <= current_low && interval.high >= current_high) { + QLIST_FOREACH(ep, &domain->endpoint_list, next) { + virtio_iommu_notify_unmap(ep->iommu_mr, current_low, + current_high); + } g_tree_remove(domain->mappings, iter_key); trace_virtio_iommu_unmap_done(domain_id, current_low, current_high); } else { @@ -755,6 +847,107 @@ static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) return (ua > ub) - (ua < ub); } +static gboolean virtio_iommu_remap(gpointer key, gpointer value, gpointer data) +{ + VirtIOIOMMUMapping *mapping = (VirtIOIOMMUMapping *) value; + VirtIOIOMMUInterval *interval = (VirtIOIOMMUInterval *) key; + IOMMUMemoryRegion *mr = (IOMMUMemoryRegion *) data; + + trace_virtio_iommu_remap(mr->parent_obj.name, interval->low, interval->high, + mapping->phys_addr); + virtio_iommu_notify_map(mr, interval->low, interval->high, + mapping->phys_addr, mapping->flags); + return false; +} + +static void virtio_iommu_replay(IOMMUMemoryRegion *mr, IOMMUNotifier *n) +{ + IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); + VirtIOIOMMU *s = sdev->viommu; + uint32_t sid; + VirtIOIOMMUEndpoint *ep; + + sid = virtio_iommu_get_bdf(sdev); + + qemu_mutex_lock(&s->mutex); + + if (!s->endpoints) { + goto unlock; + } + + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(sid)); + if (!ep || !ep->domain) { + goto unlock; + } + + g_tree_foreach(ep->domain->mappings, virtio_iommu_remap, mr); + +unlock: + qemu_mutex_unlock(&s->mutex); +} + +static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr, + IOMMUNotifierFlag old, + IOMMUNotifierFlag new, + Error **errp) +{ + if (old == IOMMU_NOTIFIER_NONE) { + trace_virtio_iommu_notify_flag_add(iommu_mr->parent_obj.name); + } else if (new == IOMMU_NOTIFIER_NONE) { + trace_virtio_iommu_notify_flag_del(iommu_mr->parent_obj.name); + } + return 0; +} + +/* + * The default mask (TARGET_PAGE_MASK) is the smallest supported guest granule, + * for example 0xfffffffffffff000. When an assigned device has page size + * restrictions due to the hardware IOMMU configuration, apply this restriction + * to the mask. + */ +static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, + uint64_t new_mask, + Error **errp) +{ + IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); + VirtIOIOMMU *s = sdev->viommu; + uint64_t cur_mask = s->config.page_size_mask; + + trace_virtio_iommu_set_page_size_mask(mr->parent_obj.name, cur_mask, + new_mask); + + if ((cur_mask & new_mask) == 0) { + error_setg(errp, "virtio-iommu page mask 0x%"PRIx64 + " is incompatible with mask 0x%"PRIx64, cur_mask, new_mask); + return -1; + } + + /* + * After the machine is finalized, we can't change the mask anymore. If by + * chance the hotplugged device supports the same granule, we can still + * accept it. Having a different masks is possible but the guest will use + * sub-optimal block sizes, so warn about it. + */ + if (qdev_hotplug) { + int new_granule = ctz64(new_mask); + int cur_granule = ctz64(cur_mask); + + if (new_granule != cur_granule) { + error_setg(errp, "virtio-iommu page mask 0x%"PRIx64 + " is incompatible with mask 0x%"PRIx64, cur_mask, + new_mask); + return -1; + } else if (new_mask != cur_mask) { + warn_report("virtio-iommu page mask 0x%"PRIx64 + " does not match 0x%"PRIx64, cur_mask, new_mask); + } + return 0; + } + + s->config.page_size_mask &= new_mask; + return 0; +} + static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -910,9 +1103,14 @@ static gboolean reconstruct_endpoints(gpointer key, gpointer value, VirtIOIOMMU *s = (VirtIOIOMMU *)data; VirtIOIOMMUDomain *d = (VirtIOIOMMUDomain *)value; VirtIOIOMMUEndpoint *iter; + IOMMUMemoryRegion *mr; QLIST_FOREACH(iter, &d->endpoint_list, next) { + mr = virtio_iommu_mr(s, iter->id); + assert(mr); + iter->domain = d; + iter->iommu_mr = mr; g_tree_insert(s->endpoints, GUINT_TO_POINTER(iter->id), iter); } return false; /* continue the domain traversal */ @@ -979,6 +1177,9 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass, IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); imrc->translate = virtio_iommu_translate; + imrc->replay = virtio_iommu_replay; + imrc->notify_flag_changed = virtio_iommu_notify_flag_changed; + imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask; } static const TypeInfo virtio_iommu_info = { diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index 913f4a3326..fa5395cd88 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -76,6 +76,12 @@ static void virtio_mem_pci_fill_device_info(const MemoryDeviceState *md, info->type = MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM; } +static uint64_t virtio_mem_pci_get_min_alignment(const MemoryDeviceState *md) +{ + return object_property_get_uint(OBJECT(md), VIRTIO_MEM_BLOCK_SIZE_PROP, + &error_abort); +} + static void virtio_mem_pci_size_change_notify(Notifier *notifier, void *data) { VirtIOMEMPCI *pci_mem = container_of(notifier, VirtIOMEMPCI, @@ -110,6 +116,7 @@ static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) mdc->get_plugged_size = virtio_mem_pci_get_plugged_size; mdc->get_memory_region = virtio_mem_pci_get_memory_region; mdc->fill_device_info = virtio_mem_pci_fill_device_info; + mdc->get_min_alignment = virtio_mem_pci_get_min_alignment; } static void virtio_mem_pci_instance_init(Object *obj) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 7c8ca9f28b..655824ff81 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -33,10 +33,83 @@ #include "trace.h" /* - * Use QEMU_VMALLOC_ALIGN, so no THP will have to be split when unplugging - * memory (e.g., 2MB on x86_64). + * Let's not allow blocks smaller than 1 MiB, for example, to keep the tracking + * bitmap small. */ -#define VIRTIO_MEM_MIN_BLOCK_SIZE ((uint32_t)QEMU_VMALLOC_ALIGN) +#define VIRTIO_MEM_MIN_BLOCK_SIZE ((uint32_t)(1 * MiB)) + +#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \ + defined(__powerpc64__) +#define VIRTIO_MEM_DEFAULT_THP_SIZE ((uint32_t)(2 * MiB)) +#else + /* fallback to 1 MiB (e.g., the THP size on s390x) */ +#define VIRTIO_MEM_DEFAULT_THP_SIZE VIRTIO_MEM_MIN_BLOCK_SIZE +#endif + +/* + * We want to have a reasonable default block size such that + * 1. We avoid splitting THPs when unplugging memory, which degrades + * performance. + * 2. We avoid placing THPs for plugged blocks that also cover unplugged + * blocks. + * + * The actual THP size might differ between Linux kernels, so we try to probe + * it. In the future (if we ever run into issues regarding 2.), we might want + * to disable THP in case we fail to properly probe the THP size, or if the + * block size is configured smaller than the THP size. + */ +static uint32_t thp_size; + +#define HPAGE_PMD_SIZE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" +static uint32_t virtio_mem_thp_size(void) +{ + gchar *content = NULL; + const char *endptr; + uint64_t tmp; + + if (thp_size) { + return thp_size; + } + + /* + * Try to probe the actual THP size, fallback to (sane but eventually + * incorrect) default sizes. + */ + if (g_file_get_contents(HPAGE_PMD_SIZE_PATH, &content, NULL, NULL) && + !qemu_strtou64(content, &endptr, 0, &tmp) && + (!endptr || *endptr == '\n')) { + /* + * Sanity-check the value, if it's too big (e.g., aarch64 with 64k base + * pages) or weird, fallback to something smaller. + */ + if (!tmp || !is_power_of_2(tmp) || tmp > 16 * MiB) { + warn_report("Read unsupported THP size: %" PRIx64, tmp); + } else { + thp_size = tmp; + } + } + + if (!thp_size) { + thp_size = VIRTIO_MEM_DEFAULT_THP_SIZE; + warn_report("Could not detect THP size, falling back to %" PRIx64 + " MiB.", thp_size / MiB); + } + + g_free(content); + return thp_size; +} + +static uint64_t virtio_mem_default_block_size(RAMBlock *rb) +{ + const uint64_t page_size = qemu_ram_pagesize(rb); + + /* We can have hugetlbfs with a page size smaller than the THP size. */ + if (page_size == qemu_real_host_page_size) { + return MAX(page_size, virtio_mem_thp_size()); + } + return MAX(page_size, VIRTIO_MEM_MIN_BLOCK_SIZE); +} + /* * Size the usable region bigger than the requested size if possible. Esp. * Linux guests will only add (aligned) memory blocks in case they fully @@ -227,6 +300,9 @@ static void virtio_mem_resize_usable_region(VirtIOMEM *vmem, uint64_t newsize = MIN(memory_region_size(&vmem->memdev->mr), requested_size + VIRTIO_MEM_USABLE_EXTENT); + /* The usable region size always has to be multiples of the block size. */ + newsize = QEMU_ALIGN_UP(newsize, vmem->block_size); + if (!requested_size) { newsize = 0; } @@ -440,15 +516,33 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) rb = vmem->memdev->mr.ram_block; page_size = qemu_ram_pagesize(rb); + /* + * If the block size wasn't configured by the user, use a sane default. This + * allows using hugetlbfs backends of any page size without manual + * intervention. + */ + if (!vmem->block_size) { + vmem->block_size = virtio_mem_default_block_size(rb); + } + if (vmem->block_size < page_size) { error_setg(errp, "'%s' property has to be at least the page size (0x%" PRIx64 ")", VIRTIO_MEM_BLOCK_SIZE_PROP, page_size); return; + } else if (vmem->block_size < virtio_mem_default_block_size(rb)) { + warn_report("'%s' property is smaller than the default block size (%" + PRIx64 " MiB)", VIRTIO_MEM_BLOCK_SIZE_PROP, + virtio_mem_default_block_size(rb) / MiB); } else if (!QEMU_IS_ALIGNED(vmem->requested_size, vmem->block_size)) { error_setg(errp, "'%s' property has to be multiples of '%s' (0x%" PRIx64 ")", VIRTIO_MEM_REQUESTED_SIZE_PROP, VIRTIO_MEM_BLOCK_SIZE_PROP, vmem->block_size); return; + } else if (!QEMU_IS_ALIGNED(vmem->addr, vmem->block_size)) { + error_setg(errp, "'%s' property has to be multiples of '%s' (0x%" PRIx64 + ")", VIRTIO_MEM_ADDR_PROP, VIRTIO_MEM_BLOCK_SIZE_PROP, + vmem->block_size); + return; } else if (!QEMU_IS_ALIGNED(memory_region_size(&vmem->memdev->mr), vmem->block_size)) { error_setg(errp, "'%s' property memdev size has to be multiples of" @@ -734,6 +828,18 @@ static void virtio_mem_get_block_size(Object *obj, Visitor *v, const char *name, const VirtIOMEM *vmem = VIRTIO_MEM(obj); uint64_t value = vmem->block_size; + /* + * If not configured by the user (and we're not realized yet), use the + * default block size we would use with the current memory backend. + */ + if (!value) { + if (vmem->memdev && memory_region_is_ram(&vmem->memdev->mr)) { + value = virtio_mem_default_block_size(vmem->memdev->mr.ram_block); + } else { + value = virtio_mem_thp_size(); + } + } + visit_type_size(v, name, &value, errp); } @@ -813,7 +919,6 @@ static void virtio_mem_instance_init(Object *obj) { VirtIOMEM *vmem = VIRTIO_MEM(obj); - vmem->block_size = VIRTIO_MEM_MIN_BLOCK_SIZE; notifier_list_init(&vmem->size_change_notifiers); vmem->precopy_notifier.notify = virtio_mem_precopy_notify; diff --git a/include/exec/memory.h b/include/exec/memory.h index aff6ef7605..0f3e6bcd5e 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -397,6 +397,32 @@ struct IOMMUMemoryRegionClass { * @iommu: the IOMMUMemoryRegion */ int (*num_indexes)(IOMMUMemoryRegion *iommu); + + /** + * @iommu_set_page_size_mask: + * + * Restrict the page size mask that can be supported with a given IOMMU + * memory region. Used for example to propagate host physical IOMMU page + * size mask limitations to the virtual IOMMU. + * + * Optional method: if this method is not provided, then the default global + * page mask is used. + * + * @iommu: the IOMMUMemoryRegion + * + * @page_size_mask: a bitmask of supported page sizes. At least one bit, + * representing the smallest page size, must be set. Additional set bits + * represent supported block sizes. For example a host physical IOMMU that + * uses page tables with a page size of 4kB, and supports 2MB and 4GB + * blocks, will set mask 0x40201000. A granule of 4kB with indiscriminate + * block sizes is specified with mask 0xfffffffffffff000. + * + * Returns 0 on success, or a negative error. In case of failure, the error + * object must be created. + */ + int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, + uint64_t page_size_mask, + Error **errp); }; typedef struct CoalescedMemoryRange CoalescedMemoryRange; @@ -1410,6 +1436,18 @@ int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr); /** + * memory_region_iommu_set_page_size_mask: set the supported page + * sizes for a given IOMMU memory region + * + * @iommu_mr: IOMMU memory region + * @page_size_mask: supported page size mask + * @errp: pointer to Error*, to store an error if it happens. + */ +int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, + uint64_t page_size_mask, + Error **errp); + +/** * memory_region_name: get a memory region's name * * Returns the string that was used to initialize the memory region. diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 294024be5f..d1ea000d3d 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -144,6 +144,7 @@ struct ICH9LPCState { #define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7) #define ICH9_LPC_PMBASE_RTE 0x1 #define ICH9_LPC_PMBASE_DEFAULT 0x1 + #define ICH9_LPC_ACPI_CTRL 0x44 #define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 #define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0) diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index 30d7e99f52..48d2611fc5 100644 --- a/include/hw/mem/memory-device.h +++ b/include/hw/mem/memory-device.h @@ -89,6 +89,16 @@ struct MemoryDeviceClass { MemoryRegion *(*get_memory_region)(MemoryDeviceState *md, Error **errp); /* + * Optional: Return the desired minimum alignment of the device in guest + * physical address space. The final alignment is computed based on this + * alignment and the alignment requirements of the memory region. + * + * Called when plugging the memory device to detect the required alignment + * during address assignment. + */ + uint64_t (*get_min_alignment)(const MemoryDeviceState *md); + + /* * Translate the memory device into #MemoryDeviceInfo. */ void (*fill_device_info)(const MemoryDeviceState *md, diff --git a/include/hw/mips/cps.h b/include/hw/mips/cps.h index 859a8d4a67..04d636246a 100644 --- a/include/hw/mips/cps.h +++ b/include/hw/mips/cps.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/misc/mips_cpc.h b/include/hw/misc/mips_cpc.h index e5dccea151..fcafbd5e00 100644 --- a/include/hw/misc/mips_cpc.h +++ b/include/hw/misc/mips_cpc.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h index 96347dbf65..50d961106d 100644 --- a/include/hw/misc/mips_itu.h +++ b/include/hw/misc/mips_itu.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/pci-host/xilinx-pcie.h b/include/hw/pci-host/xilinx-pcie.h index f079e50db4..89be88d87f 100644 --- a/include/hw/pci-host/xilinx-pcie.h +++ b/include/hw/pci-host/xilinx-pcie.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 839bfb153c..4a8bc75415 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -141,7 +141,7 @@ void vhost_dev_reset_inflight(struct vhost_inflight *inflight); void vhost_dev_free_inflight(struct vhost_inflight *inflight); void vhost_dev_save_inflight(struct vhost_inflight *inflight, QEMUFile *f); int vhost_dev_load_inflight(struct vhost_inflight *inflight, QEMUFile *f); -int vhost_dev_prepare_inflight(struct vhost_dev *hdev); +int vhost_dev_prepare_inflight(struct vhost_dev *hdev, VirtIODevice *vdev); int vhost_dev_set_inflight(struct vhost_dev *dev, struct vhost_inflight *inflight); int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, diff --git a/include/qapi/util.h b/include/qapi/util.h index bc312e90aa..6178e98e97 100644 --- a/include/qapi/util.h +++ b/include/qapi/util.h @@ -19,6 +19,8 @@ typedef struct QEnumLookup { const char *qapi_enum_lookup(const QEnumLookup *lookup, int val); int qapi_enum_parse(const QEnumLookup *lookup, const char *buf, int def, Error **errp); +bool qapi_bool_parse(const char *name, const char *value, bool *obj, + Error **errp); int parse_qapi_name(const char *name, bool complete); diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 4bbf4834ea..986ed8e15f 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -205,6 +205,7 @@ int qemu_pstrcmp0(const char **str1, const char **str2); * as the prefix. For example, if `bindir` is `/usr/bin` and @dir is * `/usr/share/qemu`, the function will append `../share/qemu` to the * directory that contains the running executable and return the result. + * The returned string should be freed by the caller. */ char *get_relocated_path(const char *dir); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index bf8c1bd253..0b02a92602 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2188,7 +2188,8 @@ static uintptr_t pgd_find_hole_fallback(uintptr_t guest_size, uintptr_t brk, PROT_NONE, flags, -1, 0); if (mmap_start != MAP_FAILED) { munmap((void *) align_start, guest_size); - if (MAP_FIXED_NOREPLACE || mmap_start == (void *) align_start) { + if (MAP_FIXED_NOREPLACE != 0 || + mmap_start == (void *) align_start) { return (uintptr_t) mmap_start + offset; } } @@ -3484,7 +3485,9 @@ static abi_ulong vma_dump_size(const struct vm_area_struct *vma) if (vma->vma_flags & PROT_EXEC) { char page[TARGET_PAGE_SIZE]; - copy_from_user(page, vma->vma_start, sizeof (page)); + if (copy_from_user(page, vma->vma_start, sizeof (page))) { + return 0; + } if ((page[EI_MAG0] == ELFMAG0) && (page[EI_MAG1] == ELFMAG1) && (page[EI_MAG2] == ELFMAG2) && diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 553e8ca7f5..cfe7ba5c47 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -104,18 +104,22 @@ void cpu_loop(CPUMIPSState *env) if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { goto done_syscall; } + /* fall through */ case 7: if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { goto done_syscall; } + /* fall through */ case 6: if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { goto done_syscall; } + /* fall through */ case 5: if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { goto done_syscall; } + /* fall through */ default: break; } diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6fef8181e7..3160a9ba06 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -7592,7 +7592,9 @@ static int do_futex_time64(target_ulong uaddr, int op, int val, target_ulong tim case FUTEX_WAIT_BITSET: if (timeout) { pts = &ts; - target_to_host_timespec64(pts, timeout); + if (target_to_host_timespec64(pts, timeout)) { + return -TARGET_EFAULT; + } } else { pts = NULL; } diff --git a/meson.build b/meson.build index 39ac5cf6d8..f5175010df 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('qemu', ['c'], meson_version: '>=0.55.0', - default_options: ['warning_level=1', 'c_std=gnu99', 'cpp_std=gnu++11', - 'b_colorout=auto'], + default_options: ['warning_level=1', 'c_std=gnu99', 'cpp_std=gnu++11', 'b_colorout=auto'] + + (meson.version().version_compare('>=0.56.0') ? [ 'b_staticpic=false' ] : []), version: run_command('head', meson.source_root() / 'VERSION').stdout().strip()) not_found = dependency('', required: false) diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 56e9bad33d..a6a6684df1 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1762,7 +1762,8 @@ err_out: goto out; } -void hmp_screendump(Monitor *mon, const QDict *qdict) +void coroutine_fn +hmp_screendump(Monitor *mon, const QDict *qdict) { const char *filename = qdict_get_str(qdict, "filename"); const char *id = qdict_get_try_str(qdict, "device"); diff --git a/net/net.c b/net/net.c index 7a2a0fb5ac..794c652282 100644 --- a/net/net.c +++ b/net/net.c @@ -1050,6 +1050,9 @@ static void show_netdevs(void) #ifdef CONFIG_POSIX "vhost-user", #endif +#ifdef CONFIG_VHOST_VDPA + "vhost-vdpa", +#endif }; printf("Available netdev backend types:\n"); diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 99c476db8c..fe659ec9e2 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -145,6 +145,10 @@ static void vhost_vdpa_cleanup(NetClientState *nc) g_free(s->vhost_net); s->vhost_net = NULL; } + if (s->vhost_vdpa.device_fd >= 0) { + qemu_close(s->vhost_vdpa.device_fd); + s->vhost_vdpa.device_fd = -1; + } } static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 7781c23a42..587f31baf6 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -368,7 +368,6 @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) } -/* mimics qemu-option.c::parse_option_bool() */ static bool opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { @@ -379,19 +378,8 @@ opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) if (!opt) { return false; } - if (opt->str) { - if (strcmp(opt->str, "on") == 0 || - strcmp(opt->str, "yes") == 0 || - strcmp(opt->str, "y") == 0) { - *obj = true; - } else if (strcmp(opt->str, "off") == 0 || - strcmp(opt->str, "no") == 0 || - strcmp(opt->str, "n") == 0) { - *obj = false; - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, - "on|yes|y|off|no|n"); + if (!qapi_bool_parse(opt->name, opt->str, obj, errp)) { return false; } } else { diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c index 29a6c98b53..3c24bb3d45 100644 --- a/qapi/qapi-util.c +++ b/qapi/qapi-util.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/ctype.h" +#include "qapi/qmp/qerror.h" const char *qapi_enum_lookup(const QEnumLookup *lookup, int val) { @@ -40,6 +41,28 @@ int qapi_enum_parse(const QEnumLookup *lookup, const char *buf, return def; } +bool qapi_bool_parse(const char *name, const char *value, bool *obj, Error **errp) +{ + if (g_str_equal(value, "on") || + g_str_equal(value, "yes") || + g_str_equal(value, "true") || + g_str_equal(value, "y")) { + *obj = true; + return true; + } + if (g_str_equal(value, "off") || + g_str_equal(value, "no") || + g_str_equal(value, "false") || + g_str_equal(value, "n")) { + *obj = false; + return true; + } + + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, + "'on' or 'off'"); + return false; +} + /* * Parse a valid QAPI name from @str. * A valid name consists of letters, digits, hyphen and underscore. diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 7b184b50a7..23843b242e 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -512,11 +512,7 @@ static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, return false; } - if (!strcmp(str, "on")) { - *obj = true; - } else if (!strcmp(str, "off")) { - *obj = false; - } else { + if (!qapi_bool_parse(name, str, obj, NULL)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, full_name(qiv, name), "'on' or 'off'"); return false; diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index 6e53396ea3..197139c1c0 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -332,22 +332,7 @@ static bool parse_type_bool(Visitor *v, const char *name, bool *obj, StringInputVisitor *siv = to_siv(v); assert(siv->lm == LM_NONE); - if (!strcasecmp(siv->string, "on") || - !strcasecmp(siv->string, "yes") || - !strcasecmp(siv->string, "true")) { - *obj = true; - return true; - } - if (!strcasecmp(siv->string, "off") || - !strcasecmp(siv->string, "no") || - !strcasecmp(siv->string, "false")) { - *obj = false; - return true; - } - - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "boolean"); - return false; + return qapi_bool_parse(name ? name : "null", siv->string, obj, errp); } static bool parse_type_str(Visitor *v, const char *name, char **obj, diff --git a/qapi/ui.json b/qapi/ui.json index 9d6721037f..6c7b33cb72 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -98,7 +98,8 @@ # ## { 'command': 'screendump', - 'data': {'filename': 'str', '*device': 'str', '*head': 'int'} } + 'data': {'filename': 'str', '*device': 'str', '*head': 'int'}, + 'coroutine': true } ## # == Spice diff --git a/roms/Makefile b/roms/Makefile index 1489d47350..7045e374d3 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -102,7 +102,7 @@ build-seabios-config-%: config.% OUT=$(CURDIR)/seabios/builds/$*/ all -.PHONY: sgabios skiboot +.PHONY: sgabios skiboot qboot sgabios: $(MAKE) -C sgabios cp sgabios/sgabios.bin ../pc-bios diff --git a/scripts/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index fcae4a0c26..3b1c82b63d 100755 --- a/scripts/oss-fuzz/build.sh +++ b/scripts/oss-fuzz/build.sh @@ -91,7 +91,7 @@ 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/" +cp "./qemu-fuzz-i386" "$DEST_DIR/bin/qemu-fuzz-i386.base" # 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 @@ -104,7 +104,7 @@ do # 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" \ + ln "$DEST_DIR/bin/qemu-fuzz-i386.base" \ "$DEST_DIR/qemu-fuzz-i386-target-$target" fi done diff --git a/softmmu/memory.c b/softmmu/memory.c index 21d533d8ed..71951fe4dc 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1841,6 +1841,19 @@ static int memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr, return ret; } +int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, + uint64_t page_size_mask, + Error **errp) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + int ret = 0; + + if (imrc->iommu_set_page_size_mask) { + ret = imrc->iommu_set_page_size_mask(iommu_mr, page_size_mask, errp); + } + return ret; +} + int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp) { diff --git a/softmmu/physmem.c b/softmmu/physmem.c index a9adedb9f8..0b31be2928 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2723,22 +2723,14 @@ static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) static bool prepare_mmio_access(MemoryRegion *mr) { - bool unlocked = !qemu_mutex_iothread_locked(); bool release_lock = false; - if (unlocked) { + if (!qemu_mutex_iothread_locked()) { qemu_mutex_lock_iothread(); - unlocked = false; release_lock = true; } if (mr->flush_coalesced_mmio) { - if (unlocked) { - qemu_mutex_lock_iothread(); - } qemu_flush_coalesced_mmio_buffer(); - if (unlocked) { - qemu_mutex_unlock_iothread(); - } } return release_lock; diff --git a/softmmu/vl.c b/softmmu/vl.c index a537a0377f..a71164494e 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -4284,9 +4284,6 @@ void qemu_init(int argc, char **argv, char **envp) qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, &error_fatal); - /* connect semihosting console input if requested */ - qemu_semihosting_console_init(); - if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) exit(1); if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0) @@ -4296,6 +4293,7 @@ void qemu_init(int argc, char **argv, char **envp) /* now chardevs have been created we may have semihosting to connect */ qemu_semihosting_connect_chardevs(); + qemu_semihosting_console_init(); /* If no default VGA is requested, the default is "none". */ if (default_vga) { diff --git a/target/mips/cp0_helper.c b/target/mips/cp0_helper.c index 12143ac55b..709cc9a7e3 100644 --- a/target/mips/cp0_helper.c +++ b/target/mips/cp0_helper.c @@ -8,7 +8,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/dsp_helper.c b/target/mips/dsp_helper.c index 8c58eeb0bf..09b6e5fb15 100644 --- a/target/mips/dsp_helper.c +++ b/target/mips/dsp_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/fpu_helper.c b/target/mips/fpu_helper.c index 6cc956c023..020b768e87 100644 --- a/target/mips/fpu_helper.c +++ b/target/mips/fpu_helper.c @@ -8,7 +8,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index 98f56e660d..e39f8d75cf 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -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/mips/helper.c b/target/mips/helper.c index afd78b1990..063b65c052 100644 --- a/target/mips/helper.c +++ b/target/mips/helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/lmmi_helper.c b/target/mips/lmmi_helper.c index 6c645cf679..abeb7736ae 100644 --- a/target/mips/lmmi_helper.c +++ b/target/mips/lmmi_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/mips-semi.c b/target/mips/mips-semi.c index 10a710c1e8..898251aa02 100644 --- a/target/mips/mips-semi.c +++ b/target/mips/mips-semi.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c index 6865addaf6..249f0fdad8 100644 --- a/target/mips/msa_helper.c +++ b/target/mips/msa_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 0050d0616b..5184a1838b 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/mips/translate.c b/target/mips/translate.c index f449758606..c64a1bc42e 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -10,7 +10,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -31442,8 +31442,8 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #else ctx->mem_idx = hflags_mmu_index(ctx->hflags); #endif - ctx->default_tcg_memop_mask = (ctx->insn_flags & ISA_MIPS32R6) ? - MO_UNALN : MO_ALIGN; + ctx->default_tcg_memop_mask = (ctx->insn_flags & (ISA_MIPS32R6 | ISA_MIPS64R6 | + INSN_LOONGSON3A)) ? MO_UNALN : MO_ALIGN; LOG_DISAS("\ntb %p idx %d hflags %04x\n", ctx->base.tb, ctx->mem_idx, ctx->hflags); diff --git a/target/mips/translate_init.c.inc b/target/mips/translate_init.c.inc index fb5a9b38e5..ea85d5c6a7 100644 --- a/target/mips/translate_init.c.inc +++ b/target/mips/translate_init.c.inc @@ -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/ppc/excp_helper.c b/target/ppc/excp_helper.c index d7411bcc81..ad0612555d 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -527,6 +527,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) break; case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ msr |= env->error_code; + /* fall through */ case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ diff --git a/tcg/optimize.c b/tcg/optimize.c index 9952c28bdc..220f4601d5 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1484,30 +1484,29 @@ void tcg_optimize(TCGContext *s) } } } - /* fall through */ + goto do_reset_output; default: do_default: - /* - * Default case: we know nothing about operation (or were unable - * to compute the operation result) so no propagation is done. - */ - for (i = 0; i < nb_oargs; i++) { - reset_temp(op->args[i]); - /* - * Save the corresponding known-zero bits mask for the - * first output argument (only one supported so far). - */ - if (i == 0) { - arg_info(op->args[i])->mask = mask; + /* Default case: we know nothing about operation (or were unable + to compute the operation result) so no propagation is done. + We trash everything if the operation is the end of a basic + block, otherwise we only trash the output args. "mask" is + the non-zero bits mask for the first output arg. */ + if (def->flags & TCG_OPF_BB_END) { + bitmap_zero(temps_used.l, nb_temps); + } else { + do_reset_output: + for (i = 0; i < nb_oargs; i++) { + reset_temp(op->args[i]); + /* Save the corresponding known-zero bits mask for the + first output argument (only one supported so far). */ + if (i == 0) { + arg_info(op->args[i])->mask = mask; + } } } break; - - case INDEX_op_set_label: - /* Trash everything at the start of a new extended bb. */ - bitmap_zero(temps_used.l, nb_temps); - break; } /* Eliminate duplicate and redundant fence instructions. */ diff --git a/tcg/tcg.c b/tcg/tcg.c index f49f1a7f35..43c6cf8f52 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -335,10 +335,11 @@ static bool tcg_resolve_relocs(TCGContext *s) static void set_jmp_reset_offset(TCGContext *s, int which) { - size_t off = tcg_current_code_size(s); - s->tb_jmp_reset_offset[which] = off; - /* Make sure that we didn't overflow the stored offset. */ - assert(s->tb_jmp_reset_offset[which] == off); + /* + * We will check for overflow at the end of the opcode loop in + * tcg_gen_code, where we bound tcg_current_code_size to UINT16_MAX. + */ + s->tb_jmp_reset_offset[which] = tcg_current_code_size(s); } #include "tcg-target.c.inc" diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index eef242dc80..5af944a5fb 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -217,7 +217,7 @@ int main(int argc, char **argv) add_cdrom_param_tests(sparc64machines); } else if (!strncmp(arch, "mips64", 6)) { const char *mips64machines[] = { - "magnum", "malta", "mips", "pica61", NULL + "magnum", "malta", "pica61", NULL }; add_cdrom_param_tests(mips64machines); } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c index 9f22340ee5..bbec166dbc 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -104,7 +104,8 @@ static QList *device_type_list(QTestState *qts, bool abstract) static void test_one_device(QTestState *qts, const char *type) { QDict *resp; - char *help; + char *help, *escaped; + GRegex *comma; g_test_message("Testing device '%s'", type); @@ -113,8 +114,13 @@ static void test_one_device(QTestState *qts, const char *type) type); qobject_unref(resp); - help = qtest_hmp(qts, "device_add \"%s,help\"", type); + comma = g_regex_new(",", 0, 0, NULL); + escaped = g_regex_replace_literal(comma, type, -1, 0, ",,", 0, NULL); + g_regex_unref(comma); + + help = qtest_hmp(qts, "device_add \"%s,help\"", escaped); g_free(help); + g_free(escaped); } static void test_device_intro_list(void) diff --git a/tests/qtest/endianness-test.c b/tests/qtest/endianness-test.c index 4e79e22c28..09ecb531f1 100644 --- a/tests/qtest/endianness-test.c +++ b/tests/qtest/endianness-test.c @@ -27,11 +27,9 @@ struct TestCase { static const TestCase test_cases[] = { { "i386", "pc", -1 }, - { "mips", "mips", 0x14000000, .bswap = true }, { "mips", "malta", 0x10000000, .bswap = true }, { "mips64", "magnum", 0x90000000, .bswap = true }, { "mips64", "pica61", 0x90000000, .bswap = true }, - { "mips64", "mips", 0x14000000, .bswap = true }, { "mips64", "malta", 0x10000000, .bswap = true }, { "mips64el", "fuloong2e", 0x1fd00000 }, { "ppc", "g3beige", 0xfe000000, .bswap = true, .superio = "i82378" }, diff --git a/tests/qtest/fuzz-test.c b/tests/qtest/fuzz-test.c index 2f38bb1ec2..9cb4c42bde 100644 --- a/tests/qtest/fuzz-test.c +++ b/tests/qtest/fuzz-test.c @@ -34,6 +34,19 @@ static void test_lp1878263_megasas_zero_iov_cnt(void) qtest_quit(s); } +static void test_lp1878642_pci_bus_get_irq_level_assert(void) +{ + QTestState *s; + + s = qtest_init("-M pc-q35-5.0 " + "-nographic -monitor none -serial none " + "-d guest_errors -trace pci*"); + + qtest_outl(s, 0xcf8, 0x8400f841); + qtest_outl(s, 0xcfc, 0xebed205d); + qtest_outl(s, 0x5d02, 0xebed205d); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -43,6 +56,8 @@ int main(int argc, char **argv) if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { qtest_add_func("fuzz/test_lp1878263_megasas_zero_iov_cnt", test_lp1878263_megasas_zero_iov_cnt); + qtest_add_func("fuzz/test_lp1878642_pci_bus_get_irq_level_assert", + test_lp1878642_pci_bus_get_irq_level_assert); } return g_test_run(); diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index a8f5864883..262a963d2e 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -192,7 +192,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr, bool is_write) */ if (dma_patterns->len == 0 || len == 0 - /* || mr != MACHINE(qdev_get_machine())->ram */ + || mr != current_machine->ram || is_write || addr > current_machine->ram_size) { return; @@ -229,10 +229,10 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr, bool is_write) 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); + void *buf_base = pattern_alloc(p, ar.size); + void *buf = buf_base; hwaddr l, addr1; MemoryRegion *mr1; - uint8_t *ram_ptr; while (len > 0) { l = len; mr1 = address_space_translate(first_cpu->as, @@ -244,30 +244,27 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr, bool is_write) 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; + 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, addr, buf, l); } 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); + g_free(buf_base); /* Increment the index of the pattern for the next DMA access */ dma_pattern_index = (dma_pattern_index + 1) % dma_patterns->len; @@ -301,6 +298,11 @@ static bool get_io_address(address_range *result, AddressSpace *as, } while (cb_info.index != index && !cb_info.found); *result = cb_info.result; + if (result->size) { + offset = offset % result->size; + result->addr += offset; + result->size -= offset; + } return cb_info.found; } diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index b943577b8c..cee1a2a60f 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -70,7 +70,7 @@ static GString *qos_build_main_args(void) { char **path = fuzz_path_vec; QOSGraphNode *test_node; - GString *cmd_line = g_string_new(path[0]); + GString *cmd_line; void *test_arg; if (!path) { @@ -79,6 +79,7 @@ static GString *qos_build_main_args(void) } /* Before test */ + cmd_line = g_string_new(path[0]); current_path = path; test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]); test_arg = test_node->u.test.arg; diff --git a/tests/qtest/ivshmem-test.c b/tests/qtest/ivshmem-test.c index d5c8b9f128..dfa69424ed 100644 --- a/tests/qtest/ivshmem-test.c +++ b/tests/qtest/ivshmem-test.c @@ -135,7 +135,7 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) static void setup_vm(IVState *s) { char *cmd = g_strdup_printf("-object memory-backend-file" - ",id=mb1,size=1M,share,mem-path=/dev/shm%s" + ",id=mb1,size=1M,share=on,mem-path=/dev/shm%s" " -device ivshmem-plain,memdev=mb1", tmpshm); setup_vm_cmd(s, cmd, false); diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index 2946abc15a..fba3e7a954 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -637,10 +637,13 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, AHCICommand *cmd; int rc; AHCIOpts *opts; + uint64_t buffer_in; opts = g_memdup((opts_in == NULL ? &default_opts : opts_in), sizeof(AHCIOpts)); + buffer_in = opts->buffer; + /* No guest buffer provided, create one. */ if (opts->size && !opts->buffer) { opts->buffer = ahci_alloc(ahci, opts->size); @@ -686,7 +689,7 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, g_assert_cmpint(rc, ==, 0); } ahci_command_free(cmd); - if (opts->buffer != opts_in->buffer) { + if (opts->buffer != buffer_in) { ahci_free(ahci, opts->buffer); } g_free(opts); diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 99deff47ef..be0fb430dd 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -110,8 +110,13 @@ static int socket_accept(int sock) struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT, .tv_usec = 0 }; - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, - sizeof(timeout)); + if (qemu_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + (void *)&timeout, sizeof(timeout))) { + fprintf(stderr, "%s failed to set SO_RCVTIMEO: %s\n", + __func__, strerror(errno)); + close(sock); + return -1; + } do { addrlen = sizeof(addr); diff --git a/tools/virtiofsd/meson.build b/tools/virtiofsd/meson.build index e1a4dc98d9..17edecf55c 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: { 'libexecdir' : get_option('libexecdir') }, + configuration: { 'libexecdir' : get_option('prefix') / get_option('libexecdir') }, install_dir: qemu_datadir / 'vhost-user') diff --git a/ui/console.c b/ui/console.c index 820e408170..e8e59707d3 100644 --- a/ui/console.c +++ b/ui/console.c @@ -168,6 +168,7 @@ struct QemuConsole { QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; + CoQueue dump_queue; QTAILQ_ENTRY(QemuConsole) next; }; @@ -195,7 +196,6 @@ static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); -static bool ppm_save(int fd, DisplaySurface *ds, Error **errp); static void gui_update(void *opaque) { @@ -264,6 +264,7 @@ static void gui_setup_refresh(DisplayState *ds) void graphic_hw_update_done(QemuConsole *con) { + qemu_co_queue_restart_all(&con->dump_queue); } void graphic_hw_update(QemuConsole *con) @@ -311,16 +312,16 @@ void graphic_hw_invalidate(QemuConsole *con) } } -static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) +static bool ppm_save(int fd, pixman_image_t *image, Error **errp) { - int width = pixman_image_get_width(ds->image); - int height = pixman_image_get_height(ds->image); + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); g_autofree char *header = NULL; g_autoptr(pixman_image_t) linebuf = NULL; int y; - trace_ppm_save(fd, ds); + trace_ppm_save(fd, image); header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); if (qio_channel_write_all(QIO_CHANNEL(ioc), @@ -330,7 +331,7 @@ static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); for (y = 0; y < height; y++) { - qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y); + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); if (qio_channel_write_all(QIO_CHANNEL(ioc), (char *)pixman_image_get_data(linebuf), pixman_image_get_stride(linebuf), errp) < 0) { @@ -341,9 +342,17 @@ static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) return true; } -void qmp_screendump(const char *filename, bool has_device, const char *device, - bool has_head, int64_t head, Error **errp) +static void graphic_hw_update_bh(void *con) { + graphic_hw_update(con); +} + +/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ +void coroutine_fn +qmp_screendump(const char *filename, bool has_device, const char *device, + bool has_head, int64_t head, Error **errp) +{ + g_autoptr(pixman_image_t) image = NULL; QemuConsole *con; DisplaySurface *surface; int fd; @@ -366,12 +375,24 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, } } - graphic_hw_update(con); + if (qemu_co_queue_empty(&con->dump_queue)) { + /* Defer the update, it will restart the pending coroutines */ + aio_bh_schedule_oneshot(qemu_get_aio_context(), + graphic_hw_update_bh, con); + } + qemu_co_queue_wait(&con->dump_queue, NULL); + + /* + * All pending coroutines are woken up, while the BQL is held. No + * further graphic update are possible until it is released. Take + * an image ref before that. + */ surface = qemu_console_surface(con); if (!surface) { error_setg(errp, "no surface"); return; } + image = pixman_image_ref(surface->image); fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); if (fd == -1) { @@ -380,7 +401,12 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, return; } - if (!ppm_save(fd, surface, errp)) { + /* + * The image content could potentially be updated as the coroutine + * yields and releases the BQL. It could produce corrupted dump, but + * it should be otherwise safe. + */ + if (!ppm_save(fd, image, errp)) { qemu_unlink(filename); } } @@ -1296,6 +1322,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, obj = object_new(TYPE_QEMU_CONSOLE); s = QEMU_CONSOLE(obj); + qemu_co_queue_init(&s->dump_queue); s->head = head; object_property_add_link(obj, "device", TYPE_DEVICE, (Object **)&s->device, diff --git a/ui/trace-events b/ui/trace-events index b7d7270c02..0ffcdb4408 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -15,7 +15,7 @@ displaysurface_create_pixman(void *display_surface) "surface=%p" displaysurface_free(void *display_surface) "surface=%p" displaychangelistener_register(void *dcl, const char *name) "%p [ %s ]" displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" -ppm_save(int fd, void *display_surface) "fd=%d surface=%p" +ppm_save(int fd, void *image) "fd=%d image=%p" # gtk-egl.c # gtk-gl-area.c diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 0517b2ead9..f67111a366 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -111,7 +111,8 @@ size_t vnc_client_write_sasl(VncState *vs) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } return ret; diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index f072e16ace..d9c212ff32 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -79,7 +79,8 @@ static void vnc_tls_handshake_done(QIOTask *task, g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT, + vnc_client_io, vs, NULL); start_auth_vencrypt_subauth(vs); } } diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 929391f85d..dbbfbefe56 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -151,7 +151,8 @@ void vnc_jobs_consume_buffer(VncState *vs) } if (vs->disconnecting == FALSE) { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT, + vnc_client_io, vs, NULL); } } buffer_move(&vs->output, &vs->jobs_buffer); diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 95c9703c72..6d79f3e5a5 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -41,13 +41,14 @@ static void vncws_tls_handshake_done(QIOTask *task, g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL); + QIO_CHANNEL(vs->ioc), G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_handshake_io, vs, NULL); } } gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, - GIOCondition condition G_GNUC_UNUSED, + GIOCondition condition, void *opaque) { VncState *vs = opaque; @@ -59,6 +60,11 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, vs->ioc_tag = 0; } + if (condition & (G_IO_HUP | G_IO_ERR)) { + vnc_client_error(vs); + return TRUE; + } + tls = qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, @@ -105,13 +111,14 @@ static void vncws_handshake_done(QIOTask *task, g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } } gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, - GIOCondition condition G_GNUC_UNUSED, + GIOCondition condition, void *opaque) { VncState *vs = opaque; @@ -122,6 +129,11 @@ gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, vs->ioc_tag = 0; } + if (condition & (G_IO_HUP | G_IO_ERR)) { + vnc_client_error(vs); + return TRUE; + } + wioc = qio_channel_websock_new_server(vs->ioc); qio_channel_set_name(QIO_CHANNEL(wioc), "vnc-ws-server-websock"); diff --git a/ui/vnc.c b/ui/vnc.c index f006aa1afd..49235056f7 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1398,7 +1398,8 @@ static size_t vnc_client_write_plain(VncState *vs) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } return ret; @@ -1435,7 +1436,8 @@ static void vnc_client_write(VncState *vs) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } vnc_unlock_output(vs); } @@ -1551,6 +1553,12 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, VncState *vs = opaque; assert(vs->magic == VNC_MAGIC); + + if (condition & (G_IO_HUP | G_IO_ERR)) { + vnc_disconnect_start(vs); + return TRUE; + } + if (condition & G_IO_IN) { if (vnc_client_read(vs) < 0) { /* vs is free()ed here */ @@ -1612,7 +1620,8 @@ void vnc_write(VncState *vs, const void *data, size_t len) g_source_remove(vs->ioc_tag); } vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_OUT, + vnc_client_io, vs, NULL); } buffer_append(&vs->output, data, len); @@ -3077,14 +3086,17 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, vs->websocket = 1; if (vd->tlscreds) { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_tls_handshake_io, vs, NULL); } else { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vncws_handshake_io, vs, NULL); } } else { vs->ioc_tag = qio_channel_add_watch( - vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); + vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR, + vnc_client_io, vs, NULL); } vnc_client_cache_addr(vs); diff --git a/util/cutils.c b/util/cutils.c index c395974fab..9498e28e1a 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -937,7 +937,7 @@ char *get_relocated_path(const char *dir) /* Fail if qemu_init_exec_dir was not called. */ assert(exec_dir[0]); if (!starts_with_prefix(dir) || !starts_with_prefix(bindir)) { - return strdup(dir); + return g_strdup(dir); } result = g_string_new(exec_dir); diff --git a/util/meson.build b/util/meson.build index c5159ad79d..f359af0d46 100644 --- a/util/meson.build +++ b/util/meson.build @@ -66,7 +66,7 @@ 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: [ + util_ss.add(when: 'CONFIG_LINUX', if_true: [ files('vhost-user-server.c'), vhost_user ]) util_ss.add(files('block-helpers.c')) diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c index 36927b5f88..5816bf8900 100644 --- a/util/qemu-coroutine-lock.c +++ b/util/qemu-coroutine-lock.c @@ -85,15 +85,13 @@ static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) return true; } -bool coroutine_fn qemu_co_queue_next(CoQueue *queue) +bool qemu_co_queue_next(CoQueue *queue) { - assert(qemu_in_coroutine()); return qemu_co_queue_do_restart(queue, true); } -void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) +void qemu_co_queue_restart_all(CoQueue *queue) { - assert(qemu_in_coroutine()); qemu_co_queue_do_restart(queue, false); } diff --git a/util/qemu-option.c b/util/qemu-option.c index b9f93a7f8b..acefbc23fa 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -96,21 +96,6 @@ const char *get_opt_value(const char *p, char **value) return offset; } -static bool parse_option_bool(const char *name, const char *value, bool *ret, - Error **errp) -{ - if (!strcmp(value, "on")) { - *ret = 1; - } else if (!strcmp(value, "off")) { - *ret = 0; - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name, "'on' or 'off'"); - return false; - } - return true; -} - static bool parse_option_number(const char *name, const char *value, uint64_t *ret, Error **errp) { @@ -363,7 +348,7 @@ static bool qemu_opt_get_bool_helper(QemuOpts *opts, const char *name, if (opt == NULL) { def_val = find_default_by_name(opts, name); if (def_val) { - parse_option_bool(name, def_val, &ret, &error_abort); + qapi_bool_parse(name, def_val, &ret, &error_abort); } return ret; } @@ -471,8 +456,7 @@ static bool qemu_opt_parse(QemuOpt *opt, Error **errp) /* nothing */ return true; case QEMU_OPT_BOOL: - return parse_option_bool(opt->name, opt->str, &opt->value.boolean, - errp); + return qapi_bool_parse(opt->name, opt->str, &opt->value.boolean, errp); case QEMU_OPT_NUMBER: return parse_option_number(opt->name, opt->str, &opt->value.uint, errp); |