[libvirt] [PATCH] bhyve: add bootrom support

Roman Bogorodskiy bogorodskiy at gmail.com
Mon Nov 2 13:24:09 UTC 2015


Recently, bhyve got UEFI support that eliminates necessity of
having two-step process of starting a VM, i.e. loading OS
using loader like bhyveloader or grub and then calling bhyve itself.

>From user perspective usage looks the following: if a domain XML
contains the '<bootloader>' element and it's value is a path
to non-executable file, then it's treated as a path to bootrom
file and external loader is not execute.

All the existing behavior remains the same, e.g. if bootloader
is not specified, bhyveloader is used. If loader is specified and
it points to an executable file, it's executed before running
bhyve.

Implementation consists of two major parts:

 * Add probing for capability by checking if the current bhyve
   binary accepts '-l bootrom' argument meaning that UEFI is
   supported
 * virBhyveProcessBuildBhyveCmd is extended with 'external_loader'
   argument. If it decides (based on the schema described above)
   that external loader is not needed, it sets it to false. All
   the related code changed accordingly to act based on value
   of external_loader.
---
 src/bhyve/bhyve_capabilities.c                     | 11 ++++
 src/bhyve/bhyve_capabilities.h                     |  1 +
 src/bhyve/bhyve_command.c                          | 20 +++++++-
 src/bhyve/bhyve_command.h                          |  1 +
 src/bhyve/bhyve_driver.c                           | 15 +++---
 src/bhyve/bhyve_process.c                          | 58 ++++++++++++----------
 .../bhyvexml2argvdata/bhyvexml2argv-bhyveload.args |  3 ++
 .../bhyvexml2argv-bhyveload.ldargs                 |  1 +
 .../bhyvexml2argvdata/bhyvexml2argv-bhyveload.xml  | 24 +++++++++
 tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.args |  4 ++
 tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.xml  | 23 +++++++++
 tests/bhyvexml2argvmock.c                          | 13 +++++
 tests/bhyvexml2argvtest.c                          | 39 ++++++++-------
 13 files changed, 162 insertions(+), 51 deletions(-)
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.args
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.ldargs
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.xml
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.args
 create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.xml

diff --git a/src/bhyve/bhyve_capabilities.c b/src/bhyve/bhyve_capabilities.c
index d2970a2..fbdd63c 100644
--- a/src/bhyve/bhyve_capabilities.c
+++ b/src/bhyve/bhyve_capabilities.c
@@ -166,6 +166,17 @@ virBhyveProbeCaps(unsigned int *caps)
     if (strstr(help, "-u:") != NULL)
         *caps |= BHYVE_CAP_RTC_UTC;
 
+    virCommandFree(cmd);
+
+    cmd = virCommandNew(binary);
+    virCommandAddArgList(cmd, "-l", "bootrom", NULL);
+    if (virCommandRun(cmd, &exit) < 0) {
+        ret = -1;
+        goto out;
+    }
+    if (exit == 1)
+        *caps |= BHYVE_CAP_BOOTROM;
+
  out:
     VIR_FREE(help);
     virCommandFree(cmd);
diff --git a/src/bhyve/bhyve_capabilities.h b/src/bhyve/bhyve_capabilities.h
index 0eb22a4..25dfb2c 100644
--- a/src/bhyve/bhyve_capabilities.h
+++ b/src/bhyve/bhyve_capabilities.h
@@ -33,6 +33,7 @@ typedef enum {
 
 typedef enum {
     BHYVE_CAP_RTC_UTC = 1,
+    BHYVE_CAP_BOOTROM = 2,
 } virBhyveCapsFlags;
 
 int virBhyveProbeGrubCaps(virBhyveGrubCapsFlags *caps);
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index 6576029..10d504e 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -216,7 +216,9 @@ bhyveBuildDiskArgStr(const virDomainDef *def ATTRIBUTE_UNUSED,
 
 virCommandPtr
 virBhyveProcessBuildBhyveCmd(virConnectPtr conn,
-                             virDomainDefPtr def, bool dryRun)
+                             virDomainDefPtr def,
+                             bool *external_loader,
+                             bool dryRun)
 {
     /*
      * /usr/sbin/bhyve -c 2 -m 256 -AI -H -P \
@@ -230,6 +232,22 @@ virBhyveProcessBuildBhyveCmd(virConnectPtr conn,
 
     virCommandPtr cmd = virCommandNew(BHYVE);
 
+    /* Check what loader to use */
+    if ((def->os.bootloader != NULL) &&
+        (!virFileIsExecutable(def->os.bootloader))) {
+        *external_loader = false;
+        if ((bhyveDriverGetCaps(conn) & BHYVE_CAP_BOOTROM) == 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Installed bhyve binary does not support "
+                          "bootrom"));
+            goto error;
+        }
+        virCommandAddArg(cmd, "-l");
+        virCommandAddArgFormat(cmd, "bootrom,%s", def->os.bootloader);
+    } else {
+        *external_loader = true;
+    }
+
     /* CPUs */
     virCommandAddArg(cmd, "-c");
     virCommandAddArgFormat(cmd, "%d", def->vcpus);
diff --git a/src/bhyve/bhyve_command.h b/src/bhyve/bhyve_command.h
index 22a959d..20a3bf0 100644
--- a/src/bhyve/bhyve_command.h
+++ b/src/bhyve/bhyve_command.h
@@ -32,6 +32,7 @@
 
 virCommandPtr virBhyveProcessBuildBhyveCmd(virConnectPtr conn,
                                            virDomainDefPtr def,
+                                           bool *external_loader,
                                            bool dryRun);
 
 virCommandPtr
diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c
index 4840a0e..7290251 100644
--- a/src/bhyve/bhyve_driver.c
+++ b/src/bhyve/bhyve_driver.c
@@ -698,6 +698,7 @@ bhyveConnectDomainXMLToNative(virConnectPtr conn,
     virDomainDefPtr def = NULL;
     virCommandPtr cmd = NULL, loadcmd = NULL;
     virCapsPtr caps = NULL;
+    bool external_loader = NULL;
     char *ret = NULL;
 
     virCheckFlags(0, NULL);
@@ -721,15 +722,17 @@ bhyveConnectDomainXMLToNative(virConnectPtr conn,
     if (bhyveDomainAssignAddresses(def, NULL) < 0)
         goto cleanup;
 
-    if (!(loadcmd = virBhyveProcessBuildLoadCmd(conn, def, "<device.map>",
-                                                NULL)))
+    if (!(cmd = virBhyveProcessBuildBhyveCmd(conn, def, &external_loader, true)))
         goto cleanup;
 
-    if (!(cmd = virBhyveProcessBuildBhyveCmd(conn, def, true)))
-        goto cleanup;
+    if (external_loader) {
+        if (!(loadcmd = virBhyveProcessBuildLoadCmd(conn, def, "<device.map>",
+                                                    NULL)))
+            goto cleanup;
 
-    virBufferAdd(&buf, virCommandToString(loadcmd), -1);
-    virBufferAddChar(&buf, '\n');
+        virBufferAdd(&buf, virCommandToString(loadcmd), -1);
+        virBufferAddChar(&buf, '\n');
+    }
     virBufferAdd(&buf, virCommandToString(cmd), -1);
 
     if (virBufferCheckError(&buf) < 0)
diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c
index 284641a..d62cccd 100644
--- a/src/bhyve/bhyve_process.c
+++ b/src/bhyve/bhyve_process.c
@@ -114,6 +114,7 @@ virBhyveProcessStart(virConnectPtr conn,
     virCommandPtr load_cmd = NULL;
     bhyveConnPtr privconn = conn->privateData;
     int ret = -1, rc;
+    bool external_loader = NULL;
 
     if (virAsprintf(&logfile, "%s/%s.log",
                     BHYVE_LOG_DIR, vm->def->name) < 0)
@@ -150,6 +151,7 @@ virBhyveProcessStart(virConnectPtr conn,
     /* Call bhyve to start the VM */
     if (!(cmd = virBhyveProcessBuildBhyveCmd(conn,
                                              vm->def,
+                                             &external_loader,
                                              false)))
         goto cleanup;
 
@@ -159,38 +161,40 @@ virBhyveProcessStart(virConnectPtr conn,
     virCommandSetPidFile(cmd, privconn->pidfile);
     virCommandDaemonize(cmd);
 
-    /* Now bhyve command is constructed, meaning the
-     * domain is ready to be started, so we can build
-     * and execute bhyveload command */
-    rc = virBhyveFormatDevMapFile(vm->def->name, &devmap_file);
-    if (rc < 0)
-        goto cleanup;
-
-    if (!(load_cmd = virBhyveProcessBuildLoadCmd(conn, vm->def, devmap_file,
-                                                 &devicemap)))
-        goto cleanup;
-    virCommandSetOutputFD(load_cmd, &logfd);
-    virCommandSetErrorFD(load_cmd, &logfd);
+    if (external_loader) {
+        /* Now bhyve command is constructed, meaning the
+         * domain is ready to be started, so we can build
+         * and execute bhyveload command */
+        rc = virBhyveFormatDevMapFile(vm->def->name, &devmap_file);
+        if (rc < 0)
+            goto cleanup;
 
-    if (devicemap != NULL) {
-        rc = virFileWriteStr(devmap_file, devicemap, 0644);
-        if (rc) {
-            virReportSystemError(errno,
-                                 _("Cannot write device.map '%s'"),
-                                 devmap_file);
+        if (!(load_cmd = virBhyveProcessBuildLoadCmd(conn, vm->def, devmap_file,
+                                                     &devicemap)))
             goto cleanup;
+        virCommandSetOutputFD(load_cmd, &logfd);
+        virCommandSetErrorFD(load_cmd, &logfd);
+
+        if (devicemap != NULL) {
+            rc = virFileWriteStr(devmap_file, devicemap, 0644);
+            if (rc) {
+                virReportSystemError(errno,
+                                     _("Cannot write device.map '%s'"),
+                                     devmap_file);
+                goto cleanup;
+            }
         }
-    }
 
-    /* Log generated command line */
-    virCommandWriteArgLog(load_cmd, logfd);
-    if ((pos = lseek(logfd, 0, SEEK_END)) < 0)
-        VIR_WARN("Unable to seek to end of logfile: %s",
-                 virStrerror(errno, ebuf, sizeof(ebuf)));
+        /* Log generated command line */
+        virCommandWriteArgLog(load_cmd, logfd);
+        if ((pos = lseek(logfd, 0, SEEK_END)) < 0)
+            VIR_WARN("Unable to seek to end of logfile: %s",
+                     virStrerror(errno, ebuf, sizeof(ebuf)));
 
-    VIR_DEBUG("Loading domain '%s'", vm->def->name);
-    if (virCommandRun(load_cmd, NULL) < 0)
-        goto cleanup;
+        VIR_DEBUG("Loading domain '%s'", vm->def->name);
+        if (virCommandRun(load_cmd, NULL) < 0)
+            goto cleanup;
+    }
 
     /* Now we can start the domain */
     VIR_DEBUG("Starting domain '%s'", vm->def->name);
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.args b/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.args
new file mode 100644
index 0000000..118735e
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.args
@@ -0,0 +1,3 @@
+/usr/sbin/bhyve -c 1 -m 214 -u -H -P -s 0:0,hostbridge \
+-s 3:0,virtio-net,faketapdev,mac=52:54:00:00:00:00 \
+-s 2:0,ahci-hd,/tmp/freebsd.img bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.ldargs
new file mode 100644
index 0000000..56f7fb3
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.ldargs
@@ -0,0 +1 @@
+/usr/sbin/bhyveload -foo
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.xml
new file mode 100644
index 0000000..9dac10c
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-bhyveload.xml
@@ -0,0 +1,24 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+  <memory>219136</memory>
+  <vcpu>1</vcpu>
+  <bootloader>/usr/sbin/bhyveload</bootloader>
+  <bootloader_args>-foo</bootloader_args>
+  <os>
+    <type>hvm</type>
+  </os>
+  <devices>
+    <disk type='file'>
+      <driver name='file' type='raw'/>
+      <source file='/tmp/freebsd.img'/>
+      <target dev='hda' bus='sata'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+    </disk>
+    <interface type='bridge'>
+      <model type='virtio'/>
+      <source bridge="virbr0"/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+    </interface>
+  </devices>
+</domain>
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.args b/tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.args
new file mode 100644
index 0000000..d46ffab
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.args
@@ -0,0 +1,4 @@
+/usr/sbin/bhyve -l bootrom,/path/to/rom.fd \
+-c 1 -m 214 -u -H -P -s 0:0,hostbridge \
+-s 3:0,virtio-net,faketapdev,mac=52:54:00:00:00:00 \
+-s 2:0,ahci-hd,/tmp/freebsd.img bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.xml
new file mode 100644
index 0000000..2da80c5
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-bootrom.xml
@@ -0,0 +1,23 @@
+<domain type='bhyve'>
+  <name>bhyve</name>
+  <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+  <memory>219136</memory>
+  <vcpu>1</vcpu>
+  <bootloader>/path/to/rom.fd</bootloader>
+  <os>
+    <type>hvm</type>
+  </os>
+  <devices>
+    <disk type='file'>
+      <driver name='file' type='raw'/>
+      <source file='/tmp/freebsd.img'/>
+      <target dev='hda' bus='sata'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+    </disk>
+    <interface type='bridge'>
+      <model type='virtio'/>
+      <source bridge="virbr0"/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+    </interface>
+  </devices>
+</domain>
diff --git a/tests/bhyvexml2argvmock.c b/tests/bhyvexml2argvmock.c
index 41058ca..797dc20 100644
--- a/tests/bhyvexml2argvmock.c
+++ b/tests/bhyvexml2argvmock.c
@@ -1,5 +1,6 @@
 #include <config.h>
 
+#include "virfile.h"
 #include "virstring.h"
 #include "virnetdev.h"
 #include "virnetdevtap.h"
@@ -48,3 +49,15 @@ int virNetDevSetOnline(const char *ifname ATTRIBUTE_UNUSED,
 {
     return 0;
 }
+
+bool virFileIsExecutable(const char *file)
+{
+    if (STREQ(file, "/fizz_buzz_bazz"))
+        return true;
+    else if (STREQ(file, "/usr/local/sbin/grub-bhyve"))
+        return true;
+    else if (STREQ(file, "/usr/sbin/bhyveload"))
+        return true;
+
+    return false;
+}
diff --git a/tests/bhyvexml2argvtest.c b/tests/bhyvexml2argvtest.c
index 3e57a78..2ff1ae3 100644
--- a/tests/bhyvexml2argvtest.c
+++ b/tests/bhyvexml2argvtest.c
@@ -23,6 +23,7 @@ static int testCompareXMLToArgvFiles(const char *xml,
     virDomainDefPtr vmdef = NULL;
     virCommandPtr cmd = NULL, ldcmd = NULL;
     virConnectPtr conn;
+    bool external_loader = NULL;
     int ret = -1;
 
     if (!(conn = virGetConnect()))
@@ -34,33 +35,35 @@ static int testCompareXMLToArgvFiles(const char *xml,
 
     conn->privateData = &driver;
 
-    if (!(cmd = virBhyveProcessBuildBhyveCmd(conn, vmdef, false)))
+    if (!(cmd = virBhyveProcessBuildBhyveCmd(conn, vmdef, &external_loader, false)))
         goto out;
 
     if (!(actualargv = virCommandToString(cmd)))
         goto out;
 
-    if (!(ldcmd = virBhyveProcessBuildLoadCmd(conn, vmdef, "<device.map>",
-                                              &actualdm)))
-        goto out;
-
-    if (actualdm != NULL)
-        virTrimSpaces(actualdm, NULL);
-
-    if (!(actualld = virCommandToString(ldcmd)))
-        goto out;
+    if (external_loader) {
+        if (!(ldcmd = virBhyveProcessBuildLoadCmd(conn, vmdef, "<device.map>",
+                                                  &actualdm)))
+            goto out;
 
-    if (virtTestCompareToFile(actualargv, cmdline) < 0)
-        goto out;
+        if (actualdm != NULL)
+            virTrimSpaces(actualdm, NULL);
 
-    if (virtTestCompareToFile(actualld, ldcmdline) < 0)
-        goto out;
+        if (!(actualld = virCommandToString(ldcmd)))
+            goto out;
 
-    if (virFileExists(dmcmdline) || actualdm) {
-        if (virtTestCompareToFile(actualdm, dmcmdline) < 0)
+        if (virtTestCompareToFile(actualld, ldcmdline) < 0)
             goto out;
+
+        if (virFileExists(dmcmdline) || actualdm) {
+            if (virtTestCompareToFile(actualdm, dmcmdline) < 0)
+                goto out;
+        }
     }
 
+    if (virtTestCompareToFile(actualargv, cmdline) < 0)
+        goto out;
+
     ret = 0;
 
  out:
@@ -118,7 +121,7 @@ mymain(void)
     } while (0)
 
     driver.grubcaps = BHYVE_GRUB_CAP_CONSDEV;
-    driver.bhyvecaps = BHYVE_CAP_RTC_UTC;
+    driver.bhyvecaps = BHYVE_CAP_RTC_UTC | BHYVE_CAP_BOOTROM;
 
     DO_TEST("base");
     DO_TEST("acpiapic");
@@ -130,8 +133,10 @@ mymain(void)
     DO_TEST("grub-defaults");
     DO_TEST("grub-bootorder");
     DO_TEST("grub-bootorder2");
+    DO_TEST("bhyveload");
     DO_TEST("bhyveload-explicitargs");
     DO_TEST("custom-loader");
+    DO_TEST("bootrom");
     DO_TEST("disk-cdrom-grub");
     DO_TEST("serial-grub");
     DO_TEST("localtime");
-- 
2.6.1




More information about the libvir-list mailing list