| 1 | /* |
| 2 | * Various tests for emulated CD-ROM drives. |
| 3 | * |
| 4 | * Copyright (c) 2018 Red Hat Inc. |
| 5 | * |
| 6 | * Author: |
| 7 | * Thomas Huth <thuth@redhat.com> |
| 8 | * |
| 9 | * This work is licensed under the terms of the GNU GPL, version 2 |
| 10 | * or later. See the COPYING file in the top-level directory. |
| 11 | */ |
| 12 | |
| 13 | #include "qemu/osdep.h" |
| 14 | #include "libqtest.h" |
| 15 | #include "boot-sector.h" |
| 16 | #include "qobject/qdict.h" |
| 17 | #include "qobject/qlist.h" |
| 18 | |
| 19 | static char isoimage[] = "cdrom-boot-iso-XXXXXX"; |
| 20 | |
| 21 | static int exec_xorrisofs(const char **args) |
| 22 | { |
| 23 | gchar *out_err = NULL; |
| 24 | gint exit_status = -1; |
| 25 | bool success; |
| 26 | |
| 27 | success = g_spawn_sync(NULL, (gchar **)args, NULL, |
| 28 | G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, |
| 29 | NULL, NULL, NULL, &out_err, &exit_status, NULL); |
| 30 | if (!success) { |
| 31 | return -ENOENT; |
| 32 | } |
| 33 | if (out_err) { |
| 34 | fputs(out_err, stderr); |
| 35 | g_free(out_err); |
| 36 | } |
| 37 | |
| 38 | return exit_status; |
| 39 | } |
| 40 | |
| 41 | static int prepare_image(const char *arch, char *isoimagepath) |
| 42 | { |
| 43 | char srcdir[] = "cdrom-test-dir-XXXXXX"; |
| 44 | char *codefile = NULL; |
| 45 | int ifh, ret = -1; |
| 46 | const char *args[] = { |
| 47 | "xorrisofs", "-quiet", "-l", "-no-emul-boot", |
| 48 | "-b", NULL, "-o", isoimagepath, srcdir, NULL |
| 49 | }; |
| 50 | |
| 51 | ifh = mkstemp(isoimagepath); |
| 52 | if (ifh < 0) { |
| 53 | perror("Error creating temporary iso image file"); |
| 54 | return -1; |
| 55 | } |
| 56 | if (!g_mkdtemp(srcdir)) { |
| 57 | perror("Error creating temporary directory"); |
| 58 | goto cleanup; |
| 59 | } |
| 60 | |
| 61 | if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || |
| 62 | g_str_equal(arch, "s390x")) { |
| 63 | codefile = g_strdup_printf("%s/bootcode-XXXXXX", srcdir); |
| 64 | ret = boot_sector_init(codefile); |
| 65 | if (ret) { |
| 66 | goto cleanup; |
| 67 | } |
| 68 | } else { |
| 69 | /* Just create a dummy file */ |
| 70 | char txt[] = "empty disc"; |
| 71 | codefile = g_strdup_printf("%s/readme.txt", srcdir); |
| 72 | if (!g_file_set_contents(codefile, txt, sizeof(txt) - 1, NULL)) { |
| 73 | fprintf(stderr, "Failed to create '%s'\n", codefile); |
| 74 | goto cleanup; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | args[5] = strchr(codefile, '/') + 1; |
| 79 | ret = exec_xorrisofs(args); |
| 80 | if (ret) { |
| 81 | fprintf(stderr, "xorrisofs failed: %i\n", ret); |
| 82 | } |
| 83 | |
| 84 | unlink(codefile); |
| 85 | |
| 86 | cleanup: |
| 87 | g_free(codefile); |
| 88 | rmdir(srcdir); |
| 89 | close(ifh); |
| 90 | |
| 91 | return ret; |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Check that at least the -cdrom parameter is basically working, i.e. we can |
| 96 | * see the filename of the ISO image in the output of "query-block" afterwards |
| 97 | */ |
| 98 | static void test_cdrom_param(gconstpointer data) |
| 99 | { |
| 100 | QTestState *qts; |
| 101 | QDict *response; |
| 102 | QList *ret; |
| 103 | QListEntry *entry; |
| 104 | bool found = false; |
| 105 | |
| 106 | qts = qtest_initf("-M %s -cdrom %s", (const char *)data, isoimage); |
| 107 | response = qtest_qmp(qts, "{'execute': 'query-block'}"); |
| 108 | g_assert(response && qdict_haskey(response, "return")); |
| 109 | ret = qdict_get_qlist(response, "return"); |
| 110 | |
| 111 | QLIST_FOREACH_ENTRY(ret, entry) { |
| 112 | QDict *entry_dict = qobject_to(QDict, entry->value); |
| 113 | QDict *inserted = qdict_get_qdict(entry_dict, "inserted"); |
| 114 | if (inserted) { |
| 115 | const char *file = qdict_get_str(inserted, "file"); |
| 116 | if (file && strstr(file, isoimage)) { |
| 117 | found = true; |
| 118 | break; |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | g_assert(found); |
| 124 | qobject_unref(response); |
| 125 | qtest_quit(qts); |
| 126 | } |
| 127 | |
| 128 | static void add_cdrom_param_tests(const char **machines) |
| 129 | { |
| 130 | while (*machines) { |
| 131 | if (qtest_has_machine(*machines)) { |
| 132 | char *testname = g_strdup_printf("cdrom/param/%s", *machines); |
| 133 | qtest_add_data_func(testname, *machines, test_cdrom_param); |
| 134 | g_free(testname); |
| 135 | } |
| 136 | machines++; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | static void test_cdboot(gconstpointer data) |
| 141 | { |
| 142 | QTestState *qts; |
| 143 | |
| 144 | qts = qtest_initf("-accel kvm -accel tcg -no-shutdown %s%s", (const char *)data, |
| 145 | isoimage); |
| 146 | boot_sector_test(qts); |
| 147 | qtest_quit(qts); |
| 148 | } |
| 149 | |
| 150 | static void add_x86_tests(void) |
| 151 | { |
| 152 | if (!qtest_has_accel("tcg") && !qtest_has_accel("kvm")) { |
| 153 | g_test_skip("No KVM or TCG accelerator available, skipping boot tests"); |
| 154 | return; |
| 155 | } |
| 156 | |
| 157 | if (qtest_has_machine("pc")) { |
| 158 | qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); |
| 159 | if (qtest_has_device("virtio-scsi-ccw")) { |
| 160 | qtest_add_data_func("cdrom/boot/virtio-scsi", |
| 161 | "-device virtio-scsi -device scsi-cd,drive=cdr " |
| 162 | "-blockdev file,node-name=cdr,filename=", |
| 163 | test_cdboot); |
| 164 | } |
| 165 | |
| 166 | if (qtest_has_device("am53c974")) { |
| 167 | qtest_add_data_func("cdrom/boot/am53c974", |
| 168 | "-device am53c974 -device scsi-cd,drive=cd1 " |
| 169 | "-drive if=none,id=cd1,format=raw,file=", |
| 170 | test_cdboot); |
| 171 | } |
| 172 | if (qtest_has_device("dc390")) { |
| 173 | qtest_add_data_func("cdrom/boot/dc390", |
| 174 | "-device dc390 -device scsi-cd,drive=cd1 " |
| 175 | "-blockdev file,node-name=cd1,filename=", |
| 176 | test_cdboot); |
| 177 | } |
| 178 | if (qtest_has_device("lsi53c895a")) { |
| 179 | qtest_add_data_func("cdrom/boot/lsi53c895a", |
| 180 | "-device lsi53c895a -device scsi-cd,drive=cd1 " |
| 181 | "-blockdev file,node-name=cd1,filename=", |
| 182 | test_cdboot); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | /* |
| 187 | * Unstable CI test under load |
| 188 | * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html |
| 189 | */ |
| 190 | if (g_test_slow() && qtest_has_machine("isapc")) { |
| 191 | qtest_add_data_func("cdrom/boot/isapc", "-M isapc " |
| 192 | "-drive if=ide,media=cdrom,file=", test_cdboot); |
| 193 | } |
| 194 | |
| 195 | if (qtest_has_machine("q35")) { |
| 196 | if (qtest_has_device("megasas")) { |
| 197 | qtest_add_data_func("cdrom/boot/megasas", "-M q35 " |
| 198 | "-device megasas -device scsi-cd,drive=cd1 " |
| 199 | "-blockdev file,node-name=cd1,filename=", |
| 200 | test_cdboot); |
| 201 | } |
| 202 | if (qtest_has_device("megasas-gen2")) { |
| 203 | qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " |
| 204 | "-device megasas-gen2 -device scsi-cd,drive=cd1 " |
| 205 | "-blockdev file,node-name=cd1,filename=", |
| 206 | test_cdboot); |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | static void add_s390x_tests(void) |
| 212 | { |
| 213 | if (!qtest_has_accel("tcg") && !qtest_has_accel("kvm")) { |
| 214 | g_test_skip("No KVM or TCG accelerator available, skipping boot tests"); |
| 215 | } |
| 216 | if (!qtest_has_device("virtio-blk-ccw")) { |
| 217 | return; |
| 218 | } |
| 219 | |
| 220 | qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); |
| 221 | |
| 222 | if (!qtest_has_device("virtio-scsi-ccw")) { |
| 223 | return; |
| 224 | } |
| 225 | |
| 226 | qtest_add_data_func("cdrom/boot/virtio-scsi", |
| 227 | "-device virtio-scsi -device scsi-cd,drive=cdr " |
| 228 | "-blockdev file,node-name=cdr,filename=", test_cdboot); |
| 229 | qtest_add_data_func("cdrom/boot/with-bootindex", |
| 230 | "-device virtio-serial -device virtio-scsi " |
| 231 | "-device virtio-blk,drive=d1 " |
| 232 | "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " |
| 233 | "-device virtio-blk,drive=d2,bootindex=1 " |
| 234 | "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); |
| 235 | qtest_add_data_func("cdrom/boot/as-fallback-device", |
| 236 | "-device virtio-serial -device virtio-scsi " |
| 237 | "-device virtio-blk,drive=d1,bootindex=1 " |
| 238 | "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " |
| 239 | "-device virtio-blk,drive=d2,bootindex=2 " |
| 240 | "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); |
| 241 | qtest_add_data_func("cdrom/boot/as-last-option", |
| 242 | "-device virtio-serial -device virtio-scsi " |
| 243 | "-device virtio-blk,drive=d1,bootindex=1 " |
| 244 | "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " |
| 245 | "-device virtio-blk,drive=d2,bootindex=2 " |
| 246 | "-drive driver=null-co,read-zeroes=on,if=none,id=d2 " |
| 247 | "-device virtio-blk,drive=d3,bootindex=3 " |
| 248 | "-drive driver=null-co,read-zeroes=on,if=none,id=d3 " |
| 249 | "-device scsi-hd,drive=d4,bootindex=4 " |
| 250 | "-drive driver=null-co,read-zeroes=on,if=none,id=d4 " |
| 251 | "-device scsi-hd,drive=d5,bootindex=5 " |
| 252 | "-drive driver=null-co,read-zeroes=on,if=none,id=d5 " |
| 253 | "-device virtio-blk,drive=d6,bootindex=6 " |
| 254 | "-drive driver=null-co,read-zeroes=on,if=none,id=d6 " |
| 255 | "-device scsi-hd,drive=d7,bootindex=7 " |
| 256 | "-drive driver=null-co,read-zeroes=on,if=none,id=d7 " |
| 257 | "-device scsi-cd,drive=d8,bootindex=8 " |
| 258 | "-drive if=none,id=d8,media=cdrom,file=", test_cdboot); |
| 259 | if (qtest_has_device("x-terminal3270")) { |
| 260 | qtest_add_data_func("cdrom/boot/without-bootindex", |
| 261 | "-device virtio-scsi -device virtio-serial " |
| 262 | "-device x-terminal3270 -device virtio-blk,drive=d1 " |
| 263 | "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " |
| 264 | "-device virtio-blk,drive=d2 " |
| 265 | "-drive if=none,id=d2,media=cdrom,file=", |
| 266 | test_cdboot); |
| 267 | } |
| 268 | if (qtest_has_device("virtio-blk-pci")) { |
| 269 | qtest_add_data_func("cdrom/boot/pci-bus-with-bootindex", |
| 270 | "-device virtio-scsi -device virtio-serial " |
| 271 | "-device virtio-blk-pci,drive=d1,bootindex=1 " |
| 272 | "-drive if=none,id=d1,media=cdrom,file=", |
| 273 | test_cdboot); |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | int main(int argc, char **argv) |
| 278 | { |
| 279 | int ret; |
| 280 | const char *arch = qtest_get_arch(); |
| 281 | const char *xorrisocheck[] = { "xorrisofs", "-version", NULL }; |
| 282 | |
| 283 | g_test_init(&argc, &argv, NULL); |
| 284 | |
| 285 | if (exec_xorrisofs(xorrisocheck)) { |
| 286 | /* xorrisofs not available - so can't run tests */ |
| 287 | return g_test_run(); |
| 288 | } |
| 289 | |
| 290 | ret = prepare_image(arch, isoimage); |
| 291 | if (ret) { |
| 292 | return ret; |
| 293 | } |
| 294 | |
| 295 | if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { |
| 296 | add_x86_tests(); |
| 297 | } else if (g_str_equal(arch, "s390x")) { |
| 298 | add_s390x_tests(); |
| 299 | } else if (g_str_equal(arch, "ppc64")) { |
| 300 | const char *ppcmachines[] = { |
| 301 | "pseries", "mac99", "g3beige", "40p", NULL |
| 302 | }; |
| 303 | add_cdrom_param_tests(ppcmachines); |
| 304 | } else if (g_str_equal(arch, "sparc")) { |
| 305 | const char *sparcmachines[] = { |
| 306 | "LX", "SPARCClassic", "SPARCbook", "SS-10", "SS-20", "SS-4", |
| 307 | "SS-5", "SS-600MP", "Voyager", "leon3_generic", NULL |
| 308 | }; |
| 309 | add_cdrom_param_tests(sparcmachines); |
| 310 | } else if (g_str_equal(arch, "sparc64")) { |
| 311 | const char *sparc64machines[] = { |
| 312 | "niagara", "sun4u", "sun4v", NULL |
| 313 | }; |
| 314 | add_cdrom_param_tests(sparc64machines); |
| 315 | } else if (!strncmp(arch, "mips64", 6)) { |
| 316 | const char *mips64machines[] = { |
| 317 | "magnum", "malta", "pica61", NULL |
| 318 | }; |
| 319 | add_cdrom_param_tests(mips64machines); |
| 320 | } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { |
| 321 | const char *armmachines[] = { |
| 322 | "realview-eb", "realview-eb-mpcore", "realview-pb-a8", |
| 323 | "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15", |
| 324 | "vexpress-a9", NULL |
| 325 | }; |
| 326 | add_cdrom_param_tests(armmachines); |
| 327 | if (qtest_has_device("virtio-blk-pci")) { |
| 328 | const char *virtmachine[] = { "virt", NULL }; |
| 329 | add_cdrom_param_tests(virtmachine); |
| 330 | } |
| 331 | } else if (g_str_equal(arch, "loongarch64")) { |
| 332 | if (qtest_has_device("virtio-blk-pci")) { |
| 333 | const char *virtmachine[] = { "virt", NULL }; |
| 334 | add_cdrom_param_tests(virtmachine); |
| 335 | } |
| 336 | } else { |
| 337 | const char *nonemachine[] = { "none", NULL }; |
| 338 | add_cdrom_param_tests(nonemachine); |
| 339 | } |
| 340 | |
| 341 | ret = g_test_run(); |
| 342 | |
| 343 | unlink(isoimage); |
| 344 | |
| 345 | return ret; |
| 346 | } |