3ec523
From ac0859aae7162255848effdc560368266845dfce Mon Sep 17 00:00:00 2001
3ec523
From: Michal Privoznik <mprivozn@redhat.com>
3ec523
Date: Thu, 7 Aug 2014 13:50:00 +0200
3ec523
Subject: [PATCH] qemu: Implement extended loader and nvram
3ec523
3ec523
QEMU now supports UEFI with the following command line:
3ec523
3ec523
  -drive file=/usr/share/OVMF/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on \
3ec523
  -drive file=/usr/share/OVMF/OVMF_VARS.fd,if=pflash,format=raw,unit=1 \
3ec523
3ec523
where the first line reflects <loader> and the second one <nvram>.
3ec523
Moreover, these two lines obsolete the -bios argument.
3ec523
3ec523
Note that UEFI is unusable without ACPI. This is handled properly now.
3ec523
Among with this extension, the variable file is expected to be
3ec523
writable and hence we need security drivers to label it.
3ec523
3ec523
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
3ec523
Acked-by: Laszlo Ersek <lersek@redhat.com>
3ec523
(cherry picked from commit 542899168c382610dbad9a597d27ef3d7c699f68)
3ec523
---
3ec523
 src/qemu/qemu_command.c                            | 94 +++++++++++++++++++++-
3ec523
 src/security/security_dac.c                        |  8 ++
3ec523
 src/security/security_selinux.c                    |  8 ++
3ec523
 .../qemuxml2argvdata/qemuxml2argv-bios-nvram.args  | 10 +++
3ec523
 tests/qemuxml2argvtest.c                           |  2 +
3ec523
 5 files changed, 118 insertions(+), 4 deletions(-)
3ec523
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args
3ec523
3ec523
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
3ec523
index 3cb2e0b..718533b 100644
3ec523
--- a/src/qemu/qemu_command.c
3ec523
+++ b/src/qemu/qemu_command.c
3ec523
@@ -7370,6 +7370,94 @@ qemuBuildChrDeviceCommandLine(virCommandPtr cmd,
3ec523
     return 0;
3ec523
 }
3ec523
 
3ec523
+static int
3ec523
+qemuBuildDomainLoaderCommandLine(virCommandPtr cmd,
3ec523
+                                 virDomainDefPtr def,
3ec523
+                                 virQEMUCapsPtr qemuCaps)
3ec523
+{
3ec523
+    int ret = -1;
3ec523
+    virDomainLoaderDefPtr loader = def->os.loader;
3ec523
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
3ec523
+    int unit = 0;
3ec523
+
3ec523
+    if (!loader)
3ec523
+        return 0;
3ec523
+
3ec523
+    switch ((virDomainLoader) loader->type) {
3ec523
+    case VIR_DOMAIN_LOADER_TYPE_ROM:
3ec523
+        virCommandAddArg(cmd, "-bios");
3ec523
+        virCommandAddArg(cmd, loader->path);
3ec523
+        break;
3ec523
+
3ec523
+    case VIR_DOMAIN_LOADER_TYPE_PFLASH:
3ec523
+        /* UEFI is supported only for x86_64 currently */
3ec523
+        if (def->os.arch != VIR_ARCH_X86_64) {
3ec523
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
3ec523
+                           _("pflash is not supported for %s guest architecture"),
3ec523
+                           virArchToString(def->os.arch));
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+
3ec523
+        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE)) {
3ec523
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
3ec523
+                           _("this QEMU binary doesn't support -drive"));
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_FORMAT)) {
3ec523
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
3ec523
+                           _("this QEMU binary doesn't support passing "
3ec523
+                             "drive format"));
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_ACPI) &&
3ec523
+            def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON) {
3ec523
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
3ec523
+                           _("ACPI must be enabled in order to use UEFI"));
3ec523
+            goto cleanup;
3ec523
+        }
3ec523
+
3ec523
+        virBufferAsprintf(&buf,
3ec523
+                          "file=%s,if=pflash,format=raw,unit=%d",
3ec523
+                          loader->path, unit);
3ec523
+        unit++;
3ec523
+
3ec523
+        if (loader->readonly) {
3ec523
+            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) {
3ec523
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
3ec523
+                               _("this qemu doesn't support passing "
3ec523
+                                 "readonly attribute"));
3ec523
+                goto cleanup;
3ec523
+            }
3ec523
+
3ec523
+            virBufferAsprintf(&buf, ",readonly=%s",
3ec523
+                              virTristateSwitchTypeToString(loader->readonly));
3ec523
+        }
3ec523
+
3ec523
+        virCommandAddArg(cmd, "-drive");
3ec523
+        virCommandAddArgBuffer(cmd, &buf;;
3ec523
+
3ec523
+        if (loader->nvram) {
3ec523
+            virBufferFreeAndReset(&buf;;
3ec523
+            virBufferAsprintf(&buf,
3ec523
+                              "file=%s,if=pflash,format=raw,unit=%d",
3ec523
+                              loader->nvram, unit);
3ec523
+
3ec523
+            virCommandAddArg(cmd, "-drive");
3ec523
+            virCommandAddArgBuffer(cmd, &buf;;
3ec523
+        }
3ec523
+        break;
3ec523
+
3ec523
+    case VIR_DOMAIN_LOADER_TYPE_LAST:
3ec523
+        /* nada */
3ec523
+        break;
3ec523
+    }
3ec523
+
3ec523
+    ret = 0;
3ec523
+ cleanup:
3ec523
+    virBufferFreeAndReset(&buf;;
3ec523
+    return ret;
3ec523
+}
3ec523
+
3ec523
 qemuBuildCommandLineCallbacks buildCommandLineCallbacks = {
3ec523
     .qemuGetSCSIDeviceSgName = virSCSIDeviceGetSgName,
3ec523
 };
3ec523
@@ -7525,10 +7613,8 @@ qemuBuildCommandLine(virConnectPtr conn,
3ec523
             virCommandAddArg(cmd, "-enable-nesting");
3ec523
     }
3ec523
 
3ec523
-    if (def->os.loader) {
3ec523
-        virCommandAddArg(cmd, "-bios");
3ec523
-        virCommandAddArg(cmd, def->os.loader->path);
3ec523
-    }
3ec523
+    if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0)
3ec523
+        goto error;
3ec523
 
3ec523
     /* Set '-m MB' based on maxmem, because the lower 'memory' limit
3ec523
      * is set post-startup using the balloon driver. If balloon driver
3ec523
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
3ec523
index e62828e..e398d2c 100644
3ec523
--- a/src/security/security_dac.c
3ec523
+++ b/src/security/security_dac.c
3ec523
@@ -960,6 +960,10 @@ virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr,
3ec523
             rc = -1;
3ec523
     }
3ec523
 
3ec523
+    if (def->os.loader && def->os.loader->nvram &&
3ec523
+        virSecurityDACRestoreSecurityFileLabel(def->os.loader->nvram) < 0)
3ec523
+        rc = -1;
3ec523
+
3ec523
     if (def->os.kernel &&
3ec523
         virSecurityDACRestoreSecurityFileLabel(def->os.kernel) < 0)
3ec523
         rc = -1;
3ec523
@@ -1036,6 +1040,10 @@ virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr,
3ec523
     if (virSecurityDACGetImageIds(secdef, priv, &user, &group))
3ec523
         return -1;
3ec523
 
3ec523
+    if (def->os.loader && def->os.loader->nvram &&
3ec523
+        virSecurityDACSetOwnership(def->os.loader->nvram, user, group) < 0)
3ec523
+        return -1;
3ec523
+
3ec523
     if (def->os.kernel &&
3ec523
         virSecurityDACSetOwnership(def->os.kernel, user, group) < 0)
3ec523
         return -1;
3ec523
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
3ec523
index c078cab..a409c19 100644
3ec523
--- a/src/security/security_selinux.c
3ec523
+++ b/src/security/security_selinux.c
3ec523
@@ -1911,6 +1911,10 @@ virSecuritySELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr,
3ec523
                                      mgr) < 0)
3ec523
         rc = -1;
3ec523
 
3ec523
+    if (def->os.loader && def->os.loader->nvram &&
3ec523
+        virSecuritySELinuxRestoreSecurityFileLabel(mgr, def->os.loader->nvram) < 0)
3ec523
+        rc = -1;
3ec523
+
3ec523
     if (def->os.kernel &&
3ec523
         virSecuritySELinuxRestoreSecurityFileLabel(mgr, def->os.kernel) < 0)
3ec523
         rc = -1;
3ec523
@@ -2294,6 +2298,10 @@ virSecuritySELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr,
3ec523
                                      mgr) < 0)
3ec523
         return -1;
3ec523
 
3ec523
+    if (def->os.loader && def->os.loader->nvram &&
3ec523
+        virSecuritySELinuxSetFilecon(def->os.loader->nvram, data->content_context) < 0)
3ec523
+        return -1;
3ec523
+
3ec523
     if (def->os.kernel &&
3ec523
         virSecuritySELinuxSetFilecon(def->os.kernel, data->content_context) < 0)
3ec523
         return -1;
3ec523
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args b/tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args
3ec523
new file mode 100644
3ec523
index 0000000..b51e8f3
3ec523
--- /dev/null
3ec523
+++ b/tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args
3ec523
@@ -0,0 +1,10 @@
3ec523
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
3ec523
+/usr/bin/qemu -S -M pc \
3ec523
+-drive file=/usr/share/OVMF/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on \
3ec523
+-drive file=/usr/share/OVMF/OVMF_VARS.fd,if=pflash,format=raw,unit=1 \
3ec523
+-m 1024 -smp 1 -nographic -nodefaults \
3ec523
+-monitor unix:/tmp/test-monitor,server,nowait -boot c -usb \
3ec523
+-drive file=/dev/HostVG/QEMUGuest1,if=none,id=drive-ide0-0-0,format=raw \
3ec523
+-device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \
3ec523
+-serial pty -device usb-tablet,id=input0 \
3ec523
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
3ec523
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
3ec523
index 3feb2fe..5c28253 100644
3ec523
--- a/tests/qemuxml2argvtest.c
3ec523
+++ b/tests/qemuxml2argvtest.c
3ec523
@@ -642,6 +642,8 @@ mymain(void)
3ec523
     DO_TEST_FAILURE("reboot-timeout-enabled", NONE);
3ec523
 
3ec523
     DO_TEST("bios", QEMU_CAPS_DEVICE, QEMU_CAPS_SGA);
3ec523
+    DO_TEST("bios-nvram", QEMU_CAPS_DEVICE, QEMU_CAPS_DRIVE,
3ec523
+            QEMU_CAPS_DRIVE_FORMAT, QEMU_CAPS_DRIVE_READONLY);
3ec523
     DO_TEST("clock-utc", QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_DEVICE);
3ec523
     DO_TEST("clock-localtime", NONE);
3ec523
     DO_TEST("clock-localtime-basis-localtime", QEMU_CAPS_RTC);