Add an example recipe (based on configs/pc_x86_64_efi_defconfig) to
generate an x86-64 image which boots the system using the EFI Boot Guard
bootloader. The recipe is an example of how to create an image using
EFI Boot Guard, including how to create the disk image & how to configure
the bootloader.
The created example image contains two boot slots; one of which contains
the environment built by Buildroot and the other is an empty, dummy slot
(e.g. cannot be booted from).
EFI Boot Guard provides a tool for generating UKI images, which can then
be signed for Secure Boot. For simplicity, this is not enabled in the
example recipe.
Also add instructions (and a script) to boot the example image in QEMU.
DEVELOPERS | 2 +
board/pc-efibootguard/README.md | 89 ++++++++++++++++++++++++++++++++
board/pc-efibootguard/genimage.cfg | 59 +++++++++++++++++++++
board/pc-efibootguard/linux.fragment | 15 ++++++
board/pc-efibootguard/post-build.sh | 33 ++++++++++++
board/pc-efibootguard/post-image.sh | 7 +++
board/pc-efibootguard/run-image-qemu.sh | 9 ++++
configs/pc_x86_64_efibootguard_defconfig | 51 ++++++++++++++++++
8 files changed, 265 insertions(+)
diff --git a/DEVELOPERS b/DEVELOPERS
index 246b1aa44e..ddc71d9639 100644
--- a/DEVELOPERS
+++ b/DEVELOPERS
@@ -704,7 +704,9 @@ F: package/perl-time-parsedate/
F: package/perl-x10/
N: Christopher Obbard <
obb...@gmail.com>
+F: board/pc-efibootguard/
F: boot/efibootguard/
+F: configs/pc_x86_64_efibootguard_defconfig
diff --git a/board/pc-efibootguard/README.md b/board/pc-efibootguard/README.md
new file mode 100644
index 0000000000..1bbeff4c48
--- /dev/null
+++ b/board/pc-efibootguard/README.md
@@ -0,0 +1,89 @@
+# EFI Boot Guard Example Recipe
+
+This recipe creates a basic x86 disk image using [EFI Boot Guard](
https://github.com/siemens/efibootguard/tree/master)
+as the bootloader with two root filesystem slots.
+
+The image is intended to demonstrate a typical A/B boot layout. The first slot is
+populated with the root filesystem generated as part of the Buildroot build,
+while the second slot is left empty so that it can be used later by an update
+tool, which is out-of-scope for this example recipe.
+
+The recipe also creates the EFI system partition and installs the EFI Boot Guard
+bootloader components needed to select and boot one of the available slots. Each
+slot has its own EFI Boot Guard environment, containing the kernel image and
+configuration.
+
+This example is deliberately minimal. It is intended as a reference for users who
+want to integrate EFI Boot Guard into their own platform-specific images, rather
+than as a complete production-ready update setup.
+
+This recipe was designed to be ran in QEMU and may need additional changes to run
+on real hardware.
+
+
+## Build the image
+
+Configure Buildroot with:
+
+```
+$ make pc_x86_64_efibootguard_defconfig
+```
+
+
+Build the image with:
+
+```
+$ make
+```
+
+The raw disk image is available under `output/images/disk.img`.
+
+
+## Emulation in QEMU
+
+Emulate the system in QEMU with:
+
+```
+$ qemu-system-x86_64 \
+ -M pc \
+ -drive if=pflash,format=raw,readonly=on,file=</path/to/OVMF_CODE.fd> \
+ -drive file=output/images/disk.img,if=virtio,format=raw \
+ -boot menu=on \
+ -net nic,model=virtio \
+ -net user
+```
+
+Note that `</path/to/OVMF.fd>` needs to point to a valid x86_64 UEFI
+firmware image for qemu. It may be provided by your distribution as a
+edk2 or OVMF package, in a path such as `/usr/share/OVMF/OVMF_CODE_4M.fd` in
+Debian.
+
+
+## EFI Boot Guard userspace tools
+
+The example image includes the EFI Boot Guard userspace tools. These can be used
+to inspect and update the boot metadata stored in each slot environment.
+
+`bg_printenv` prints the current EFI Boot Guard boot configuration:
+
+```
+# bg_printenv
+```
+
+`bg_setenv` updates the state and revision of a slot. For example, the following
+commands initialise both slots and prefers booting from slot `0`:
+
+```
+# bg_setenv --part 0 --ustate OK --revision 1
+# bg_setenv --part 1 --ustate OK --revision 0
+```
+
+In this example, both slots are marked as usable, but slot `0` has the higher
+revision. EFI Boot Guard will therefore select it before slot `1`.
+
+For more information on configuring EFI Boot Guard, see the
+[upstream documentation](
https://github.com/siemens/efibootguard/blob/master/README.md).
+
+An update framework (such as RAUC) can use these tools to switch the preferred
+boot slot after installing an update and to mark a slot as failed if the updated
+system does not boot successfully.
diff --git a/board/pc-efibootguard/genimage.cfg b/board/pc-efibootguard/genimage.cfg
new file mode 100644
index 0000000000..2437a89b63
--- /dev/null
+++ b/board/pc-efibootguard/genimage.cfg
@@ -0,0 +1,59 @@
+image EFI.vfat {
+ vfat {
+ file EFI/BOOT/BOOTX64.EFI {
+ image = "efibootguard/efibootguardx64.efi"
+ }
+ }
+
+ size = 16352K # 16MB - 32KB
+}
+
+image boot-a.vfat {
+ vfat {}
+
+ size = 128M
+ srcpath = "output/images/boot-a-part"
+}
+
+# # boot-b is a placeholder
+image boot-b.vfat {
+ vfat {}
+
+ size = 128M
+ srcpath = "output/images/boot-b-part"
+}
+
+image disk.img {
+ hdimage {
+ partition-table-type = "gpt"
+ }
+
+ partition EFI {
+ image = "EFI.vfat"
+ partition-type-uuid = esp
+ offset = 32K
+ bootable = true
+ }
+
+ partition boot-a {
+ partition-type-uuid = fat32
+ image = "boot-a.vfat"
+ }
+
+ partition boot-b {
+ partition-type-uuid = fat32
+ image = "boot-b.vfat"
+ }
+
+ partition system-a {
+ partition-type-uuid = root-x86-64
+ image = "rootfs.ext4"
+ size = 2G
+ }
+
+ # system-b is a placeholder
+ partition system-b {
+ partition-type-uuid = root-x86-64
+ size = 2G
+ }
+}
diff --git a/board/pc-efibootguard/linux.fragment b/board/pc-efibootguard/linux.fragment
new file mode 100644
index 0000000000..f9f2c3fd06
--- /dev/null
+++ b/board/pc-efibootguard/linux.fragment
@@ -0,0 +1,15 @@
+# efistub
+CONFIG_EFI=y
+CONFIG_EFI_STUB=y
+CONFIG_EFIVAR_FS=y
+CONFIG_EFI_PARTITION=y
+
+# efibootguard tools (bg_printenv / bg_setenv) use fat partition
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_NLS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
diff --git a/board/pc-efibootguard/post-build.sh b/board/pc-efibootguard/post-build.sh
new file mode 100755
index 0000000000..4f3e3254d8
--- /dev/null
+++ b/board/pc-efibootguard/post-build.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+set -e
+
+# Kernel cmdline to append
+KERNEL_CMDLINE=""
+
+# Create efibootguard boot-a slot configuration
+mkdir -p "${BINARIES_DIR}"/boot-a-part
+printf "boot-a" | iconv -f ascii -t UTF-16LE > "${BINARIES_DIR}"/boot-a-part/EFILABEL
+"${HOST_DIR}"/bin/bg_setenv \
+ --verbose \
+ --watchdog=0 \
+ --filepath="${BINARIES_DIR}"/boot-a-part/BGENV.DAT \
+ --revision=2 \
+ --ustate=OK \
+ --kernel="C:boot-a:bzImage" \
+ --args="root=PARTLABEL=system-a $KERNEL_CMDLINE"
+
+# Create efibootguard boot-a slot configuration (unbootable)
+mkdir -p "${BINARIES_DIR}"/boot-b-part
+printf "boot-b" | iconv -f ascii -t UTF-16LE > "${BINARIES_DIR}"/boot-b-part/EFILABEL
+"${HOST_DIR}"/bin/bg_setenv \
+ --verbose \
+ --watchdog=0 \
+ --filepath="${BINARIES_DIR}"/boot-b-part/BGENV.DAT \
+ --revision=1 \
+ --ustate=FAILED \
+ --kernel="C:boot-b:bzImage" \
+ --args="root=PARTLABEL=system-b $KERNEL_CMDLINE"
+
+# Copy kernel into boot-a.
+cp "${BINARIES_DIR}"/bzImage "${BINARIES_DIR}"/boot-a-part/
diff --git a/board/pc-efibootguard/post-image.sh b/board/pc-efibootguard/post-image.sh
new file mode 100755
index 0000000000..eec88f707d
--- /dev/null
+++ b/board/pc-efibootguard/post-image.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+UUID=$(dumpe2fs "$BINARIES_DIR/rootfs.ext2" 2>/dev/null | sed -n 's/^Filesystem UUID: *\(.*\)/\1/p')
+sed "s/UUID_TMP/$UUID/g" board/pc-efibootguard/genimage.cfg > "$BINARIES_DIR/genimage.cfg"
+support/scripts/genimage.sh -c "$BINARIES_DIR/genimage.cfg"
diff --git a/board/pc-efibootguard/run-image-qemu.sh b/board/pc-efibootguard/run-image-qemu.sh
new file mode 100755
index 0000000000..32be727c3e
--- /dev/null
+++ b/board/pc-efibootguard/run-image-qemu.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+qemu-system-x86_64 \
+ -M pc \
+ -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
+ -drive file=output/images/disk.img,if=virtio,format=raw \
+ -boot menu=on \
+ -net nic,model=virtio \
+ -net user
diff --git a/configs/pc_x86_64_efibootguard_defconfig b/configs/pc_x86_64_efibootguard_defconfig
new file mode 100644
index 0000000000..7220b2d7ce
--- /dev/null
+++ b/configs/pc_x86_64_efibootguard_defconfig
@@ -0,0 +1,51 @@
+BR2_x86_64=y
+BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_6_12=y
+BR2_GLOBAL_PATCH_DIR="board/pc/patches"
+BR2_DOWNLOAD_FORCE_CHECK_HASHES=y
+BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
+BR2_TARGET_GENERIC_GETTY_PORT="tty1"
+BR2_ROOTFS_POST_BUILD_SCRIPT="board/pc-efibootguard/post-build.sh"
+BR2_ROOTFS_POST_IMAGE_SCRIPT="board/pc-efibootguard/post-image.sh"
+BR2_LINUX_KERNEL=y
+BR2_LINUX_KERNEL_CUSTOM_VERSION=y
+BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.12.47"
+BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
+BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/pc/linux.config"
+BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="board/pc-efibootguard/linux.fragment"
+BR2_LINUX_KERNEL_INSTALL_TARGET=y
+BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
+BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
+BR2_PACKAGE_LINUX_FIRMWARE=y
+BR2_PACKAGE_LINUX_FIRMWARE_ATHEROS_9170=y
+BR2_PACKAGE_LINUX_FIRMWARE_ATHEROS_9271=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_3160=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_3168=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_5000=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_6000G2A=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_6000G2B=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_7260=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_7265D=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_8000C=y
+BR2_PACKAGE_LINUX_FIRMWARE_IWLWIFI_8265=y
+BR2_PACKAGE_LINUX_FIRMWARE_RALINK_RT73=y
+BR2_PACKAGE_LINUX_FIRMWARE_RALINK_RT2XX=y
+BR2_PACKAGE_LINUX_FIRMWARE_RTL_81XX=y
+BR2_PACKAGE_LINUX_FIRMWARE_RTL_87XX=y
+BR2_PACKAGE_LINUX_FIRMWARE_RTL_88XX=y
+BR2_PACKAGE_LINUX_FIRMWARE_RTL_8169=y
+BR2_PACKAGE_ACPID=y
+BR2_PACKAGE_CONNMAN=y
+BR2_PACKAGE_CONNMAN_WIFI=y
+BR2_PACKAGE_CONNMAN_CLIENT=y
+BR2_PACKAGE_WIRELESS_REGDB=y
+BR2_TARGET_ROOTFS_EXT2=y
+BR2_TARGET_ROOTFS_EXT2_4=y
+BR2_TARGET_ROOTFS_EXT2_SIZE="120M"
+# BR2_TARGET_ROOTFS_TAR is not set
+BR2_TARGET_EFIBOOTGUARD=y
+BR2_TARGET_EFIBOOTGUARD_X86_64=y
+BR2_TARGET_EFIBOOTGUARD_INSTALL_TOOLS=y
+BR2_TARGET_HOST_EFIBOOTGUARD=y
+BR2_PACKAGE_HOST_DOSFSTOOLS=y
+BR2_PACKAGE_HOST_GENIMAGE=y
+BR2_PACKAGE_HOST_MTOOLS=y
--
2.53.0