Blob Blame History Raw
From 5ba28e31fde4b758ecdb5aea88d4aab7b9894f46 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 18 Aug 2016 12:35:16 +0100
Subject: [PATCH] Generate the lists of UEFI firmware paths.

Previously we had a list of UEFI paths in src/uefi.c, which was
accessed in virt-v2v using a private (guestfs_int_*) API and some C
binding code.  This was clumsy and required the paths to be replicated
in the virt-v2v unit tests.

Instead just generate the list of paths from the generator, creating
src/uefi.c and v2v/uefi.ml with the same content.

Remove the C bindings and the virt-v2v unit tests associated with UEFI
paths.

Cherry picked from commit 04fc67775e27851ce9b6c7a082ff80f1e1793365.
For this complex cherry pick I had to take into account earlier
RHEL-only patches where we removed several sets of firmware.
---
 .gitignore                      |   3 +
 generator/Makefile.am           |   3 +
 generator/main.ml               |   5 ++
 generator/uefi.ml               | 126 ++++++++++++++++++++++++++++++++++++++++
 generator/uefi.mli              |  21 +++++++
 generator/utils.ml              |  16 +++++
 generator/utils.mli             |   6 ++
 po/POTFILES                     |   1 +
 po/POTFILES-ml                  |   1 +
 src/Makefile.am                 |   1 +
 src/appliance.c                 |  13 ++++-
 src/guestfs-internal-frontend.h |  14 ++++-
 src/utils.c                     |  22 -------
 v2v/Makefile.am                 |   4 ++
 v2v/output_libvirt.ml           |   3 +-
 v2v/output_qemu.ml              |   4 +-
 v2v/utils-c.c                   |  45 --------------
 v2v/utils.ml                    |  11 +---
 v2v/utils.mli                   |   7 +--
 19 files changed, 218 insertions(+), 88 deletions(-)
 create mode 100644 generator/uefi.ml
 create mode 100644 generator/uefi.mli

diff --git a/.gitignore b/.gitignore
index 85b19d4..2baf137 100644
--- a/.gitignore
+++ b/.gitignore
@@ -469,6 +469,7 @@ Makefile.in
 /src/structs-free.c
 /src/structs-print.c
 /src/structs-print.h
+/src/uefi.c
 /src/unit-tests
 /stamp-h1
 /sysprep/.depend
@@ -579,6 +580,8 @@ Makefile.in
 /v2v/test-harness/dllv2v_test_harness.so
 /v2v/test-harness/stamp-virt-v2v-test-harness.pod
 /v2v/test-harness/virt-v2v-test-harness.1
+/v2v/uefi.ml
+/v2v/uefi.mli
 /v2v/v2v_unit_tests
 /v2v/virt-v2v
 /v2v/virt-v2v.1
diff --git a/generator/Makefile.am b/generator/Makefile.am
index 393c566..3a53054 100644
--- a/generator/Makefile.am
+++ b/generator/Makefile.am
@@ -75,6 +75,8 @@ sources = \
 	tests_c_api.ml \
 	tests_c_api.mli \
 	types.ml \
+	uefi.ml \
+	uefi.mli \
 	utils.ml \
 	utils.mli \
 	xdr.ml \
@@ -112,6 +114,7 @@ objects = \
 	bindtests.cmo \
 	errnostring.cmo \
 	customize.cmo \
+	uefi.cmo \
 	main.cmo
 
 EXTRA_DIST = $(sources) files-generated.txt
diff --git a/generator/main.ml b/generator/main.ml
index 42f6f8b..f242ed9 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -45,6 +45,7 @@ open Gobject
 open Golang
 open Bindtests
 open Errnostring
+open Uefi
 open Customize
 
 let perror msg = function
@@ -210,6 +211,10 @@ Run it from the top source directory using the command
     generate_gobject_session_header;
   output_to "gobject/src/session.c" generate_gobject_session_source;
 
+  output_to "src/uefi.c" generate_uefi_c;
+  output_to "v2v/uefi.ml" generate_uefi_ml;
+  output_to "v2v/uefi.mli" generate_uefi_mli;
+
   output_to "customize/customize_cmdline.mli" generate_customize_cmdline_mli;
   output_to "customize/customize_cmdline.ml" generate_customize_cmdline_ml;
   output_to "customize/customize-synopsis.pod" generate_customize_synopsis_pod;
diff --git a/generator/uefi.ml b/generator/uefi.ml
new file mode 100644
index 0000000..3b371b8
--- /dev/null
+++ b/generator/uefi.ml
@@ -0,0 +1,126 @@
+(* libguestfs
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+(* Please read generator/README first. *)
+
+open Utils
+open Pr
+open Docstrings
+
+(* danpb is proposing that libvirt supports E<lt>loader type="efi"/E<gt>
+ * (L<https://bugzilla.redhat.com/1217444#c6>).  If that happens we can
+ * simplify or even remove this code.
+ *)
+
+(* Order is significant *within architectures only*. *)
+let firmware = [
+    "x86_64",
+    "/usr/share/OVMF/OVMF_CODE.fd",
+    None,
+    "/usr/share/OVMF/OVMF_VARS.fd",
+    [];
+
+    "aarch64",
+    "/usr/share/AAVMF/AAVMF_CODE.fd",
+    None,
+    "/usr/share/AAVMF/AAVMF_VARS.fd",
+    [];
+]
+
+let arches = sort_uniq (List.map (fun (arch, _, _, _, _) -> arch) firmware)
+
+let generate_uefi_c () =
+  generate_header CStyle LGPLv2plus;
+
+  pr "#include <config.h>\n";
+  pr "\n";
+  pr "#include <stdio.h>\n";
+  pr "\n";
+  pr "#include \"guestfs.h\"\n";
+  pr "#include \"guestfs-internal-frontend.h\"\n";
+
+  List.iter (
+    fun arch ->
+      let firmware =
+        List.filter (fun (arch', _, _, _, _) -> arch = arch') firmware in
+      pr "\n";
+      pr "struct uefi_firmware\n";
+      pr "guestfs_int_uefi_%s_firmware[] = {\n" arch;
+      List.iter (
+        fun (_, code, code_debug, vars, flags) ->
+          assert (flags = []);
+          pr "  { \"%s\",\n" (c_quote code);
+          (match code_debug with
+           | None -> pr "    NULL,\n"
+           | Some code_debug -> pr "    \"%s\",\n" (c_quote code_debug)
+          );
+          pr "    \"%s\",\n" (c_quote vars);
+          pr "    0\n";
+          pr "  },\n";
+      ) firmware;
+      pr "};\n";
+  ) arches
+
+let generate_uefi_ml () =
+  generate_header OCamlStyle GPLv2plus;
+
+  pr "\
+type uefi_firmware = {
+  code : string;
+  code_debug : string option;
+  vars : string;
+  flags : unit list;
+}
+";
+  List.iter (
+    fun arch ->
+      let firmware =
+        List.filter (fun (arch', _, _, _, _) -> arch = arch') firmware in
+      pr "\n";
+      pr "let uefi_%s_firmware = [\n" arch;
+      List.iter (
+        fun (_, code, code_debug, vars, flags) ->
+          assert (flags = []);
+          pr "  { code = %S;\n" code;
+          (match code_debug with
+           | None -> pr "    code_debug = None;\n"
+           | Some code_debug -> pr "    code_debug = Some %S;\n" code_debug
+          );
+          pr "    vars = %S;\n" vars;
+          pr "    flags = [];\n";
+          pr "  };\n";
+      ) firmware;
+      pr "]\n";
+  ) arches
+
+let generate_uefi_mli () =
+  generate_header OCamlStyle GPLv2plus;
+
+  pr "\
+(** UEFI paths. *)
+
+type uefi_firmware = {
+  code : string;                (** code file *)
+  code_debug : string option;   (** code debug file *)
+  vars : string;                (** vars template file *)
+  flags : unit list;            (** flags *)
+}
+
+";
+
+  List.iter (pr "val uefi_%s_firmware : uefi_firmware list\n") arches
diff --git a/generator/uefi.mli b/generator/uefi.mli
new file mode 100644
index 0000000..64aa1f8
--- /dev/null
+++ b/generator/uefi.mli
@@ -0,0 +1,21 @@
+(* libguestfs
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+val generate_uefi_c : unit -> unit
+val generate_uefi_ml : unit -> unit
+val generate_uefi_mli : unit -> unit
diff --git a/generator/utils.ml b/generator/utils.ml
index 6fb04dc..fdb2b9b 100644
--- a/generator/utils.ml
+++ b/generator/utils.ml
@@ -231,6 +231,22 @@ let mapi f xs =
   in
   loop 0 xs
 
+let uniq ?(cmp = Pervasives.compare) xs =
+  let rec loop acc = function
+    | [] -> acc
+    | [x] -> x :: acc
+    | x :: (y :: _ as xs) when cmp x y = 0 ->
+       loop acc xs
+    | x :: (y :: _ as xs) ->
+       loop (x :: acc) xs
+  in
+  List.rev (loop [] xs)
+
+let sort_uniq ?(cmp = Pervasives.compare) xs =
+  let xs = List.sort cmp xs in
+  let xs = uniq ~cmp xs in
+  xs
+
 let count_chars c str =
   let count = ref 0 in
   for i = 0 to String.length str - 1 do
diff --git a/generator/utils.mli b/generator/utils.mli
index 41dd47d..a69328f 100644
--- a/generator/utils.mli
+++ b/generator/utils.mli
@@ -84,6 +84,12 @@ val iteri : (int -> 'a -> unit) -> 'a list -> unit
 
 val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list
 
+val uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list
+(** Uniquify a list (the list must be sorted first). *)
+
+val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list
+(** Sort and uniquify a list. *)
+
 val count_chars : char -> string -> int
 (** Count number of times the character occurs in string. *)
 
diff --git a/po/POTFILES b/po/POTFILES
index 756fc5e..a45744b 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -357,6 +357,7 @@ src/structs-copy.c
 src/structs-free.c
 src/structs-print.c
 src/tmpdirs.c
+src/uefi.c
 src/umask.c
 src/unit-tests.c
 src/utils.c
diff --git a/po/POTFILES-ml b/po/POTFILES-ml
index 8b1cad7..0c6cfe5 100644
--- a/po/POTFILES-ml
+++ b/po/POTFILES-ml
@@ -129,6 +129,7 @@ v2v/output_vdsm.ml
 v2v/target_bus_assignment.ml
 v2v/test-harness/v2v_test_harness.ml
 v2v/types.ml
+v2v/uefi.ml
 v2v/utils.ml
 v2v/v2v.ml
 v2v/v2v_unit_tests.ml
diff --git a/src/Makefile.am b/src/Makefile.am
index ab36329..e4fc5b6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -232,6 +232,7 @@ libutils_la_SOURCES = \
 	structs-cleanup.c \
 	structs-print.c \
 	structs-print.h \
+	uefi.c \
 	utils.c
 libutils_la_CPPFLAGS = $(libguestfs_la_CPPFLAGS)
 libutils_la_CFLAGS = $(libguestfs_la_CFLAGS)
diff --git a/src/appliance.c b/src/appliance.c
index 1687a93..6409cfb 100644
--- a/src/appliance.c
+++ b/src/appliance.c
@@ -435,9 +435,10 @@ guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars)
 #ifdef __aarch64__
   size_t i;
 
-  for (i = 0; guestfs_int_aavmf_firmware[i] != NULL; i += 2) {
-    const char *codefile = guestfs_int_aavmf_firmware[i];
-    const char *varsfile = guestfs_int_aavmf_firmware[i+1];
+  for (i = 0; guestfs_int_aavmf_firmware[i].code != NULL; ++i) {
+    const char *codefile = guestfs_int_aavmf_firmware[i].code;
+    const char *code_debug_file = guestfs_int_aavmf_firmware[i].code_debug;
+    const char *varsfile = guestfs_int_aavmf_firmware[i].vars;
 
     if (access (codefile, R_OK) == 0 && access (varsfile, R_OK) == 0) {
       CLEANUP_CMD_CLOSE struct command *copycmd = guestfs_int_new_command (g);
@@ -458,6 +459,12 @@ guestfs_int_get_uefi (guestfs_h *g, char **code, char **vars)
         return -1;
       }
 
+      /* If debugging is enabled and we can find the code file with
+       * debugging enabled, use that instead.
+       */
+      if (g->verbose && access (code_debug_file, R_OK) == 0)
+	codefile = code_debug_file;
+
       /* Caller frees. */
       *code = safe_strdup (g, codefile);
       *vars = varst;
diff --git a/src/guestfs-internal-frontend.h b/src/guestfs-internal-frontend.h
index ed9165a..1dba172 100644
--- a/src/guestfs-internal-frontend.h
+++ b/src/guestfs-internal-frontend.h
@@ -110,9 +110,6 @@ extern int guestfs_int_random_string (char *ret, size_t len);
 extern char *guestfs_int_drive_name (size_t index, char *ret);
 extern ssize_t guestfs_int_drive_index (const char *);
 extern int guestfs_int_is_true (const char *str);
-extern const char *guestfs_int_ovmf_i386_firmware[];
-extern const char *guestfs_int_ovmf_x86_64_firmware[];
-extern const char *guestfs_int_aavmf_firmware[];
 //extern void guestfs_int_fadvise_normal (int fd);
 extern void guestfs_int_fadvise_sequential (int fd);
 extern void guestfs_int_fadvise_random (int fd);
@@ -121,6 +118,17 @@ extern void guestfs_int_fadvise_noreuse (int fd);
 //extern void guestfs_int_fadvise_willneed (int fd);
 extern char *guestfs_int_shell_unquote (const char *str);
 
+/* uefi.c */
+struct uefi_firmware {
+  const char *code;		/* code file (NULL = end of list) */
+  const char *code_debug;	/* code file with debugging msgs (may be NULL)*/
+  const char *vars;		/* vars template file */
+  int flags;                    /* flags */
+};
+extern struct uefi_firmware guestfs_int_ovmf_i386_firmware[];
+extern struct uefi_firmware guestfs_int_ovmf_x86_64_firmware[];
+extern struct uefi_firmware guestfs_int_aavmf_firmware[];
+
 /* These functions are used internally by the CLEANUP_* macros.
  * Don't call them directly.
  */
diff --git a/src/utils.c b/src/utils.c
index 8ffc1ed..ab18f52 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -321,28 +321,6 @@ guestfs_int_is_true (const char *str)
   return -1;
 }
 
-/* See src/appliance.c:guestfs_int_get_uefi. */
-const char *
-guestfs_int_ovmf_i386_firmware[] = {
-  NULL
-};
-
-const char *
-guestfs_int_ovmf_x86_64_firmware[] = {
-  "/usr/share/OVMF/OVMF_CODE.fd",
-  "/usr/share/OVMF/OVMF_VARS.fd",
-
-  NULL
-};
-
-const char *
-guestfs_int_aavmf_firmware[] = {
-  "/usr/share/AAVMF/AAVMF_CODE.fd",
-  "/usr/share/AAVMF/AAVMF_VARS.fd",
-
-  NULL
-};
-
 #if 0 /* not used yet */
 /**
  * Hint that we will read or write the file descriptor normally.
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index 0486cfe..c1ebb17 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -72,6 +72,7 @@ SOURCES_MLI = \
 	OVF.mli \
 	target_bus_assignment.mli \
 	types.mli \
+	uefi.mli \
 	utils.mli \
 	vCenter.mli \
 	windows.mli \
@@ -81,6 +82,7 @@ SOURCES_MLI = \
 SOURCES_ML = \
 	types.ml \
 	xml.ml \
+	uefi.ml \
 	utils.ml \
 	vCenter.ml \
 	domainxml.ml \
@@ -219,6 +221,7 @@ COPY_TO_LOCAL_BOBJECTS = \
 	$(top_builddir)/mllib/StatVFS.cmo \
 	$(top_builddir)/mllib/curl.cmo \
 	xml.cmo \
+	uefi.cmo \
 	utils.cmo \
 	vCenter.cmo \
 	domainxml.cmo \
@@ -380,6 +383,7 @@ v2v_unit_tests_BOBJECTS = \
 	$(top_builddir)/mllib/regedit.cmo \
 	types.cmo \
 	xml.cmo \
+	uefi.cmo \
 	utils.cmo \
 	DOM.cmo \
 	OVF.cmo \
diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml
index 5a404ee..df0963e 100644
--- a/v2v/output_libvirt.ml
+++ b/v2v/output_libvirt.ml
@@ -118,7 +118,8 @@ let create_libvirt_xml ?pool source target_buses guestcaps
           * (https://bugzilla.redhat.com/show_bug.cgi?id=1217444#c6) but
           * until that day we have to use a bunch of heuristics. XXX
           *)
-         let code, vars_template = find_uefi_firmware guestcaps.gcaps_arch in
+         let { Uefi.code = code; vars = vars_template } =
+           find_uefi_firmware guestcaps.gcaps_arch in
          [ e "loader" ["readonly", "yes"; "type", "pflash"] [ PCData code ];
            e "nvram" ["template", vars_template] [] ] in
 
diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml
index 94f80c2..2a944d7 100644
--- a/v2v/output_qemu.ml
+++ b/v2v/output_qemu.ml
@@ -69,7 +69,7 @@ object
 
     (match uefi_firmware with
      | None -> ()
-     | Some (_, vars_template) ->
+     | Some { Uefi.vars = vars_template } ->
         fpf "# Make a copy of the UEFI variables template\n";
         fpf "uefi_vars=\"$(mktemp)\"\n";
         fpf "cp %s \"$uefi_vars\"\n" (quote vars_template);
@@ -83,7 +83,7 @@ object
 
     (match uefi_firmware with
      | None -> ()
-     | Some (code, _) ->
+     | Some { Uefi.code = code } ->
         fpf "%s-drive if=pflash,format=raw,file=%s,readonly" nl (quote code);
         fpf "%s-drive if=pflash,format=raw,file=\"$uefi_vars\"" nl
     );
diff --git a/v2v/utils-c.c b/v2v/utils-c.c
index 77de321..af74c12 100644
--- a/v2v/utils-c.c
+++ b/v2v/utils-c.c
@@ -66,51 +66,6 @@ v2v_utils_drive_index (value strv)
   CAMLreturn (Val_int (r));
 }
 
-static value
-get_firmware (char **firmware)
-{
-  CAMLparam0 ();
-  CAMLlocal5 (rv, v, v1, v2, cons);
-  size_t i, len;
-
-  rv = Val_int (0);
-
-  /* Build the list backwards so we don't have to reverse it at the end. */
-  len = guestfs_int_count_strings (firmware);
-
-  for (i = len; i > 0; i -= 2) {
-    v1 = caml_copy_string (firmware[i-2]);
-    v2 = caml_copy_string (firmware[i-1]);
-    v = caml_alloc (2, 0);
-    Store_field (v, 0, v1);
-    Store_field (v, 1, v2);
-    cons = caml_alloc (2, 0);
-    Store_field (cons, 1, rv);
-    rv = cons;
-    Store_field (cons, 0, v);
-  }
-
-  CAMLreturn (rv);
-}
-
-value
-v2v_utils_ovmf_i386_firmware (value unitv)
-{
-  return get_firmware ((char **) guestfs_int_ovmf_i386_firmware);
-}
-
-value
-v2v_utils_ovmf_x86_64_firmware (value unitv)
-{
-  return get_firmware ((char **) guestfs_int_ovmf_x86_64_firmware);
-}
-
-value
-v2v_utils_aavmf_firmware (value unitv)
-{
-  return get_firmware ((char **) guestfs_int_aavmf_firmware);
-}
-
 value
 v2v_utils_shell_unquote (value strv)
 {
diff --git a/v2v/utils.ml b/v2v/utils.ml
index d88f8ad..e7aef79 100644
--- a/v2v/utils.ml
+++ b/v2v/utils.ml
@@ -82,25 +82,20 @@ let qemu_supports_sound_card = function
   | Types.USBAudio
     -> false
 
-external ovmf_i386_firmware : unit -> (string * string) list = "v2v_utils_ovmf_i386_firmware"
-external ovmf_x86_64_firmware : unit -> (string * string) list = "v2v_utils_ovmf_x86_64_firmware"
-external aavmf_firmware : unit -> (string * string) list = "v2v_utils_aavmf_firmware"
-
 (* Find the UEFI firmware. *)
 let find_uefi_firmware guest_arch =
   let files =
     (* The lists of firmware are actually defined in src/utils.c. *)
     match guest_arch with
-    | "i386" | "i486" | "i586" | "i686" -> ovmf_i386_firmware ()
-    | "x86_64" -> ovmf_x86_64_firmware ()
-    | "aarch64" -> aavmf_firmware ()
+    | "x86_64" -> Uefi.uefi_x86_64_firmware
+    | "aarch64" -> Uefi.uefi_aarch64_firmware
     | arch ->
        error (f_"don't know how to convert UEFI guests for architecture %s")
              guest_arch in
   let rec loop = function
     | [] ->
        error (f_"cannot find firmware for UEFI guests.\n\nYou probably need to install OVMF, or AAVMF (if using aarch64)")
-    | ((code, vars_template) as ret) :: rest ->
+    | ({ Uefi.code = code; vars = vars_template } as ret) :: rest ->
        if Sys.file_exists code && Sys.file_exists vars_template then ret
        else loop rest
   in
diff --git a/v2v/utils.mli b/v2v/utils.mli
index 4daf38c..bdabab4 100644
--- a/v2v/utils.mli
+++ b/v2v/utils.mli
@@ -43,10 +43,9 @@ val kvm_arch : string -> string
 val qemu_supports_sound_card : Types.source_sound_model -> bool
 (** Does qemu support the given sound card? *)
 
-val find_uefi_firmware : string -> string * string
-(** Find the UEFI firmware for the guest architecture.  Returns a
-    pair [(code_file, vars_file)].  This cannot return an error, it
-    calls [error] and fails instead. *)
+val find_uefi_firmware : string -> Uefi.uefi_firmware
+(** Find the UEFI firmware for the guest architecture.
+    This cannot return an error, it calls [error] and fails instead. *)
 
 val compare_app2_versions : Guestfs.application2 -> Guestfs.application2 -> int
 (** Compare two app versions. *)
-- 
1.8.3.1