Denis Plotnikov
2020-Jul-24 12:36 UTC
[Libguestfs] [PATCH v2] v2v: fix UEFI bootloader for linux guests
v2: Rich, I hope I've done all modifications according to your comments, namely: * moved the code from linux_bootloaders to convert_linux * made minor code modifications --- Not all UEFI guests can survive conversion, because of lost bootloader information in UEFI NVRAM. But some guest can cope with this because they have a fallback bootloader and use UEFI Removable Media Boot Behavior to load (see https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf 3.5.1.1 Removable Media Boot Behavior). If UEFI firmware can't find a bootloader in its settings it uses the removable media boot behavior to find a bootloader. We can fix the guests which don't have such a fallback loader by providing a temporary one. This bootloader is used for the first boot only, then the conversion script restores the initial bootloader settings and removes the temporary loader. Signed-off-by: Denis Plotnikov <dplotnikov@virtuozzo.com> --- v2v/convert_linux.ml | 123 ++++++++++++++++++++++++++++++++++++++ v2v/linux_bootloaders.ml | 7 +++ v2v/linux_bootloaders.mli | 4 ++ 3 files changed, 134 insertions(+) diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml index e91ae120f..045afa98b 100644 --- a/v2v/convert_linux.ml +++ b/v2v/convert_linux.ml @@ -1122,6 +1122,129 @@ shutdown -h -P +0 Linux.augeas_reload g ); + (* Some linux uefi setups can't boot after conversion because of + lost uefi boot entries. The uefi boot entries are stored in uefi + NVRAM. The NVRAM content isn't a part of vm disk content and + usualy can't be converted alongside the vm. + If a vm doesn't have uefi fallback path (/EFI/BOOT/BOOT<arch>.efi) + the vm is unbootable after conversion. + The following code tries to make an uefi fallback path for + a uefi linux vm. + *) + match inspect.i_firmware with + | I_UEFI _ -> ( + (* Standard uefi fallback path *) + let uefi_fallback_path = "/boot/efi/EFI/BOOT/" in + + (* Helper function checks if 'source' contains 's' *) + let contains source s = ( + let re = Str.regexp_string s in + try + ignore (Str.search_forward re source 0); + true + with Not_found -> false + ) in + + let cant_fix_uefi () + info (f_"Can't fix UEFI bootloader. VM may not boot.") + in + + let get_uefi_arch_suffix = function + | "x86_64" -> Some "X64" + | "x86_32" -> Some "X32" + | _ -> None + in + + match get_uefi_arch_suffix inspect.i_arch with + | None -> cant_fix_uefi () + | Some suffix -> ( + let uefi_fallback_name + sprintf "%sBOOT%s.EFI" uefi_fallback_path suffix in + + let file_exists file + if g#exists file then + true + else ( + info (f_"Can't find file: '%s' needed for UEFI fixing") + file; + false ) + in + + let grub_config = bootloader#get_config_file () in + + let grub_path + String.sub grub_config 0 (String.rindex grub_config '/') in + + if g#exists uefi_fallback_name then + (* don't do anything if uefi fallback exists *) + () + else ( + info (f_"Fixing UEFI bootloader."); + match inspect.i_distro, inspect.i_major_version with + | "centos", 6 -> + (* to make a bootable uefi centos 6 we need to + * copy grub.efi and grub.conf to UEFI fallback path + * and rename them to BOOT<arch>.efi and BOOT<arch>.conf + * correspondingly *) + let uefi_grub_name = String.concat "" [grub_path; "/grub.efi"] in + let uefi_grub_conf = String.concat "" [ + String.sub uefi_fallback_name 0 + (String.rindex uefi_fallback_name '.'); + ".conf" ] in + if file_exists uefi_grub_name && file_exists grub_config then ( + g#mkdir_p uefi_fallback_path; + g#cp uefi_grub_name uefi_fallback_name; + g#cp grub_config uefi_grub_conf; + let fix_script = sprintf +"#!/bin/bash +efibootmgr -c -L \"CentOS 6\" +rm -rf %s" uefi_fallback_path in + Firstboot.add_firstboot_script + g inspect.i_root "fix uefi boot" fix_script) + else + cant_fix_uefi () + | "ubuntu", 14 -> + (* to make a bootable uefi ubuntu 14 we need to + * copy shim<arch>.efi to UEFI fallback path + * and rename it to BOOT<arch>.efi, also we copy + * grub.efi and grub.cfg to UEFI fallback path without renaming *) + let arch_suffix = String.lowercase_ascii suffix in + + let shim + String.concat "" [grub_path; "/shim"; arch_suffix; ".efi"] in + let uefi_grub_name + String.concat "" [grub_path; "/grub"; arch_suffix; ".efi"] in + + if file_exists shim && file_exists uefi_grub_name + && file_exists grub_config then ( + g#mkdir_p uefi_fallback_path; + g#cp shim uefi_fallback_name; + g#cp uefi_grub_name uefi_fallback_path; + g#cp grub_config uefi_fallback_path; + (* if the shim is at the standard path, clean up uefi fixing + * if not, then just don't clean up and leave the temp loader + * at UEFI fallback path for simplicity + *) + if contains shim "/boot/efi/EFI/ubuntu/shim" then + let fix_script = sprintf +"#!/bin/bash +sudo efibootmgr -c -L ubuntu -l \\\\EFI\\\\ubuntu\\\\shim%s.efi +rm -rf %s" arch_suffix uefi_fallback_path in + Firstboot.add_firstboot_script + g inspect.i_root "fix uefi boot" fix_script + else + ()) + else + cant_fix_uefi () + | _, _ -> + info (f_"No UEFI fix rule for %s %d") + inspect.i_distro inspect.i_major_version; + cant_fix_uefi () + ) + ) + ) + | I_BIOS -> (); + (* Delete blkid caches if they exist, since they will refer to the old * device names. blkid will rebuild these on demand. * diff --git a/v2v/linux_bootloaders.ml b/v2v/linux_bootloaders.ml index de3d107e9..4ca28782b 100644 --- a/v2v/linux_bootloaders.ml +++ b/v2v/linux_bootloaders.ml @@ -36,6 +36,7 @@ class virtual bootloader = object method virtual configure_console : unit -> unit method virtual remove_console : unit -> unit method update () = () + method virtual get_config_file : unit -> string end (* Helper function for SUSE: remove (hdX,X) prefix from a path. *) @@ -184,6 +185,9 @@ object loop paths; g#aug_save () + + method get_config_file () + grub_config end (** The method used to get and set the default kernel in Grub2. *) @@ -342,6 +346,9 @@ object (self) method update () ignore (g#command [| grub2_mkconfig_cmd; "-o"; grub_config |]) + + method get_config_file () + grub_config end (* Helper type used in detect_bootloader. *) diff --git a/v2v/linux_bootloaders.mli b/v2v/linux_bootloaders.mli index 30cdfe3c7..d9f0be8e1 100644 --- a/v2v/linux_bootloaders.mli +++ b/v2v/linux_bootloaders.mli @@ -44,6 +44,10 @@ class virtual bootloader : object (** Update the bootloader: For grub2 only this runs the [grub2-mkconfig] command to rebuild the configuration. This is not necessary for grub-legacy. *) + + method virtual get_config_file : unit -> string + (** Returns the path to the bootloader config file, + e.g /boot/grub/grub.cfg *) end (** Encapsulates a Linux boot loader as object. *) -- 2.17.0
Richard W.M. Jones
2020-Jul-24 14:20 UTC
Re: [Libguestfs] [PATCH v2] v2v: fix UEFI bootloader for linux guests
On Fri, Jul 24, 2020 at 03:36:58PM +0300, Denis Plotnikov wrote:> v2: > Rich, I hope I've done all modifications according to your comments, namely: > * moved the code from linux_bootloaders to convert_linux > * made minor code modificationsThanks - I pushed it. I squashed in some changes which are stylistic only (hopefully - I hope I correctly understood what the "contains" function was doing). The diff versus your patch is attached. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
Sam Eiderman
2020-Aug-26 12:19 UTC
Re: [Libguestfs] [PATCH v2] v2v: fix UEFI bootloader for linux guests
A bit late but regarding '| "x86_32" -> Some "X32"', Is "x86_32" actually a valid i_arch? I was under the impression that it is being set to i386. Thanks! On Fri, Jul 24, 2020 at 6:20 PM Denis Plotnikov <dplotnikov@virtuozzo.com> wrote:> > > On 24.07.2020 17:20, Richard W.M. Jones wrote: > > On Fri, Jul 24, 2020 at 03:36:58PM +0300, Denis Plotnikov wrote: > >> v2: > >> Rich, I hope I've done all modifications according to your comments, > namely: > >> * moved the code from linux_bootloaders to convert_linux > >> * made minor code modifications > > Thanks - I pushed it. > > > > I squashed in some changes which are stylistic only (hopefully - I > > hope I correctly understood what the "contains" function was doing). > > > > The diff versus your patch is attached. > > > > Rich. > > > Ok, thanks! > > Denis > > _______________________________________________ > Libguestfs mailing list > Libguestfs@redhat.com > https://www.redhat.com/mailman/listinfo/libguestfs > >
Richard W.M. Jones
2020-Aug-26 12:55 UTC
Re: [Libguestfs] [PATCH v2] v2v: fix UEFI bootloader for linux guests
On Wed, Aug 26, 2020 at 03:19:22PM +0300, Sam Eiderman wrote:> A bit late but regarding '| "x86_32" -> Some "X32"', > Is "x86_32" actually a valid i_arch? I was under the impression that it is > being set to i386.You're correct, the only valid value for i_arch on i386 is "i386": https://libguestfs.org/guestfs.3.html#guestfs_file_architecture I guess no one really uses i386, but I'll push a fix shortly, thanks. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html
Reasonably Related Threads
- [PATCH v2] v2v: fix UEFI bootloader for linux guests
- [PATCH] v2v: fix UEFI bootloader for linux guests
- [PATCH] v2v: convert-windows: remove installation reference for prl_strg driver
- [PATCH] daemon: drop error message check in do_part_expand_gpt
- [PATCH v2] v2v: factor out bootloader handling