diff mbox series

[RFC] uki.bbclass: add multi-profile UKI support

Message ID 20260402202616.3177283-1-pavel@loebl.cz
State New
Headers show
Series [RFC] uki.bbclass: add multi-profile UKI support | expand

Commit Message

Pavel Löbl April 2, 2026, 8:25 p.m. UTC
Allow generating multi-profile UKI [1]. This is quite usefull for
dual slot A/B deploymets with secureboot active.

Currently only .cmdline sections are supported. But can be easily
extended with others.

[1] https://uapi-group.org/specifications/specs/unified_kernel_image/#multi-profile-ukis

Signed-off-by: Pavel Löbl <pavel@loebl.cz>
---
 meta/classes-recipe/uki.bbclass | 48 +++++++++++++++++++++++++++++----
 1 file changed, 43 insertions(+), 5 deletions(-)

Comments

Paul Barker April 3, 2026, 7:47 a.m. UTC | #1
On Thu, 2026-04-02 at 22:25 +0200, Pavel Löbl wrote:
> Allow generating multi-profile UKI [1]. This is quite usefull for
> dual slot A/B deploymets with secureboot active.
> 
> Currently only .cmdline sections are supported. But can be easily
> extended with others.
> 
> [1] https://uapi-group.org/specifications/specs/unified_kernel_image/#multi-profile-ukis
> 
> Signed-off-by: Pavel Löbl <pavel@loebl.cz>
> ---
>  meta/classes-recipe/uki.bbclass | 48 +++++++++++++++++++++++++++++----
>  1 file changed, 43 insertions(+), 5 deletions(-)
> 
> diff --git a/meta/classes-recipe/uki.bbclass b/meta/classes-recipe/uki.bbclass
> index d16f3c95561e..c2d21304c793 100644
> --- a/meta/classes-recipe/uki.bbclass
> +++ b/meta/classes-recipe/uki.bbclass
> @@ -59,6 +59,17 @@
>  #   - see efi-uki-bootdisk.wks.in how to create ESP partition which hosts systemd-boot,
>  #     config file(s) for systemd-boot and the UKI binaries.
>  #
> +#   - to generate multi-profile UKI (currently only supports .cmdline section)
> +#
> +#     UKI_PROFILE[0] = "TITLE=Recovery boot"
> +#     UKI_PROFILE_CMDLINE[0] = "rootwait recovery"
> +#
> +#     UKI_PROFILE[1] = "TITLE=Boot from slot A"
> +#     UKI_PROFILE_CMDLINE[1] = "rootwait root=LABEL=root_a"
> +#
> +#     UKI_PROFILE[2] = "TITLE=Boot from slot B"
> +#     UKI_PROFILE_CMDLINE[2] = "rootwait root=LABEL=root_b"
> +#

Hi Pavel, thanks for the RFC.

To make things easier to debug and to follow existing conventions, I'd
suggest:

    UKI_PROFILES = "slot_a slot_b recovery"

    UKI_PROFILES[recovery] = "TITLE=Recovery boot"
    UKI_PROFILE_CMDLINE[recovery] = "rootwait recovery"

    UKI_PROFILES[slot_a] = "TITLE=Boot from slot A"
    UKI_PROFILE_CMDLINE[slot_a] = "rootwait root=LABEL=root_a"

    UKI_PROFILES[slot_b] = "TITLE=Boot from slot B"
    UKI_PROFILE_CMDLINE[slot_b] = "rootwait root=LABEL=root_b"

See the uboot-config class [1] for an example of this pattern.

[1]: https://docs.yoctoproject.org/ref-manual/classes.html#uboot-config

If typical usage will always have `TITLE=...` for each UKI profile, you
could also use a UKI_PROFILE_TITLE[profile] variable to make things
clearer.

Best regards,
Pavel Löbl April 3, 2026, 8:38 a.m. UTC | #2
On Fri, Apr 03, 2026 at 08:47:17AM +0100, Paul Barker wrote:
> On Thu, 2026-04-02 at 22:25 +0200, Pavel L�bl wrote:
> > Allow generating multi-profile UKI [1]. This is quite usefull for
> > dual slot A/B deploymets with secureboot active.
> > 
> > Currently only .cmdline sections are supported. But can be easily
> > extended with others.
> > 
> > [1] https://uapi-group.org/specifications/specs/unified_kernel_image/#multi-profile-ukis
> > 
> > Signed-off-by: Pavel L�bl <pavel@loebl.cz>
> > ---
> >  meta/classes-recipe/uki.bbclass | 48 +++++++++++++++++++++++++++++----
> >  1 file changed, 43 insertions(+), 5 deletions(-)
> > 
> > diff --git a/meta/classes-recipe/uki.bbclass b/meta/classes-recipe/uki.bbclass
> > index d16f3c95561e..c2d21304c793 100644
> > --- a/meta/classes-recipe/uki.bbclass
> > +++ b/meta/classes-recipe/uki.bbclass
> > @@ -59,6 +59,17 @@
> >  #   - see efi-uki-bootdisk.wks.in how to create ESP partition which hosts systemd-boot,
> >  #     config file(s) for systemd-boot and the UKI binaries.
> >  #
> > +#   - to generate multi-profile UKI (currently only supports .cmdline section)
> > +#
> > +#     UKI_PROFILE[0] = "TITLE=Recovery boot"
> > +#     UKI_PROFILE_CMDLINE[0] = "rootwait recovery"
> > +#
> > +#     UKI_PROFILE[1] = "TITLE=Boot from slot A"
> > +#     UKI_PROFILE_CMDLINE[1] = "rootwait root=LABEL=root_a"
> > +#
> > +#     UKI_PROFILE[2] = "TITLE=Boot from slot B"
> > +#     UKI_PROFILE_CMDLINE[2] = "rootwait root=LABEL=root_b"
> > +#
> 
> Hi Pavel, thanks for the RFC.
> 
> To make things easier to debug and to follow existing conventions, I'd
> suggest:
> 
>     UKI_PROFILES = "slot_a slot_b recovery"
> 
>     UKI_PROFILES[recovery] = "TITLE=Recovery boot"
>     UKI_PROFILE_CMDLINE[recovery] = "rootwait recovery"
> 
>     UKI_PROFILES[slot_a] = "TITLE=Boot from slot A"
>     UKI_PROFILE_CMDLINE[slot_a] = "rootwait root=LABEL=root_a"
> 
>     UKI_PROFILES[slot_b] = "TITLE=Boot from slot B"
>     UKI_PROFILE_CMDLINE[slot_b] = "rootwait root=LABEL=root_b"
> 
Hi Paul,

the idea was to use the same indexes as firmware will be passing to
stub. As multi-profile UKI only supports numerical ones (actually
it's just the order in which they appear in PE32 binary).

In your example user would have to pass @0 from firmware to select
slot_a, @1 for slot_b and @2 for recovery.

I have no issues with that, just wanted to make that clear.

Regards,
Pavel
> 
> Best regards,
> 
> -- 
> Paul Barker
>
diff mbox series

Patch

diff --git a/meta/classes-recipe/uki.bbclass b/meta/classes-recipe/uki.bbclass
index d16f3c95561e..c2d21304c793 100644
--- a/meta/classes-recipe/uki.bbclass
+++ b/meta/classes-recipe/uki.bbclass
@@ -59,6 +59,17 @@ 
 #   - see efi-uki-bootdisk.wks.in how to create ESP partition which hosts systemd-boot,
 #     config file(s) for systemd-boot and the UKI binaries.
 #
+#   - to generate multi-profile UKI (currently only supports .cmdline section)
+#
+#     UKI_PROFILE[0] = "TITLE=Recovery boot"
+#     UKI_PROFILE_CMDLINE[0] = "rootwait recovery"
+#
+#     UKI_PROFILE[1] = "TITLE=Boot from slot A"
+#     UKI_PROFILE_CMDLINE[1] = "rootwait root=LABEL=root_a"
+#
+#     UKI_PROFILE[2] = "TITLE=Boot from slot B"
+#     UKI_PROFILE_CMDLINE[2] = "rootwait root=LABEL=root_b"
+#
 
 DEPENDS += "\
     os-release \
@@ -109,21 +120,46 @@  python do_uki() {
     import glob
     import bb.process
 
-    # base ukify command, can be extended if needed
-    ukify_cmd = d.getVar('UKIFY_CMD')
+    def generate_profiles(ukify_cmd):
+        join_profiles = ""
+        profile_num = 0
+        while (profile := d.getVarFlag("UKI_PROFILE", str(profile_num))):
+            ukify_cmd += " --profile='%s'" % (profile)
+
+            cmdline = d.getVarFlag("UKI_PROFILE_CMDLINE", str(profile_num))
+            if cmdline:
+                ukify_cmd += " --cmdline='%s'" % (cmdline)
+
+            profile_file = "%s/uki-profile%s.efi" % (d.getVar('DEPLOY_DIR_IMAGE') , profile_num)
+            ukify_cmd += " --output=%s" % (profile_file)
+            join_profiles += " --join-profile=%s" % (profile_file)
+
+            bb.debug(2, "uki: running command: %s" % (ukify_cmd))
+            out, err = bb.process.run(ukify_cmd, shell=True)
+            bb.debug(2, "%s\n%s" % (out, err))
+
+            profile_num += 1
+
+        return join_profiles
+
 
     deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE')
 
+    # base ukify command, can be extended if needed
+    ukify_common_cmd = d.getVar('UKIFY_CMD')
+
     # architecture
     target_arch = d.getVar('EFI_ARCH')
     if target_arch:
-        ukify_cmd += " --efi-arch %s" % (target_arch)
+        ukify_common_cmd += " --efi-arch %s" % (target_arch)
 
-    # systemd stubs
+    # systemd stubs, required even for UKI profile generation
     stub = "%s/linux%s.efi.stub" % (d.getVar('DEPLOY_DIR_IMAGE'), target_arch)
     if not os.path.exists(stub):
         bb.fatal(f"ERROR: cannot find {stub}.")
-    ukify_cmd += " --stub %s" % (stub)
+    ukify_common_cmd += " --stub %s" % (stub)
+
+    ukify_cmd = ukify_common_cmd
 
     # initrd
     uki_fstype = d.getVar("INITRAMFS_FSTYPES").split()[0]
@@ -185,6 +221,8 @@  python do_uki() {
     if cert:
         ukify_cmd += " --secureboot-certificate='%s'" % (cert)
 
+    ukify_cmd += generate_profiles(ukify_common_cmd)
+
     # custom output UKI filename
     output = " --output=%s/%s" % (d.getVar('DEPLOY_DIR_IMAGE'), d.getVar('UKI_FILENAME'))
     ukify_cmd += " %s" % (output)