The QEMU driver supports booting Xen guests via the Xenner hypervisor. For such paravirtualized guests there is no regular BIOS, so the bootloader has to be run on the host. Xenner defaults to pygrub, but since libvirt has a generic syntax for bootloaders, we should use it. So this patch adds support for the <bootloader> syntax in the QEMU drive, and passes this to Xenner via the -bootloader arg. The patch is overly large, because when we have a bootloader we need to skip the kernel/initrd/cmdline/boot elements & thus caused alot of intrusive code re-indentation . b/tests/qemuxml2argvdata/qemuxml2argv-bootloader.args | 1 b/tests/qemuxml2argvdata/qemuxml2argv-bootloader.xml | 23 + src/qemu_conf.c | 318 +++++++++--------- src/qemu_conf.h | 1 tests/qemuxml2argvtest.c | 1 tests/qemuxml2xmltest.c | 1 6 files changed, 200 insertions(+), 145 deletions(-) Dan. diff -r f6b693192cb2 src/qemu_conf.c --- a/src/qemu_conf.c Mon May 12 09:51:07 2008 -0400 +++ b/src/qemu_conf.c Mon May 12 10:16:21 2008 -0400 @@ -1702,22 +1702,37 @@ xmlXPathFreeObject(obj); + /* Extract bootloader */ + obj = xmlXPathEval(BAD_CAST "string(/domain/bootloader)", ctxt); + if ((obj != NULL) && (obj->type == XPATH_STRING) && + (obj->stringval != NULL) && (obj->stringval[0] != 0)) { + strncpy(def->os.bootloader, (const char*)obj->stringval, sizeof(def->os.bootloader)); + NUL_TERMINATE(def->os.bootloader); + xmlXPathFreeObject(obj); + + /* Set a default OS type, since <type> is optional with bootloader */ + strcpy(def->os.type, "xen"); + } + /* Extract OS type info */ obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1])", ctxt); if ((obj == NULL) || (obj->type != XPATH_STRING) || (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + if (!def->os.type[0]) { + qemudReportError(conn, NULL, NULL, VIR_ERR_OS_TYPE, + "%s", _("no OS type")); + goto error; + } + } else { + strcpy(def->os.type, (const char *)obj->stringval); + xmlXPathFreeObject(obj); + } + + if (!virCapabilitiesSupportsGuestOSType(driver->caps, def->os.type)) { qemudReportError(conn, NULL, NULL, VIR_ERR_OS_TYPE, - "%s", _("no OS type")); - goto error; - } - if (!virCapabilitiesSupportsGuestOSType(driver->caps, (const char*)obj->stringval)) { - qemudReportError(conn, NULL, NULL, VIR_ERR_OS_TYPE, - "%s", obj->stringval); - goto error; - } - strcpy(def->os.type, (const char *)obj->stringval); - xmlXPathFreeObject(obj); - + "%s", def->os.type); + goto error; + } obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@arch)", ctxt); if ((obj == NULL) || (obj->type != XPATH_STRING) || @@ -1772,75 +1787,76 @@ xmlXPathFreeObject(obj); - obj = xmlXPathEval(BAD_CAST "string(/domain/os/kernel[1])", ctxt); - if ((obj != NULL) && (obj->type == XPATH_STRING) && - (obj->stringval != NULL) && (obj->stringval[0] != 0)) { - if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("kernel path too long")); - goto error; - } - strcpy(def->os.kernel, (const char *)obj->stringval); - } - xmlXPathFreeObject(obj); - - - obj = xmlXPathEval(BAD_CAST "string(/domain/os/initrd[1])", ctxt); - if ((obj != NULL) && (obj->type == XPATH_STRING) && - (obj->stringval != NULL) && (obj->stringval[0] != 0)) { - if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("initrd path too long")); - goto error; - } - strcpy(def->os.initrd, (const char *)obj->stringval); - } - xmlXPathFreeObject(obj); - - - obj = xmlXPathEval(BAD_CAST "string(/domain/os/cmdline[1])", ctxt); - if ((obj != NULL) && (obj->type == XPATH_STRING) && - (obj->stringval != NULL) && (obj->stringval[0] != 0)) { - if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("cmdline arguments too long")); - goto error; - } - strcpy(def->os.cmdline, (const char *)obj->stringval); - } - xmlXPathFreeObject(obj); - - - /* analysis of the disk devices */ - obj = xmlXPathEval(BAD_CAST "/domain/os/boot", ctxt); - if ((obj != NULL) && (obj->type == XPATH_NODESET) && - (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { - for (i = 0; i < obj->nodesetval->nodeNr && i < QEMUD_MAX_BOOT_DEVS ; i++) { - if (!(prop = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "dev"))) - continue; - if (STREQ((char *)prop, "hd")) { - def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_DISK; - } else if (STREQ((char *)prop, "fd")) { - def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_FLOPPY; - } else if (STREQ((char *)prop, "cdrom")) { - def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_CDROM; - } else if (STREQ((char *)prop, "network")) { - def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_NET; - } else { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("unknown boot device \'%s\'"), (char*)prop); - goto error; - } - xmlFree(prop); - prop = NULL; - } - } - xmlXPathFreeObject(obj); - if (def->os.nBootDevs == 0) { - def->os.nBootDevs = 1; - def->os.bootDevs[0] = QEMUD_BOOT_DISK; - } - + if (!def->os.bootloader[0]) { + obj = xmlXPathEval(BAD_CAST "string(/domain/os/kernel[1])", ctxt); + if ((obj != NULL) && (obj->type == XPATH_STRING) && + (obj->stringval != NULL) && (obj->stringval[0] != 0)) { + if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("kernel path too long")); + goto error; + } + strcpy(def->os.kernel, (const char *)obj->stringval); + } + xmlXPathFreeObject(obj); + + + obj = xmlXPathEval(BAD_CAST "string(/domain/os/initrd[1])", ctxt); + if ((obj != NULL) && (obj->type == XPATH_STRING) && + (obj->stringval != NULL) && (obj->stringval[0] != 0)) { + if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("initrd path too long")); + goto error; + } + strcpy(def->os.initrd, (const char *)obj->stringval); + } + xmlXPathFreeObject(obj); + + + obj = xmlXPathEval(BAD_CAST "string(/domain/os/cmdline[1])", ctxt); + if ((obj != NULL) && (obj->type == XPATH_STRING) && + (obj->stringval != NULL) && (obj->stringval[0] != 0)) { + if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("cmdline arguments too long")); + goto error; + } + strcpy(def->os.cmdline, (const char *)obj->stringval); + } + xmlXPathFreeObject(obj); + + + /* analysis of the disk devices */ + obj = xmlXPathEval(BAD_CAST "/domain/os/boot", ctxt); + if ((obj != NULL) && (obj->type == XPATH_NODESET) && + (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) { + for (i = 0; i < obj->nodesetval->nodeNr && i < QEMUD_MAX_BOOT_DEVS ; i++) { + if (!(prop = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "dev"))) + continue; + if (STREQ((char *)prop, "hd")) { + def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_DISK; + } else if (STREQ((char *)prop, "fd")) { + def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_FLOPPY; + } else if (STREQ((char *)prop, "cdrom")) { + def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_CDROM; + } else if (STREQ((char *)prop, "network")) { + def->os.bootDevs[def->os.nBootDevs++] = QEMUD_BOOT_NET; + } else { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown boot device \'%s\'"), (char*)prop); + goto error; + } + xmlFree(prop); + prop = NULL; + } + } + xmlXPathFreeObject(obj); + if (def->os.nBootDevs == 0) { + def->os.nBootDevs = 1; + def->os.bootDevs[0] = QEMUD_BOOT_DISK; + } + } obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt); if ((obj == NULL) || (obj->type != XPATH_STRING) || @@ -2371,6 +2387,7 @@ (vm->def->os.kernel[0] ? 2 : 0) + /* kernel */ (vm->def->os.initrd[0] ? 2 : 0) + /* initrd */ (vm->def->os.cmdline[0] ? 2 : 0) + /* cmdline */ + (vm->def->os.bootloader[0] ? 2 : 0) + /* bootloader */ (vm->def->graphicsType == QEMUD_GRAPHICS_VNC ? 2 : (vm->def->graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)) + /* graphics */ (vm->migrateFrom[0] ? 3 : 0); /* migrateFrom */ @@ -2438,47 +2455,54 @@ goto no_memory; } - for (i = 0 ; i < vm->def->os.nBootDevs ; i++) { - switch (vm->def->os.bootDevs[i]) { - case QEMUD_BOOT_CDROM: - boot[i] = 'd'; - break; - case QEMUD_BOOT_FLOPPY: - boot[i] = 'a'; - break; - case QEMUD_BOOT_DISK: - boot[i] = 'c'; - break; - case QEMUD_BOOT_NET: - boot[i] = 'n'; - break; - default: - boot[i] = 'c'; - break; - } - } - boot[vm->def->os.nBootDevs] = '\0'; - if (!((*argv)[++n] = strdup("-boot"))) - goto no_memory; - if (!((*argv)[++n] = strdup(boot))) - goto no_memory; - - if (vm->def->os.kernel[0]) { - if (!((*argv)[++n] = strdup("-kernel"))) - goto no_memory; - if (!((*argv)[++n] = strdup(vm->def->os.kernel))) - goto no_memory; - } - if (vm->def->os.initrd[0]) { - if (!((*argv)[++n] = strdup("-initrd"))) - goto no_memory; - if (!((*argv)[++n] = strdup(vm->def->os.initrd))) - goto no_memory; - } - if (vm->def->os.cmdline[0]) { - if (!((*argv)[++n] = strdup("-append"))) - goto no_memory; - if (!((*argv)[++n] = strdup(vm->def->os.cmdline))) + if (!vm->def->os.bootloader[0]) { + for (i = 0 ; i < vm->def->os.nBootDevs ; i++) { + switch (vm->def->os.bootDevs[i]) { + case QEMUD_BOOT_CDROM: + boot[i] = 'd'; + break; + case QEMUD_BOOT_FLOPPY: + boot[i] = 'a'; + break; + case QEMUD_BOOT_DISK: + boot[i] = 'c'; + break; + case QEMUD_BOOT_NET: + boot[i] = 'n'; + break; + default: + boot[i] = 'c'; + break; + } + } + boot[vm->def->os.nBootDevs] = '\0'; + if (!((*argv)[++n] = strdup("-boot"))) + goto no_memory; + if (!((*argv)[++n] = strdup(boot))) + goto no_memory; + + if (vm->def->os.kernel[0]) { + if (!((*argv)[++n] = strdup("-kernel"))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def->os.kernel))) + goto no_memory; + } + if (vm->def->os.initrd[0]) { + if (!((*argv)[++n] = strdup("-initrd"))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def->os.initrd))) + goto no_memory; + } + if (vm->def->os.cmdline[0]) { + if (!((*argv)[++n] = strdup("-append"))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def->os.cmdline))) + goto no_memory; + } + } else { + if (!((*argv)[++n] = strdup("-bootloader"))) + goto no_memory; + if (!((*argv)[++n] = strdup(vm->def->os.bootloader))) goto no_memory; } @@ -3809,6 +3833,8 @@ virBufferVSprintf(&buf, " <memory>%lu</memory>\n", def->maxmem); virBufferVSprintf(&buf, " <currentMemory>%lu</currentMemory>\n", def->memory); virBufferVSprintf(&buf, " <vcpu>%d</vcpu>\n", def->vcpus); + if (def->os.bootloader[0]) + virBufferVSprintf(&buf, " <bootloader>%s</bootloader>\n", def->os.bootloader); virBufferAddLit(&buf, " <os>\n"); if (def->virtType == QEMUD_VIRT_QEMU) @@ -3817,30 +3843,32 @@ else virBufferVSprintf(&buf, " <type>%s</type>\n", def->os.type); - if (def->os.kernel[0]) - virBufferVSprintf(&buf, " <kernel>%s</kernel>\n", def->os.kernel); - if (def->os.initrd[0]) - virBufferVSprintf(&buf, " <initrd>%s</initrd>\n", def->os.initrd); - if (def->os.cmdline[0]) - virBufferVSprintf(&buf, " <cmdline>%s</cmdline>\n", def->os.cmdline); - - for (n = 0 ; n < def->os.nBootDevs ; n++) { - const char *boottype = "hd"; - switch (def->os.bootDevs[n]) { - case QEMUD_BOOT_FLOPPY: - boottype = "fd"; - break; - case QEMUD_BOOT_DISK: - boottype = "hd"; - break; - case QEMUD_BOOT_CDROM: - boottype = "cdrom"; - break; - case QEMUD_BOOT_NET: - boottype = "network"; - break; - } - virBufferVSprintf(&buf, " <boot dev='%s'/>\n", boottype); + if (!def->os.bootloader[0]) { + if (def->os.kernel[0]) + virBufferVSprintf(&buf, " <kernel>%s</kernel>\n", def->os.kernel); + if (def->os.initrd[0]) + virBufferVSprintf(&buf, " <initrd>%s</initrd>\n", def->os.initrd); + if (def->os.cmdline[0]) + virBufferVSprintf(&buf, " <cmdline>%s</cmdline>\n", def->os.cmdline); + + for (n = 0 ; n < def->os.nBootDevs ; n++) { + const char *boottype = "hd"; + switch (def->os.bootDevs[n]) { + case QEMUD_BOOT_FLOPPY: + boottype = "fd"; + break; + case QEMUD_BOOT_DISK: + boottype = "hd"; + break; + case QEMUD_BOOT_CDROM: + boottype = "cdrom"; + break; + case QEMUD_BOOT_NET: + boottype = "network"; + break; + } + virBufferVSprintf(&buf, " <boot dev='%s'/>\n", boottype); + } } virBufferAddLit(&buf, " </os>\n"); diff -r f6b693192cb2 src/qemu_conf.h --- a/src/qemu_conf.h Mon May 12 09:51:07 2008 -0400 +++ b/src/qemu_conf.h Mon May 12 10:16:21 2008 -0400 @@ -270,6 +270,7 @@ char initrd[PATH_MAX]; char cmdline[PATH_MAX]; char binary[PATH_MAX]; + char bootloader[PATH_MAX]; }; /* Guest VM main configuration */ diff -r f6b693192cb2 tests/qemuxml2argvdata/qemuxml2argv-bootloader.args --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-bootloader.args Mon May 12 10:16:21 2008 -0400 @@ -0,0 +1,1 @@ +/usr/bin/qemu-kvm -M xenner -m 214 -smp 1 -nographic -monitor pty -no-acpi -bootloader /usr/bin/pygrub -cdrom /dev/cdrom -net none -serial none -parallel none -usb \ No newline at end of file diff -r f6b693192cb2 tests/qemuxml2argvdata/qemuxml2argv-bootloader.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/qemuxml2argvdata/qemuxml2argv-bootloader.xml Mon May 12 10:16:21 2008 -0400 @@ -0,0 +1,23 @@ +<domain type='kvm'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <bootloader>/usr/bin/pygrub</bootloader> + <os> + <type>xen</type> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-kvm</emulator> + <disk type='block' device='cdrom'> + <source dev='/dev/cdrom'/> + <target dev='hdc' bus='ide'/> + <readonly/> + </disk> + </devices> +</domain> diff -r f6b693192cb2 tests/qemuxml2argvtest.c --- a/tests/qemuxml2argvtest.c Mon May 12 09:51:07 2008 -0400 +++ b/tests/qemuxml2argvtest.c Mon May 12 10:16:21 2008 -0400 @@ -144,6 +144,7 @@ DO_TEST("boot-cdrom", 0); DO_TEST("boot-network", 0); DO_TEST("boot-floppy", 0); + DO_TEST("bootloader", 0); DO_TEST("clock-utc", 0); DO_TEST("clock-localtime", 0); DO_TEST("disk-cdrom", 0); diff -r f6b693192cb2 tests/qemuxml2xmltest.c --- a/tests/qemuxml2xmltest.c Mon May 12 09:51:07 2008 -0400 +++ b/tests/qemuxml2xmltest.c Mon May 12 10:16:21 2008 -0400 @@ -97,6 +97,7 @@ DO_TEST("boot-cdrom"); DO_TEST("boot-network"); DO_TEST("boot-floppy"); + DO_TEST("bootloader"); DO_TEST("clock-utc"); DO_TEST("clock-localtime"); DO_TEST("disk-cdrom"); -- |: Red Hat, Engineering, Boston -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list