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