Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make bootloader updates on UEFI-based systems work #299

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
174 changes: 99 additions & 75 deletions grml-debootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ Options for Virtual Machine deployment:
--vmsize <size> Use specified size for size of VM file (default: 2G).
Syntax as supported by qemu-img, like: --vmsize 3G
--vmefi Create an EFI boot partition for the VM.
--efi-id <id> Sets the efi bootloader ID for the VM. Only relevant when
used with --vmefi.

Configuration options:

Expand Down Expand Up @@ -300,7 +302,7 @@ cleanup() {
# ugly, but make sure we really don't leave anything (/proc /proc and
# /dev /dev are intended, trying to work around timing issues, see #657023)
for ARG in /run/udev /sys /proc /proc /dev/pts /dev/pts /dev /dev ; do
[ -x "$MNTPOINT"/bin/umount ] && { clean_chroot "$MNTPOINT" umount $ARG >/dev/null 2>&1 || true; }
[ -x "$MNTPOINT"/bin/umount ] && { clean_chroot "$MNTPOINT" umount $ARG >/dev/null 2>&1 || true; }
umount "$MNTPOINT"/$ARG >/dev/null 2>&1 || true
done

Expand Down Expand Up @@ -376,7 +378,7 @@ fi
# }}}

# cmdline handling {{{
CMDLINE_OPTS=mirror:,iso:,release:,target:,mntpoint:,debopt:,defaultinterfaces,interactive,nodebootstrap,nointerfaces,nokernel,nopackages,filesystem:,config:,confdir:,packages:,chroot-scripts:,scripts:,post-scripts:,pre-scripts:,debconf:,vm,vmfile,vmsize:,vmefi,keep_src_list,hostname:,password:,nopassword,grmlrepos,backportrepos,bootappend:,grub:,efi:,arch:,insecure,verbose,help,version,force,debug,contrib,non-free,remove-configs,sshcopyid,sshcopyauth
CMDLINE_OPTS=mirror:,iso:,release:,target:,mntpoint:,debopt:,defaultinterfaces,interactive,nodebootstrap,nointerfaces,nokernel,nopackages,filesystem:,config:,confdir:,packages:,chroot-scripts:,scripts:,post-scripts:,pre-scripts:,debconf:,vm,vmfile,vmsize:,vmefi,efi-id:,keep_src_list,hostname:,password:,nopassword,grmlrepos,backportrepos,bootappend:,grub:,efi:,arch:,insecure,verbose,help,version,force,debug,contrib,non-free,remove-configs,sshcopyid,sshcopyauth

if ! _opt_temp=$(getopt --name grml-debootstrap -o +m:i:r:t:p:c:d:vhV --long \
$CMDLINE_OPTS -- "$@"); then
Expand Down Expand Up @@ -413,6 +415,9 @@ while :; do
--vmefi) # Create an EFI boot partition for the VM
_opt_vmefi="T"
;;
--efi-id)
shift; _opt_efi_id="$1"
;;
--mntpoint|-p) # Mountpoint used for mounting the target system
shift; _opt_mntpoint="$1"
;;
Expand Down Expand Up @@ -579,6 +584,7 @@ done
[ "$_opt_vmfile" ] && VMFILE=1 && VIRTUAL=1
[ "$_opt_vmsize" ] && VMSIZE=$_opt_vmsize
[ "$_opt_vmefi" ] && VMEFI=1
[ "$_opt_efi_id" ] && EFI_ID=$_opt_efi_id
[ "$_opt_mntpoint" ] && MNTPOINT=$_opt_mntpoint
[ "$_opt_debopt" ] && DEBOOTSTRAP_OPT=$_opt_debopt
[ "$_opt_interactive" ] && INTERACTIVE=1
Expand Down Expand Up @@ -1323,13 +1329,6 @@ mkfs() {
fi

if [ -n "$MKFS" ] ; then

if [ -n "${ARM_EFI_TARGET}" ] ; then
einfo "Running mkfs.fat $MKFS_OPTS on $ARM_EFI_TARGET"
mkfs.fat -n "EFI" "$ARM_EFI_TARGET"
MKFS_OPTS="$MKFS_OPTS -L LINUX"
fi

einfo "Running $MKFS $MKFS_OPTS on $TARGET"
# shellcheck disable=SC2086
"$MKFS" $MKFS_OPTS "$TARGET"
Expand Down Expand Up @@ -1501,60 +1500,60 @@ prepare_vm() {

ORIG_TARGET="$TARGET" # store for later reuse

case "$ARCH" in
i386|amd64) true;;
arm64) VMEFI=1;;
esac

if [ "$ARCH" = 'arm64' ]; then
# arm64 support largely only exists for GPT
VMEFI=1
fi

if [ -n "$VMFILE" ]; then
qemu-img create -f raw "${TARGET}" "${VMSIZE}"
fi
if [ -n "$VMEFI" ]; then
parted -s "${TARGET}" 'mklabel gpt'
parted -s "${TARGET}" 'mkpart ESP fat32 1MiB 101MiB'
parted -s "${TARGET}" 'set 1 boot on'
parted -s "${TARGET}" 'mkpart bios_grub 101MiB 102MiB'
parted -s "${TARGET}" 'set 2 bios_grub on'
parted -s "${TARGET}" 'mkpart primary ext4 102MiB 100%'

else
# arm64 support largely only exists for GPT
if [ "$ARCH" = 'arm64' ]; then
einfo "Setting up GPT partitions for arm64"
parted -s "${TARGET}" 'mklabel gpt'
parted -s "${TARGET}" 'mkpart ESP fat32 1MiB 10MiB'
parted -s "${TARGET}" 'set 1 boot on'
parted -s "${TARGET}" 'mkpart LINUX ext4 10MiB 100%'
# No need for a bios_grub parttion on ARM64
parted -s "${TARGET}" 'mkpart primary ext4 101MiB 100%'
else
parted -s "${TARGET}" 'mklabel msdos'
if [ "$FIXED_DISK_IDENTIFIERS" = "yes" ] ; then
einfo "Adjusting disk signature to a fixed (non-random) value"
MBRTMPFILE=$(mktemp)
dd if="${TARGET}" of="${MBRTMPFILE}" bs=512 count=1
echo -en "\\x41\\x41\\x41\\x41" | dd of="${MBRTMPFILE}" conv=notrunc seek=440 bs=1
dd if="${MBRTMPFILE}" of="${TARGET}" conv=notrunc
fi
parted -s "${TARGET}" 'mkpart primary ext4 4MiB 100%'
parted -s "${TARGET}" 'set 1 boot on'
parted -s "${TARGET}" 'mkpart bios_grub 101MiB 102MiB'
parted -s "${TARGET}" 'set 2 bios_grub on'
parted -s "${TARGET}" 'mkpart primary ext4 102MiB 100%'
fi
else
parted -s "${TARGET}" 'mklabel msdos'
if [ "$FIXED_DISK_IDENTIFIERS" = "yes" ] ; then
einfo "Adjusting disk signature to a fixed (non-random) value"
MBRTMPFILE=$(mktemp)
dd if="${TARGET}" of="${MBRTMPFILE}" bs=512 count=1
echo -en "\\x41\\x41\\x41\\x41" | dd of="${MBRTMPFILE}" conv=notrunc seek=440 bs=1
dd if="${MBRTMPFILE}" of="${TARGET}" conv=notrunc
fi
parted -s "${TARGET}" 'mkpart primary ext4 4MiB 100%'
parted -s "${TARGET}" 'set 1 boot on'
fi

DEVINFO=$(kpartx -asv "$TARGET") # e.g. 'add map loop0p1 (254:5): 0 20477 linear 7:0 3' - will be multi-line for arm64
DEVINFO=$(kpartx -asv "$TARGET") # e.g. 'add map loop0p1 (254:5): 0 20477 linear 7:0 3' - will be multi-line for EFI VMs
if [ -z "${DEVINFO}" ] ; then
eerror "Error setting up loopback device."
bailout 1
fi

# if we're building for arm64, we operate on the first line of $DEVINFO which is the EFI partition
if [ "$ARCH" = 'arm64' ]; then
LOOP_PART="${DEVINFO##add map }" # 'loop0p1 (254:5): 0 20477 linear 7:0 3'
LOOP_PART="${LOOP_PART// */}" # 'loop0p1'
LOOP_DISK="${LOOP_PART%p*}" # 'loop0'
export ARM_EFI_TARGET="/dev/mapper/$LOOP_PART"
DEVINFO=${DEVINFO##*$'\n'} # now set $DEVINFO to the last line which is the OS partition
fi

# hopefully this always works as expected
LOOP_PART="${DEVINFO##add map }" # 'loop0p1 (254:5): 0 20477 linear 7:0 3'
LOOP_PART="${LOOP_PART// */}" # 'loop0p1'
if [ -n "$VMEFI" ]; then
export EFI_TARGET="/dev/mapper/$LOOP_PART" # '/dev/mapper/loop0p1'
LOOP_PART="${LOOP_PART%p1}p3"
if [ "$ARCH" = 'arm64' ]; then
LOOP_PART="${LOOP_PART%p1}p2"
else
LOOP_PART="${LOOP_PART%p1}p3"
fi
fi
LOOP_DISK="${LOOP_PART%p*}" # 'loop0'
export TARGET="/dev/mapper/$LOOP_PART" # '/dev/mapper/loop0p1'
Expand All @@ -1581,74 +1580,102 @@ grub_install() {
bailout 1
fi

if [ -n "${ARM_EFI_TARGET}" ]; then
mkdir -p "${MNTPOINT}"/boot/efi
if ! mount "${ARM_EFI_TARGET}" "${MNTPOINT}"/boot/efi ; then
eerror "Error: Mounting ${ARM_EFI_TARGET} failed, can not continue."
bailout 1
fi
fi

mount -t proc none "${MNTPOINT}"/proc
mount -t sysfs none "${MNTPOINT}"/sys
mount -t devtmpfs udev "${MNTPOINT}"/dev
mount -t devpts devpts "${MNTPOINT}"/dev/pts

if [ -n "$ARM_EFI_TARGET" ]; then
einfo "Installing Grub as bootloader into EFI."

clean_chroot "${MNTPOINT}" grub-install --target=arm64-efi --efi-directory=/boot/efi --bootloader-id=debian --recheck --no-nvram --removable
# Has chroot-script installed GRUB to MBR using grub-install (successfully), already?
# chroot-script skips installation for unset ${GRUB}
elif [[ -z "${GRUB}" ]] || ! dd if="${GRUB}" bs=512 count=1 2>/dev/null | cat -v | grep -Fq GRUB; then
if [[ -z "${GRUB}" ]] || ! dd if="${GRUB}" bs=512 count=1 2>/dev/null | cat -v | grep -Fq GRUB; then
einfo "Installing Grub as bootloader."

if ! clean_chroot "${MNTPOINT}" dpkg --list grub-pc 2>/dev/null | grep -q '^ii' ; then
echo "Notice: grub-pc package not present yet, installing it therefore."
# shellcheck disable=SC2086
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-pc
local grub_pc_package_name=''

if [ -z "$VMEFI" ]; then
grub_pc_package_name='grub-pc'
elif [ "$ARCH" != 'arm64' ]; then
# We install grub-pc-bin instead of grub-pc when EFI is enabled, because
# otherwise the EFI bootloader won't be automatically updated when GRUB
# packages are uploaded. Doing this means that the BIOS bootloader won't
# be automatically updated, which stinks, however the BIOS bootloader
# doesn't have the same security concerns as the EFI bootloader (there's
# no Secure Boot to grapple with when using legacy BIOS boot) so it's
# better to let the BIOS bootloader lag behind and update the EFI one
# than to let the EFI bootloader lag behind and update the BIOS one.
grub_pc_package_name='grub-pc-bin'
fi

if [ -n "${grub_pc_package_name}" ]; then
if ! clean_chroot "${MNTPOINT}" dpkg --list "${grub_pc_package_name}" 2>/dev/null | grep -q '^ii' ; then
echo "Notice: ${grub_pc_package_name} package not present yet, installing it therefore."
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS "${grub_pc_package_name}"
fi
fi

mkdir -p "${MNTPOINT}/boot/grub"
if ! [ -d "${MNTPOINT}"/usr/lib/grub/i386-pc/ ] ; then
eerror "Error: grub not installed inside Virtual Machine. Can not install bootloader."
bailout 1
if [ -d "${MNTPOINT}"/usr/lib/grub/i386-pc/ ]; then
cp -a "${MNTPOINT}"/usr/lib/grub/i386-pc "${MNTPOINT}/boot/grub/"
elif [ "$ARCH" != 'arm64' ]; then
eerror "Error: grub not installed inside Virtual Machine. Can not install bootloader."
bailout 1
fi
cp -a "${MNTPOINT}"/usr/lib/grub/i386-pc "${MNTPOINT}/boot/grub/"

if [ -n "$VMEFI" ]; then

mkdir -p "${MNTPOINT}"/boot/efi
mount -t vfat "${EFI_TARGET}" "${MNTPOINT}"/boot/efi

if [ -z "${EFI_ID}" ]; then
EFI_ID="$(
source "${MNTPOINT}"/etc/default/grub
for file in "${MNTPOINT}"/etc/default/grub.d/*.cfg; do
if [ -f "${file}" ]; then
source "${file}"
fi
done
echo "${GRUB_DISTRIBUTOR}"
)"
else
# This is prefixed with a `z` so that it takes precedence over any other GRUB config.
echo "GRUB_DISTRIBUTOR='${EFI_ID}'" > "${MNTPOINT}"/etc/default/grub.d/z-grml-debootstrap-efi-id.cfg
fi

mkdir -p "${MNTPOINT}"/boot/efi/EFI/BOOT
mkdir -p "${MNTPOINT}"/boot/efi/EFI/"${EFI_ID}"

if ! clean_chroot "${MNTPOINT}" dpkg --list shim-signed 2>/dev/null | grep -q '^ii' ; then
echo "Notice: shim-signed package not present yet, installing it therefore."
# shellcheck disable=SC2086
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS shim-signed
clean_chroot "${MNTPOINT}" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS shim-signed
fi

if [ "$(dpkg --print-architecture)" = "arm64" ]; then
# The EFI bootloader will automatically be installed to the proper location when the
# corresponding grub-efi-ARCH package is installed.
if [ "${ARCH}" = "arm64" ]; then
clean_chroot "$MNTPOINT" debconf-set-selections <<< 'grub-efi-arm64 grub2/force_efi_extra_removable boolean true'
if ! clean_chroot "${MNTPOINT}" dpkg --list grub-efi-arm64-signed 2>/dev/null | grep -q '^ii' ; then
echo "Notice: grub-efi-arm64-signed package not present yet, installing it therefore."
# shellcheck disable=SC2086
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-arm64-bin grub-efi-arm64-signed
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-arm64-bin grub-efi-arm64-signed grub-efi-arm64
clean_chroot "$MNTPOINT" dpkg-reconfigure --priority=critical grub-efi-arm64
fi
clean_chroot "$MNTPOINT" grub-install --target=arm64-efi --efi-directory=/boot/efi --uefi-secure-boot --removable "/dev/$LOOP_DISK"
elif [ "$(dpkg --print-architecture)" = "i386" ]; then
elif [ "${ARCH}" = "i386" ]; then
clean_chroot "$MNTPOINT" debconf-set-selections <<< 'grub-efi-ia32 grub2/force_efi_extra_removable boolean true'
if ! clean_chroot "${MNTPOINT}" dpkg --list grub-efi-ia32-signed 2>/dev/null | grep -q '^ii' ; then
echo "Notice: grub-efi-ia32-signed package not present yet, installing it therefore."
# shellcheck disable=SC2086
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-ia32-bin grub-efi-ia32-signed
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-ia32-bin grub-efi-ia32-signed grub-efi-ia32
clean_chroot "$MNTPOINT" dpkg-reconfigure --priority=critical grub-efi-ia32
fi
clean_chroot "$MNTPOINT" grub-install --target=i386-efi --efi-directory=/boot/efi --uefi-secure-boot --removable "/dev/$LOOP_DISK"
clean_chroot "$MNTPOINT" grub-install --target=i386-pc "/dev/$LOOP_DISK"
else
clean_chroot "$MNTPOINT" debconf-set-selections <<< 'grub-efi-amd64 grub2/force_efi_extra_removable boolean true'
if ! clean_chroot "${MNTPOINT}" dpkg --list grub-efi-amd64-signed 2>/dev/null | grep -q '^ii' ; then
echo "Notice: grub-efi-amd64-signed package not present yet, installing it therefore."
# shellcheck disable=SC2086
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-amd64-bin grub-efi-amd64-signed
clean_chroot "$MNTPOINT" DEBIAN_FRONTEND=$DEBIAN_FRONTEND apt-get -y --no-install-recommends install $DPKG_OPTIONS grub-efi-amd64-bin grub-efi-amd64-signed grub-efi-amd64
clean_chroot "$MNTPOINT" dpkg-reconfigure --priority=critical grub-efi-amd64
fi
clean_chroot "$MNTPOINT" grub-install --target=x86_64-efi --efi-directory=/boot/efi --uefi-secure-boot --removable "/dev/$LOOP_DISK"
clean_chroot "$MNTPOINT" grub-install --target=i386-pc "/dev/$LOOP_DISK"
fi
else
Expand Down Expand Up @@ -1699,9 +1726,6 @@ grub_install() {
if grep -q '^GRUB_DISABLE_LINUX_UUID=.*true' "${MNTPOINT}"/etc/default/grub 2>/dev/null ; then
ewarn "GRUB_DISABLE_LINUX_UUID is set to true in /etc/default/grub, not adjusting root= in grub.cfg."
ewarn "Please note that your system might NOT be able to properly boot."
elif [ -z "$ARM_EFI_TARGET" ]; then
einfo "Adjusting grub.cfg for successful boot sequence."
sed -i "s;root=[^ ]\\+;root=UUID=$TARGET_UUID;" "${MNTPOINT}"/boot/grub/grub.cfg
fi

# workaround for Debian bug #918590 with lvm + udev:
Expand Down