From 5ba28e31fde4b758ecdb5aea88d4aab7b9894f46 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" 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 Eloader type="efi"/E + * (L). 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 \n"; + pr "\n"; + pr "#include \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