public inbox for openembedded-core@lists.openembedded.org
 help / color / mirror / Atom feed
* [RFC PATCH] uki.bbclass: add multi-profile UKI support
@ 2026-04-02 20:25 Pavel Löbl
  2026-04-03  7:47 ` [OE-core] " Paul Barker
  2026-04-07  6:36 ` Mikko Rapeli
  0 siblings, 2 replies; 5+ messages in thread
From: Pavel Löbl @ 2026-04-02 20:25 UTC (permalink / raw)
  To: openembedded-core; +Cc: Michelle Lin, Mikko Rapeli, Pavel Löbl

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"
+#
 
 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)
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [OE-core] [RFC PATCH] uki.bbclass: add multi-profile UKI support
  2026-04-02 20:25 [RFC PATCH] uki.bbclass: add multi-profile UKI support Pavel Löbl
@ 2026-04-03  7:47 ` Paul Barker
  2026-04-03  8:38   ` Pavel Löbl
  2026-04-07  6:36 ` Mikko Rapeli
  1 sibling, 1 reply; 5+ messages in thread
From: Paul Barker @ 2026-04-03  7:47 UTC (permalink / raw)
  To: Pavel Löbl, openembedded-core; +Cc: Michelle Lin, Mikko Rapeli

[-- Attachment #1: Type: text/plain, Size: 2241 bytes --]

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,

-- 
Paul Barker


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 252 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [OE-core] [RFC PATCH] uki.bbclass: add multi-profile UKI support
  2026-04-03  7:47 ` [OE-core] " Paul Barker
@ 2026-04-03  8:38   ` Pavel Löbl
  2026-04-08  9:17     ` Paul Barker
  0 siblings, 1 reply; 5+ messages in thread
From: Pavel Löbl @ 2026-04-03  8:38 UTC (permalink / raw)
  To: Paul Barker; +Cc: openembedded-core, Michelle Lin, Mikko Rapeli

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
> 




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC PATCH] uki.bbclass: add multi-profile UKI support
  2026-04-02 20:25 [RFC PATCH] uki.bbclass: add multi-profile UKI support Pavel Löbl
  2026-04-03  7:47 ` [OE-core] " Paul Barker
@ 2026-04-07  6:36 ` Mikko Rapeli
  1 sibling, 0 replies; 5+ messages in thread
From: Mikko Rapeli @ 2026-04-07  6:36 UTC (permalink / raw)
  To: Pavel Löbl; +Cc: openembedded-core, Michelle Lin

Hi,

On Thu, Apr 02, 2026 at 10:25:29PM +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

In addition to what Paul mentioned, please extend
meta/lib/oeqa/selftest/cases/uki.py to cover this feature. Maybe
even boot the target qemu machine with both profiles, if possible.

Cheers,

-Mikko

> 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"
> +#
>  
>  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)
> -- 
> 2.53.0
> 


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [OE-core] [RFC PATCH] uki.bbclass: add multi-profile UKI support
  2026-04-03  8:38   ` Pavel Löbl
@ 2026-04-08  9:17     ` Paul Barker
  0 siblings, 0 replies; 5+ messages in thread
From: Paul Barker @ 2026-04-08  9:17 UTC (permalink / raw)
  To: Pavel Löbl; +Cc: openembedded-core, Michelle Lin, Mikko Rapeli

[-- Attachment #1: Type: text/plain, Size: 3073 bytes --]

On Fri, 2026-04-03 at 10:38 +0200, Pavel Löbl wrote:
> 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.

Ah, this makes sense. I agree that keeping the same indexes as used
during boot is the right design. We want to avoid trying to figure out
how many indexes to expect by looping until we see an error though. So,
perhaps:

    UKI_NUM_PROFILES = "3"
    UKI_PROFILE[0] = ...
    UKI_PROFILE_CMDLINE[0] = ...
    ...
    UKI_PROFILE[2] = ...
    UKI_PROFILE_CMDLINE[2] = ...

How does that look?

Thanks,

-- 
Paul Barker


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 252 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-04-08  9:18 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-02 20:25 [RFC PATCH] uki.bbclass: add multi-profile UKI support Pavel Löbl
2026-04-03  7:47 ` [OE-core] " Paul Barker
2026-04-03  8:38   ` Pavel Löbl
2026-04-08  9:17     ` Paul Barker
2026-04-07  6:36 ` Mikko Rapeli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox