1 /* 2 * libqos driver framework 3 * 4 * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License version 2 as published by the Free Software Foundation. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, see <http://www.gnu.org/licenses/> 17 */ 18 19 #include "qemu/osdep.h" 20 #include <getopt.h> 21 #include "libqtest.h" 22 #include "qapi/qmp/qdict.h" 23 #include "qapi/qmp/qbool.h" 24 #include "qapi/qmp/qstring.h" 25 #include "qemu/module.h" 26 #include "qapi/qmp/qlist.h" 27 #include "libqos/malloc.h" 28 #include "libqos/qgraph.h" 29 #include "libqos/qgraph_internal.h" 30 #include "libqos/qos_external.h" 31 32 33 34 void apply_to_node(const char *name, bool is_machine, bool is_abstract) 35 { 36 char *machine_name = NULL; 37 if (is_machine) { 38 const char *arch = qtest_get_arch(); 39 machine_name = g_strconcat(arch, "/", name, NULL); 40 name = machine_name; 41 } 42 qos_graph_node_set_availability(name, true); 43 if (is_abstract) { 44 qos_delete_cmd_line(name); 45 } 46 g_free(machine_name); 47 } 48 49 /** 50 * apply_to_qlist(): using QMP queries QEMU for a list of 51 * machines and devices available, and sets the respective node 52 * as true. If a node is found, also all its produced and contained 53 * child are marked available. 54 * 55 * See qos_graph_node_set_availability() for more info 56 */ 57 void apply_to_qlist(QList *list, bool is_machine) 58 { 59 const QListEntry *p; 60 const char *name; 61 bool abstract; 62 QDict *minfo; 63 QObject *qobj; 64 QString *qstr; 65 QBool *qbool; 66 67 for (p = qlist_first(list); p; p = qlist_next(p)) { 68 minfo = qobject_to(QDict, qlist_entry_obj(p)); 69 qobj = qdict_get(minfo, "name"); 70 qstr = qobject_to(QString, qobj); 71 name = qstring_get_str(qstr); 72 73 qobj = qdict_get(minfo, "abstract"); 74 if (qobj) { 75 qbool = qobject_to(QBool, qobj); 76 abstract = qbool_get_bool(qbool); 77 } else { 78 abstract = false; 79 } 80 81 apply_to_node(name, is_machine, abstract); 82 qobj = qdict_get(minfo, "alias"); 83 if (qobj) { 84 qstr = qobject_to(QString, qobj); 85 name = qstring_get_str(qstr); 86 apply_to_node(name, is_machine, abstract); 87 } 88 } 89 } 90 91 QGuestAllocator *get_machine_allocator(QOSGraphObject *obj) 92 { 93 return obj->get_driver(obj, "memory"); 94 } 95 96 /** 97 * allocate_objects(): given an array of nodes @arg, 98 * walks the path invoking all constructors and 99 * passing the corresponding parameter in order to 100 * continue the objects allocation. 101 * Once the test is reached, return the object it consumes. 102 * 103 * Since the machine and QEDGE_CONSUMED_BY nodes allocate 104 * memory in the constructor, g_test_queue_destroy is used so 105 * that after execution they can be safely free'd. (The test's 106 * ->before callback is also welcome to use g_test_queue_destroy). 107 * 108 * Note: as specified in walk_path() too, @arg is an array of 109 * char *, where arg[0] is a pointer to the command line 110 * string that will be used to properly start QEMU when executing 111 * the test, and the remaining elements represent the actual objects 112 * that will be allocated. 113 */ 114 void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc) 115 { 116 int current = 0; 117 QGuestAllocator *alloc; 118 QOSGraphObject *parent = NULL; 119 QOSGraphEdge *edge; 120 QOSGraphNode *node; 121 void *edge_arg; 122 void *obj; 123 124 node = qos_graph_get_node(path[current]); 125 g_assert(node->type == QNODE_MACHINE); 126 127 obj = qos_machine_new(node, qts); 128 qos_object_queue_destroy(obj); 129 130 alloc = get_machine_allocator(obj); 131 if (p_alloc) { 132 *p_alloc = alloc; 133 } 134 135 for (;;) { 136 if (node->type != QNODE_INTERFACE) { 137 qos_object_start_hw(obj); 138 parent = obj; 139 } 140 141 /* follow edge and get object for next node constructor */ 142 current++; 143 edge = qos_graph_get_edge(path[current - 1], path[current]); 144 node = qos_graph_get_node(path[current]); 145 146 if (node->type == QNODE_TEST) { 147 g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY); 148 return obj; 149 } 150 151 switch (qos_graph_edge_get_type(edge)) { 152 case QEDGE_PRODUCES: 153 obj = parent->get_driver(parent, path[current]); 154 break; 155 156 case QEDGE_CONSUMED_BY: 157 edge_arg = qos_graph_edge_get_arg(edge); 158 obj = qos_driver_new(node, obj, alloc, edge_arg); 159 qos_object_queue_destroy(obj); 160 break; 161 162 case QEDGE_CONTAINS: 163 obj = parent->get_device(parent, path[current]); 164 break; 165 } 166 } 167 } 168 169