From 2a6f395b9a849942804fae626dad505d17f247da Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Wed, 14 Jun 2017 11:49:39 -0300 Subject: [PATCH 01/15] device-crash-test: Fix regexp on whitelist The "||" in the whitelist entry was not escaped, making the regexp match all strings, on every single cases where QEMU aborted. Signed-off-by: Eduardo Habkost Message-Id: <20170614144939.1115-1-ehabkost@redhat.com> --- scripts/device-crash-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 5f90e9bb54..e77b693eb2 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -219,7 +219,7 @@ ERROR_WHITELIST = [ {'exitcode':-6, 'log':r"Object .* is not an instance of type spapr-machine", 'loglevel':logging.ERROR}, {'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR}, {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat || se->instance_id == 0' failed", 'loglevel':logging.ERROR}, + {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR}, {'exitcode':-11, 'device':'stm32f205-soc', 'loglevel':logging.ERROR, 'expected':True}, {'exitcode':-11, 'device':'xlnx,zynqmp', 'loglevel':logging.ERROR, 'expected':True}, {'exitcode':-11, 'device':'mips-cps', 'loglevel':logging.ERROR, 'expected':True}, From 3f0058bbc1f057dee0c6b819b1119f1f58a3a6e8 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 7 Jul 2017 18:30:51 -0300 Subject: [PATCH 02/15] tests: Test case for object_resolve_path*() Test for partial path lookup using object_resolve_path*(). Signed-off-by: Eduardo Habkost Message-Id: <20170707213052.13087-2-ehabkost@redhat.com> Tested-by: Mark Cave-Ayland Reviewed-by: Igor Mammedov Signed-off-by: Eduardo Habkost --- tests/check-qom-proplist.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c index 8e432e9ab6..f6e7823338 100644 --- a/tests/check-qom-proplist.c +++ b/tests/check-qom-proplist.c @@ -568,6 +568,44 @@ static void test_dummy_delchild(void) object_unparent(OBJECT(dev)); } +static void test_qom_partial_path(void) +{ + Object *root = object_get_objects_root(); + Object *cont1 = container_get(root, "/cont1"); + Object *obj1 = object_new(TYPE_DUMMY); + Object *obj2a = object_new(TYPE_DUMMY); + Object *obj2b = object_new(TYPE_DUMMY); + bool ambiguous; + + /* Objects created: + * /cont1 + * /cont1/obj1 + * /cont1/obj2 (obj2a) + * /obj2 (obj2b) + */ + object_property_add_child(cont1, "obj1", obj1, &error_abort); + object_unref(obj1); + object_property_add_child(cont1, "obj2", obj2a, &error_abort); + object_unref(obj2a); + object_property_add_child(root, "obj2", obj2b, &error_abort); + object_unref(obj2b); + + ambiguous = false; + g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous)); + g_assert(ambiguous); + + ambiguous = false; + g_assert(!object_resolve_path("obj2", &ambiguous)); + g_assert(ambiguous); + + ambiguous = false; + g_assert(object_resolve_path("obj1", &ambiguous) == obj1); + g_assert(!ambiguous); + + object_unparent(obj2b); + object_unparent(cont1); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -585,6 +623,7 @@ int main(int argc, char **argv) g_test_add_func("/qom/proplist/getenum", test_dummy_getenum); g_test_add_func("/qom/proplist/iterator", test_dummy_iterator); g_test_add_func("/qom/proplist/delchild", test_dummy_delchild); + g_test_add_func("/qom/resolve/partial", test_qom_partial_path); return g_test_run(); } From 5eb6a3c50185e101f87382f41fb66eed5784e7ac Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Mon, 10 Jul 2017 21:43:01 -0300 Subject: [PATCH 03/15] qdev: fix the order compat and global properties are applied The current code recursively applies global properties from child up to parent types. This can cause properties passed with the -global option to be silently overridden by internal compat properties. This is exactly what happened with virtio-*-pci drivers since commit: "9a4c0e220d8a hw/virtio-pci: fix virtio behaviour" Passing -device virtio-blk-pci.disable-modern=off had no effect on 2.6 machine types because the internal virtio-pci.disable-modern=on compat property always prevailed. A workaround for this was included with commit 0bcba41f ("machine: Convert abstract typename on compat_props to subclass names"). This patch fixes the issue properly by reversing the logic: we now go through the global property list and, for each property, we check if it is applicable to the device. This results in compat properties being applied first, in the order they appear in the HW_COMPAT_* macros, followed by global properties, in the order they appear on the command line. Signed-off-by: Greg Kurz Message-Id: <148103887228.22326.478406873609299999.stgit@bahia.lab.toulouse-stg.fr.ibm.com> Signed-off-by: Eduardo Habkost Message-Id: <20170711004303.3902-2-ehabkost@redhat.com> Reviewed-by: Cornelia Huck Reviewed-by: Halil Pasic Signed-off-by: Eduardo Habkost --- hw/core/qdev-properties.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index dcecdf03e5..58a8f92d92 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1149,8 +1149,7 @@ int qdev_prop_check_globals(void) return ret; } -static void qdev_prop_set_globals_for_type(DeviceState *dev, - const char *typename) +void qdev_prop_set_globals(DeviceState *dev) { GList *l; @@ -1158,7 +1157,7 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, GlobalProperty *prop = l->data; Error *err = NULL; - if (strcmp(typename, prop->driver) != 0) { + if (object_dynamic_cast(OBJECT(dev), prop->driver) == NULL) { continue; } prop->used = true; @@ -1176,16 +1175,6 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, } } -void qdev_prop_set_globals(DeviceState *dev) -{ - ObjectClass *class = object_get_class(OBJECT(dev)); - - do { - qdev_prop_set_globals_for_type(dev, object_class_get_name(class)); - class = object_class_get_parent(class); - } while (class); -} - /* --- 64bit unsigned int 'size' type --- */ static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, From 3caca5555844cc7921f0d61f286a4f2b7ebc0a8e Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 10 Jul 2017 21:43:02 -0300 Subject: [PATCH 04/15] test-qdev-global-props: Test global property ordering Test case to detect the bug fixed by commit "qdev: fix the order compat and global properties are applied". Signed-off-by: Eduardo Habkost Message-Id: <20170711004303.3902-3-ehabkost@redhat.com> Reviewed-by: Cornelia Huck Reviewed-by: Greg Kurz Reviewed-by: Halil Pasic Signed-off-by: Eduardo Habkost --- tests/test-qdev-global-props.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c index b25fe892ed..d81b0862d5 100644 --- a/tests/test-qdev-global-props.c +++ b/tests/test-qdev-global-props.c @@ -33,6 +33,8 @@ #define STATIC_TYPE(obj) \ OBJECT_CHECK(MyType, (obj), TYPE_STATIC_PROPS) +#define TYPE_SUBCLASS "static_prop_subtype" + #define PROP_DEFAULT 100 typedef struct MyType { @@ -63,6 +65,11 @@ static const TypeInfo static_prop_type = { .class_init = static_prop_class_init, }; +static const TypeInfo subclass_type = { + .name = TYPE_SUBCLASS, + .parent = TYPE_STATIC_PROPS, +}; + /* Test simple static property setting to default value */ static void test_static_prop_subprocess(void) { @@ -279,12 +286,35 @@ static void test_dynamic_globalprop_nouser(void) g_test_trap_assert_stdout(""); } +/* Test if global props affecting subclasses are applied in the right order */ +static void test_subclass_global_props(void) +{ + MyType *mt; + /* Global properties must be applied in the order they were registered */ + static GlobalProperty props[] = { + { TYPE_STATIC_PROPS, "prop1", "101" }, + { TYPE_SUBCLASS, "prop1", "102" }, + { TYPE_SUBCLASS, "prop2", "103" }, + { TYPE_STATIC_PROPS, "prop2", "104" }, + {} + }; + + qdev_prop_register_global_list(props); + + mt = STATIC_TYPE(object_new(TYPE_SUBCLASS)); + qdev_init_nofail(DEVICE(mt)); + + g_assert_cmpuint(mt->prop1, ==, 102); + g_assert_cmpuint(mt->prop2, ==, 104); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); module_call_init(MODULE_INIT_QOM); type_register_static(&static_prop_type); + type_register_static(&subclass_type); type_register_static(&dynamic_prop_type); type_register_static(&hotplug_type); type_register_static(&nohotplug_type); @@ -310,6 +340,9 @@ int main(int argc, char **argv) g_test_add_func("/qdev/properties/dynamic/global/nouser", test_dynamic_globalprop_nouser); + g_test_add_func("/qdev/properties/global/subclass", + test_subclass_global_props); + g_test_run(); return 0; From 6d1e30c4ac78b93c64730a68a86c9f27900352d5 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 10 Jul 2017 21:43:03 -0300 Subject: [PATCH 05/15] Revert "machine: Convert abstract typename on compat_props to subclass names" This reverts commit 0bcba41fe379e4c6834adcf1456d9099db31a5b2. The bug addressed by that commit is now fixed in a better way by the commit "qdev: fix the order compat and global properties are applied". Signed-off-by: Eduardo Habkost Message-Id: <20170711004303.3902-4-ehabkost@redhat.com> Acked-by: Cornelia Huck Reviewed-by: Greg Kurz Reviewed-by: Halil Pasic Signed-off-by: Eduardo Habkost --- hw/core/machine.c | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index dc431fabf5..41b53a17ad 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -770,18 +770,11 @@ static void machine_class_finalize(ObjectClass *klass, void *data) g_free(mc->name); } -static void machine_register_compat_for_subclass(ObjectClass *oc, void *opaque) -{ - GlobalProperty *p = opaque; - register_compat_prop(object_class_get_name(oc), p->property, p->value); -} - void machine_register_compat_props(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); int i; GlobalProperty *p; - ObjectClass *oc; if (!mc->compat_props) { return; @@ -789,22 +782,9 @@ void machine_register_compat_props(MachineState *machine) for (i = 0; i < mc->compat_props->len; i++) { p = g_array_index(mc->compat_props, GlobalProperty *, i); - oc = object_class_by_name(p->driver); - if (oc && object_class_is_abstract(oc)) { - /* temporary hack to make sure we do not override - * globals set explicitly on -global: if an abstract class - * is on compat_props, register globals for all its - * non-abstract subtypes instead. - * - * This doesn't solve the problem for cases where - * a non-abstract typename mentioned on compat_props - * has subclasses, like spapr-pci-host-bridge. - */ - object_class_foreach(machine_register_compat_for_subclass, - p->driver, false, p); - } else { - register_compat_prop(p->driver, p->property, p->value); - } + /* Machine compat_props must never cause errors: */ + p->errp = &error_abort; + qdev_prop_register_global(p); } } From ebcc479eee740937e70a94a468effcf2126a572b Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 7 Jul 2017 18:30:52 -0300 Subject: [PATCH 06/15] qom: Fix ambiguous path detection when ambiguous=NULL object_resolve_path*() ambiguous path detection breaks when ambiguous==NULL and the object tree have 3 objects of the same type and only 2 of them are under the same parent. e.g.: /container/obj1 (TYPE_FOO) /container/obj2 (TYPE_FOO) /obj2 (TYPE_FOO) With the above tree, object_resolve_path_type("", TYPE_FOO, NULL) will incorrectly return /obj2, because the search inside "/container" will return NULL, and the match at "/obj2" won't be detected as ambiguous. Fix that by always calling object_resolve_partial_path() with a non-NULL ambiguous parameter. Test case included. Reported-by: Igor Mammedov Cc: Mark Cave-Ayland Signed-off-by: Eduardo Habkost Message-Id: <20170707213052.13087-3-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- qom/object.c | 17 ++++++++--------- tests/check-qom-proplist.c | 3 +++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/qom/object.c b/qom/object.c index dfdbd50f04..fe6e744b4d 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1712,15 +1712,13 @@ static Object *object_resolve_partial_path(Object *parent, typename, ambiguous); if (found) { if (obj) { - if (ambiguous) { - *ambiguous = true; - } + *ambiguous = true; return NULL; } obj = found; } - if (ambiguous && *ambiguous) { + if (*ambiguous) { return NULL; } } @@ -1729,7 +1727,7 @@ static Object *object_resolve_partial_path(Object *parent, } Object *object_resolve_path_type(const char *path, const char *typename, - bool *ambiguous) + bool *ambiguousp) { Object *obj; gchar **parts; @@ -1738,11 +1736,12 @@ Object *object_resolve_path_type(const char *path, const char *typename, assert(parts); if (parts[0] == NULL || strcmp(parts[0], "") != 0) { - if (ambiguous) { - *ambiguous = false; - } + bool ambiguous = false; obj = object_resolve_partial_path(object_get_root(), parts, - typename, ambiguous); + typename, &ambiguous); + if (ambiguousp) { + *ambiguousp = ambiguous; + } } else { obj = object_resolve_abs_path(object_get_root(), parts, typename, 1); } diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c index f6e7823338..432b66585f 100644 --- a/tests/check-qom-proplist.c +++ b/tests/check-qom-proplist.c @@ -593,14 +593,17 @@ static void test_qom_partial_path(void) ambiguous = false; g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous)); g_assert(ambiguous); + g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL)); ambiguous = false; g_assert(!object_resolve_path("obj2", &ambiguous)); g_assert(ambiguous); + g_assert(!object_resolve_path("obj2", NULL)); ambiguous = false; g_assert(object_resolve_path("obj1", &ambiguous) == obj1); g_assert(!ambiguous); + g_assert(object_resolve_path("obj1", NULL) == obj1); object_unparent(obj2b); object_unparent(cont1); From 6e99c075a0171b7ff74386749125510a6641ea49 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 14 Jul 2017 10:40:06 +0100 Subject: [PATCH 07/15] fw_cfg: switch fw_cfg_find() to locate the fw_cfg device by type rather than path This will enable the fw_cfg device to be placed anywhere within the QOM tree regardless of its machine location. Note that we also add a comment to document the behaviour that we return NULL to indicate failure where either no fw_cfg device or multiple fw_cfg devices are found. Signed-off-by: Mark Cave-Ayland Message-Id: <1500025208-14827-2-git-send-email-mark.cave-ayland@ilande.co.uk> Reviewed-by: Igor Mammedov Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- hw/nvram/fw_cfg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index e881e3b812..3a988b669b 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -1017,7 +1017,8 @@ FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) FWCfgState *fw_cfg_find(void) { - return FW_CFG(object_resolve_path(FW_CFG_PATH, NULL)); + /* Returns NULL unless there is exactly one fw_cfg device */ + return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL)); } static void fw_cfg_class_init(ObjectClass *klass, void *data) From 38f3adc34de83bf75d2023831dc520d32568a2d9 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 14 Jul 2017 10:40:07 +0100 Subject: [PATCH 08/15] fw_cfg: move qdev_init_nofail() from fw_cfg_init1() to callers When looking to instantiate a TYPE_FW_CFG_MEM or TYPE_FW_CFG_IO device to be able to wire it up differently, it is much more convenient for the caller to instantiate the device and have the fw_cfg default files already preloaded during realize. Move fw_cfg_init1() to the end of both the fw_cfg_mem_realize() and fw_cfg_io_realize() functions so it no longer needs to be called manually when instantiating the device, and also rename it to fw_cfg_common_realize() which better describes its new purpose. Since it is now the responsibility of the machine to wire up the fw_cfg device it is necessary to introduce a object_property_add_child() call into fw_cfg_init_io() and fw_cfg_init_mem() to link the fw_cfg device to the root machine object as before. Finally with the previous change to fw_cfg_find() we can now remove the assert() preventing multiple fw_cfg devices being instantiated and replace them with a simple call to fw_cfg_find() at realize time instead. This allows us to remove FW_CFG_NAME and FW_CFG_PATH since they are no longer required. Signed-off-by: Mark Cave-Ayland Reviewed-by: Igor Mammedov Message-Id: <1500025208-14827-3-git-send-email-mark.cave-ayland@ilande.co.uk> Reviewed-by: Eduardo Habkost Signed-off-by: Eduardo Habkost --- hw/nvram/fw_cfg.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 3a988b669b..97c7677073 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -37,9 +37,6 @@ #define FW_CFG_FILE_SLOTS_DFLT 0x20 -#define FW_CFG_NAME "fw_cfg" -#define FW_CFG_PATH "/machine/" FW_CFG_NAME - #define TYPE_FW_CFG "fw_cfg" #define TYPE_FW_CFG_IO "fw_cfg_io" #define TYPE_FW_CFG_MEM "fw_cfg_mem" @@ -909,17 +906,16 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) -static void fw_cfg_init1(DeviceState *dev) +static void fw_cfg_common_realize(DeviceState *dev, Error **errp) { FWCfgState *s = FW_CFG(dev); MachineState *machine = MACHINE(qdev_get_machine()); uint32_t version = FW_CFG_VERSION; - assert(!object_resolve_path(FW_CFG_PATH, NULL)); - - object_property_add_child(OBJECT(machine), FW_CFG_NAME, OBJECT(s), NULL); - - qdev_init_nofail(dev); + if (!fw_cfg_find()) { + error_setg(errp, "at most one %s device is permitted", TYPE_FW_CFG); + return; + } fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); fw_cfg_add_bytes(s, FW_CFG_UUID, &qemu_uuid, 16); @@ -952,7 +948,9 @@ FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, qdev_prop_set_bit(dev, "dma_enabled", false); } - fw_cfg_init1(dev); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(dev), NULL); + qdev_init_nofail(dev); sbd = SYS_BUS_DEVICE(dev); ios = FW_CFG_IO(dev); @@ -990,7 +988,9 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, qdev_prop_set_bit(dev, "dma_enabled", false); } - fw_cfg_init1(dev); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(dev), NULL); + qdev_init_nofail(dev); sbd = SYS_BUS_DEVICE(dev); sysbus_mmio_map(sbd, 0, ctl_addr); @@ -1021,6 +1021,7 @@ FWCfgState *fw_cfg_find(void) return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL)); } + static void fw_cfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1092,6 +1093,8 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma", sizeof(dma_addr_t)); } + + fw_cfg_common_realize(dev, errp); } static void fw_cfg_io_class_init(ObjectClass *klass, void *data) @@ -1158,6 +1161,8 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) sizeof(dma_addr_t)); sysbus_init_mmio(sbd, &FW_CFG(s)->dma_iomem); } + + fw_cfg_common_realize(dev, errp); } static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) From 39736e18cda64c501caab00d93ebfd8b4cf6b36e Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 14 Jul 2017 10:40:08 +0100 Subject: [PATCH 09/15] fw_cfg: move QOM type defines and fw_cfg types into fw_cfg.h By exposing FWCfgIoState and FWCfgMemState internals we allow the possibility for the internal MemoryRegion fields to be mapped by name for boards that wish to wire up the fw_cfg device themselves. Signed-off-by: Mark Cave-Ayland Reviewed-by: Laszlo Ersek Message-Id: <1500025208-14827-4-git-send-email-mark.cave-ayland@ilande.co.uk> Signed-off-by: Eduardo Habkost --- hw/nvram/fw_cfg.c | 49 +------------------------------------- include/hw/nvram/fw_cfg.h | 50 +++++++++++++++++++++++++++++++++++++++ include/qemu/typedefs.h | 1 + 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 97c7677073..5bd904487f 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -37,14 +37,6 @@ #define FW_CFG_FILE_SLOTS_DFLT 0x20 -#define TYPE_FW_CFG "fw_cfg" -#define TYPE_FW_CFG_IO "fw_cfg_io" -#define TYPE_FW_CFG_MEM "fw_cfg_mem" - -#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG) -#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO) -#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM) - /* FW_CFG_VERSION bits */ #define FW_CFG_VERSION 0x01 #define FW_CFG_VERSION_DMA 0x02 @@ -58,51 +50,12 @@ #define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ -typedef struct FWCfgEntry { +struct FWCfgEntry { uint32_t len; bool allow_write; uint8_t *data; void *callback_opaque; FWCfgReadCallback read_callback; -} FWCfgEntry; - -struct FWCfgState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint16_t file_slots; - FWCfgEntry *entries[2]; - int *entry_order; - FWCfgFiles *files; - uint16_t cur_entry; - uint32_t cur_offset; - Notifier machine_ready; - - int fw_cfg_order_override; - - bool dma_enabled; - dma_addr_t dma_addr; - AddressSpace *dma_as; - MemoryRegion dma_iomem; -}; - -struct FWCfgIoState { - /*< private >*/ - FWCfgState parent_obj; - /*< public >*/ - - MemoryRegion comb_iomem; -}; - -struct FWCfgMemState { - /*< private >*/ - FWCfgState parent_obj; - /*< public >*/ - - MemoryRegion ctl_iomem, data_iomem; - uint32_t data_width; - MemoryRegionOps wide_data_ops; }; #define JPG_FILE 0 diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index b980cbaebf..b77ea48abb 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -1,8 +1,19 @@ #ifndef FW_CFG_H #define FW_CFG_H +#include "qemu/typedefs.h" #include "exec/hwaddr.h" #include "hw/nvram/fw_cfg_keys.h" +#include "hw/sysbus.h" +#include "sysemu/dma.h" + +#define TYPE_FW_CFG "fw_cfg" +#define TYPE_FW_CFG_IO "fw_cfg_io" +#define TYPE_FW_CFG_MEM "fw_cfg_mem" + +#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG) +#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO) +#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM) typedef struct FWCfgFile { uint32_t size; /* file size */ @@ -35,6 +46,45 @@ typedef struct FWCfgDmaAccess { typedef void (*FWCfgReadCallback)(void *opaque); +struct FWCfgState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint16_t file_slots; + FWCfgEntry *entries[2]; + int *entry_order; + FWCfgFiles *files; + uint16_t cur_entry; + uint32_t cur_offset; + Notifier machine_ready; + + int fw_cfg_order_override; + + bool dma_enabled; + dma_addr_t dma_addr; + AddressSpace *dma_as; + MemoryRegion dma_iomem; +}; + +struct FWCfgIoState { + /*< private >*/ + FWCfgState parent_obj; + /*< public >*/ + + MemoryRegion comb_iomem; +}; + +struct FWCfgMemState { + /*< private >*/ + FWCfgState parent_obj; + /*< public >*/ + + MemoryRegion ctl_iomem, data_iomem; + uint32_t data_width; + MemoryRegionOps wide_data_ops; +}; + /** * fw_cfg_add_bytes: * @s: fw_cfg device being modified diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index b19159104c..7b0d4e7e05 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -30,6 +30,7 @@ typedef struct DisplaySurface DisplaySurface; typedef struct DriveInfo DriveInfo; typedef struct Error Error; typedef struct EventNotifier EventNotifier; +typedef struct FWCfgEntry FWCfgEntry; typedef struct FWCfgIoState FWCfgIoState; typedef struct FWCfgMemState FWCfgMemState; typedef struct FWCfgState FWCfgState; From 1ce36bfe6424243082d3d7c2330e1a0a4ff72a43 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Tue, 9 May 2017 14:27:36 +0100 Subject: [PATCH 10/15] i386: expose "TCGTCGTCGTCG" in the 0x40000000 CPUID leaf Currently when running KVM, we expose "KVMKVMKVM\0\0\0" in the 0x40000000 CPUID leaf. Other hypervisors (VMWare, HyperV, Xen, BHyve) all do the same thing, which leaves TCG as the odd one out. The CPUID signature is used by software to detect which virtual environment they are running in and (potentially) change behaviour in certain ways. For example, systemd supports a ConditionVirtualization= setting in unit files. The virt-what command can also report the virt type it is running on Currently both these apps have to resort to custom hacks like looking for 'fw-cfg' entry in the /proc/device-tree file to identify TCG. This change thus proposes a signature "TCGTCGTCGTCG" to be reported when running under TCG. To hide this, the -cpu option tcg-cpuid=off can be used. Signed-off-by: Daniel P. Berrange Message-Id: <20170509132736.10071-3-berrange@redhat.com> Signed-off-by: Eduardo Habkost --- include/hw/i386/pc.h | 5 +++++ target/i386/cpu.c | 28 ++++++++++++++++++++++++++++ target/i386/cpu.h | 1 + 3 files changed, 34 insertions(+) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index f48d167207..d80859bfad 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -379,6 +379,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); #define PC_COMPAT_2_8 \ HW_COMPAT_2_8 \ + {\ + .driver = TYPE_X86_CPU,\ + .property = "tcg-cpuid",\ + .value = "off",\ + },\ {\ .driver = "kvmclock",\ .property = "x-mach-use-reliable-get-clock",\ diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4de91d5801..71983ef4e6 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2632,12 +2632,15 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, CPUState *cs = CPU(cpu); uint32_t pkg_offset; uint32_t limit; + uint32_t signature[3]; /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { limit = env->cpuid_xlevel2; } else if (index >= 0x80000000) { limit = env->cpuid_xlevel; + } else if (index >= 0x40000000) { + limit = 0x40000001; } else { limit = env->cpuid_level; } @@ -2872,6 +2875,30 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x40000000: + /* + * CPUID code in kvm_arch_init_vcpu() ignores stuff + * set here, but we restrict to TCG none the less. + */ + if (tcg_enabled() && cpu->expose_tcg) { + memcpy(signature, "TCGTCGTCGTCG", 12); + *eax = 0x40000001; + *ebx = signature[0]; + *ecx = signature[1]; + *edx = signature[2]; + } else { + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + } + break; + case 0x40000001: + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; case 0x80000000: *eax = env->cpuid_xlevel; *ebx = env->cpuid_vendor1; @@ -4018,6 +4045,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("kvm-no-smi-migration", X86CPU, kvm_no_smi_migration, false), DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), + DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), DEFINE_PROP_END_OF_LIST() }; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7a228afd04..051867399b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1218,6 +1218,7 @@ struct X86CPU { bool check_cpuid; bool enforce_cpuid; bool expose_kvm; + bool expose_tcg; bool migratable; bool max_features; /* Enable all supported features automatically */ uint32_t apic_id; From cf70879f14d83287d0d6af3b0d7ba7a322ea9ece Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 23 Jun 2017 12:29:09 -0300 Subject: [PATCH 11/15] i386: Update comment about XSAVES on Skylake-Client Reported-by: Paolo Bonzini Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 71983ef4e6..e702691795 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1331,7 +1331,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX, /* Missing: XSAVES (not supported by some Linux versions, - * including v4.1 to v4.6). + * including v4.1 to v4.12). * KVM doesn't yet expose any XSAVES state save component, * and the only one defined in Skylake (processor tracing) * probably will block migration anyway. From 53f9a6f45fb214540cb40af45efc11ac40ac454c Mon Sep 17 00:00:00 2001 From: "Boqun Feng (Intel)" Date: Wed, 21 Jun 2017 13:29:34 +0800 Subject: [PATCH 12/15] i386: add Skylake-Server cpu model Introduce Skylake-Server cpu mode which inherits the features from Skylake-Client and supports some additional features that are: AVX512, CLWB and PGPE1GB. Signed-off-by: Boqun Feng (Intel) Message-Id: <20170621052935.20715-1-boqun.feng@gmail.com> [ehabkost: copied comment about XSAVES from Skylake-Client] Signed-off-by: Eduardo Habkost --- target/i386/cpu.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e702691795..0bbda76323 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1344,6 +1344,54 @@ static X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x80000008, .model_id = "Intel Core Processor (Skylake)", }, + { + .name = "Skylake-Server", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 85, + .stepping = 4, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | + CPUID_7_0_EBX_AVX512VL, + /* Missing: XSAVES (not supported by some Linux versions, + * including v4.1 to v4.12). + * KVM doesn't yet expose any XSAVES state save component, + * and the only one defined in Skylake (processor tracing) + * probably will block migration anyway. + */ + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Processor (Skylake)", + }, { .name = "Opteron_G1", .level = 5, From dbb2a604a94f3899fa34bd1ede462f213e822e03 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 7 Jul 2017 09:22:13 -0300 Subject: [PATCH 13/15] tests: Simplify abstract-interfaces check with a helper Add a new type_list_find() helper to device-introspect-test.c, to simplify the code at test_abstract_interfaces(). Reviewed-by: Eric Blake Signed-off-by: Eduardo Habkost Message-Id: <20170707122215.8819-2-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- tests/device-introspect-test.c | 41 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index b1abb2ad89..f1e6dddfa3 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -45,6 +45,22 @@ static QList *qom_list_types(const char *implements, bool abstract) return ret; } +/* Find an entry on a list returned by qom-list-types */ +static QDict *type_list_find(QList *types, const char *name) +{ + QListEntry *e; + + QLIST_FOREACH_ENTRY(types, e) { + QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + const char *ename = qdict_get_str(d, "name"); + if (!strcmp(ename, name)) { + return d; + } + } + + return NULL; +} + static QList *device_type_list(bool abstract) { return qom_list_types("device", abstract); @@ -125,7 +141,7 @@ static void test_abstract_interfaces(void) { QList *all_types; QList *obj_types; - QListEntry *ae; + QListEntry *e; qtest_start(common_args); /* qom-list-types implements=interface would return any type @@ -138,24 +154,15 @@ static void test_abstract_interfaces(void) all_types = qom_list_types(NULL, false); obj_types = qom_list_types("object", false); - QLIST_FOREACH_ENTRY(all_types, ae) { - QDict *at = qobject_to_qdict(qlist_entry_obj(ae)); - const char *aname = qdict_get_str(at, "name"); - QListEntry *oe; - const char *found = NULL; + QLIST_FOREACH_ENTRY(all_types, e) { + QDict *d = qobject_to_qdict(qlist_entry_obj(e)); - QLIST_FOREACH_ENTRY(obj_types, oe) { - QDict *ot = qobject_to_qdict(qlist_entry_obj(oe)); - const char *oname = qdict_get_str(ot, "name"); - if (!strcmp(aname, oname)) { - found = oname; - break; - } - } + /* + * An interface (incorrectly) marked as non-abstract would + * appear on all_types, but not on obj_types: + */ + g_assert(type_list_find(obj_types, qdict_get_str(d, "name"))); - /* Using g_assert_cmpstr() will give more useful failure - * messages than g_assert(found) */ - g_assert_cmpstr(aname, ==, found); } QDECREF(all_types); From 87467eae37e9b4727152292e09acead3ba3c95ed Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 7 Jul 2017 09:22:14 -0300 Subject: [PATCH 14/15] qmp: Include 'abstract' field on 'qom-list-types' output A client may be interested in getting the list of both abstract and non-abstract types. Instead of requiring them to make multiple queries with different filter arguments, just return an 'abstract' field in 'qom-list-types'. In addition to the new test code for validating this field, update the abstract-interfaces test case to query for all 'interface' subtypes (including abstract ones), and to look at the 'abstract' field directly. Reviewed-by: Eric Blake Signed-off-by: Eduardo Habkost Message-Id: <20170707122215.8819-3-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- qapi-schema.json | 5 +++- qmp.c | 1 + tests/device-introspect-test.c | 53 +++++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/qapi-schema.json b/qapi-schema.json index ab438ead70..6486690584 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3051,10 +3051,13 @@ # # @name: the type name found in the search # +# @abstract: the type is abstract and can't be directly instantiated. +# Omitted if false. (since 2.10) +# # Since: 1.1 ## { 'struct': 'ObjectTypeInfo', - 'data': { 'name': 'str' } } + 'data': { 'name': 'str', '*abstract': 'bool' } } ## # @qom-list-types: diff --git a/qmp.c b/qmp.c index 2cd40c3080..7a9de06af4 100644 --- a/qmp.c +++ b/qmp.c @@ -433,6 +433,7 @@ static void qom_list_types_tramp(ObjectClass *klass, void *data) info = g_malloc0(sizeof(*info)); info->name = g_strdup(object_class_get_name(klass)); + info->has_abstract = info->abstract = object_class_is_abstract(klass); e = g_malloc0(sizeof(*e)); e->value = info; diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index f1e6dddfa3..78d4811ba3 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -103,6 +103,33 @@ static void test_device_intro_list(void) qtest_end(); } +static void test_qom_list_fields(void) +{ + QList *all_types; + QList *non_abstract; + QListEntry *e; + + qtest_start(common_args); + + all_types = qom_list_types(NULL, true); + non_abstract = qom_list_types(NULL, false); + + QLIST_FOREACH_ENTRY(all_types, e) { + QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); + bool abstract = qdict_haskey(d, "abstract") ? + qdict_get_bool(d, "abstract") : + false; + bool expected_abstract = !type_list_find(non_abstract, name); + + g_assert(abstract == expected_abstract); + } + + QDECREF(all_types); + QDECREF(non_abstract); + qtest_end(); +} + static void test_device_intro_none(void) { qtest_start(common_args); @@ -144,25 +171,24 @@ static void test_abstract_interfaces(void) QListEntry *e; qtest_start(common_args); - /* qom-list-types implements=interface would return any type - * that implements _any_ interface (not just interface types), - * so use a trick to find the interface type names: - * - list all object types - * - list all types, and look for items that are not - * on the first list + /* + * qom-list-types implements=interface returns all types that + * implement _any_ interface (not just interface types), but + * we can filter them out because interfaces don't implement + * the "object" type. */ - all_types = qom_list_types(NULL, false); - obj_types = qom_list_types("object", false); + all_types = qom_list_types("interface", true); + obj_types = qom_list_types("object", true); QLIST_FOREACH_ENTRY(all_types, e) { QDict *d = qobject_to_qdict(qlist_entry_obj(e)); - /* - * An interface (incorrectly) marked as non-abstract would - * appear on all_types, but not on obj_types: - */ - g_assert(type_list_find(obj_types, qdict_get_str(d, "name"))); + if (type_list_find(obj_types, qdict_get_str(d, "name"))) { + /* Not an interface type */ + continue; + } + g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); } QDECREF(all_types); @@ -175,6 +201,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("device/introspect/list", test_device_intro_list); + qtest_add_func("device/introspect/list-fields", test_qom_list_fields); qtest_add_func("device/introspect/none", test_device_intro_none); qtest_add_func("device/introspect/abstract", test_device_intro_abstract); qtest_add_func("device/introspect/concrete", test_device_intro_concrete); From f86285c571c49e781d3fa46f3bef3835b8e6f393 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 7 Jul 2017 09:22:15 -0300 Subject: [PATCH 15/15] qmp: Include parent type on 'qom-list-types' output Include name of parent type of each type on 'qom-list-types' output. Without this, there's no way to figure out the parents of a given type without making additional 'qom-list-types' queries. In addition to the test case for the new feature, update the abstract-interface test case to use the new field and avoid the "qom-list-types implements=object" trick. Reviewed-by: Eric Blake Signed-off-by: Eduardo Habkost Message-Id: <20170707122215.8819-4-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- qapi-schema.json | 4 +- qmp.c | 5 ++ tests/device-introspect-test.c | 84 ++++++++++++++++++++++++++++++---- 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/qapi-schema.json b/qapi-schema.json index 6486690584..8b015bee2e 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3054,10 +3054,12 @@ # @abstract: the type is abstract and can't be directly instantiated. # Omitted if false. (since 2.10) # +# @parent: Name of parent type, if any (since 2.10) +# # Since: 1.1 ## { 'struct': 'ObjectTypeInfo', - 'data': { 'name': 'str', '*abstract': 'bool' } } + 'data': { 'name': 'str', '*abstract': 'bool', '*parent': 'str' } } ## # @qom-list-types: diff --git a/qmp.c b/qmp.c index 7a9de06af4..b86201e349 100644 --- a/qmp.c +++ b/qmp.c @@ -430,10 +430,15 @@ static void qom_list_types_tramp(ObjectClass *klass, void *data) { ObjectTypeInfoList *e, **pret = data; ObjectTypeInfo *info; + ObjectClass *parent = object_class_get_parent(klass); info = g_malloc0(sizeof(*info)); info->name = g_strdup(object_class_get_name(klass)); info->has_abstract = info->abstract = object_class_is_abstract(klass); + if (parent) { + info->has_parent = true; + info->parent = g_strdup(object_class_get_name(parent)); + } e = g_malloc0(sizeof(*e)); e->value = info; diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index 78d4811ba3..f7162c023f 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -45,6 +45,40 @@ static QList *qom_list_types(const char *implements, bool abstract) return ret; } +/* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */ +static QDict *qom_type_index(QList *types) +{ + QDict *index = qdict_new(); + QListEntry *e; + + QLIST_FOREACH_ENTRY(types, e) { + QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); + QINCREF(d); + qdict_put(index, name, d); + } + return index; +} + +/* Check if @parent is present in the parent chain of @type */ +static bool qom_has_parent(QDict *index, const char *type, const char *parent) +{ + while (type) { + QDict *d = qdict_get_qdict(index, type); + const char *p = d && qdict_haskey(d, "parent") ? + qdict_get_str(d, "parent") : + NULL; + + if (!strcmp(type, parent)) { + return true; + } + + type = p; + } + + return false; +} + /* Find an entry on a list returned by qom-list-types */ static QDict *type_list_find(QList *types, const char *name) { @@ -103,6 +137,30 @@ static void test_device_intro_list(void) qtest_end(); } +/* + * Ensure all entries returned by qom-list-types implements= + * have as a parent. + */ +static void test_qom_list_parents(const char *parent) +{ + QList *types; + QListEntry *e; + QDict *index; + + types = qom_list_types(parent, true); + index = qom_type_index(types); + + QLIST_FOREACH_ENTRY(types, e) { + QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); + + g_assert(qom_has_parent(index, name, parent)); + } + + QDECREF(types); + QDECREF(index); +} + static void test_qom_list_fields(void) { QList *all_types; @@ -125,6 +183,10 @@ static void test_qom_list_fields(void) g_assert(abstract == expected_abstract); } + test_qom_list_parents("object"); + test_qom_list_parents("device"); + test_qom_list_parents("sys-bus-device"); + QDECREF(all_types); QDECREF(non_abstract); qtest_end(); @@ -167,23 +229,25 @@ static void test_device_intro_concrete(void) static void test_abstract_interfaces(void) { QList *all_types; - QList *obj_types; QListEntry *e; + QDict *index; qtest_start(common_args); - /* - * qom-list-types implements=interface returns all types that - * implement _any_ interface (not just interface types), but - * we can filter them out because interfaces don't implement - * the "object" type. - */ + all_types = qom_list_types("interface", true); - obj_types = qom_list_types("object", true); + index = qom_type_index(all_types); QLIST_FOREACH_ENTRY(all_types, e) { QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + const char *name = qdict_get_str(d, "name"); - if (type_list_find(obj_types, qdict_get_str(d, "name"))) { + /* + * qom-list-types implements=interface returns all types + * that implement _any_ interface (not just interface + * types), so skip the ones that don't have "interface" + * on the parent type chain. + */ + if (!qom_has_parent(index, name, "interface")) { /* Not an interface type */ continue; } @@ -192,7 +256,7 @@ static void test_abstract_interfaces(void) } QDECREF(all_types); - QDECREF(obj_types); + QDECREF(index); qtest_end(); }