Skip to content

Instantly share code, notes, and snippets.

@robvanoostenrijk
Created December 19, 2023 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robvanoostenrijk/4f1500fa2c04fbc193d712461a6e799e to your computer and use it in GitHub Desktop.
Save robvanoostenrijk/4f1500fa2c04fbc193d712461a6e799e to your computer and use it in GitHub Desktop.
Alpine EFI Kernel
#!/bin/ash
set -euo pipefail
readonly HOOK_NAME='efi-kernel'
# Defaults
output_dir='/boot/efi/Alpine'
output_name='linux-{flavor}.efi'
backup_old=yes
skip_flavors=
die() {
printf "$HOOK_NAME: %s\n" "$2" >&2
exit $1
}
if [ $# -lt 2 ]; then
echo "Usage: $0 <flavor> <new-version> <old-version>" >&2
exit 1
fi
readonly FLAVOR=$1
readonly NEW_VERSION=$2
readonly OLD_VERSION=${3:-}
case "$(cat /etc/apk/arch)" in
aarch64) readonly MARCH="aa64";;
arm*) readonly MARCH="arm";;
riscv64) readonly MARCH="riscv64";;
x86) readonly MARCH="ia32";;
x86_64) readonly MARCH="x64";;
esac
# Hook triggered for the kernel removal, nothing to do here.
[ "$NEW_VERSION" ] || exit 0
. /etc/kernel-hooks.d/efi-kernel.conf
[ "$skip_flavors" ] && for flavor in $skip_flavors; do
[ "$flavor" = "$FLAVOR" ] \
&& die 0 "skipping UEFI image creation for $FLAVOR kernel"
done
vmlinuz="/boot/vmlinuz-${FLAVOR}"
initrd="/boot/initramfs-${FLAVOR}"
output_name=$(echo "$output_name" \
| sed "s/{flavor}/$FLAVOR/; s/{version}/$NEW_VERSION/")
output="$output_dir/$output_name"
if [ "$backup_old" = yes ] && [ -f "$output" ]; then
cp -a "$output" "$output.bak"
fi
echo "==> $HOOK_NAME: creating UEFI Unified Kernel Image with $vmlinuz"
mkdir -p "$output_dir"
cp -v "$vmlinuz" "$output_dir/vmlinuz-${FLAVOR}"
cp -v "$initrd" "$output_dir/initramfs-${FLAVOR}"
# Just a simple sanity check.
dir="/${output_dir#/}"
while [ "$dir" ]; do
mount -t vfat | grep -Fq " on $dir type vfat " && exit 0
dir=${dir%/*}
done
die 0 "WARNING: $output_dir is not on UEFI System Partition as it should be!"

The default Alpine kernel is compiled with EFI_STUB=y, this allows booting the kernel directly from EFI firmware.

This approach works on:

  • Google Cloud
  • Scaleway
  • Oracle Cloud

* Other cloud providers are untested

In order to prepare for EFI booting, install the following packages:

apk install efibootmgr kernel-hooks

On GPT installations, Alpine mounts the ESP under /boot/efi by default. So we can define the deploy the attached kernel hook script and configuration.

Deploy the kernel to the ESP partition by executing:

# apk fix kernel-hooks
Executing kernel-hooks-0.2-r0.trigger
kernel-hooks: executing hook 10-efi-kernel.hook (virt, 6.6.7-0, )
==> efi-kernel: creating UEFI Unified Kernel Image with /boot/vmlinuz-virt
'/boot/vmlinuz-virt' -> '/boot/efi/vmlinuz-virt'
'/boot/initramfs-virt' -> '/boot/efi/initramfs-virt'

Finally the EFI firmware needs to be configured to boot the newly deployed EFI kernel:

sudo efibootmgr --create --disk /dev/sda --part 1 --label 'Alpine Linux' --loader '\vmlinuz-virt' --unicode 'root=UUID=2ee1ab97-12dd-4545-8755-f622a1cf2ded ro quiet rootfstype=ext4 console=ttyS0,38400n8d initrd=\initramfs-virt'

Replace root UUID and console settings as necessary.

Validate that the boot option is successfully configured:

# efibootmgr -u
BootCurrent: 0007
Timeout: 0 seconds
BootOrder: 0007,0000,0001,0002,0003,0004,0005,0006
.
.
Boot0007* Alpine Linux	HD(1,GPT,5cc32e8c-b098-476d-9da1-89ba89f68aed,0x800,0x31800)/File(\vmlinuz-virt)root=UUID=2ee1ab97-12dd-4545-8755-f622a1cf2ded ro quiet rootfstype=ext4 console=ttyS0,38400n8d initrd=\initramfs-virt

The virtual machine can now boot directly into the EFI kernel, skipping any bootloader such as GRUB

# Configuration for efi-kernel hook
# Kernel command line to embed into UEFI image.
# Value starting with "/" or "." will be interpreted as a path to a file that
# contains kernel command line parameters (lines starting with "#" will be
# stripped and newlines replaced with spaces).
#
# Absolute path to a directory on EFI System Partition where to put the
# UEFI kernel and initrd.
output_dir="/boot/efi"
# Name of the output UEFI image file. It can contain placeholders:
# '{flavor}' will be replaced with the kernel flavor (e.g. "lts", "virt"),
# '{version}' will be replaced with the kernel version (e.g. "5.10.51-0").
#output_name="linux-{flavor}.efi"
# Whether to backup old signed UEFI image (appends ".bak" to the file name).
backup_old=yes
# Kernel flavors to skip this hook for (e.g. "edge virt"). If empty, it runs
# for all installed flavors.
#skip_flavors=
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment