Blame SOURCES/0161-v2v-efi-Support-output-of-UEFI-guests-for-some-outpu.patch

ffd6ed
From 5eda67359f6fa49b3cc1500f0b2dd88811865a69 Mon Sep 17 00:00:00 2001
ffd6ed
From: "Richard W.M. Jones" <rjones@redhat.com>
ffd6ed
Date: Thu, 30 Apr 2015 15:24:15 +0100
ffd6ed
Subject: [PATCH] v2v: efi: Support output of UEFI guests, for some output
ffd6ed
 drivers (RHBZ#1184690).
ffd6ed
ffd6ed
Add the Types.target_firmware type, which stores our final decision
ffd6ed
for whether the guest requires BIOS or UEFI on the target.
ffd6ed
ffd6ed
Not all output modes support UEFI.  In order to fail as early as
ffd6ed
possible if conversion isn't going to be possible, there is an
ffd6ed
output#supported_firmware method which returns the list of supported
ffd6ed
target_firmware.
ffd6ed
ffd6ed
Add the target_firmware parameter to output#create_metadata so it can
ffd6ed
select the correct firmware in the final metadata.
ffd6ed
ffd6ed
(cherry picked from commit 40558450dd87190c9dc2649cadbf284ae0a21606)
ffd6ed
---
ffd6ed
 v2v/output_glance.ml   |  7 ++++++-
ffd6ed
 v2v/output_libvirt.ml  | 30 ++++++++++++++++++++++++------
ffd6ed
 v2v/output_libvirt.mli |  2 +-
ffd6ed
 v2v/output_local.ml    |  6 ++++--
ffd6ed
 v2v/output_null.ml     |  4 +++-
ffd6ed
 v2v/output_qemu.ml     | 28 +++++++++++++++++++++++++++-
ffd6ed
 v2v/output_rhev.ml     |  7 ++++++-
ffd6ed
 v2v/output_vdsm.ml     |  7 ++++++-
ffd6ed
 v2v/types.ml           |  9 ++++++++-
ffd6ed
 v2v/types.mli          |  9 ++++++++-
ffd6ed
 v2v/utils.ml           | 29 +++++++++++++++++++++++++++++
ffd6ed
 v2v/v2v.ml             | 20 +++++++++++++++++++-
ffd6ed
 v2v/virt-v2v.pod       | 31 +++++++++++++++++++++++++++++++
ffd6ed
 13 files changed, 172 insertions(+), 17 deletions(-)
ffd6ed
ffd6ed
diff --git a/v2v/output_glance.ml b/v2v/output_glance.ml
ffd6ed
index c2fb553..f120cfa 100644
ffd6ed
--- a/v2v/output_glance.ml
ffd6ed
+++ b/v2v/output_glance.ml
ffd6ed
@@ -40,6 +40,8 @@ object
ffd6ed
 
ffd6ed
   method as_options = "-o glance"
ffd6ed
 
ffd6ed
+  method supported_firmware = [ TargetBIOS ]
ffd6ed
+
ffd6ed
   method prepare_targets source targets =
ffd6ed
     (* This does nothing useful except to check that the user has
ffd6ed
      * supplied all the correct auth environment variables to make
ffd6ed
@@ -60,7 +62,10 @@ object
ffd6ed
         { t with target_file = target_file }
ffd6ed
     ) targets
ffd6ed
 
ffd6ed
-  method create_metadata source targets guestcaps inspect =
ffd6ed
+  method create_metadata source targets guestcaps inspect target_firmware =
ffd6ed
+    (* See #supported_firmware above. *)
ffd6ed
+    assert (target_firmware = TargetBIOS);
ffd6ed
+
ffd6ed
     (* Upload the disk image (there should only be one - see above). *)
ffd6ed
     let { target_file = target_file; target_format = target_format } =
ffd6ed
       List.hd targets in
ffd6ed
diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml
ffd6ed
index 368f235..48d39e2 100644
ffd6ed
--- a/v2v/output_libvirt.ml
ffd6ed
+++ b/v2v/output_libvirt.ml
ffd6ed
@@ -75,7 +75,8 @@ let append_attr attr = function
ffd6ed
   | PCData _ | Comment _ -> assert false
ffd6ed
   | Element e -> e.e_attrs <- e.e_attrs @ [attr]
ffd6ed
 
ffd6ed
-let create_libvirt_xml ?pool source targets guestcaps target_features =
ffd6ed
+let create_libvirt_xml ?pool source targets guestcaps
ffd6ed
+                       target_features target_firmware =
ffd6ed
   let memory_k = source.s_memory /^ 1024L in
ffd6ed
 
ffd6ed
   (* We have the machine features of the guest when it was on the
ffd6ed
@@ -113,6 +114,23 @@ let create_libvirt_xml ?pool source targets guestcaps target_features =
ffd6ed
 
ffd6ed
   let features = List.sort compare (StringSet.elements features) in
ffd6ed
 
ffd6ed
+  (* The <os> section subelements. *)
ffd6ed
+  let os_section =
ffd6ed
+    let loader =
ffd6ed
+      match target_firmware with
ffd6ed
+      | TargetBIOS -> []
ffd6ed
+      | TargetUEFI ->
ffd6ed
+         (* danpb is proposing that libvirt supports <loader type="efi"/>,
ffd6ed
+          * (https://bugzilla.redhat.com/show_bug.cgi?id=1217444#c6) but
ffd6ed
+          * until that day we have to use a bunch of heuristics. XXX
ffd6ed
+          *)
ffd6ed
+         let code, vars_template = find_uefi_firmware guestcaps.gcaps_arch in
ffd6ed
+         [ e "loader" ["type", "pflash"] [ PCData code ];
ffd6ed
+           e "nvram" ["template", vars_template] [] ] in
ffd6ed
+
ffd6ed
+    (e "type" ["arch", guestcaps.gcaps_arch] [PCData "hvm"]) :: loader in
ffd6ed
+
ffd6ed
+  (* Disks. *)
ffd6ed
   let disks =
ffd6ed
     let block_prefix =
ffd6ed
       match guestcaps.gcaps_block_bus with
ffd6ed
@@ -281,9 +299,7 @@ let create_libvirt_xml ?pool source targets guestcaps target_features =
ffd6ed
       e "memory" ["unit", "KiB"] [PCData (Int64.to_string memory_k)];
ffd6ed
       e "currentMemory" ["unit", "KiB"] [PCData (Int64.to_string memory_k)];
ffd6ed
       e "vcpu" [] [PCData (string_of_int source.s_vcpu)];
ffd6ed
-      e "os" [] [
ffd6ed
-        e "type" ["arch", guestcaps.gcaps_arch] [PCData "hvm"];
ffd6ed
-      ];
ffd6ed
+      e "os" [] os_section;
ffd6ed
       e "features" [] (List.map (fun s -> e s [] []) features);
ffd6ed
 
ffd6ed
       e "on_poweroff" [] [PCData "destroy"];
ffd6ed
@@ -305,6 +321,8 @@ class output_libvirt verbose oc output_pool = object
ffd6ed
     | None -> sprintf "-o libvirt -os %s" output_pool
ffd6ed
     | Some uri -> sprintf "-o libvirt -oc %s -os %s" uri output_pool
ffd6ed
 
ffd6ed
+  method supported_firmware = [ TargetBIOS; TargetUEFI ]
ffd6ed
+
ffd6ed
   method prepare_targets source targets =
ffd6ed
     (* Get the capabilities from libvirt. *)
ffd6ed
     let cmd =
ffd6ed
@@ -360,7 +378,7 @@ class output_libvirt verbose oc output_pool = object
ffd6ed
         { t with target_file = target_file }
ffd6ed
     ) targets
ffd6ed
 
ffd6ed
-  method create_metadata source targets guestcaps _ =
ffd6ed
+  method create_metadata source targets guestcaps _ target_firmware =
ffd6ed
     (* We copied directly into the final pool directory.  However we
ffd6ed
      * have to tell libvirt.
ffd6ed
      *)
ffd6ed
@@ -385,7 +403,7 @@ class output_libvirt verbose oc output_pool = object
ffd6ed
     (* Create the metadata. *)
ffd6ed
     let doc =
ffd6ed
       create_libvirt_xml ~pool:output_pool source targets
ffd6ed
-        guestcaps target_features in
ffd6ed
+        guestcaps target_features target_firmware in
ffd6ed
 
ffd6ed
     let tmpfile, chan = Filename.open_temp_file "v2vlibvirt" ".xml" in
ffd6ed
     DOM.doc_to_chan chan doc;
ffd6ed
diff --git a/v2v/output_libvirt.mli b/v2v/output_libvirt.mli
ffd6ed
index da41956..def1b05 100644
ffd6ed
--- a/v2v/output_libvirt.mli
ffd6ed
+++ b/v2v/output_libvirt.mli
ffd6ed
@@ -23,5 +23,5 @@ val output_libvirt : bool -> string option -> string -> Types.output
ffd6ed
     {!Types.output} object specialized for writing output to
ffd6ed
     libvirt. *)
ffd6ed
 
ffd6ed
-val create_libvirt_xml : ?pool:string -> Types.source -> Types.target list -> Types.guestcaps -> string list -> DOM.doc
ffd6ed
+val create_libvirt_xml : ?pool:string -> Types.source -> Types.target list -> Types.guestcaps -> string list -> Types.target_firmware -> DOM.doc
ffd6ed
 (** This is called from {!Output_local} to generate the libvirt XML. *)
ffd6ed
diff --git a/v2v/output_local.ml b/v2v/output_local.ml
ffd6ed
index ffcfad0..3df0769 100644
ffd6ed
--- a/v2v/output_local.ml
ffd6ed
+++ b/v2v/output_local.ml
ffd6ed
@@ -29,6 +29,8 @@ class output_local verbose dir = object
ffd6ed
 
ffd6ed
   method as_options = sprintf "-o local -os %s" dir
ffd6ed
 
ffd6ed
+  method supported_firmware = [ TargetBIOS; TargetUEFI ]
ffd6ed
+
ffd6ed
   method prepare_targets source targets =
ffd6ed
     List.map (
ffd6ed
       fun t ->
ffd6ed
@@ -36,7 +38,7 @@ class output_local verbose dir = object
ffd6ed
         { t with target_file = target_file }
ffd6ed
     ) targets
ffd6ed
 
ffd6ed
-  method create_metadata source targets guestcaps _ =
ffd6ed
+  method create_metadata source targets guestcaps _ target_firmware =
ffd6ed
     (* We don't know what target features the hypervisor supports, but
ffd6ed
      * assume a common set that libvirt supports.
ffd6ed
      *)
ffd6ed
@@ -48,7 +50,7 @@ class output_local verbose dir = object
ffd6ed
 
ffd6ed
     let doc =
ffd6ed
       Output_libvirt.create_libvirt_xml source targets
ffd6ed
-        guestcaps target_features in
ffd6ed
+        guestcaps target_features target_firmware in
ffd6ed
 
ffd6ed
     let name = source.s_name in
ffd6ed
     let file = dir // name ^ ".xml" in
ffd6ed
diff --git a/v2v/output_null.ml b/v2v/output_null.ml
ffd6ed
index 97495e6..81aaf5f 100644
ffd6ed
--- a/v2v/output_null.ml
ffd6ed
+++ b/v2v/output_null.ml
ffd6ed
@@ -39,6 +39,8 @@ object
ffd6ed
 
ffd6ed
   method as_options = "-o null"
ffd6ed
 
ffd6ed
+  method supported_firmware = [ TargetBIOS; TargetUEFI ]
ffd6ed
+
ffd6ed
   method prepare_targets source targets =
ffd6ed
     List.map (
ffd6ed
       fun t ->
ffd6ed
@@ -46,7 +48,7 @@ object
ffd6ed
         { t with target_file = target_file }
ffd6ed
     ) targets
ffd6ed
 
ffd6ed
-  method create_metadata _ _ _ _ = ()
ffd6ed
+  method create_metadata _ _ _ _ _ = ()
ffd6ed
 end
ffd6ed
 
ffd6ed
 let output_null = new output_null
ffd6ed
diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml
ffd6ed
index 77152a4..c593030 100644
ffd6ed
--- a/v2v/output_qemu.ml
ffd6ed
+++ b/v2v/output_qemu.ml
ffd6ed
@@ -31,6 +31,8 @@ object
ffd6ed
   method as_options =
ffd6ed
     sprintf "-o qemu -os %s%s" dir (if qemu_boot then " --qemu-boot" else "")
ffd6ed
 
ffd6ed
+  method supported_firmware = [ TargetBIOS; TargetUEFI ]
ffd6ed
+
ffd6ed
   method prepare_targets source targets =
ffd6ed
     List.map (
ffd6ed
       fun t ->
ffd6ed
@@ -38,20 +40,44 @@ object
ffd6ed
         { t with target_file = target_file }
ffd6ed
     ) targets
ffd6ed
 
ffd6ed
-  method create_metadata source targets guestcaps inspect =
ffd6ed
+  method create_metadata source targets guestcaps inspect target_firmware =
ffd6ed
     let name = source.s_name in
ffd6ed
     let file = dir // name ^ ".sh" in
ffd6ed
 
ffd6ed
+    let uefi_firmware =
ffd6ed
+      match target_firmware with
ffd6ed
+      | TargetBIOS -> None
ffd6ed
+      | TargetUEFI ->
ffd6ed
+         Some (find_uefi_firmware guestcaps.gcaps_arch) in
ffd6ed
+
ffd6ed
     let chan = open_out file in
ffd6ed
 
ffd6ed
     let fpf fs = fprintf chan fs in
ffd6ed
     let nl = " \\\n\t" in
ffd6ed
     fpf "#!/bin/sh -\n";
ffd6ed
     fpf "\n";
ffd6ed
+
ffd6ed
+    (match uefi_firmware with
ffd6ed
+     | None -> ()
ffd6ed
+     | Some (_, vars_template) ->
ffd6ed
+        fpf "# Make a copy of the UEFI variables template\n";
ffd6ed
+        fpf "uefi_vars=\"$(mktemp)\"\n";
ffd6ed
+        fpf "cp %s \"$uefi_vars\"\n" (quote vars_template);
ffd6ed
+        fpf "\n"
ffd6ed
+    );
ffd6ed
+
ffd6ed
     fpf "/usr/libexec/qemu-kvm";
ffd6ed
     fpf "%s-no-user-config -nodefaults" nl;
ffd6ed
     fpf "%s-name %s" nl (quote source.s_name);
ffd6ed
     fpf "%s-machine accel=kvm:tcg" nl;
ffd6ed
+
ffd6ed
+    (match uefi_firmware with
ffd6ed
+     | None -> ()
ffd6ed
+     | Some (code, _) ->
ffd6ed
+        fpf "%s-drive if=pflash,format=raw,file=%s,readonly" nl (quote code);
ffd6ed
+        fpf "%s-drive if=pflash,format=raw,file=\"$uefi_vars\"" nl
ffd6ed
+    );
ffd6ed
+
ffd6ed
     fpf "%s-m %Ld" nl (source.s_memory /^ 1024L /^ 1024L);
ffd6ed
     if source.s_vcpu > 1 then
ffd6ed
       fpf "%s-smp %d" nl source.s_vcpu;
ffd6ed
diff --git a/v2v/output_rhev.ml b/v2v/output_rhev.ml
ffd6ed
index 0671e14..826bb5f 100644
ffd6ed
--- a/v2v/output_rhev.ml
ffd6ed
+++ b/v2v/output_rhev.ml
ffd6ed
@@ -122,6 +122,8 @@ object
ffd6ed
       | Some `Server -> " --vmtype server"
ffd6ed
       | Some `Desktop -> " --vmtype desktop")
ffd6ed
 
ffd6ed
+  method supported_firmware = [ TargetBIOS ]
ffd6ed
+
ffd6ed
   (* RHEV doesn't support serial consoles.  This causes the conversion
ffd6ed
    * step to remove it.
ffd6ed
    *)
ffd6ed
@@ -276,7 +278,10 @@ object
ffd6ed
     )
ffd6ed
 
ffd6ed
   (* This is called after conversion to write the OVF metadata. *)
ffd6ed
-  method create_metadata source targets guestcaps inspect =
ffd6ed
+  method create_metadata source targets guestcaps inspect target_firmware =
ffd6ed
+    (* See #supported_firmware above. *)
ffd6ed
+    assert (target_firmware = TargetBIOS);
ffd6ed
+
ffd6ed
     (* Create the metadata. *)
ffd6ed
     let ovf = OVF.create_ovf verbose source targets guestcaps inspect
ffd6ed
       output_alloc vmtype esd_uuid image_uuids vol_uuids vm_uuid in
ffd6ed
diff --git a/v2v/output_vdsm.ml b/v2v/output_vdsm.ml
ffd6ed
index c7a243e..35edeb5 100644
ffd6ed
--- a/v2v/output_vdsm.ml
ffd6ed
+++ b/v2v/output_vdsm.ml
ffd6ed
@@ -50,6 +50,8 @@ object
ffd6ed
       | Some `Server -> " --vmtype server"
ffd6ed
       | Some `Desktop -> " --vmtype desktop")
ffd6ed
 
ffd6ed
+  method supported_firmware = [ TargetBIOS ]
ffd6ed
+
ffd6ed
   (* RHEV doesn't support serial consoles.  This causes the conversion
ffd6ed
    * step to remove it.
ffd6ed
    *)
ffd6ed
@@ -163,7 +165,10 @@ object
ffd6ed
       ?clustersize path format size
ffd6ed
 
ffd6ed
   (* This is called after conversion to write the OVF metadata. *)
ffd6ed
-  method create_metadata source targets guestcaps inspect =
ffd6ed
+  method create_metadata source targets guestcaps inspect target_firmware =
ffd6ed
+    (* See #supported_firmware above. *)
ffd6ed
+    assert (target_firmware = TargetBIOS);
ffd6ed
+
ffd6ed
     (* Create the metadata. *)
ffd6ed
     let ovf = OVF.create_ovf verbose source targets guestcaps inspect
ffd6ed
       output_alloc vmtype dd_uuid
ffd6ed
diff --git a/v2v/types.ml b/v2v/types.ml
ffd6ed
index 01c65a3..41d2686 100644
ffd6ed
--- a/v2v/types.ml
ffd6ed
+++ b/v2v/types.ml
ffd6ed
@@ -216,6 +216,12 @@ target_overlay.ov_source = %s
ffd6ed
     t.target_overlay.ov_overlay_file
ffd6ed
     t.target_overlay.ov_source.s_qemu_uri
ffd6ed
 
ffd6ed
+type target_firmware = TargetBIOS | TargetUEFI
ffd6ed
+
ffd6ed
+let string_of_target_firmware = function
ffd6ed
+  | TargetBIOS -> "bios"
ffd6ed
+  | TargetUEFI -> "uefi"
ffd6ed
+
ffd6ed
 type inspect = {
ffd6ed
   i_root : string;
ffd6ed
   i_type : string;
ffd6ed
@@ -305,8 +311,9 @@ end
ffd6ed
 class virtual output verbose = object
ffd6ed
   method virtual as_options : string
ffd6ed
   method virtual prepare_targets : source -> target list -> target list
ffd6ed
+  method virtual supported_firmware : target_firmware list
ffd6ed
   method check_target_free_space (_ : source) (_ : target list) = ()
ffd6ed
   method disk_create = (new Guestfs.guestfs ())#disk_create
ffd6ed
-  method virtual create_metadata : source -> target list -> guestcaps -> inspect -> unit
ffd6ed
+  method virtual create_metadata : source -> target list -> guestcaps -> inspect -> target_firmware -> unit
ffd6ed
   method keep_serial_console = true
ffd6ed
 end
ffd6ed
diff --git a/v2v/types.mli b/v2v/types.mli
ffd6ed
index 9fc9e29..da398d3 100644
ffd6ed
--- a/v2v/types.mli
ffd6ed
+++ b/v2v/types.mli
ffd6ed
@@ -133,6 +133,10 @@ type target = {
ffd6ed
 
ffd6ed
 val string_of_target : target -> string
ffd6ed
 
ffd6ed
+type target_firmware = TargetBIOS | TargetUEFI
ffd6ed
+
ffd6ed
+val string_of_target_firmware : target_firmware -> string
ffd6ed
+
ffd6ed
 type inspect = {
ffd6ed
   i_root : string;                      (** Root device. *)
ffd6ed
   i_type : string;                      (** Usual inspection fields. *)
ffd6ed
@@ -195,10 +199,13 @@ class virtual output : bool -> object
ffd6ed
       This is just used for pretty-printing log messages. *)
ffd6ed
   method virtual prepare_targets : source -> target list -> target list
ffd6ed
   (** Called before conversion to prepare the output. *)
ffd6ed
+  method virtual supported_firmware : target_firmware list
ffd6ed
+  (** Does this output method support UEFI?  Allows us to abort early if
ffd6ed
+      conversion is impossible. *)
ffd6ed
   method check_target_free_space : source -> target list -> unit
ffd6ed
   (** Called before conversion.  Can be used to check there is enough space
ffd6ed
       on the target, using the [target.target_estimated_size] field. *)
ffd6ed
-  method virtual create_metadata : source -> target list -> guestcaps -> inspect -> unit
ffd6ed
+  method virtual create_metadata : source -> target list -> guestcaps -> inspect -> target_firmware -> unit
ffd6ed
   (** Called after conversion to finish off and create metadata. *)
ffd6ed
   method disk_create : ?backingfile:string -> ?backingformat:string -> ?preallocation:string -> ?compat:string -> ?clustersize:int -> string -> string -> int64 -> unit
ffd6ed
   (** Called in order to create disks on the target.  The method has the
ffd6ed
diff --git a/v2v/utils.ml b/v2v/utils.ml
ffd6ed
index 477033d..e9d3262 100644
ffd6ed
--- a/v2v/utils.ml
ffd6ed
+++ b/v2v/utils.ml
ffd6ed
@@ -84,6 +84,35 @@ let qemu_supports_sound_card = function
ffd6ed
   | USBAudio
ffd6ed
     -> false
ffd6ed
 
ffd6ed
+(* Find the UEFI firmware. *)
ffd6ed
+let find_uefi_firmware guest_arch =
ffd6ed
+  let files =
ffd6ed
+    match guest_arch with
ffd6ed
+    | "i386" | "i486" | "i586" | "i686" ->
ffd6ed
+       [ "/usr/share/edk2.git/ovmf-ia32/OVMF_CODE-pure-efi.fd",
ffd6ed
+         "/usr/share/edk2.git/ovmf-ia32/OVMF_VARS-pure-efi.fd" ]
ffd6ed
+    | "x86_64" ->
ffd6ed
+       [ "/usr/share/OVMF/OVMF_CODE.fd",
ffd6ed
+         "/usr/share/OVMF/OVMF_VARS.fd";
ffd6ed
+         "/usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd",
ffd6ed
+         "/usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd" ]
ffd6ed
+    | "aarch64" ->
ffd6ed
+       [ "/usr/share/AAVMF/AAVMF_CODE.fd",
ffd6ed
+         "/usr/share/AAVMF/AAVMF_VARS.fd";
ffd6ed
+         "/usr/share/edk2.git/aarch64/QEMU_EFI-pflash.raw",
ffd6ed
+         "/usr/share/edk2.git/aarch64/vars-template-pflash.raw" ]
ffd6ed
+    | arch ->
ffd6ed
+       error (f_"don't know how to convert UEFI guests for architecture %s")
ffd6ed
+             guest_arch in
ffd6ed
+  let rec loop = function
ffd6ed
+    | [] ->
ffd6ed
+       error (f_"cannot find firmware for UEFI guests.\n\nYou probably need to install OVMF, or Gerd's firmware repo (https://www.kraxel.org/repos/), or AAVMF (if using aarch64)")
ffd6ed
+    | ((code, vars_template) as ret) :: rest ->
ffd6ed
+       if Sys.file_exists code && Sys.file_exists vars_template then ret
ffd6ed
+       else loop rest
ffd6ed
+  in
ffd6ed
+  loop files
ffd6ed
+
ffd6ed
 let compare_app2_versions app1 app2 =
ffd6ed
   let i = compare app1.Guestfs.app2_epoch app2.Guestfs.app2_epoch in
ffd6ed
   if i <> 0 then i
ffd6ed
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
ffd6ed
index eb1c66e..859b92a 100644
ffd6ed
--- a/v2v/v2v.ml
ffd6ed
+++ b/v2v/v2v.ml
ffd6ed
@@ -217,6 +217,24 @@ let rec main () =
ffd6ed
   msg (f_"Inspecting the overlay");
ffd6ed
   let inspect = inspect_source ~verbose g root_choice in
ffd6ed
 
ffd6ed
+  (* Does the guest require UEFI on the target? *)
ffd6ed
+  let target_firmware =
ffd6ed
+    match source.s_firmware with
ffd6ed
+    | BIOS -> TargetBIOS
ffd6ed
+    | UEFI -> TargetUEFI
ffd6ed
+    | UnknownFirmware ->
ffd6ed
+       if inspect.i_uefi then TargetUEFI else TargetBIOS in
ffd6ed
+  let supported_firmware = output#supported_firmware in
ffd6ed
+  if not (List.mem target_firmware supported_firmware) then
ffd6ed
+    error (f_"this guest cannot run on the target, because the target does not support %s firmware (supported firmware on target: %s)")
ffd6ed
+          (string_of_target_firmware target_firmware)
ffd6ed
+          (String.concat " "
ffd6ed
+            (List.map string_of_target_firmware supported_firmware));
ffd6ed
+  (match target_firmware with
ffd6ed
+   | TargetBIOS -> ()
ffd6ed
+   | TargetUEFI ->
ffd6ed
+       info ~prog (f_"This guest requires UEFI on the target to boot."));
ffd6ed
+
ffd6ed
   (* The guest free disk space check and the target free space
ffd6ed
    * estimation both require statvfs information from mountpoints, so
ffd6ed
    * get that information first.
ffd6ed
@@ -409,7 +427,7 @@ let rec main () =
ffd6ed
 
ffd6ed
   (* Create output metadata. *)
ffd6ed
   msg (f_"Creating output metadata");
ffd6ed
-  output#create_metadata source targets guestcaps inspect;
ffd6ed
+  output#create_metadata source targets guestcaps inspect target_firmware;
ffd6ed
 
ffd6ed
   (* Save overlays if --debug-overlays option was used. *)
ffd6ed
   if debug_overlays then (
ffd6ed
diff --git a/v2v/virt-v2v.pod b/v2v/virt-v2v.pod
ffd6ed
index 5ee3bd8..7d24ceb 100644
ffd6ed
--- a/v2v/virt-v2v.pod
ffd6ed
+++ b/v2v/virt-v2v.pod
ffd6ed
@@ -699,6 +699,37 @@ loaded.
ffd6ed
 
ffd6ed
 =back
ffd6ed
 
ffd6ed
+=head1 UEFI
ffd6ed
+
ffd6ed
+VMware allows you to present UEFI firmware to guests (instead of the
ffd6ed
+ordinary PC BIOS).  Virt-v2v can convert these guests, but requires
ffd6ed
+that UEFI is supported by the target hypervisor.
ffd6ed
+
ffd6ed
+Currently KVM supports OVMF, a partially open source UEFI firmware,
ffd6ed
+and can run these guests.
ffd6ed
+
ffd6ed
+Since OVMF support was only recently added to KVM (in 2014/2015), not
ffd6ed
+all target environments support UEFI guests yet:
ffd6ed
+
ffd6ed
+=over 4
ffd6ed
+
ffd6ed
+=item UEFI on libvirt, qemu
ffd6ed
+
ffd6ed
+Supported.  Virt-v2v will generate the correct libvirt XML (metadata)
ffd6ed
+automatically, but note that the same version of OVMF must be
ffd6ed
+installed on the conversion host as is installed on the target
ffd6ed
+hypervisor, else you will have to adjust paths in the metadata.
ffd6ed
+
ffd6ed
+=item UEFI on OpenStack
ffd6ed
+
ffd6ed
+Not supported.
ffd6ed
+
ffd6ed
+=item UEFI on RHEV
ffd6ed
+
ffd6ed
+Not supported.
ffd6ed
+
ffd6ed
+=back
ffd6ed
+
ffd6ed
 =head1 NETWORKS AND BRIDGES
ffd6ed
 
ffd6ed
 Guests are usually connected to one or more networks, and when
ffd6ed
-- 
ffd6ed
1.8.3.1
ffd6ed