[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