Blame SOURCES/0175-v2v-Use-OVMF-secure-boot-file-RHBZ-1367615.patch

e76f14
From 263e0ff554c4c68962fb689dec541dabf5cc71c9 Mon Sep 17 00:00:00 2001
e76f14
From: "Richard W.M. Jones" <rjones@redhat.com>
e76f14
Date: Wed, 17 Aug 2016 14:27:24 +0100
e76f14
Subject: [PATCH] v2v: Use OVMF secure boot file (RHBZ#1367615).
e76f14
e76f14
From RHEL 7.3, Red Hat have decided to only compile the secure boot
e76f14
version of OVMF on x86-64, with flags -D SECURE_BOOT_ENABLE -D SMM_REQUIRE.
e76f14
e76f14
The filename has also changed to reflect this - it is now
e76f14
/usr/share/OVMF/OVMF_CODE.secboot.fd.  The old file
e76f14
/usr/share/OVMF/OVMF_CODE.fd is no longer shipped.
e76f14
e76f14
However switching to using this variant of OVMF for UEFI guests is not
e76f14
just a matter of changing the filename.  The new OVMF variant won't
e76f14
run unless we also change:
e76f14
e76f14
 - The qemu machine model, from the default ("pc" ==
e76f14
   "pc-i440fx-rhel7.3.0" or later) to Q35 ("q35" == "pc-q35-rhel7.3.0"
e76f14
   or later).
e76f14
e76f14
 - Add <smm> under <features>.
e76f14
e76f14
 - Set <loader ... secure="yes">.
e76f14
e76f14
NB: On RHEL the changes requires qemu-kvm-rhev.  It is no longer
e76f14
possible to convert UEFI guests using the basic qemu-kvm.
e76f14
e76f14
Thanks: Laszlo Ersek, Ming Xie.
e76f14
(cherry picked from commit ba5c5a8713c4418c861edecacff761f4d3720508)
e76f14
---
e76f14
 generator/uefi.ml               | 24 ++++++++++++++++++------
e76f14
 src/appliance.c                 |  6 ++++--
e76f14
 src/guestfs-internal-frontend.h |  3 ++-
e76f14
 src/guestfs-internal.h          |  2 +-
e76f14
 src/launch-direct.c             | 15 ++++++++++++++-
e76f14
 src/launch-libvirt.c            | 16 +++++++++++++++-
e76f14
 v2v/output_libvirt.ml           | 40 +++++++++++++++++++++++++++++-----------
e76f14
 v2v/output_qemu.ml              | 13 ++++++++++---
e76f14
 v2v/virt-v2v.pod                |  2 ++
e76f14
 9 files changed, 95 insertions(+), 26 deletions(-)
e76f14
e76f14
diff --git a/generator/uefi.ml b/generator/uefi.ml
e76f14
index 3b371b8..661c1d7 100644
e76f14
--- a/generator/uefi.ml
e76f14
+++ b/generator/uefi.ml
e76f14
@@ -35,6 +35,16 @@ let firmware = [
e76f14
     "/usr/share/OVMF/OVMF_VARS.fd",
e76f14
     [];
e76f14
 
e76f14
+    (* From RHEL 7.3, only secure boot variants of UEFI are shipped.
e76f14
+     * This requires additional qemu options, see RHBZ#1367615 for
e76f14
+     * details.
e76f14
+     *)
e76f14
+    "x86_64",
e76f14
+    "/usr/share/OVMF/OVMF_CODE.secboot.fd",
e76f14
+    None,
e76f14
+    "/usr/share/OVMF/OVMF_VARS.fd",
e76f14
+    [ "UEFI_FLAG_SECURE_BOOT_REQUIRED" ];
e76f14
+
e76f14
     "aarch64",
e76f14
     "/usr/share/AAVMF/AAVMF_CODE.fd",
e76f14
     None,
e76f14
@@ -63,14 +73,13 @@ let generate_uefi_c () =
e76f14
       pr "guestfs_int_uefi_%s_firmware[] = {\n" arch;
e76f14
       List.iter (
e76f14
         fun (_, code, code_debug, vars, flags) ->
e76f14
-          assert (flags = []);
e76f14
           pr "  { \"%s\",\n" (c_quote code);
e76f14
           (match code_debug with
e76f14
            | None -> pr "    NULL,\n"
e76f14
            | Some code_debug -> pr "    \"%s\",\n" (c_quote code_debug)
e76f14
           );
e76f14
           pr "    \"%s\",\n" (c_quote vars);
e76f14
-          pr "    0\n";
e76f14
+          pr "    %s\n" (if flags <> [] then String.concat "|" flags else "0");
e76f14
           pr "  },\n";
e76f14
       ) firmware;
e76f14
       pr "};\n";
e76f14
@@ -84,8 +93,10 @@ type uefi_firmware = {
e76f14
   code : string;
e76f14
   code_debug : string option;
e76f14
   vars : string;
e76f14
-  flags : unit list;
e76f14
+  flags : uefi_flags;
e76f14
 }
e76f14
+and uefi_flags = uefi_flag list
e76f14
+and uefi_flag = UEFI_FLAG_SECURE_BOOT_REQUIRED
e76f14
 ";
e76f14
   List.iter (
e76f14
     fun arch ->
e76f14
@@ -95,14 +106,13 @@ type uefi_firmware = {
e76f14
       pr "let uefi_%s_firmware = [\n" arch;
e76f14
       List.iter (
e76f14
         fun (_, code, code_debug, vars, flags) ->
e76f14
-          assert (flags = []);
e76f14
           pr "  { code = %S;\n" code;
e76f14
           (match code_debug with
e76f14
            | None -> pr "    code_debug = None;\n"
e76f14
            | Some code_debug -> pr "    code_debug = Some %S;\n" code_debug
e76f14
           );
e76f14
           pr "    vars = %S;\n" vars;
e76f14
-          pr "    flags = [];\n";
e76f14
+          pr "    flags = [%s];\n" (String.concat "; " flags);
e76f14
           pr "  };\n";
e76f14
       ) firmware;
e76f14
       pr "]\n";
e76f14
@@ -118,8 +128,10 @@ type uefi_firmware = {
e76f14
   code : string;                (** code file *)
e76f14
   code_debug : string option;   (** code debug file *)
e76f14
   vars : string;                (** vars template file *)
e76f14
-  flags : unit list;            (** flags *)
e76f14
+  flags : uefi_flags;           (** flags *)
e76f14
 }
e76f14
+and uefi_flags = uefi_flag list
e76f14
+and uefi_flag = UEFI_FLAG_SECURE_BOOT_REQUIRED
e76f14
 
e76f14
 ";
e76f14
 
e76f14
diff --git a/src/appliance.c b/src/appliance.c
e76f14
index 6409cfb..a3af164 100644
e76f14
--- a/src/appliance.c
e76f14
+++ b/src/appliance.c
e76f14
@@ -427,10 +427,10 @@ dir_contains_files (const char *dir, ...)
e76f14
  * cause appliance building to fail (no UEFI firmware is not an
e76f14
  * error).
e76f14
  *
e76f14
- * XXX See also v2v/utils.ml:find_uefi_firmware
e76f14
+ * See also v2v/utils.ml:find_uefi_firmware
e76f14
  */
e76f14
 int
e76f14
-guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars)
e76f14
+guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars, int *flags)
e76f14
 {
e76f14
 #ifdef __aarch64__
e76f14
   size_t i;
e76f14
@@ -468,6 +468,7 @@ guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars)
e76f14
       /* Caller frees. */
e76f14
       *code = safe_strdup (g, codefile);
e76f14
       *vars = varst;
e76f14
+      *flags = guestfs_int_aavmf_firmware[i].flags;
e76f14
       return 0;
e76f14
     }
e76f14
   }
e76f14
@@ -475,5 +476,6 @@ guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars)
e76f14
 
e76f14
   /* Not found. */
e76f14
   *code = *vars = NULL;
e76f14
+  *flags = 0;
e76f14
   return 0;
e76f14
 }
e76f14
diff --git a/src/guestfs-internal-frontend.h b/src/guestfs-internal-frontend.h
e76f14
index 1dba172..ebf8063 100644
e76f14
--- a/src/guestfs-internal-frontend.h
e76f14
+++ b/src/guestfs-internal-frontend.h
e76f14
@@ -123,7 +123,8 @@ struct uefi_firmware {
e76f14
   const char *code;		/* code file (NULL = end of list) */
e76f14
   const char *code_debug;	/* code file with debugging msgs (may be NULL)*/
e76f14
   const char *vars;		/* vars template file */
e76f14
-  int flags;                    /* flags */
e76f14
+  int flags;                    /* various flags, see below */
e76f14
+#define UEFI_FLAG_SECURE_BOOT_REQUIRED 1 /* secure boot (see RHBZ#1367615) */
e76f14
 };
e76f14
 extern struct uefi_firmware guestfs_int_ovmf_i386_firmware[];
e76f14
 extern struct uefi_firmware guestfs_int_ovmf_x86_64_firmware[];
e76f14
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
e76f14
index 44dae32..2002253 100644
e76f14
--- a/src/guestfs-internal.h
e76f14
+++ b/src/guestfs-internal.h
e76f14
@@ -756,7 +756,7 @@ extern const char *guestfs_int_drive_protocol_to_string (enum drive_protocol pro
e76f14
 
e76f14
 /* appliance.c */
e76f14
 extern int guestfs_int_build_appliance (guestfs_h *g, char **kernel, char **dtb, char **initrd, char **appliance);
e76f14
-extern int guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars);
e76f14
+extern int guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars, int *flags);
e76f14
 
e76f14
 /* launch.c */
e76f14
 extern int64_t guestfs_int_timeval_diff (const struct timeval *x, const struct timeval *y);
e76f14
diff --git a/src/launch-direct.c b/src/launch-direct.c
e76f14
index 1081445..426bb60 100644
e76f14
--- a/src/launch-direct.c
e76f14
+++ b/src/launch-direct.c
e76f14
@@ -242,6 +242,7 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
e76f14
   CLEANUP_FREE char *uefi_code = NULL, *uefi_vars = NULL;
e76f14
   CLEANUP_FREE char *kernel = NULL, *dtb = NULL,
e76f14
     *initrd = NULL, *appliance = NULL;
e76f14
+  int uefi_flags;
e76f14
   int has_appliance_drive;
e76f14
   CLEANUP_FREE char *appliance_dev = NULL;
e76f14
   uint32_t size;
e76f14
@@ -434,8 +435,20 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
e76f14
   }
e76f14
 
e76f14
   /* UEFI (firmware) if required. */
e76f14
-  if (guestfs_int_get_uefi (g, &uefi_code, &uefi_vars) == -1)
e76f14
+  if (guestfs_int_get_uefi (g, &uefi_code, &uefi_vars, &uefi_flags) == -1)
e76f14
     goto cleanup0;
e76f14
+  if (uefi_flags & UEFI_FLAG_SECURE_BOOT_REQUIRED) {
e76f14
+    /* Implementing this requires changes to the qemu command line.
e76f14
+     * See RHBZ#1367615 for details.  As the guestfs_int_get_uefi
e76f14
+     * function is only implemented for aarch64, and UEFI secure boot
e76f14
+     * is some way off on aarch64 (2017/2018), we only need to worry
e76f14
+     * about this later.
e76f14
+     */
e76f14
+    error (g, "internal error: direct backend "
e76f14
+           "does not implement UEFI secure boot, "
e76f14
+           "see comments in the code");
e76f14
+    goto cleanup0;
e76f14
+  }
e76f14
   if (uefi_code) {
e76f14
     ADD_CMDLINE ("-drive");
e76f14
     ADD_CMDLINE_PRINTF ("if=pflash,format=raw,file=%s,readonly", uefi_code);
e76f14
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
e76f14
index 4c29409..505f3f0 100644
e76f14
--- a/src/launch-libvirt.c
e76f14
+++ b/src/launch-libvirt.c
e76f14
@@ -312,6 +312,7 @@ launch_libvirt (guestfs_h *g, void *datav, const char *libvirt_uri)
e76f14
   int r;
e76f14
   uint32_t size;
e76f14
   CLEANUP_FREE void *buf = NULL;
e76f14
+  int uefi_flags;
e76f14
 
e76f14
   params.current_proc_is_root = geteuid () == 0;
e76f14
 
e76f14
@@ -404,8 +405,21 @@ launch_libvirt (guestfs_h *g, void *datav, const char *libvirt_uri)
e76f14
     goto cleanup;
e76f14
 
e76f14
   /* UEFI code and variables, on architectures where that is required. */
e76f14
-  if (guestfs_int_get_uefi (g, &data->uefi_code, &data->uefi_vars) == -1)
e76f14
+  if (guestfs_int_get_uefi (g, &data->uefi_code, &data->uefi_vars,
e76f14
+                            &uefi_flags) == -1)
e76f14
     goto cleanup;
e76f14
+  if (uefi_flags & UEFI_FLAG_SECURE_BOOT_REQUIRED) {
e76f14
+    /* Implementing this requires changes to the libvirt XML.  See
e76f14
+     * RHBZ#1367615 for details.  As the guestfs_int_get_uefi function
e76f14
+     * is only implemented for aarch64, and UEFI secure boot is some
e76f14
+     * way off on aarch64 (2017/2018), we only need to worry about
e76f14
+     * this later.
e76f14
+     */
e76f14
+    error (g, "internal error: libvirt backend "
e76f14
+           "does not implement UEFI secure boot, "
e76f14
+           "see comments in the code");
e76f14
+    goto cleanup;
e76f14
+  }
e76f14
 
e76f14
   /* Misc backend settings. */
e76f14
   guestfs_push_error_handler (g, NULL, NULL);
e76f14
diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml
e76f14
index df0963e..ed430a2 100644
e76f14
--- a/v2v/output_libvirt.ml
e76f14
+++ b/v2v/output_libvirt.ml
e76f14
@@ -71,6 +71,16 @@ let target_features_of_capabilities_doc doc arch =
e76f14
 
e76f14
 let create_libvirt_xml ?pool source target_buses guestcaps
e76f14
                        target_features target_firmware =
e76f14
+  let uefi_firmware =
e76f14
+    match target_firmware with
e76f14
+    | TargetBIOS -> None
e76f14
+    | TargetUEFI -> Some (find_uefi_firmware guestcaps.gcaps_arch) in
e76f14
+  let secure_boot_required =
e76f14
+    match uefi_firmware with
e76f14
+    | Some { Uefi.flags = flags }
e76f14
+         when List.mem Uefi.UEFI_FLAG_SECURE_BOOT_REQUIRED flags -> true
e76f14
+    | _ -> false in
e76f14
+
e76f14
   let memory_k = source.s_memory /^ 1024L in
e76f14
 
e76f14
   (* We have the machine features of the guest when it was on the
e76f14
@@ -106,24 +116,32 @@ let create_libvirt_xml ?pool source target_buses guestcaps
e76f14
     StringSet.inter(*section*) force_features target_features in
e76f14
   let features = StringSet.union features force_features in
e76f14
 
e76f14
+  (* Add <smm> feature if UEFI requires it.  Note that libvirt
e76f14
+   * capabilities doesn't list this feature even if it is supported
e76f14
+   * by qemu, so we have to blindly add it, which might cause libvirt
e76f14
+   * to fail. (XXX)
e76f14
+   *)
e76f14
+  let features =
e76f14
+    if secure_boot_required then StringSet.add "smm" features else features in
e76f14
+
e76f14
   let features = List.sort compare (StringSet.elements features) in
e76f14
 
e76f14
   (* The <os> section subelements. *)
e76f14
   let os_section =
e76f14
+    let machine = if secure_boot_required then [ "machine", "q35" ] else [] in
e76f14
+
e76f14
     let loader =
e76f14
-      match target_firmware with
e76f14
-      | TargetBIOS -> []
e76f14
-      | TargetUEFI ->
e76f14
-         (* danpb is proposing that libvirt supports <loader type="efi"/>,
e76f14
-          * (https://bugzilla.redhat.com/show_bug.cgi?id=1217444#c6) but
e76f14
-          * until that day we have to use a bunch of heuristics. XXX
e76f14
-          *)
e76f14
-         let { Uefi.code = code; vars = vars_template } =
e76f14
-           find_uefi_firmware guestcaps.gcaps_arch in
e76f14
-         [ e "loader" ["readonly", "yes"; "type", "pflash"] [ PCData code ];
e76f14
+      match uefi_firmware with
e76f14
+      | None -> []
e76f14
+      | Some { Uefi.code = code; vars = vars_template } ->
e76f14
+         let secure =
e76f14
+           if secure_boot_required then [ "secure", "yes" ] else [] in
e76f14
+         [ e "loader" (["readonly", "yes"; "type", "pflash"] @ secure)
e76f14
+             [ PCData code ];
e76f14
            e "nvram" ["template", vars_template] [] ] in
e76f14
 
e76f14
-    (e "type" ["arch", guestcaps.gcaps_arch] [PCData "hvm"]) :: loader in
e76f14
+    (e "type" (["arch", guestcaps.gcaps_arch] @ machine) [PCData "hvm"])
e76f14
+    :: loader in
e76f14
 
e76f14
   (* The devices. *)
e76f14
   let devices = ref [] in
e76f14
diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml
e76f14
index 2a944d7..55620d1 100644
e76f14
--- a/v2v/output_qemu.ml
e76f14
+++ b/v2v/output_qemu.ml
e76f14
@@ -57,8 +57,12 @@ object
e76f14
     let uefi_firmware =
e76f14
       match target_firmware with
e76f14
       | TargetBIOS -> None
e76f14
-      | TargetUEFI ->
e76f14
-         Some (find_uefi_firmware guestcaps.gcaps_arch) in
e76f14
+      | TargetUEFI -> Some (find_uefi_firmware guestcaps.gcaps_arch) in
e76f14
+    let secure_boot_required =
e76f14
+      match uefi_firmware with
e76f14
+      | Some { Uefi.flags = flags }
e76f14
+           when List.mem Uefi.UEFI_FLAG_SECURE_BOOT_REQUIRED flags -> true
e76f14
+      | _ -> false in
e76f14
 
e76f14
     let chan = open_out file in
e76f14
 
e76f14
@@ -79,11 +83,14 @@ object
e76f14
     fpf "/usr/libexec/qemu-kvm";
e76f14
     fpf "%s-no-user-config -nodefaults" nl;
e76f14
     fpf "%s-name %s" nl (quote source.s_name);
e76f14
-    fpf "%s-machine accel=kvm:tcg" nl;
e76f14
+    fpf "%s-machine %saccel=kvm:tcg" nl
e76f14
+        (if secure_boot_required then "q35,smm=on," else "");
e76f14
 
e76f14
     (match uefi_firmware with
e76f14
      | None -> ()
e76f14
      | Some { Uefi.code = code } ->
e76f14
+        if secure_boot_required then
e76f14
+          fpf "%s-global driver=cfi.pflash01,property=secure,value=on" nl;
e76f14
         fpf "%s-drive if=pflash,format=raw,file=%s,readonly" nl (quote code);
e76f14
         fpf "%s-drive if=pflash,format=raw,file=\"$uefi_vars\"" nl
e76f14
     );
e76f14
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
e76f14
index 3e24025..32085c1 100644
e76f14
--- a/v2v/virt-v2v.pod
e76f14
+++ b/v2v/virt-v2v.pod
e76f14
@@ -783,6 +783,8 @@ automatically, but note that the same version of OVMF must be
e76f14
 installed on the conversion host as is installed on the target
e76f14
 hypervisor, else you will have to adjust paths in the metadata.
e76f14
 
e76f14
+On RHEL E<ge> 7.3, only qemu-kvm-rhev (not qemu-kvm) is supported.
e76f14
+
e76f14
 =item UEFI on OpenStack
e76f14
 
e76f14
 Not supported.
e76f14
-- 
7af31e
1.8.3.1
e76f14