xref: /qemu/tests/qtest/readconfig-test.c (revision b49f4755)
1 /*
2  * Validate -readconfig
3  *
4  * Copyright (c) 2022 Red Hat, Inc.
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  */
9 
10 #include "qemu/osdep.h"
11 #include "libqtest.h"
12 #include "qapi/error.h"
13 #include "qapi/qapi-visit-machine.h"
14 #include "qapi/qapi-visit-qom.h"
15 #include "qapi/qapi-visit-ui.h"
16 #include "qapi/qmp/qdict.h"
17 #include "qapi/qmp/qlist.h"
18 #include "qapi/qobject-input-visitor.h"
19 #include "qapi/qmp/qstring.h"
20 #include "qemu/units.h"
21 
22 static QTestState *qtest_init_with_config(const char *cfgdata)
23 {
24     GError *error = NULL;
25     g_autofree char *args = NULL;
26     int cfgfd = -1;
27     g_autofree char *cfgpath = NULL;
28     QTestState *qts;
29     ssize_t ret;
30 
31     cfgfd = g_file_open_tmp("readconfig-test-XXXXXX", &cfgpath, &error);
32     g_assert_no_error(error);
33     g_assert_cmpint(cfgfd, >=, 0);
34 
35     ret = qemu_write_full(cfgfd, cfgdata, strlen(cfgdata));
36     close(cfgfd);
37     if (ret < 0) {
38         unlink(cfgpath);
39     }
40     g_assert_cmpint(ret, ==, strlen(cfgdata));
41 
42     args = g_strdup_printf("-nodefaults -machine none -readconfig %s", cfgpath);
43 
44     qts = qtest_init(args);
45 
46     unlink(cfgpath);
47 
48     return qts;
49 }
50 
51 static void test_x86_memdev_resp(QObject *res, const char *mem_id, int size)
52 {
53     Visitor *v;
54     g_autoptr(MemdevList) memdevs = NULL;
55     Memdev *memdev;
56 
57     g_assert(res);
58     v = qobject_input_visitor_new(res);
59     visit_type_MemdevList(v, NULL, &memdevs, &error_abort);
60 
61     g_assert(memdevs);
62     g_assert(memdevs->value);
63     g_assert(!memdevs->next);
64 
65     memdev = memdevs->value;
66     g_assert_cmpstr(memdev->id, ==, mem_id);
67     g_assert_cmpint(memdev->size, ==, size * MiB);
68 
69     visit_free(v);
70 }
71 
72 static void test_x86_memdev(void)
73 {
74     QDict *resp;
75     QTestState *qts;
76     const char *cfgdata =
77         "[memory]\n"
78         "size = \"200\"";
79 
80     qts = qtest_init_with_config(cfgdata);
81     /* Test valid command */
82     resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
83     test_x86_memdev_resp(qdict_get(resp, "return"), "ram", 200);
84     qobject_unref(resp);
85 
86     qtest_quit(qts);
87 }
88 
89 /* FIXME: The test is currently broken on FreeBSD */
90 #if defined(CONFIG_SPICE) && !defined(__FreeBSD__)
91 static void test_spice_resp(QObject *res)
92 {
93     Visitor *v;
94     g_autoptr(SpiceInfo) spice = NULL;
95 
96     g_assert(res);
97     v = qobject_input_visitor_new(res);
98     visit_type_SpiceInfo(v, "spice", &spice, &error_abort);
99 
100     g_assert(spice);
101     g_assert(spice->enabled);
102 
103     visit_free(v);
104 }
105 
106 static void test_spice(void)
107 {
108     QDict *resp;
109     QTestState *qts;
110     const char *cfgdata =
111         "[spice]\n"
112 #ifndef WIN32
113         "unix = \"on\"\n"
114 #endif
115         "disable-ticketing = \"on\"\n";
116 
117     qts = qtest_init_with_config(cfgdata);
118     /* Test valid command */
119     resp = qtest_qmp(qts, "{ 'execute': 'query-spice' }");
120     test_spice_resp(qdict_get(resp, "return"));
121     qobject_unref(resp);
122 
123     qtest_quit(qts);
124 }
125 #endif
126 
127 static void test_object_available(QObject *res, const char *name,
128                                   const char *type)
129 {
130     Visitor *v;
131     g_autoptr(ObjectPropertyInfoList) objs = NULL;
132     ObjectPropertyInfoList *tmp;
133     ObjectPropertyInfo *obj;
134     bool object_available = false;
135     g_autofree char *childtype = g_strdup_printf("child<%s>", type);
136 
137     g_assert(res);
138     v = qobject_input_visitor_new(res);
139     visit_type_ObjectPropertyInfoList(v, NULL, &objs, &error_abort);
140 
141     g_assert(objs);
142     tmp = objs;
143     while (tmp) {
144         g_assert(tmp->value);
145 
146         obj = tmp->value;
147         if (g_str_equal(obj->name, name) && g_str_equal(obj->type, childtype)) {
148             object_available = true;
149             break;
150         }
151 
152         tmp = tmp->next;
153     }
154 
155     g_assert(object_available);
156 
157     visit_free(v);
158 }
159 
160 static void test_object_rng(void)
161 {
162     QDict *resp;
163     QTestState *qts;
164     const char *cfgdata =
165         "[object]\n"
166         "qom-type = \"rng-builtin\"\n"
167         "id = \"rng0\"\n";
168 
169     qts = qtest_init_with_config(cfgdata);
170     /* Test valid command */
171     resp = qtest_qmp(qts,
172                      "{ 'execute': 'qom-list',"
173                      "  'arguments': {'path': '/objects' }}");
174     test_object_available(qdict_get(resp, "return"), "rng0", "rng-builtin");
175     qobject_unref(resp);
176 
177     qtest_quit(qts);
178 }
179 
180 static void test_docs_config_ich9(void)
181 {
182     QTestState *qts;
183     QDict *resp;
184     QObject *qobj;
185 
186     qts = qtest_initf("-nodefaults -readconfig docs/config/ich9-ehci-uhci.cfg");
187 
188     resp = qtest_qmp(qts, "{ 'execute': 'qom-list',"
189                           "  'arguments': {'path': '/machine/peripheral' }}");
190     qobj = qdict_get(resp, "return");
191     test_object_available(qobj, "ehci", "ich9-usb-ehci1");
192     test_object_available(qobj, "uhci-1", "ich9-usb-uhci1");
193     test_object_available(qobj, "uhci-2", "ich9-usb-uhci2");
194     test_object_available(qobj, "uhci-3", "ich9-usb-uhci3");
195     qobject_unref(resp);
196 
197     qtest_quit(qts);
198 }
199 
200 #if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP)
201 
202 static char *make_temp_img(const char *template, const char *format, int size)
203 {
204     GError *error = NULL;
205     char *temp_name;
206     int fd;
207 
208     /* Create a temporary image names */
209     fd = g_file_open_tmp(template, &temp_name, &error);
210     if (fd == -1) {
211         fprintf(stderr, "unable to create file: %s\n", error->message);
212         g_error_free(error);
213         return NULL;
214     }
215     close(fd);
216 
217     if (!mkimg(temp_name, format, size)) {
218         fprintf(stderr, "qemu-img failed to create %s\n", temp_name);
219         g_free(temp_name);
220         return NULL;
221     }
222 
223     return temp_name;
224 }
225 
226 struct device {
227     const char *name;
228     const char *type;
229 };
230 
231 static void test_docs_q35(const char *input_file, struct device *devices)
232 {
233     QTestState *qts;
234     QDict *resp;
235     QObject *qobj;
236     int ret, i;
237     g_autofree char *cfg_file = NULL, *sedcmd = NULL;
238     g_autofree char *hd_file = NULL, *cd_file = NULL;
239 
240     /* Check that all the devices are available in the QEMU binary */
241     for (i = 0; devices[i].name; i++) {
242         if (!qtest_has_device(devices[i].type)) {
243             g_test_skip("one of the required devices is not available");
244             return;
245         }
246     }
247 
248     hd_file = make_temp_img("qtest_disk_XXXXXX.qcow2", "qcow2", 1);
249     cd_file = make_temp_img("qtest_cdrom_XXXXXX.iso", "raw", 1);
250     if (!hd_file || !cd_file) {
251         g_test_skip("could not create disk images");
252         goto cleanup;
253     }
254 
255     /* Create a temporary config file where we replace the disk image names */
256     ret = g_file_open_tmp("q35-emulated-XXXXXX.cfg", &cfg_file, NULL);
257     if (ret == -1) {
258         g_test_skip("could not create temporary config file");
259         goto cleanup;
260     }
261     close(ret);
262 
263     sedcmd = g_strdup_printf("sed -e 's,guest.qcow2,%s,' -e 's,install.iso,%s,'"
264                              " %s %s > '%s'",
265                              hd_file, cd_file,
266                              !qtest_has_accel("kvm") ? "-e '/accel/d'" : "",
267                              input_file, cfg_file);
268     ret = system(sedcmd);
269     if (ret) {
270         g_test_skip("could not modify temporary config file");
271         goto cleanup;
272     }
273 
274     qts = qtest_initf("-machine none -nodefaults -readconfig %s", cfg_file);
275 
276     /* Check memory size */
277     resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
278     test_x86_memdev_resp(qdict_get(resp, "return"), "pc.ram", 1024);
279     qobject_unref(resp);
280 
281     resp = qtest_qmp(qts, "{ 'execute': 'qom-list',"
282                           "  'arguments': {'path': '/machine/peripheral' }}");
283     qobj = qdict_get(resp, "return");
284 
285     /* Check that all the devices have been created */
286     for (i = 0; devices[i].name; i++) {
287         test_object_available(qobj, devices[i].name, devices[i].type);
288     }
289 
290     qobject_unref(resp);
291 
292     qtest_quit(qts);
293 
294 cleanup:
295     if (hd_file) {
296         unlink(hd_file);
297     }
298     if (cd_file) {
299         unlink(cd_file);
300     }
301     if (cfg_file) {
302         unlink(cfg_file);
303     }
304 }
305 
306 static void test_docs_q35_emulated(void)
307 {
308     struct device devices[] = {
309         { "ich9-pcie-port-1", "ioh3420" },
310         { "ich9-pcie-port-2", "ioh3420" },
311         { "ich9-pcie-port-3", "ioh3420" },
312         { "ich9-pcie-port-4", "ioh3420" },
313         { "ich9-pci-bridge", "i82801b11-bridge" },
314         { "ich9-ehci-1", "ich9-usb-ehci1" },
315         { "ich9-ehci-2", "ich9-usb-ehci2" },
316         { "ich9-uhci-1", "ich9-usb-uhci1" },
317         { "ich9-uhci-2", "ich9-usb-uhci2" },
318         { "ich9-uhci-3", "ich9-usb-uhci3" },
319         { "ich9-uhci-4", "ich9-usb-uhci4" },
320         { "ich9-uhci-5", "ich9-usb-uhci5" },
321         { "ich9-uhci-6", "ich9-usb-uhci6" },
322         { "sata-disk", "ide-hd" },
323         { "sata-optical-disk", "ide-cd" },
324         { "net", "e1000" },
325         { "video", "VGA" },
326         { "ich9-hda-audio", "ich9-intel-hda" },
327         { "ich9-hda-duplex", "hda-duplex" },
328         { NULL, NULL }
329     };
330 
331     test_docs_q35("docs/config/q35-emulated.cfg", devices);
332 }
333 
334 static void test_docs_q35_virtio_graphical(void)
335 {
336     struct device devices[] = {
337         { "pcie.1", "pcie-root-port" },
338         { "pcie.2", "pcie-root-port" },
339         { "pcie.3", "pcie-root-port" },
340         { "pcie.4", "pcie-root-port" },
341         { "pcie.5", "pcie-root-port" },
342         { "pcie.6", "pcie-root-port" },
343         { "pcie.7", "pcie-root-port" },
344         { "pcie.8", "pcie-root-port" },
345         { "scsi", "virtio-scsi-pci" },
346         { "scsi-disk", "scsi-hd" },
347         { "scsi-optical-disk", "scsi-cd" },
348         { "net", "virtio-net-pci" },
349         { "usb", "nec-usb-xhci" },
350         { "tablet", "usb-tablet" },
351         { "video", "qxl-vga" },
352         { "sound", "ich9-intel-hda" },
353         { "duplex", "hda-duplex" },
354         { NULL, NULL }
355     };
356 
357     test_docs_q35("docs/config/q35-virtio-graphical.cfg", devices);
358 }
359 
360 static void test_docs_q35_virtio_serial(void)
361 {
362     struct device devices[] = {
363         { "pcie.1", "pcie-root-port" },
364         { "pcie.2", "pcie-root-port" },
365         { "pcie.3", "pcie-root-port" },
366         { "pcie.4", "pcie-root-port" },
367         { "pcie.5", "pcie-root-port" },
368         { "pcie.6", "pcie-root-port" },
369         { "pcie.7", "pcie-root-port" },
370         { "pcie.8", "pcie-root-port" },
371         { "scsi", "virtio-scsi-pci" },
372         { "scsi-disk", "scsi-hd" },
373         { "scsi-optical-disk", "scsi-cd" },
374         { "net", "virtio-net-pci" },
375         { NULL, NULL }
376     };
377 
378     test_docs_q35("docs/config/q35-virtio-serial.cfg", devices);
379 }
380 
381 #endif /* CONFIG_LINUX */
382 
383 int main(int argc, char *argv[])
384 {
385     const char *arch;
386     g_test_init(&argc, &argv, NULL);
387 
388     arch = qtest_get_arch();
389 
390     if (g_str_equal(arch, "i386") ||
391         g_str_equal(arch, "x86_64")) {
392         qtest_add_func("readconfig/x86/memdev", test_x86_memdev);
393         if (qtest_has_device("ich9-usb-ehci1") &&
394             qtest_has_device("ich9-usb-uhci1")) {
395             qtest_add_func("readconfig/x86/ich9-ehci-uhci", test_docs_config_ich9);
396         }
397 #if defined(CONFIG_POSIX) && defined(CONFIG_SLIRP)
398         qtest_add_func("readconfig/x86/q35-emulated", test_docs_q35_emulated);
399         qtest_add_func("readconfig/x86/q35-virtio-graphical",
400                        test_docs_q35_virtio_graphical);
401         if (g_test_slow()) {
402             /*
403              * q35-virtio-serial.cfg is a subset of q35-virtio-graphical.cfg,
404              * so we can skip the test in quick mode
405              */
406             qtest_add_func("readconfig/x86/q35-virtio-serial",
407                            test_docs_q35_virtio_serial);
408         }
409 #endif
410     }
411 #if defined(CONFIG_SPICE) && !defined(__FreeBSD__)
412     qtest_add_func("readconfig/spice", test_spice);
413 #endif
414 
415     qtest_add_func("readconfig/object-rng", test_object_rng);
416 
417     return g_test_run();
418 }
419