diff --git a/backends/rng-egd.c b/backends/rng-egd.c index 25bb3b453b..2962795a8f 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -169,6 +169,7 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) if (b->opened) { error_set(errp, QERR_PERMISSION_DENIED); } else { + g_free(s->chr_name); s->chr_name = g_strdup(value); } } diff --git a/configure b/configure index 1a9daa516e..fb194fd508 100755 --- a/configure +++ b/configure @@ -2716,6 +2716,12 @@ for i in $glib_modules; do fi done +# g_test_trap_subprocess added in 2.38. Used by some tests. +glib_subprocess=yes +if ! $pkg_config --atleast-version=2.38 glib-2.0; then + glib_subprocess=no +fi + ########################################## # SHA command probe for modules if test "$modules" = yes; then @@ -4586,6 +4592,9 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ=y" >> $config_host_mak echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi +if test "glib_subprocess" = "yes" ; then + echo "CONFIG_HAS_GLIB_SUBPROCESS_TESTS=y" >> $config_host_mak +fi echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak if test "$gtk" = "yes" ; then echo "CONFIG_GTK=y" >> $config_host_mak diff --git a/hw/core/machine.c b/hw/core/machine.c index f0046d6de3..7f3418c5af 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -24,6 +24,7 @@ static void machine_set_accel(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); + g_free(ms->accel); ms->accel = g_strdup(value); } @@ -79,6 +80,7 @@ static void machine_set_kernel(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); + g_free(ms->kernel_filename); ms->kernel_filename = g_strdup(value); } @@ -93,6 +95,7 @@ static void machine_set_initrd(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); + g_free(ms->initrd_filename); ms->initrd_filename = g_strdup(value); } @@ -107,6 +110,7 @@ static void machine_set_append(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); + g_free(ms->kernel_cmdline); ms->kernel_cmdline = g_strdup(value); } @@ -121,6 +125,7 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); + g_free(ms->dtb); ms->dtb = g_strdup(value); } @@ -135,6 +140,7 @@ static void machine_set_dumpdtb(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); + g_free(ms->dumpdtb); ms->dumpdtb = g_strdup(value); } @@ -176,6 +182,7 @@ static void machine_set_dt_compatible(Object *obj, const char *value, Error **er { MachineState *ms = MACHINE(obj); + g_free(ms->dt_compatible); ms->dt_compatible = g_strdup(value); } @@ -232,6 +239,7 @@ static void machine_set_firmware(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); + g_free(ms->firmware); ms->firmware = g_strdup(value); } diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index ae0900f651..84caa1d694 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -388,28 +388,12 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) static int qdev_add_one_global(QemuOpts *opts, void *opaque) { GlobalProperty *g; - ObjectClass *oc; g = g_malloc0(sizeof(*g)); g->driver = qemu_opt_get(opts, "driver"); g->property = qemu_opt_get(opts, "property"); g->value = qemu_opt_get(opts, "value"); - oc = object_class_dynamic_cast(object_class_by_name(g->driver), - TYPE_DEVICE); - if (oc) { - DeviceClass *dc = DEVICE_CLASS(oc); - - if (dc->hotpluggable) { - /* If hotpluggable then skip not_used checking. */ - g->not_used = false; - } else { - /* Maybe a typo. */ - g->not_used = true; - } - } else { - /* Maybe a typo. */ - g->not_used = true; - } + g->user_provided = true; qdev_prop_register_global(g); return 0; } diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 3d12560f43..66556d3bf9 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -955,19 +955,35 @@ void qdev_prop_register_global_list(GlobalProperty *props) } } -int qdev_prop_check_global(void) +int qdev_prop_check_globals(void) { GlobalProperty *prop; int ret = 0; QTAILQ_FOREACH(prop, &global_props, next) { - if (!prop->not_used) { + ObjectClass *oc; + DeviceClass *dc; + if (prop->used) { + continue; + } + if (!prop->user_provided) { + continue; + } + oc = object_class_by_name(prop->driver); + oc = object_class_dynamic_cast(oc, TYPE_DEVICE); + if (!oc) { + error_report("Warning: global %s.%s has invalid class name", + prop->driver, prop->property); + ret = 1; + continue; + } + dc = DEVICE_CLASS(oc); + if (!dc->hotpluggable && !prop->used) { + error_report("Warning: global %s.%s=%s not used", + prop->driver, prop->property, prop->value); + ret = 1; continue; } - ret = 1; - error_report("Warning: \"-global %s.%s=%s\" not used", - prop->driver, prop->property, prop->value); - } return ret; } @@ -983,7 +999,7 @@ void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, if (strcmp(typename, prop->driver) != 0) { continue; } - prop->not_used = false; + prop->used = true; object_property_parse(OBJECT(dev), prop->value, prop->property, &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 77b6782fed..2c2e9dcbf4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -72,8 +72,10 @@ #define DPRINTF(fmt, ...) #endif -/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */ -unsigned acpi_data_size = 0x20000; +/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables + * (128K) and other BIOS datastructures (less than 4K reported to be used at + * the moment, 32K should be enough for a while). */ +unsigned acpi_data_size = 0x20000 + 0x8000; void pc_set_legacy_acpi_data_size(void) { acpi_data_size = 0x10000; diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index b21e7a434f..77bb93e4d7 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -163,11 +163,11 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) if (r < 0) { goto fail; } - if (!qemu_has_vnet_hdr_len(options->net_backend, - sizeof(struct virtio_net_hdr_mrg_rxbuf))) { - net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); - } if (backend_kernel) { + if (!qemu_has_vnet_hdr_len(options->net_backend, + sizeof(struct virtio_net_hdr_mrg_rxbuf))) { + net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); + } if (~net->dev.features & net->dev.backend_features) { fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n", diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 826a2a5fca..2040eac9a1 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1125,8 +1125,6 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) return num_packets; } - assert(vdev->vm_running); - if (q->async_tx.elem.out_num) { virtio_queue_set_notification(q->tx_vq, 0); return num_packets; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index ddb5da181c..f5608140f9 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -86,9 +86,6 @@ * 12 is historical, and due to x86 page size. */ #define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 -/* Flags track per-device state like workarounds for quirks in older guests. */ -#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) - static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, VirtIOPCIProxy *dev); @@ -314,12 +311,14 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) msix_unuse_all_vectors(&proxy->pci_dev); } - /* Linux before 2.6.34 sets the device as OK without enabling - the PCI device bus master bit. In this case we need to disable - some safety checks. */ - if ((val & VIRTIO_CONFIG_S_DRIVER_OK) && - !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; + /* Linux before 2.6.34 drives the device without enabling + the PCI device bus master bit. Enable it automatically + for the guest. This is a PCI spec violation but so is + initiating DMA with bus master bit clear. */ + if (val == (VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER)) { + pci_default_write_config(&proxy->pci_dev, PCI_COMMAND, + proxy->pci_dev.config[PCI_COMMAND] | + PCI_COMMAND_MASTER, 1); } break; case VIRTIO_MSI_CONFIG_VECTOR: @@ -470,13 +469,18 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + uint8_t cmd = proxy->pci_dev.config[PCI_COMMAND]; + pci_default_write_config(pci_dev, address, val, len); if (range_covers_byte(address, len, PCI_COMMAND) && !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) && - !(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { + (cmd & PCI_COMMAND_MASTER)) { + /* Bus driver disables bus mastering - make it act + * as a kind of reset to render the device quiescent. */ virtio_pci_stop_ioeventfd(proxy); - virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); + virtio_reset(vdev); + msix_unuse_all_vectors(&proxy->pci_dev); } } @@ -885,11 +889,19 @@ static void virtio_pci_vmstate_change(DeviceState *d, bool running) VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); if (running) { - /* Try to find out if the guest has bus master disabled, but is - in ready state. Then we have a buggy guest OS. */ - if ((vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && - !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; + /* Linux before 2.6.34 drives the device without enabling + the PCI device bus master bit. Enable it automatically + for the guest. This is a PCI spec violation but so is + initiating DMA with bus master bit clear. + Note: this only makes a difference when migrating + across QEMU versions from an old QEMU, as for new QEMU + bus master and driver bits are always in sync. + TODO: consider enabling conditionally for compat machine types. */ + if (vdev->status & (VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER)) { + pci_default_write_config(&proxy->pci_dev, PCI_COMMAND, + proxy->pci_dev.config[PCI_COMMAND] | + PCI_COMMAND_MASTER, 1); } virtio_pci_start_ioeventfd(proxy); } else { @@ -1030,7 +1042,6 @@ static void virtio_pci_reset(DeviceState *qdev) virtio_pci_stop_ioeventfd(proxy); virtio_bus_reset(bus); msix_unuse_all_vectors(&proxy->pci_dev); - proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } static Property virtio_pci_properties[] = { diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index ac222385d6..5c981801f3 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1108,10 +1108,7 @@ static void virtio_vmstate_change(void *opaque, int running, RunState state) BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK); - - if (running) { - vdev->vm_running = running; - } + vdev->vm_running = running; if (backend_run) { virtio_set_status(vdev, vdev->status); @@ -1124,10 +1121,6 @@ static void virtio_vmstate_change(void *opaque, int running, RunState state) if (!backend_run) { virtio_set_status(vdev, vdev->status); } - - if (!running) { - vdev->vm_running = running; - } } void virtio_init(VirtIODevice *vdev, const char *name, diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 0799ff29b0..178fee2ef6 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -242,16 +242,16 @@ struct PropertyInfo { /** * GlobalProperty: - * @not_used: Track use of a global property. Defaults to false in all C99 - * struct initializations. - * - * This prevents reports of .compat_props when they are not used. + * @user_provided: Set to true if property comes from user-provided config + * (command-line or config file). + * @used: Set to true if property was used when initializing a device. */ typedef struct GlobalProperty { const char *driver; const char *property; const char *value; - bool not_used; + bool user_provided; + bool used; QTAILQ_ENTRY(GlobalProperty) next; } GlobalProperty; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 77fe3a12b7..ae56ee59a4 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -177,7 +177,7 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); void qdev_prop_register_global(GlobalProperty *prop); void qdev_prop_register_global_list(GlobalProperty *props); -int qdev_prop_check_global(void); +int qdev_prop_check_globals(void); void qdev_prop_set_globals(DeviceState *dev, Error **errp); void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename, Error **errp); diff --git a/tests/Makefile b/tests/Makefile index d5db97ba63..a5e3d0c369 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -58,7 +58,7 @@ check-unit-y += tests/test-int128$(EXESUF) # all code tested by test-int128 is inside int128.h gcov-files-test-int128-y = check-unit-y += tests/test-bitops$(EXESUF) -check-unit-y += tests/test-qdev-global-props$(EXESUF) +check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += tests/test-qdev-global-props$(EXESUF) check-unit-y += tests/check-qom-interface$(EXESUF) gcov-files-check-qom-interface-y = qom/object.c check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF) diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c index 2bef04c76f..0be98355c0 100644 --- a/tests/test-qdev-global-props.c +++ b/tests/test-qdev-global-props.c @@ -65,7 +65,7 @@ static const TypeInfo static_prop_type = { }; /* Test simple static property setting to default value */ -static void test_static_prop(void) +static void test_static_prop_subprocess(void) { MyType *mt; @@ -75,8 +75,16 @@ static void test_static_prop(void) g_assert_cmpuint(mt->prop1, ==, PROP_DEFAULT); } +static void test_static_prop(void) +{ + g_test_trap_subprocess("/qdev/properties/static/default/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr(""); + g_test_trap_assert_stdout(""); +} + /* Test setting of static property using global properties */ -static void test_static_globalprop(void) +static void test_static_globalprop_subprocess(void) { MyType *mt; static GlobalProperty props[] = { @@ -93,10 +101,21 @@ static void test_static_globalprop(void) g_assert_cmpuint(mt->prop2, ==, PROP_DEFAULT); } +static void test_static_globalprop(void) +{ + g_test_trap_subprocess("/qdev/properties/static/global/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr(""); + g_test_trap_assert_stdout(""); +} + #define TYPE_DYNAMIC_PROPS "dynamic-prop-type" #define DYNAMIC_TYPE(obj) \ OBJECT_CHECK(MyType, (obj), TYPE_DYNAMIC_PROPS) +#define TYPE_UNUSED_HOTPLUG "hotplug-type" +#define TYPE_UNUSED_NOHOTPLUG "nohotplug-type" + static void prop1_accessor(Object *obj, Visitor *v, void *opaque, @@ -143,14 +162,56 @@ static const TypeInfo dynamic_prop_type = { .class_init = dynamic_class_init, }; -/* Test setting of static property using global properties */ -static void test_dynamic_globalprop(void) +static void hotplug_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = NULL; + dc->hotpluggable = true; +} + +static const TypeInfo hotplug_type = { + .name = TYPE_UNUSED_HOTPLUG, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyType), + .instance_init = dynamic_instance_init, + .class_init = hotplug_class_init, +}; + +static void nohotplug_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = NULL; + dc->hotpluggable = false; +} + +static const TypeInfo nohotplug_type = { + .name = TYPE_UNUSED_NOHOTPLUG, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyType), + .instance_init = dynamic_instance_init, + .class_init = nohotplug_class_init, +}; + +#define TYPE_NONDEVICE "nondevice-type" + +static const TypeInfo nondevice_type = { + .name = TYPE_NONDEVICE, + .parent = TYPE_OBJECT, +}; + +/* Test setting of dynamic properties using global properties */ +static void test_dynamic_globalprop_subprocess(void) { MyType *mt; static GlobalProperty props[] = { - { TYPE_DYNAMIC_PROPS, "prop1", "101" }, - { TYPE_DYNAMIC_PROPS, "prop2", "102" }, + { TYPE_DYNAMIC_PROPS, "prop1", "101", true }, + { TYPE_DYNAMIC_PROPS, "prop2", "102", true }, { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true }, + { TYPE_UNUSED_HOTPLUG, "prop4", "104", true }, + { TYPE_UNUSED_NOHOTPLUG, "prop5", "105", true }, + { TYPE_NONDEVICE, "prop6", "106", true }, {} }; int all_used; @@ -162,8 +223,67 @@ static void test_dynamic_globalprop(void) g_assert_cmpuint(mt->prop1, ==, 101); g_assert_cmpuint(mt->prop2, ==, 102); - all_used = qdev_prop_check_global(); + all_used = qdev_prop_check_globals(); g_assert_cmpuint(all_used, ==, 1); + g_assert(props[0].used); + g_assert(props[1].used); + g_assert(!props[2].used); + g_assert(!props[3].used); + g_assert(!props[4].used); + g_assert(!props[5].used); +} + +static void test_dynamic_globalprop(void) +{ + g_test_trap_subprocess("/qdev/properties/dynamic/global/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr_unmatched("*prop1*"); + g_test_trap_assert_stderr_unmatched("*prop2*"); + g_test_trap_assert_stderr("*Warning: global dynamic-prop-type-bad.prop3 has invalid class name\n*"); + g_test_trap_assert_stderr_unmatched("*prop4*"); + g_test_trap_assert_stderr("*Warning: global nohotplug-type.prop5=105 not used\n*"); + g_test_trap_assert_stderr("*Warning: global nondevice-type.prop6 has invalid class name\n*"); + g_test_trap_assert_stdout(""); +} + +/* Test setting of dynamic properties using user_provided=false properties */ +static void test_dynamic_globalprop_nouser_subprocess(void) +{ + MyType *mt; + static GlobalProperty props[] = { + { TYPE_DYNAMIC_PROPS, "prop1", "101" }, + { TYPE_DYNAMIC_PROPS, "prop2", "102" }, + { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103" }, + { TYPE_UNUSED_HOTPLUG, "prop4", "104" }, + { TYPE_UNUSED_NOHOTPLUG, "prop5", "105" }, + { TYPE_NONDEVICE, "prop6", "106" }, + {} + }; + int all_used; + + qdev_prop_register_global_list(props); + + mt = DYNAMIC_TYPE(object_new(TYPE_DYNAMIC_PROPS)); + qdev_init_nofail(DEVICE(mt)); + + g_assert_cmpuint(mt->prop1, ==, 101); + g_assert_cmpuint(mt->prop2, ==, 102); + all_used = qdev_prop_check_globals(); + g_assert_cmpuint(all_used, ==, 0); + g_assert(props[0].used); + g_assert(props[1].used); + g_assert(!props[2].used); + g_assert(!props[3].used); + g_assert(!props[4].used); + g_assert(!props[5].used); +} + +static void test_dynamic_globalprop_nouser(void) +{ + g_test_trap_subprocess("/qdev/properties/dynamic/global/nouser/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr(""); + g_test_trap_assert_stdout(""); } int main(int argc, char **argv) @@ -173,10 +293,29 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); type_register_static(&static_prop_type); type_register_static(&dynamic_prop_type); + type_register_static(&hotplug_type); + type_register_static(&nohotplug_type); + type_register_static(&nondevice_type); - g_test_add_func("/qdev/properties/static/default", test_static_prop); - g_test_add_func("/qdev/properties/static/global", test_static_globalprop); - g_test_add_func("/qdev/properties/dynamic/global", test_dynamic_globalprop); + g_test_add_func("/qdev/properties/static/default/subprocess", + test_static_prop_subprocess); + g_test_add_func("/qdev/properties/static/default", + test_static_prop); + + g_test_add_func("/qdev/properties/static/global/subprocess", + test_static_globalprop_subprocess); + g_test_add_func("/qdev/properties/static/global", + test_static_globalprop); + + g_test_add_func("/qdev/properties/dynamic/global/subprocess", + test_dynamic_globalprop_subprocess); + g_test_add_func("/qdev/properties/dynamic/global", + test_dynamic_globalprop); + + g_test_add_func("/qdev/properties/dynamic/global/nouser/subprocess", + test_dynamic_globalprop_nouser_subprocess); + g_test_add_func("/qdev/properties/dynamic/global/nouser", + test_dynamic_globalprop_nouser); g_test_run(); diff --git a/vl.c b/vl.c index 5db0d08496..dc792fe08b 100644 --- a/vl.c +++ b/vl.c @@ -4542,7 +4542,7 @@ int main(int argc, char **argv, char **envp) } } - qdev_prop_check_global(); + qdev_prop_check_globals(); if (vmstate_dump_file) { /* dump and exit */ dump_vmstate_json_to_file(vmstate_dump_file);