[PATCH] x86_64: add systemd-boot images
Stefan Hellermann
stefan at the2masters.de
Tue Dec 30 09:30:43 PST 2025
Hi,
I tested this with a small x86/64 config in libvirt / Virt Manager on
Fedora. It boots up fine in a virtual machine with UEFI enabled and
secure boot disabled.
Systemd-boot is smaller than grub, sizes:
openwrt-x86-64-generic-squashfs-rootfs.img.gz with grub2-efi: 4371163
openwrt-x86-64-generic-squashfs-rootfs.img.gz with systemd-boot: 4124862
(-5.6%, -240.5kB)
openwrt-x86-64-generic-squashfs-combined-efi.img.gz: 10761442
openwrt-x86-64-generic-squashfs-systemd-boot.img.gz: 9964810 (-7.4%, -778kB)
=> Nice! But probably doesn't matter on x86 :-)
But: sysupgrade the virtual machine to the same image fails:
# sysupgrade /tmp/openwrt-x86-64-generic-squashfs-systemd-boot.img.gz
Tue Dec 30 17:45:38 CET 2025 upgrade: Image metadata not present
Tue Dec 30 17:45:38 CET 2025 upgrade: Invalid image type
Image check failed.
sysupgrade to the grub2-efi Image is ok:
# sysupgrade /tmp/openwrt-x86-64-generic-squashfs-combined-efi.img.gz
Tue Dec 30 18:14:42 CET 2025 upgrade: Image metadata not present
Tue Dec 30 18:14:42 CET 2025 upgrade: with offset=0 devname=vda
Tue Dec 30 18:14:42 CET 2025 upgrade: Reading partition table from
bootdisk...
Tue Dec 30 18:14:42 CET 2025 upgrade: Extract boot sector from the image
Tue Dec 30 18:14:42 CET 2025 upgrade: Reading partition table from image...
Tue Dec 30 18:14:42 CET 2025 upgrade: Saving config files...
Tue Dec 30 18:14:42 CET 2025 upgrade: Commencing upgrade. Closing all
shell sessions.
Tue Dec 30 18:14:42 CET 2025 upgrade: Sending TERM to remaining
processes ...
Tue Dec 30 18:14:46 CET 2025 upgrade: Sending KILL to remaining
processes ...
stage2 (3617): drop_caches: 3
Tue Dec 30 18:14:52 CET 2025 upgrade: Switching to ramdisk...
EXT4-fs (loop0): unmounting filesystem d13e0cae-a45a-42aa-a617-60f90bd2a9b1.
Tue Dec 30 17:14:52 UTC 2025 upgrade: Performing system upgrade...
[...]
Compiling entire systemd to get systemd-boot seems a bit wasteful to me,
but my patch to prevent this is bad. I removed Build/Install, since it
kept compiling everything.
diff --git a/package/boot/systemd-boot/Makefile
b/package/boot/systemd-boot/Makefile
index 425f53561d..18a5c095b0 100644
--- a/package/boot/systemd-boot/Makefile
+++ b/package/boot/systemd-boot/Makefile
@@ -19,7 +19,6 @@ PKG_CPE_ID:=cpe:/a:systemd_project:systemd:259:-
PKG_LICENSE:=LGPL-2.1-or-later
PKG_LICENSE_FILES:=LICENSES/README.md
PKG_BUILD_DEPENDS:=python-pyelftools/host python-jinja2/host gperf/host
-PKG_INSTALL:=1
MESON_USE_STAGING_PYTHON:=1
@@ -47,9 +46,16 @@ MESON_ARGS += \
-Dbootloader=enabled \
--auto-features=disabled
+define Build/Compile
+ $(call Build/Compile/Meson,src/boot/systemd-bootx64.efi)
+endef
+
+# removing PKG_INSTALL:=1 is not enough
+undefine Build/Install
+
define Package/systemd-boot/install
$(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/lib/systemd-boot
- $(INSTALL_DATA)
$(PKG_INSTALL_DIR)/usr/lib/systemd/boot/efi/systemd-bootx64.efi
$(1)/usr/lib/systemd-boot/
+ $(INSTALL_DATA) $(MESON_BUILD_DIR)/src/boot/systemd-bootx64.efi
$(1)/usr/lib/systemd-boot/
endef
$(eval $(call BuildPackage,systemd-boot))
Am 30.12.25 um 12:54 schrieb Jonas Lochmann:
> grub2 has many features, including different implementations for loading
> the linux kernel. At one machine, the "linux" command cannot start
> OpenWrt while the "chainloader" command for the same kernel file works.
>
> Both commands use the same UEFI API, but the surrounding steps are
> slightly different. An UEFI native bootloader seems to be similar to
> "chainloader" in grub while avoiding a ton of extra features. One of
> those bootloaders is systemd-boot, formerly known as gummiboot.
> While it contains the name "systemd" and is part of the systemd
> codebase, the resulting efi executable can be used independent of
> systemd. systemd contains bootctl for managing a systemd-boot
> installation, but systemd-boot uses simple text configuration files
> so that this is not needed at all.
>
> Signed-off-by: Jonas Lochmann <openwrt at jonaslochmann.de>
> ---
> config/Config-images.in | 18 ++++--
> package/boot/systemd-boot/Makefile | 55 +++++++++++++++++++
> .../x86/base-files/lib/upgrade/platform.sh | 1 +
> target/linux/x86/image/Makefile | 28 ++++++++++
> target/linux/x86/image/systemdboot-entry.cfg | 4 ++
> .../linux/x86/image/systemdboot-loader.conf | 1 +
> 6 files changed, 101 insertions(+), 6 deletions(-)
> create mode 100644 package/boot/systemd-boot/Makefile
> create mode 100644 target/linux/x86/image/systemdboot-entry.cfg
> create mode 100644 target/linux/x86/image/systemdboot-loader.conf
>
> diff --git a/config/Config-images.in b/config/Config-images.in
> index fcc5fa52cb..ff93990834 100644
> --- a/config/Config-images.in
> +++ b/config/Config-images.in
> @@ -229,6 +229,12 @@ menu "Target Images"
> prompt "journal size" if TARGET_ROOTFS_UBIFS
> default ""
>
> + config SYSTEMDBOOT_IMAGES
> + bool "Build systemd-boot images (EFI x86_64 targets only)"
> + depends on TARGET_x86_64
> + depends on TARGET_ROOTFS_EXT4FS || TARGET_ROOTFS_JFFS2 || TARGET_ROOTFS_SQUASHFS || TARGET_ROOTFS_EROFS
> + select PACKAGE_systemd-boot
> +
> config GRUB_IMAGES
> bool "Build GRUB images (Linux x86 or x86_64 host only)"
> depends on TARGET_x86
> @@ -251,23 +257,23 @@ menu "Target Images"
>
> config GRUB_CONSOLE
> bool "Use Console Terminal (in addition to Serial)"
> - depends on GRUB_IMAGES || GRUB_EFI_IMAGES
> + depends on GRUB_IMAGES || GRUB_EFI_IMAGES || SYSTEMDBOOT_IMAGES
> default y
>
> config GRUB_BAUDRATE
> int "Serial port baud rate"
> - depends on GRUB_IMAGES || GRUB_EFI_IMAGES
> + depends on GRUB_IMAGES || GRUB_EFI_IMAGES || SYSTEMDBOOT_IMAGES
> default 38400 if TARGET_x86_generic
> default 115200
>
> config GRUB_FLOWCONTROL
> bool "Use RTE/CTS on serial console"
> - depends on GRUB_IMAGES || GRUB_EFI_IMAGES
> + depends on GRUB_IMAGES || GRUB_EFI_IMAGES || SYSTEMDBOOT_IMAGES
> depends on TARGET_SERIAL != ""
>
> config GRUB_BOOTOPTS
> string "Extra kernel boot options"
> - depends on GRUB_IMAGES || GRUB_EFI_IMAGES
> + depends on GRUB_IMAGES || GRUB_EFI_IMAGES || SYSTEMDBOOT_IMAGES
> help
> If you don't know, just leave it blank.
>
> @@ -279,8 +285,8 @@ menu "Target Images"
> If you don't know, 5 seconds is a reasonable default.
>
> config GRUB_TITLE
> - string "Title for the menu entry in GRUB"
> - depends on GRUB_IMAGES || GRUB_EFI_IMAGES
> + string "Title for the menu entry in GRUB and systemd-boot"
> + depends on GRUB_IMAGES || GRUB_EFI_IMAGES || SYSTEMDBOOT_IMAGES
> default "OpenWrt"
> help
> This is the title of the GRUB menu entry.
> diff --git a/package/boot/systemd-boot/Makefile b/package/boot/systemd-boot/Makefile
> new file mode 100644
> index 0000000000..425f53561d
> --- /dev/null
> +++ b/package/boot/systemd-boot/Makefile
> @@ -0,0 +1,55 @@
> +#
> +# This is free software, licensed under the GNU General Public License v2.
> +# See /LICENSE for more information.
> +#
> +
> +include $(TOPDIR)/rules.mk
> +
> +PKG_NAME:=systemd-boot
> +PKG_VERSION:=259
> +PKG_RELEASE:=1
> +
> +PKG_SOURCE_PROTO:=git
> +PKG_SOURCE_URL:=https://github.com/systemd/systemd.git
> +PKG_MIRROR_HASH:=1671487dc511d60d3c50070e8f5c6dec228a1d97cdedd7f5508a966bb06ba614
> +PKG_SOURCE_DATE:=2025-12-17
> +PKG_SOURCE_VERSION:=9ca433482f2281d71718718705ca8cd3bf562ad6
> +PKG_CPE_ID:=cpe:/a:systemd_project:systemd:259:-
> +
> +PKG_LICENSE:=LGPL-2.1-or-later
> +PKG_LICENSE_FILES:=LICENSES/README.md
> +PKG_BUILD_DEPENDS:=python-pyelftools/host python-jinja2/host gperf/host
> +PKG_INSTALL:=1
> +
> +MESON_USE_STAGING_PYTHON:=1
> +
> +include $(INCLUDE_DIR)/package.mk
> +include $(INCLUDE_DIR)/meson.mk
> +
> +define Package/systemd-boot
> + SECTION:=boot
> + CATEGORY:=Boot Loaders
> + TITLE:=Simple UEFI boot manager
> + DEPENDS:=@TARGET_x86_64 +libblkid
> + URL:=https://github.com/systemd/systemd/tree/main/src/boot
> +endef
> +
> +define Package/systemd-boot/description
> + systemd-boot Simple UEFI boot manager
> +
> + systemd-boot executes EFI images. The default entry is selected by a configured
> + pattern (glob) or an on-screen menu.
> +endef
> +
> +MESON_ARGS += \
> + -Dmode=release \
> + -Dlibc=musl \
> + -Dbootloader=enabled \
> + --auto-features=disabled
> +
> +define Package/systemd-boot/install
> + $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/lib/systemd-boot
> + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/systemd/boot/efi/systemd-bootx64.efi $(1)/usr/lib/systemd-boot/
> +endef
> +
> +$(eval $(call BuildPackage,systemd-boot))
> diff --git a/target/linux/x86/base-files/lib/upgrade/platform.sh b/target/linux/x86/base-files/lib/upgrade/platform.sh
> index 5dad7a538a..bca3d0426f 100644
> --- a/target/linux/x86/base-files/lib/upgrade/platform.sh
> +++ b/target/linux/x86/base-files/lib/upgrade/platform.sh
> @@ -125,6 +125,7 @@ platform_do_upgrade() {
> mount -t $parttype -o rw,noatime "/dev/$partdev" /mnt
> set -- $(dd if="/dev/$diskdev" bs=1 skip=1168 count=16 2>/dev/null | hexdump -v -e '8/1 "%02x "" "2/1 "%02x""-"6/1 "%02x"')
> sed -i "s/\(PARTUUID=\)[a-f0-9-]\+/\1$4$3$2$1-$6$5-$8$7-$9/ig" /mnt/boot/grub/grub.cfg
> + sed -i "s/\(PARTUUID=\)[a-f0-9-]\+/\1$4$3$2$1-$6$5-$8$7-$9/ig" /mnt/loader/entries/main.conf
> umount /mnt
> fi
> }
> diff --git a/target/linux/x86/image/Makefile b/target/linux/x86/image/Makefile
> index 29bebeb748..6f11712e87 100644
> --- a/target/linux/x86/image/Makefile
> +++ b/target/linux/x86/image/Makefile
> @@ -52,6 +52,30 @@ define Build/combined
> 256
> endef
>
> +define Build/systemd-boot
> + rm -fR $@.boot
> + $(INSTALL_DIR) $@.boot/boot $@.boot/efi/boot $@.boot/loader/entries
> +
> + $(CP) $(KDIR)/$(KERNEL_NAME) $@.boot/boot/vmlinuz
> + -$(CP) $(STAGING_DIR_ROOT)/boot/. $@.boot/boot/
> + $(CP) $(STAGING_DIR_ROOT)/usr/lib/systemd-boot/systemd-bootx64.efi $@.boot/efi/boot/bootx64.efi
> +
> + sed \
> + -e 's#@GPT_ROOTPART@#root=$(GPT_ROOTPART) rootwait#g' \
> + -e 's#@CMDLINE@#$(BOOTOPTS) $(GRUB_CONSOLE_CMDLINE)#g' \
> + -e 's#@TITLE@#$(GRUB_TITLE)#g' \
> + ./systemdboot-entry.cfg > $@.boot/loader/entries/main.conf
> +
> + $(CP) ./systemdboot-loader.conf $@.boot/loader/loader.conf
> +
> + PADDING="1" SIGNATURE="$(IMG_PART_SIGNATURE)" \
> + GUID="$(IMG_PART_DISKGUID)" $(SCRIPT_DIR)/gen_image_generic.sh \
> + $@ \
> + $(CONFIG_TARGET_KERNEL_PARTSIZE) $@.boot \
> + $(CONFIG_TARGET_ROOTFS_PARTSIZE) $(IMAGE_ROOTFS) \
> + 256
> +endef
> +
> define Build/grub-config
> rm -fR $@.boot
> $(INSTALL_DIR) $@.boot/boot/grub
> @@ -114,14 +138,18 @@ define Device/Default
> IMAGE/combined-efi.vdi := grub-config efi | combined efi | grub-install efi | qemu-image vdi
> IMAGE/combined-efi.vmdk := grub-config efi | combined efi | grub-install efi | qemu-image vmdk
> IMAGE/combined-efi.vhdx := grub-config efi | combined efi | grub-install efi | qemu-image vhdx -o subformat=dynamic
> + IMAGE/systemd-boot.img := systemd-boot | append-metadata
> + IMAGE/systemd-boot.img.gz := systemd-boot | gzip | append-metadata
> ifeq ($(CONFIG_TARGET_IMAGES_GZIP),y)
> IMAGES-y := rootfs.img.gz
> IMAGES-$$(CONFIG_GRUB_IMAGES) += combined.img.gz
> IMAGES-$$(CONFIG_GRUB_EFI_IMAGES) += combined-efi.img.gz
> + IMAGES-$$(CONFIG_SYSTEMDBOOT_IMAGES) += systemd-boot.img.gz
> else
> IMAGES-y := rootfs.img
> IMAGES-$$(CONFIG_GRUB_IMAGES) += combined.img
> IMAGES-$$(CONFIG_GRUB_EFI_IMAGES) += combined-efi.img
> + IMAGES-$$(CONFIG_SYSTEMDBOOT_IMAGES) += systemd-boot.img
> endif
> KERNEL := kernel-bin
> KERNEL_INSTALL := 1
> diff --git a/target/linux/x86/image/systemdboot-entry.cfg b/target/linux/x86/image/systemdboot-entry.cfg
> new file mode 100644
> index 0000000000..cf6bc659d0
> --- /dev/null
> +++ b/target/linux/x86/image/systemdboot-entry.cfg
> @@ -0,0 +1,4 @@
> +title @TITLE@
> +linux /boot/vmlinuz
> +options @GPT_ROOTPART@ @CMDLINE@ noinitrd
> +architecture x64
> diff --git a/target/linux/x86/image/systemdboot-loader.conf b/target/linux/x86/image/systemdboot-loader.conf
> new file mode 100644
> index 0000000000..533d233153
> --- /dev/null
> +++ b/target/linux/x86/image/systemdboot-loader.conf
> @@ -0,0 +1 @@
> +timeout menu-disabled
More information about the openwrt-devel
mailing list