[libvirt] PATCH: Support bootloaders in QEMU driver

Daniel P. Berrange berrange at redhat.com
Mon May 12 23:31:36 UTC 2008


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 :|




More information about the libvir-list mailing list