All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/21] FIT image improvements
@ 2025-06-02  7:56 AdrianF
  2025-06-02  7:56 ` [PATCH v6 01/21] devicetree: minor improvements AdrianF
                   ` (20 more replies)
  0 siblings, 21 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

This patch series re-writes the FIT image related code. The goal is to
fix [YOCTO #12912] which is a long standing issue.

Changes in comparison to v5
---------------------------

* Improve the linux-yocto-fitimage recipe:
  Replace LINUX_VERSION ?= "6.12.23" by
  PKGV = "${@get_kernelversion_file("${STAGING_KERNEL_BUILDDIR}")}"
* Extend recipe for testing the external devicetree: bbb-dtbs-as-ext.bb
  * am335x-bonegreen-ext.dts
    Add a dtb file with a compatible string (before there was only a dtbo)
  * am335x-bonegreen-ext-alias.dtb
    Add a sym-link which should end up as an additional configuration node in
    the FIT image
* Fix handling of external device trees
  * Create an extra configuration node for sym-links
  * If an external devicetree has a compatible property add the same property
    to the corresponding FIT image configuration node.
  * Sort the output of os.listdir
* Fix handling of FIT_CONF_DEFAULT_DTB
* Improve tests:
  * FIT_CONF_PREFIX
  * FIT_CONF_DEFAULT_DTB
  * Add a test for an external devicetree (not only a dtbo)
  * Add a test for an external devicetree sym-link
  * Test the new get_compatible_from_dtb function
  * Improve robustness of testiing FIT_GENERATE_KEYS = "1"
* Improve some minor finding in devicetree.bbclass

Changes in comparison to v4
---------------------------

* maintainers: add myself for kernel-signing-keys-native
  oe-selftest -r distrodata.Distrodata.test_maintainers

The intention is to provide exactly the state of today's master-next.

Changes in comparison to v3
---------------------------

* Fix ERROR: linux-yocto-6.12.27+git-r0 do_uboot_mkimage: Execution...
* Pass the kernel artifacts via the deploy directory. This makes the task
  dependencies slightly more complicated. But passing the artifacts via
  sysroot requires having some artifacts twice in the sstate-cache (once
  in sysroot and once in deploy-dir).
* Support the bundled kernel+initramfs in FIT image again. After some
  discussions, it turned out that some BSPs make use of this unusual
  combination and dropping support for it would probably lead to issues.
  Re-introducing this was also part of the motivation for passing the
  artifacts via deploy rather than via sysroot.
* Drop the patch which merged kernel-uboot.bbclass into
  kernel-uimage.bbclass. This can be done later if it seems useful.
* Drop doc patches again

Changes in comparison to v2
---------------------------

* Fix the multilib exclusion check linux-fit-image -> kernel-fit-image
  This should solve the issue with bitbake world and multilib builds

Changes in comparison to v1
---------------------------

* Exclude recipes which inherit the kernel-fit-image.bbclass from
  multilib builds. This fixes an issue discovered by the AB when
  running bitbake world with a multilib configuraton enabled.
* Remove some new tests which expected openssl from the host.
* Rebase the patches on the new bug-fix commit
  "u-boot: ensure keys are generated before assembling U-Boot FIT image"
  from Rogerio Guerra Borin <rogerio.borin@toradex.com>
  This fix required splitting the key generation into a separate recipe.
  Note: These patches depend on this commit, which is included in this
  series. As of today, the patch is already present on master-next.
* Add a cover letter with correct subject for the patch series

What gets fixed
---------------

* sstate does not work well if a FIT image contains an initramfs.
  The kernel gets re-built from scratch if the build runs from an
  empty TMPDIR:
  https://lists.openembedded.org/g/openembedded-core/message/203510.
  This is also problematic for SDK use cases since working with the
  SDK is annoying if the kernel gets re-built from scratch for no
  good reason.
* A FIT image kernel is not available as a package, but all other
  kernel image types are.
* The its-file is generated by complicated shell code with lots of
  code duplications. Switching to Python simplifies the maintenance
  (also because of the indentation with spaces and tabs in the shell
  code).
* Separating the FIT image related complexity from the kernel build
  complexity simplifies the maintenance of the kernel but also of
  the FIT image related code.
* There is already a new (but unfortunately completely unaligned)
  implementation for creating FIT images in meta-openembedded:
  https://github.com/openembedded/meta-openembedded/blob/master/meta-oe/
  classes/fitimage.bbclass. Let's hope this patch series will lay a
  solid foundation for a future merge of the two implementations.
* The new implementation in Python is also a preparation for additional
  features, such as adding different types of artifacts or generating
  the configuration nodes with greater flexibility. Currently, exactly
  one configuration per device node is supported.

Architectural change
--------------------

The existing kernel-fitimage.bbclass is designed to be added to
KERNEL_CLASSES. It appends code to the kernel's tasks and injects
additional tasks between the existing ones. Some functions rely on
running within the kernel's build folder structure.

The new implementation introduces the kernel-fit-image.bbclass,
which is intended to be inherited by an independent recipe. This
recipe takes the kernel artifacts from the sstate-cache and assembles
the FIT image entirely independently of the kernel's build tasks and
directory structure.

An example of using the new kernel-fit-image.bbclass is the
linux-yocto-fitimage.bb recipe, which builds the FIT image for the
linux-yocto kernel. The recipe looks like this:

  SUMMARY = "The Linux kernel as a FIT image (optionally with initramfs)"
  SECTION = "kernel"
  LICENSE = "GPL-2.0-with-Linux-syscall-note"
  LIC_FILES_CHKSUM = "\
    file://${COREBASE}/meta/files/common-licenses/GPL-2.0-with-Linux-syscall-note;
    md5=0bad96c422c41c3a94009dcfe1bff992"

  inherit kernel-fit-image

The configuration variables defined in the conf/image-fitimage.conf
file are handled by the kernel-fit-image.bbclass in the same way as
they are by the kernel-fitimage.bbclass. The new implementation is
99% backward compatible with the existing kernel-fitimage.bbclass.
The existing test-suite runs with minimal changes.

With the kernel-fitimage.bbclass, the FIT image was built as part of
the kernel itself. To ensure the new recipe is built automatically,
the following variables can be set, for example, in the machine
configuration file:

  # Do not install the kernel image package
  RRECOMMENDS:${KERNEL_PACKAGE_NAME}-base = ""

  # Install the FIT image package into the rootfs (there is now a package :-)
  MACHINE_EXTRA_RDEPENDS += "${PREFERRED_PROVIDER_virtual/kernel}-fitimage"

  # Configure the image.bbclass to depend on the fitImage instead of only
  # the kernel to ensure the FIT image is built with the image
  KERNEL_DEPLOY_DEPEND = "${PREFERRED_PROVIDER_virtual/kernel}-fitimage:do_deploy"

Breaking changes
----------------

* Building a kernel FIT image changes.
  Before this patch series:

    A configuration like:
      KERNEL_IMAGETYPE = "fitImage"
      KERNEL_CLASSES = "kernel-fitimage"
    and building a kernel like:
      bitbake linux-yocto
    generated a FIT image including the kernel and maybe additional
    artifacts.

  With this patch series merged, the same can be achieved like this:
    bitbake linux-yocto-fitimage

  While this simple example is even simpler than before, there are
  also other examples which might look more complicated with the
  new implementation than with the old implementation.

Proposal for merging these patch series
---------------------------------------

The patches are split into small steps which allow a step-by-step
merging:

1. New tests without impact on code:
2. Add the new implementation without changing the existing implementation
3. Refactor the old kernel-fitimage.bbclass to Python to share code
4. Remove the old kernel-fitimage.bbclass and clean up the code from

Adrian Freihofer (21):
  devicetree: minor improvements
  oe-selftest: add new ext dtb recipe
  oe-selftest: fitimage: test external dtb
  oe-selftest: fitimage: test FIT_CONF_PREFIX
  oe-selftest: fitimage: test FIT_CONF_DEFAULT_DTB
  kernel-signing-keys-native: refactor key generation into a new recipe
  maintainers: add myself for kernel-signing-keys-native
  oe-selftest: fitimage: cleanup FIT_GENERATE_KEYS
  kernel-uboot.bbclass: do not require the kernel build folder
  kernel-uboot.bbclass: deploy the vmlinux kernel binary
  kernel-fitimage: refactor order in its
  kernel-fit-image.bbclass: add a new FIT image implementation
  maintainers: add myself for linux-yocto-fitimage
  oe-selftest: fitimage: add tests for fitimage.py
  oe-selftest: fitimage: support new FIT recipe as well
  oe-selftest: fitimage: run all tests for both FIT implementations
  oe-selftest: fitimage refactor classes
  kernel-fitimage: re-write its code in Python
  oe-selftest: fitimage: remove kernel-fitimage tests
  kernel.bbclass: remove support for type fitImage
  kernel-fitimage.bbclass: remove it

 .../recipes-test/ext-dtb/bbb-dtbs-as-ext.bb   |  29 +
 .../ext-dtb/files/BBORG_RELAY-00A2.dts        |  49 +
 .../ext-dtb/files/am335x-bonegreen-ext.dts    |  14 +
 meta/classes-recipe/devicetree.bbclass        |  20 +-
 meta/classes-recipe/kernel-fit-image.bbclass  | 187 ++++
 meta/classes-recipe/kernel-fitimage.bbclass   | 839 ------------------
 meta/classes-recipe/kernel-uboot.bbclass      |  47 +-
 meta/classes-recipe/kernel-uimage.bbclass     |   3 +-
 meta/classes-recipe/kernel.bbclass            |  20 +-
 meta/classes-recipe/uboot-sign.bbclass        |   5 +-
 meta/classes/multilib.bbclass                 |   1 +
 meta/conf/distro/include/maintainers.inc      |   2 +
 meta/lib/oe/fitimage.py                       | 547 ++++++++++++
 meta/lib/oeqa/selftest/cases/fitimage.py      | 354 ++++++--
 .../kernel-signing-keys-native.bb             |  75 ++
 .../linux/linux-yocto-fitimage.bb             |  13 +
 16 files changed, 1264 insertions(+), 941 deletions(-)
 create mode 100644 meta-selftest/recipes-test/ext-dtb/bbb-dtbs-as-ext.bb
 create mode 100644 meta-selftest/recipes-test/ext-dtb/files/BBORG_RELAY-00A2.dts
 create mode 100644 meta-selftest/recipes-test/ext-dtb/files/am335x-bonegreen-ext.dts
 create mode 100644 meta/classes-recipe/kernel-fit-image.bbclass
 delete mode 100644 meta/classes-recipe/kernel-fitimage.bbclass
 create mode 100644 meta/lib/oe/fitimage.py
 create mode 100644 meta/recipes-kernel/kernel-signing-keys/kernel-signing-keys-native.bb
 create mode 100644 meta/recipes-kernel/linux/linux-yocto-fitimage.bb

-- 
2.49.0



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

* [PATCH v6 01/21] devicetree: minor improvements
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02 18:31   ` [OE-core] " Bruce Ashfield
  2025-06-02  7:56 ` [PATCH v6 02/21] oe-selftest: add new ext dtb recipe AdrianF
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

- Do not use the ${} bitbake syntax for shell internal variables
- Fix shellcheck SC2045 warning:
  Iterating over ls output is fragile. Use globs.
- Improve error handling for dtc. Print the output, not only the exit
  value.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/devicetree.bbclass | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/meta/classes-recipe/devicetree.bbclass b/meta/classes-recipe/devicetree.bbclass
index 1806cb62cbf..0d0583c13a0 100644
--- a/meta/classes-recipe/devicetree.bbclass
+++ b/meta/classes-recipe/devicetree.bbclass
@@ -109,7 +109,11 @@ def devicetree_compile(dtspath, includes, d):
         ppargs.append("-I{0}".format(i))
     ppargs += ["-o", "{0}.pp".format(dts), dtspath]
     bb.note("Running {0}".format(" ".join(ppargs)))
-    subprocess.run(ppargs, check = True)
+    try:
+        subprocess.run(ppargs, check=True, capture_output=True)
+    except subprocess.CalledProcessError as e:
+        bb.fatal(f"Command '{' '.join(ppargs)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\ndtspath: {os.path.abspath(dtspath)}")
+
 
     # determine if the file is an overlay or not (using the preprocessed file)
     isoverlay = devicetree_source_is_overlay("{0}.pp".format(dts))
@@ -125,7 +129,11 @@ def devicetree_compile(dtspath, includes, d):
     dtcargs += ["-o", "{0}.{1}".format(dtname, "dtbo" if isoverlay else "dtb")]
     dtcargs += ["-I", "dts", "-O", "dtb", "{0}.pp".format(dts)]
     bb.note("Running {0}".format(" ".join(dtcargs)))
-    subprocess.run(dtcargs, check = True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    try:
+        subprocess.run(dtcargs, check=True, capture_output=True)
+    except subprocess.CalledProcessError as e:
+        bb.fatal(f"Command '{' '.join(dtcargs)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\ndtname: {dtname}")
+
 
 python devicetree_do_compile() {
     import re
@@ -144,14 +152,14 @@ python devicetree_do_compile() {
 }
 
 devicetree_do_install() {
-    for DTB_FILE in `ls *.dtb *.dtbo`; do
-        install -Dm 0644 ${B}/${DTB_FILE} ${D}/boot/devicetree/${DTB_FILE}
+    for dtb_file in *.dtb *.dtbo; do
+        install -Dm 0644 "${B}/$dtb_file" "${D}/boot/devicetree/$dtb_file"
     done
 }
 
 devicetree_do_deploy() {
-    for DTB_FILE in `ls *.dtb *.dtbo`; do
-        install -Dm 0644 ${B}/${DTB_FILE} ${DEPLOYDIR}/devicetree/${DTB_FILE}
+    for dtb_file in *.dtb *.dtbo; do
+        install -Dm 0644 "${B}/$dtb_file" "${DEPLOYDIR}/devicetree/$dtb_file"
     done
 }
 addtask deploy before do_build after do_install
-- 
2.49.0



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

* [PATCH v6 02/21] oe-selftest: add new ext dtb recipe
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
  2025-06-02  7:56 ` [PATCH v6 01/21] devicetree: minor improvements AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 03/21] oe-selftest: fitimage: test external dtb AdrianF
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

There is no test coverage for the devicetree.bbclass class. Add a
minimalist recipe that uses this class.
This recipe compiles a devicetree overlay that does not include any
sources from the kernel build folder to keep it simple and also usable
for testing a kernel from the sstate-cache.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 .../recipes-test/ext-dtb/bbb-dtbs-as-ext.bb   | 29 +++++++++++
 .../ext-dtb/files/BBORG_RELAY-00A2.dts        | 49 +++++++++++++++++++
 .../ext-dtb/files/am335x-bonegreen-ext.dts    | 14 ++++++
 3 files changed, 92 insertions(+)
 create mode 100644 meta-selftest/recipes-test/ext-dtb/bbb-dtbs-as-ext.bb
 create mode 100644 meta-selftest/recipes-test/ext-dtb/files/BBORG_RELAY-00A2.dts
 create mode 100644 meta-selftest/recipes-test/ext-dtb/files/am335x-bonegreen-ext.dts

diff --git a/meta-selftest/recipes-test/ext-dtb/bbb-dtbs-as-ext.bb b/meta-selftest/recipes-test/ext-dtb/bbb-dtbs-as-ext.bb
new file mode 100644
index 00000000000..5055d03e54a
--- /dev/null
+++ b/meta-selftest/recipes-test/ext-dtb/bbb-dtbs-as-ext.bb
@@ -0,0 +1,29 @@
+SUMMARY = "Boeaglebone Devicetrees"
+DESCRIPTION = "Handle the dtc files of the beaglebone-yocto via devicetree.bbclass just for testing purpose"
+SECTION = "kernel"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+inherit devicetree
+
+COMPATIBLE_MACHINE  = "^(beaglebone-yocto)$"
+
+# Take a copy of a small devicetree from the kernel's source directory for handling it externally
+# Borrowed an example DTB overlay from
+# https://raw.githubusercontent.com/beagleboard/linux/refs/heads/5.10/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts
+SRC_URI = "\
+    file://am335x-bonegreen-ext.dts \
+    file://BBORG_RELAY-00A2.dts \
+"
+
+# The am335x-bonegreen-ext.dts needs also the ti directories
+DT_INCLUDE:append = " ${STAGING_KERNEL_DIR}/arch/${ARCH}/boot/dts/ti/omap"
+
+# Sym-links are handled as extra configuration nodes in FIT images.
+do_install:append() {
+    ln -sf am335x-bonegreen-ext.dtb "${D}/boot/devicetree/am335x-bonegreen-ext-alias.dtb"
+}
+
+do_deploy:append() {
+    ln -sf am335x-bonegreen-ext.dtb "${DEPLOYDIR}/devicetree/am335x-bonegreen-ext-alias.dtb"
+}
diff --git a/meta-selftest/recipes-test/ext-dtb/files/BBORG_RELAY-00A2.dts b/meta-selftest/recipes-test/ext-dtb/files/BBORG_RELAY-00A2.dts
new file mode 100644
index 00000000000..9530fa50fe5
--- /dev/null
+++ b/meta-selftest/recipes-test/ext-dtb/files/BBORG_RELAY-00A2.dts
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015 Robert Nelson <robertcnelson@gmail.com>
+ * Copyright (C) 2019 Amilcar Lucas <amilcar.lucas@iav.de>
+ */
+
+/dts-v1/;
+/plugin/;
+
+&{/chosen} {
+	overlays {
+		BBORG_RELAY-00A2.kernel = __TIMESTAMP__;
+	};
+};
+
+&ocp {
+	P9_41_pinmux { pinctrl-0 = <&P9_41_gpio_pin>;};
+	P9_42_pinmux { pinctrl-0 = <&P9_42_gpio_pin>;};
+	P9_30_pinmux { pinctrl-0 = <&P9_30_gpio_pin>;};
+	P9_27_pinmux { pinctrl-0 = <&P9_27_gpio_pin>;};
+};
+
+// relay1
+&bone_led_P9_41 {
+	status = "okay";
+	label = "relay1";
+	default-state = "keep";
+};
+
+// relay2
+&bone_led_P9_42 {
+	status = "okay";
+	label = "relay2";
+	default-state = "keep";
+};
+
+// realy3
+&bone_led_P9_30 {
+	status = "okay";
+	label = "relay3";
+	default-state = "keep";
+};
+
+// realy4
+&bone_led_P9_27 {
+	status = "okay";
+	label = "relay4";
+	default-state = "keep";
+};
diff --git a/meta-selftest/recipes-test/ext-dtb/files/am335x-bonegreen-ext.dts b/meta-selftest/recipes-test/ext-dtb/files/am335x-bonegreen-ext.dts
new file mode 100644
index 00000000000..a0b39337a9a
--- /dev/null
+++ b/meta-selftest/recipes-test/ext-dtb/files/am335x-bonegreen-ext.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/
+ */
+/dts-v1/;
+
+#include "am33xx.dtsi"
+#include "am335x-bone-common.dtsi"
+#include "am335x-bonegreen-common.dtsi"
+
+/ {
+	model = "TI AM335x BeagleBone Green External";
+	compatible = "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
+};
-- 
2.49.0



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

* [PATCH v6 03/21] oe-selftest: fitimage: test external dtb
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
  2025-06-02  7:56 ` [PATCH v6 01/21] devicetree: minor improvements AdrianF
  2025-06-02  7:56 ` [PATCH v6 02/21] oe-selftest: add new ext dtb recipe AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 04/21] oe-selftest: fitimage: test FIT_CONF_PREFIX AdrianF
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

There are several ways to insert external devicetrees and devicetree
overlays into the kernel and thus at least to some extent into the FIT
image.
So far there is no test coverage. Let's improve this as much as possible
without fully understanding all use cases.

This first test adds a devicetree overlay to a build configuration
without signing, since signing is apparently not yet meaningful when
PREFERRED_PROVIDER_virtual/dtb = “bborg-relay-00a2” is used. It is also
not entirely clear how these external devicetree overlays are used by
the configuration nodes of the FIT image. Currently, one configuration
is created per dtb dtbo node, which is not really useful for dtbo nodes.
Before this test can be extended to test devicetree overlays and
signing, the code that creates the configuration nodes in its file
probably needs some improvements in terms of more flexibility in
defining the references from configuration nodes to image nodes.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 73 ++++++++++++++++++++----
 1 file changed, 61 insertions(+), 12 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index b39f2622dff..31116979ef7 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -161,10 +161,23 @@ class FitImageTestCase(OESelftestTestCase):
 
     @staticmethod
     def _get_dtb_files(bb_vars):
+        """Return a list of devicetree names
+
+        The list should be used to check the dtb and conf nodes in the FIT image or its file.
+        In addition to the entries from KERNEL_DEVICETREE, the external devicetree and the
+        external devicetree overlay added by the test recipe bbb-dtbs-as-ext are handled as well.
+        """
         kernel_devicetree = bb_vars.get('KERNEL_DEVICETREE')
+        all_dtbs = []
+        dtb_symlinks = []
         if kernel_devicetree:
-            return [os.path.basename(dtb) for dtb in kernel_devicetree.split()]
-        return []
+            all_dtbs += [os.path.basename(dtb) for dtb in kernel_devicetree.split()]
+        # Support only the test recipe which provides 1 devicetree and 1 devicetree overlay
+        pref_prov_dtb = bb_vars.get('PREFERRED_PROVIDER_virtual/dtb')
+        if pref_prov_dtb == "bbb-dtbs-as-ext":
+            all_dtbs += ["am335x-bonegreen-ext.dtb", "BBORG_RELAY-00A2.dtbo"]
+            dtb_symlinks.append("am335x-bonegreen-ext-alias.dtb")
+        return (all_dtbs, dtb_symlinks)
 
     def _is_req_dict_in_dict(self, found_dict, req_dict):
         """
@@ -379,6 +392,7 @@ class KernelFitImageTests(FitImageTestCase):
             'KERNEL_DEVICETREE',
             'KERNEL_FIT_LINK_NAME',
             'MACHINE',
+            'PREFERRED_PROVIDER_virtual/dtb',
             'UBOOT_ARCH',
             'UBOOT_ENTRYPOINT',
             'UBOOT_LOADADDRESS',
@@ -452,7 +466,7 @@ class KernelFitImageTests(FitImageTestCase):
                 ['/', 'images', 'kernel-1', 'signature-1'],
             ]
         """
-        dtb_files = FitImageTestCase._get_dtb_files(bb_vars)
+        dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars)
         fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL']
         fit_uboot_env = bb_vars['FIT_UBOOT_ENV']
         initramfs_image = bb_vars['INITRAMFS_IMAGE']
@@ -470,9 +484,9 @@ class KernelFitImageTests(FitImageTestCase):
         if initramfs_image and initramfs_image_bundle != "1":
             images.append('ramdisk-1')
 
-        # configuration nodes
+        # configuration nodes (one per DTB and also one per symlink)
         if dtb_files:
-            configurations = [ 'conf-' + conf for conf in dtb_files ]
+            configurations = [ 'conf-' + conf for conf in dtb_files + dtb_symlinks]
         else:
             configurations = [ 'conf-1' ]
 
@@ -548,7 +562,7 @@ class KernelFitImageTests(FitImageTestCase):
 
     def _get_req_sections(self, bb_vars):
         """Generate a dictionary of expected sections in the output of dumpimage"""
-        dtb_files = FitImageTestCase._get_dtb_files(bb_vars)
+        dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars)
         fit_hash_alg = bb_vars['FIT_HASH_ALG']
         fit_sign_alg = bb_vars['FIT_SIGN_ALG']
         fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL']
@@ -584,11 +598,20 @@ class KernelFitImageTests(FitImageTestCase):
             }
         # Create a configuration section for each DTB
         if dtb_files:
-            for dtb in dtb_files:
-                req_sections['conf-' + dtb] = {
-                    "Kernel": "kernel-1",
-                    "FDT": 'fdt-' + dtb,
-                }
+            for dtb in dtb_files + dtb_symlinks:
+                # Assume that DTBs with an "-alias" in its name are symlink DTBs created e.g. by the
+                # bbb-dtbs-as-ext test recipe. Make the configuration node pointing to the real DTB.
+                real_dtb = dtb.replace("-alias", "")
+                # dtb overlays do not refer to a kernel (yet?)
+                if dtb.endswith('.dtbo'):
+                    req_sections['conf-' + dtb] = {
+                        "FDT": 'fdt-' + real_dtb,
+                    }
+                else:
+                    req_sections['conf-' + dtb] = {
+                        "Kernel": "kernel-1",
+                        "FDT": 'fdt-' + real_dtb,
+                    }
                 if initramfs_image and initramfs_image_bundle != "1":
                     req_sections['conf-' + dtb]['Init Ramdisk'] = "ramdisk-1"
         else:
@@ -635,7 +658,12 @@ class KernelFitImageTests(FitImageTestCase):
                 self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section)
                 sign_value = values.get('Sign value', None)
                 self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section)
-                dtb_path = os.path.join(deploy_dir_image, section.replace('conf-', ''))
+                dtb_file_name = section.replace('conf-', '')
+                dtb_path = os.path.join(deploy_dir_image, dtb_file_name)
+                # External devicetrees created by devicetree.bbclass are in a subfolder and have priority
+                dtb_path_ext = os.path.join(deploy_dir_image, "devicetree", dtb_file_name)
+                if os.path.exists(dtb_path_ext):
+                    dtb_path = dtb_path_ext
                 self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, dtb_path, section)
             else:
                 # Image nodes always need a hash which gets indirectly signed by the config signature
@@ -696,6 +724,27 @@ FIT_DESC = "A model description"
         self._test_fitimage(bb_vars)
 
 
+    def test_fit_image_ext_dtb_dtbo(self):
+        """
+        Summary:     Check if FIT image and Image Tree Source (its) are created correctly.
+        Expected:    1) its and FIT image are built successfully
+                     2) The its file contains also the external devicetree overlay
+                     3) Dumping the FIT image indicates the devicetree overlay
+        """
+        config = """
+# Enable creation of fitImage
+MACHINE = "beaglebone-yocto"
+KERNEL_IMAGETYPES += " fitImage "
+KERNEL_CLASSES = " kernel-fitimage "
+# Add a devicetree overlay which does not need kernel sources
+PREFERRED_PROVIDER_virtual/dtb = "bbb-dtbs-as-ext"
+"""
+        config = self._config_add_uboot_env(config)
+        self.write_config(config)
+        bb_vars = self._fit_get_bb_vars()
+        self._test_fitimage(bb_vars)
+
+
     def test_sign_fit_image_configurations(self):
         """
         Summary:     Check if FIT image and Image Tree Source (its) are created
-- 
2.49.0



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

* [PATCH v6 04/21] oe-selftest: fitimage: test FIT_CONF_PREFIX
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (2 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 03/21] oe-selftest: fitimage: test external dtb AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 05/21] oe-selftest: fitimage: test FIT_CONF_DEFAULT_DTB AdrianF
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Add a test which does not use the default FIT_CONF_PREFIX configuration.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 28 ++++++++++++++----------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 31116979ef7..812b34fab47 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -256,7 +256,7 @@ class FitImageTestCase(OESelftestTestCase):
         self.logger.debug("sigs:\n%s\n" % pprint.pformat(sigs, indent=4))
         if req_sigvalues_config or req_sigvalues_image:
             for its_path, values in sigs.items():
-                if 'conf-' in its_path:
+                if bb_vars.get('FIT_CONF_PREFIX', "conf-") in its_path:
                     reqsigvalues = req_sigvalues_config
                 else:
                     reqsigvalues = req_sigvalues_image
@@ -380,6 +380,7 @@ class KernelFitImageTests(FitImageTestCase):
         """
         internal_used = {
             'DEPLOY_DIR_IMAGE',
+            'FIT_CONF_PREFIX',
             'FIT_DESC',
             'FIT_HASH_ALG',
             'FIT_KERNEL_COMP_ALG',
@@ -486,9 +487,9 @@ class KernelFitImageTests(FitImageTestCase):
 
         # configuration nodes (one per DTB and also one per symlink)
         if dtb_files:
-            configurations = [ 'conf-' + conf for conf in dtb_files + dtb_symlinks]
+            configurations = [bb_vars['FIT_CONF_PREFIX'] + conf for conf in dtb_files + dtb_symlinks]
         else:
-            configurations = [ 'conf-1' ]
+            configurations = [bb_vars['FIT_CONF_PREFIX'] + '1']
 
         # Create a list of paths for all image and configuration nodes
         req_its_paths = []
@@ -526,7 +527,7 @@ class KernelFitImageTests(FitImageTestCase):
             if uboot_rd_entrypoint:
                 its_field_check.append("entry = <%s>;" % uboot_rd_entrypoint)
         its_field_check += [
-            # 'default = "conf-1";', needs more work
+            # 'default = bb_vars['FIT_CONF_PREFIX'] + "1";', needs more work
             'kernel = "kernel-1";',
         ]
         if initramfs_image and initramfs_image_bundle != "1":
@@ -599,33 +600,35 @@ class KernelFitImageTests(FitImageTestCase):
         # Create a configuration section for each DTB
         if dtb_files:
             for dtb in dtb_files + dtb_symlinks:
+                conf_name = bb_vars['FIT_CONF_PREFIX'] + dtb
                 # Assume that DTBs with an "-alias" in its name are symlink DTBs created e.g. by the
                 # bbb-dtbs-as-ext test recipe. Make the configuration node pointing to the real DTB.
                 real_dtb = dtb.replace("-alias", "")
                 # dtb overlays do not refer to a kernel (yet?)
                 if dtb.endswith('.dtbo'):
-                    req_sections['conf-' + dtb] = {
+                    req_sections[conf_name] = {
                         "FDT": 'fdt-' + real_dtb,
                     }
                 else:
-                    req_sections['conf-' + dtb] = {
+                    req_sections[conf_name] = {
                         "Kernel": "kernel-1",
                         "FDT": 'fdt-' + real_dtb,
                     }
                 if initramfs_image and initramfs_image_bundle != "1":
-                    req_sections['conf-' + dtb]['Init Ramdisk'] = "ramdisk-1"
+                    req_sections[conf_name]['Init Ramdisk'] = "ramdisk-1"
         else:
-            req_sections['conf-1'] = {
+            conf_name = bb_vars['FIT_CONF_PREFIX'] +  '1'
+            req_sections[conf_name] = {
                 "Kernel": "kernel-1"
             }
             if initramfs_image and initramfs_image_bundle != "1":
-                req_sections['conf-1']['Init Ramdisk'] = "ramdisk-1"
+                req_sections[conf_name]['Init Ramdisk'] = "ramdisk-1"
 
         # Add signing related properties if needed
         if uboot_sign_enable == "1":
             for section in req_sections:
                 req_sections[section]['Hash algo'] = fit_hash_alg
-                if section.startswith('conf-'):
+                if section.startswith(bb_vars['FIT_CONF_PREFIX']):
                     req_sections[section]['Hash value'] = "unavailable"
                     req_sections[section]['Sign algo'] = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname)
                     num_signatures += 1
@@ -652,13 +655,13 @@ class KernelFitImageTests(FitImageTestCase):
         fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[fit_sign_alg]
         for section, values in sections.items():
             # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1")
-            if section.startswith("conf"):
+            if section.startswith(bb_vars['FIT_CONF_PREFIX']):
                 sign_algo = values.get('Sign algo', None)
                 req_sign_algo = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname)
                 self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section)
                 sign_value = values.get('Sign value', None)
                 self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section)
-                dtb_file_name = section.replace('conf-', '')
+                dtb_file_name = section.replace(bb_vars['FIT_CONF_PREFIX'], '')
                 dtb_path = os.path.join(deploy_dir_image, dtb_file_name)
                 # External devicetrees created by devicetree.bbclass are in a subfolder and have priority
                 dtb_path_ext = os.path.join(deploy_dir_image, "devicetree", dtb_file_name)
@@ -718,6 +721,7 @@ UBOOT_RD_ENTRYPOINT = "0x88000000"
 UBOOT_LOADADDRESS = "0x80080000"
 UBOOT_ENTRYPOINT = "0x80080000"
 FIT_DESC = "A model description"
+FIT_CONF_PREFIX = "foo-"
 """
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
-- 
2.49.0



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

* [PATCH v6 05/21] oe-selftest: fitimage: test FIT_CONF_DEFAULT_DTB
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (3 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 04/21] oe-selftest: fitimage: test FIT_CONF_PREFIX AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 06/21] kernel-signing-keys-native: refactor key generation into a new recipe AdrianF
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Add some test coverage for non default FIT_CONF_DEFAULT_DTB.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 812b34fab47..8a3eb7f8c11 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -380,6 +380,7 @@ class KernelFitImageTests(FitImageTestCase):
         """
         internal_used = {
             'DEPLOY_DIR_IMAGE',
+            'FIT_CONF_DEFAULT_DTB',
             'FIT_CONF_PREFIX',
             'FIT_DESC',
             'FIT_HASH_ALG',
@@ -526,10 +527,14 @@ class KernelFitImageTests(FitImageTestCase):
                 its_field_check.append("load = <%s>;" % uboot_rd_loadaddress)
             if uboot_rd_entrypoint:
                 its_field_check.append("entry = <%s>;" % uboot_rd_entrypoint)
-        its_field_check += [
-            # 'default = bb_vars['FIT_CONF_PREFIX'] + "1";', needs more work
-            'kernel = "kernel-1";',
-        ]
+
+        fit_conf_default_dtb = bb_vars.get('FIT_CONF_DEFAULT_DTB')
+        if fit_conf_default_dtb:
+            fit_conf_prefix = bb_vars.get('FIT_CONF_PREFIX', "conf-")
+            its_field_check.append('default = "' + fit_conf_prefix + fit_conf_default_dtb + '";')
+
+        its_field_check.append('kernel = "kernel-1";')
+
         if initramfs_image and initramfs_image_bundle != "1":
             its_field_check.append('ramdisk = "ramdisk-1";')
 
@@ -773,6 +778,7 @@ UBOOT_SIGN_ENABLE = "1"
 UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
 UBOOT_SIGN_KEYNAME = "dev"
 UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
+FIT_CONF_DEFAULT_DTB = "am335x-bonegreen.dtb"
 """
         config = self._config_add_uboot_env(config)
         self.write_config(config)
-- 
2.49.0



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

* [PATCH v6 06/21] kernel-signing-keys-native: refactor key generation into a new recipe
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (4 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 05/21] oe-selftest: fitimage: test FIT_CONF_DEFAULT_DTB AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 07/21] maintainers: add myself for kernel-signing-keys-native AdrianF
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

The do_kernel_generate_rsa_keys function from kernel-fitimage.bbclass
is moved to a new recipe, kernel-signing-keys-native.bb. This
refactoring introduces no functional changes.

Intention this change:
- Remove the dependency of uboot-sign.bbclass on kernel-fitimage.bbclass.
- Simplify the use of custom key generation implementations by
  isolating the functionality into a separate recipe.

Known limitations of this (and also the previous) implementation:
- When generating from an existing TMPDIR, the existing key is reused.
  However, when generating from an empty TMPDIR or an SDK using the
  sstate-cache, a new key is generated, which may lead to
  inconsistencies.
- The use of random keys (via FIT_GENERATE_KEYS) is convenient for
  experiments but unsuitable for production environments requiring
  deterministic and secure key management.

Future improvements to consider:
- Ensure reproducibility, even when using the sstate-cache. However,
  simply storing the private key in a potentially shared sstate artifact
  may not always be ideal from a security perspective.
- Support encrypted keys via `SRC_URI` for reliable key updates.
- Enable signing with an HSM (Hardware Security Module) through
  mechanisms like PKCS#11 or post-processing scripts.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel-fitimage.bbclass   | 52 +------------
 meta/classes-recipe/uboot-sign.bbclass        |  5 +-
 .../kernel-signing-keys-native.bb             | 75 +++++++++++++++++++
 3 files changed, 82 insertions(+), 50 deletions(-)
 create mode 100644 meta/recipes-kernel/kernel-signing-keys/kernel-signing-keys-native.bb

diff --git a/meta/classes-recipe/kernel-fitimage.bbclass b/meta/classes-recipe/kernel-fitimage.bbclass
index 07786647e19..f5f02f30f0a 100644
--- a/meta/classes-recipe/kernel-fitimage.bbclass
+++ b/meta/classes-recipe/kernel-fitimage.bbclass
@@ -27,7 +27,10 @@ def get_fit_replacement_type(d):
     return replacementtype
 
 KERNEL_IMAGETYPE_REPLACEMENT ?= "${@get_fit_replacement_type(d)}"
-DEPENDS:append = " ${@'u-boot-tools-native dtc-native' if 'fitImage' in (d.getVar('KERNEL_IMAGETYPES') or '').split() else ''}"
+DEPENDS:append = " \
+    ${@'u-boot-tools-native dtc-native' if 'fitImage' in (d.getVar('KERNEL_IMAGETYPES') or '').split() else ''} \
+    ${@'kernel-signing-keys-native' if d.getVar('FIT_GENERATE_KEYS') == '1' else ''} \
+"
 
 python __anonymous () {
     # Override KERNEL_IMAGETYPE_FOR_MAKE variable, which is internal
@@ -754,53 +757,6 @@ do_assemble_fitimage_initramfs() {
 
 addtask assemble_fitimage_initramfs before do_deploy after do_bundle_initramfs
 
-do_kernel_generate_rsa_keys() {
-	if [ "${UBOOT_SIGN_ENABLE}" = "0" ] && [ "${FIT_GENERATE_KEYS}" = "1" ]; then
-		bbwarn "FIT_GENERATE_KEYS is set to 1 even though UBOOT_SIGN_ENABLE is set to 0. The keys will not be generated as they won't be used."
-	fi
-
-	if [ "${UBOOT_SIGN_ENABLE}" = "1" ] && [ "${FIT_GENERATE_KEYS}" = "1" ]; then
-
-		# Generate keys to sign configuration nodes, only if they don't already exist
-		if [ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".key ] || \
-			[ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".crt ]; then
-
-			# make directory if it does not already exist
-			mkdir -p "${UBOOT_SIGN_KEYDIR}"
-
-			bbnote "Generating RSA private key for signing fitImage"
-			openssl genrsa ${FIT_KEY_GENRSA_ARGS} -out \
-				"${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".key \
-			"${FIT_SIGN_NUMBITS}"
-
-			bbnote "Generating certificate for signing fitImage"
-			openssl req ${FIT_KEY_REQ_ARGS} "${FIT_KEY_SIGN_PKCS}" \
-				-key "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".key \
-				-out "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".crt
-		fi
-
-		# Generate keys to sign image nodes, only if they don't already exist
-		if [ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".key ] || \
-			[ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".crt ]; then
-
-			# make directory if it does not already exist
-			mkdir -p "${UBOOT_SIGN_KEYDIR}"
-
-			bbnote "Generating RSA private key for signing fitImage"
-			openssl genrsa ${FIT_KEY_GENRSA_ARGS} -out \
-				"${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".key \
-			"${FIT_SIGN_NUMBITS}"
-
-			bbnote "Generating certificate for signing fitImage"
-			openssl req ${FIT_KEY_REQ_ARGS} "${FIT_KEY_SIGN_PKCS}" \
-				-key "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".key \
-				-out "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".crt
-		fi
-	fi
-}
-
-addtask kernel_generate_rsa_keys before do_assemble_fitimage after do_compile
-
 kernel_do_deploy[vardepsexclude] = "DATETIME"
 kernel_do_deploy:append() {
 	# Update deploy directory
diff --git a/meta/classes-recipe/uboot-sign.bbclass b/meta/classes-recipe/uboot-sign.bbclass
index 796c040e8b4..73e9ce3f11f 100644
--- a/meta/classes-recipe/uboot-sign.bbclass
+++ b/meta/classes-recipe/uboot-sign.bbclass
@@ -112,13 +112,14 @@ UBOOT_FIT_CONF_USER_LOADABLES ?= ''
 UBOOT_FIT_UBOOT_LOADADDRESS ?= "${UBOOT_LOADADDRESS}"
 UBOOT_FIT_UBOOT_ENTRYPOINT ?= "${UBOOT_ENTRYPOINT}"
 
+
+DEPENDS:append = " ${@'kernel-signing-keys-native' if d.getVar('FIT_GENERATE_KEYS') == '1' else ''}"
+
 python() {
     # We need u-boot-tools-native if we're creating a U-Boot fitImage
     sign = d.getVar('UBOOT_SIGN_ENABLE') == '1'
     if d.getVar('UBOOT_FITIMAGE_ENABLE') == '1' or sign:
         d.appendVar('DEPENDS', " u-boot-tools-native dtc-native")
-    if d.getVar('FIT_GENERATE_KEYS') == '1' and sign:
-        d.appendVarFlag('do_uboot_assemble_fitimage', 'depends', ' virtual/kernel:do_kernel_generate_rsa_keys')
 }
 
 concat_dtb() {
diff --git a/meta/recipes-kernel/kernel-signing-keys/kernel-signing-keys-native.bb b/meta/recipes-kernel/kernel-signing-keys/kernel-signing-keys-native.bb
new file mode 100644
index 00000000000..704973dffb7
--- /dev/null
+++ b/meta/recipes-kernel/kernel-signing-keys/kernel-signing-keys-native.bb
@@ -0,0 +1,75 @@
+# Automatically generate key pairs in UBOOT_SIGN_KEYDIR if they do not exist.
+# The key pair is generated by the kernel-signing-keys-native recipe and is not
+# stored in the sstate cache. This can be beneficial from a security standpoint,
+# as it avoids unintentionally caching and distributing private keys.
+# However, this behavior can lead to non-reproducible builds. For example, if
+# the keys are deleted, they must be manually restored, or you must run:
+#   bitbake -c cleanall kernel-signing-keys-native
+# before new key pairs are generated.
+#
+# However, this approach is only suitable for simple or local development use
+# cases. For more advanced or production-grade scenarios, a more robust solution
+# is usually required—such as external signing or re-signing using e.g a HSM.
+
+
+SUMMARY = "Signing keys for the kernel FIT image"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
+
+require conf/image-fitimage.conf
+
+DEPENDS += "openssl-native"
+
+inherit native
+
+do_fetch[noexec] = "1"
+do_unpack[noexec] = "1"
+do_patch[noexec] = "1"
+do_configure[noexec] = "1"
+do_install[noexec] = "1"
+
+
+do_compile() {
+	if [ "${UBOOT_SIGN_ENABLE}" = "0" ] && [ "${FIT_GENERATE_KEYS}" = "1" ]; then
+		bbwarn "FIT_GENERATE_KEYS is set to 1 even though UBOOT_SIGN_ENABLE is set to 0. The keys will not be generated as they won't be used."
+	fi
+
+	if [ "${UBOOT_SIGN_ENABLE}" = "1" ] && [ "${FIT_GENERATE_KEYS}" = "1" ]; then
+
+		# Generate keys to sign configuration nodes, only if they don't already exist
+		if [ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".key ] || \
+			[ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".crt ]; then
+
+			# make directory if it does not already exist
+			mkdir -p "${UBOOT_SIGN_KEYDIR}"
+
+			bbnote "Generating RSA private key for signing fitImage"
+			openssl genrsa ${FIT_KEY_GENRSA_ARGS} -out \
+				"${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".key \
+			"${FIT_SIGN_NUMBITS}"
+
+			bbnote "Generating certificate for signing fitImage"
+			openssl req ${FIT_KEY_REQ_ARGS} "${FIT_KEY_SIGN_PKCS}" \
+				-key "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".key \
+				-out "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_KEYNAME}".crt
+		fi
+
+		# Generate keys to sign image nodes, only if they don't already exist
+		if [ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".key ] || \
+			[ ! -f "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".crt ]; then
+
+			# make directory if it does not already exist
+			mkdir -p "${UBOOT_SIGN_KEYDIR}"
+
+			bbnote "Generating RSA private key for signing fitImage"
+			openssl genrsa ${FIT_KEY_GENRSA_ARGS} -out \
+				"${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".key \
+			"${FIT_SIGN_NUMBITS}"
+
+			bbnote "Generating certificate for signing fitImage"
+			openssl req ${FIT_KEY_REQ_ARGS} "${FIT_KEY_SIGN_PKCS}" \
+				-key "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".key \
+				-out "${UBOOT_SIGN_KEYDIR}/${UBOOT_SIGN_IMG_KEYNAME}".crt
+		fi
+	fi
+}
-- 
2.49.0



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

* [PATCH v6 07/21] maintainers: add myself for kernel-signing-keys-native
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (5 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 06/21] kernel-signing-keys-native: refactor key generation into a new recipe AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 08/21] oe-selftest: fitimage: cleanup FIT_GENERATE_KEYS AdrianF
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/conf/distro/include/maintainers.inc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/meta/conf/distro/include/maintainers.inc b/meta/conf/distro/include/maintainers.inc
index 8627f72ce77..470e63af079 100644
--- a/meta/conf/distro/include/maintainers.inc
+++ b/meta/conf/distro/include/maintainers.inc
@@ -307,6 +307,7 @@ RECIPE_MAINTAINER:pn-kbd = "Unassigned <unassigned@yoctoproject.org>"
 RECIPE_MAINTAINER:pn-kea = "Unassigned <unassigned@yoctoproject.org>"
 RECIPE_MAINTAINER:pn-kern-tools-native = "Bruce Ashfield <bruce.ashfield@gmail.com>"
 RECIPE_MAINTAINER:pn-kernel-devsrc = "Bruce Ashfield <bruce.ashfield@gmail.com>"
+RECIPE_MAINTAINER:pn-kernel-signing-keys-native = "Adrian Freihofer <adrian.freihofer@siemens.com>"
 RECIPE_MAINTAINER:pn-kexec-tools = "Unassigned <unassigned@yoctoproject.org>"
 RECIPE_MAINTAINER:pn-keymaps = "Unassigned <unassigned@yoctoproject.org>"
 RECIPE_MAINTAINER:pn-kmod = "Chen Qi <Qi.Chen@windriver.com>"
-- 
2.49.0



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

* [PATCH v6 08/21] oe-selftest: fitimage: cleanup FIT_GENERATE_KEYS
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (6 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 07/21] maintainers: add myself for kernel-signing-keys-native AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 09/21] kernel-uboot.bbclass: do not require the kernel build folder AdrianF
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

It is closer to practice to use static and predictable keys to sign the
FIT images. In addition, the new kernel-signing-keys-native is only
reliable if the temporary directory is not deleted. However, depending
on how this test suite is started, this can happen.
There will therefore only be one test that uses the recipe to generate
the keys, which ensures that the recipe works in principle.
It is also ensured that no keys are present before the test and that the
recipe runs safely and is not skipped by Bitbake.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 24 ++++++++----------------
 1 file changed, 8 insertions(+), 16 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 8a3eb7f8c11..fcba28b7b76 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -760,8 +760,7 @@ PREFERRED_PROVIDER_virtual/dtb = "bbb-dtbs-as-ext"
                      and the configuration nodes are signed correctly.
         Expected:    1) its and FIT image are built successfully
                      2) Scanning the its file indicates signing is enabled
-                        as requested by UBOOT_SIGN_ENABLE (using 1 key
-                        generated by the test not via FIT_GENERATE_KEYS)
+                        as requested by UBOOT_SIGN_ENABLE
                      3) Dumping the FIT image indicates signature values
                         are present (only for the configuration nodes as
                         FIT_SIGN_INDIVIDUAL is disabled)
@@ -792,10 +791,7 @@ FIT_CONF_DEFAULT_DTB = "am335x-bonegreen.dtb"
             'UBOOT_SIGN_KEYDIR',
         ])
 
-        # Do not use the random keys generated by FIT_GENERATE_KEYS.
-        # Using a static key is probably a more realistic scenario.
         self._gen_signing_key(bb_vars)
-
         self._test_fitimage(bb_vars)
 
     def test_sign_fit_image_individual(self):
@@ -804,11 +800,11 @@ FIT_CONF_DEFAULT_DTB = "am335x-bonegreen.dtb"
                      and all nodes are signed correctly.
         Expected:    1) its and FIT image are built successfully
                      2) Scanning the its file indicates signing is enabled
-                        as requested by UBOOT_SIGN_ENABLE (using 2 keys
-                        generated via FIT_GENERATE_KEYS)
+                        as requested by UBOOT_SIGN_ENABLE
                      3) Dumping the FIT image indicates signature values
                         are present (including for images as enabled via
                         FIT_SIGN_INDIVIDUAL)
+                        This also implies that FIT_GENERATE_KEYS = "1" works.
                      4) Verify the FIT image contains the comments passed via
                         UBOOT_MKIMAGE_SIGN_ARGS once per image and per
                         configuration node.
@@ -837,6 +833,10 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
+
+        # Ensure new keys are generated and FIT_GENERATE_KEYS = "1" is tested
+        bitbake("kernel-signing-keys-native -c cleansstate")
+
         self._test_fitimage(bb_vars)
 
     def test_fit_image_sign_initramfs(self):
@@ -875,7 +875,6 @@ UBOOT_ARCH = "arm"
 UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
 UBOOT_MKIMAGE_KERNEL_TYPE = "kernel"
 UBOOT_EXTLINUX = "0"
-FIT_GENERATE_KEYS = "1"
 KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
 FIT_KERNEL_COMP_ALG = "none"
 FIT_HASH_ALG = "sha256"
@@ -892,10 +891,7 @@ FIT_HASH_ALG = "sha256"
             'UBOOT_SIGN_KEYDIR',
         ])
 
-        # Do not use the random keys generated by FIT_GENERATE_KEYS.
-        # Using a static key is probably a more realistic scenario.
         self._gen_signing_key(bb_vars)
-
         self._test_fitimage(bb_vars)
 
     def test_fit_image_sign_initramfs_bundle(self):
@@ -933,7 +929,6 @@ UBOOT_ARCH = "arm"
 UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
 UBOOT_MKIMAGE_KERNEL_TYPE = "kernel"
 UBOOT_EXTLINUX = "0"
-FIT_GENERATE_KEYS = "1"
 KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
 FIT_KERNEL_COMP_ALG = "none"
 FIT_HASH_ALG = "sha256"
@@ -941,6 +936,7 @@ FIT_HASH_ALG = "sha256"
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
+        self._gen_signing_key(bb_vars)
         self._test_fitimage(bb_vars)
 
 
@@ -1345,9 +1341,7 @@ UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
 
-        # Using a static key. FIT_GENERATE_KEYS = "1" does not work without kernel-fitimage.bbclass
         self._gen_signing_key(bb_vars)
-
         self._test_fitimage(bb_vars)
         self._check_kernel_dtb(bb_vars)
 
@@ -1508,8 +1502,6 @@ FIT_SIGN_INDIVIDUAL = "1"
 """
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
-
-        # Using a static key. FIT_GENERATE_KEYS = "1" does not work without kernel-fitimage.bbclass
         self._gen_signing_key(bb_vars)
 
         bitbake("virtual/bootloader")
-- 
2.49.0



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

* [PATCH v6 09/21] kernel-uboot.bbclass: do not require the kernel build folder
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (7 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 08/21] oe-selftest: fitimage: cleanup FIT_GENERATE_KEYS AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary AdrianF
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

The function must be executed in CWD. Make it more flexible by
specifying the kernel build folder as a parameter.

This is a refactoring without functional change. But later this change
will allow to use this function also with a kernel from the sstate-cache
instead of requiring the full kernel build folder structure.

Another preparation for using a kernel from sstate-cache is to persist
the linux_comp variable in a file next to the linux.bin file rather than
using a global shell variable.

This change also requires to adapt the kernel-uimage.bbclass
accordingly. This change also fixes a minor detail:
the kernel-uimage.bbclass used ${ instead of $ for evaluatiing a local
shell variable.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel-uboot.bbclass  | 40 ++++++++++++++---------
 meta/classes-recipe/kernel-uimage.bbclass |  3 +-
 2 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/meta/classes-recipe/kernel-uboot.bbclass b/meta/classes-recipe/kernel-uboot.bbclass
index 6d4aff6b11b..d2a63524ece 100644
--- a/meta/classes-recipe/kernel-uboot.bbclass
+++ b/meta/classes-recipe/kernel-uboot.bbclass
@@ -12,19 +12,27 @@ FIT_KERNEL_COMP_ALG_EXTENSION ?= ".gz"
 UBOOT_MKIMAGE_KERNEL_TYPE ?= "kernel"
 
 uboot_prep_kimage() {
-	if [ -e arch/${ARCH}/boot/compressed/vmlinux ]; then
+	output_dir=$1
+	# For backward compatibility with kernel-fitimage.bbclass and kernel-uboot.bbclass
+	# support calling without parameter as well
+	if [ -z "$output_dir" ]; then
+		output_dir='.'
+	fi
+
+	linux_bin=$output_dir/linux.bin
+	if [ -e "arch/${ARCH}/boot/compressed/vmlinux" ]; then
 		vmlinux_path="arch/${ARCH}/boot/compressed/vmlinux"
 		linux_suffix=""
 		linux_comp="none"
-	elif [ -e arch/${ARCH}/boot/vmlinuz.bin ]; then
-		rm -f linux.bin
-		cp -l arch/${ARCH}/boot/vmlinuz.bin linux.bin
+	elif [ -e "arch/${ARCH}/boot/vmlinuz.bin" ]; then
+		rm -f "$linux_bin"
+		cp -l "arch/${ARCH}/boot/vmlinuz.bin" "$linux_bin"
 		vmlinux_path=""
 		linux_suffix=""
 		linux_comp="none"
 	else
 		vmlinux_path="vmlinux"
-		# Use vmlinux.initramfs for linux.bin when INITRAMFS_IMAGE_BUNDLE set
+		# Use vmlinux.initramfs for $linux_bin when INITRAMFS_IMAGE_BUNDLE set
 		# As per the implementation in kernel.bbclass.
 		# See do_bundle_initramfs function
 		if [ "${INITRAMFS_IMAGE_BUNDLE}" = "1" ] && [ -e vmlinux.initramfs ]; then
@@ -34,18 +42,18 @@ uboot_prep_kimage() {
 		linux_comp="${FIT_KERNEL_COMP_ALG}"
 	fi
 
-	[ -n "${vmlinux_path}" ] && ${KERNEL_OBJCOPY} -O binary -R .note -R .comment -S "${vmlinux_path}" linux.bin
+	[ -n "$vmlinux_path" ] && ${KERNEL_OBJCOPY} -O binary -R .note -R .comment -S "$vmlinux_path" "$linux_bin"
 
-	if [ "${linux_comp}" != "none" ] ; then
-		if [ "${linux_comp}" = "gzip" ] ; then
-			gzip -9 linux.bin
-		elif [ "${linux_comp}" = "lzo" ] ; then
-			lzop -9 linux.bin
-		elif [ "${linux_comp}" = "lzma" ] ; then
-			xz --format=lzma -f -6 linux.bin
+	if [ "$linux_comp" != "none" ] ; then
+		if [ "$linux_comp" = "gzip" ] ; then
+			gzip -9 "$linux_bin"
+		elif [ "$linux_comp" = "lzo" ] ; then
+			lzop -9 "$linux_bin"
+		elif [ "$linux_comp" = "lzma" ] ; then
+			xz --format=lzma -f -6 "$linux_bin"
 		fi
-		mv -f "linux.bin${linux_suffix}" linux.bin
+		mv -f "$linux_bin$linux_suffix" "$linux_bin"
 	fi
 
-	echo "${linux_comp}"
-}
+	printf "$linux_comp" > "$output_dir/linux_comp"
+}
\ No newline at end of file
diff --git a/meta/classes-recipe/kernel-uimage.bbclass b/meta/classes-recipe/kernel-uimage.bbclass
index 1a599e656c8..e353232a0e9 100644
--- a/meta/classes-recipe/kernel-uimage.bbclass
+++ b/meta/classes-recipe/kernel-uimage.bbclass
@@ -29,6 +29,7 @@ python __anonymous () {
 do_uboot_mkimage[dirs] += "${B}"
 do_uboot_mkimage() {
 	uboot_prep_kimage
+	linux_comp="$(cat linux_comp)"
 
 	ENTRYPOINT=${UBOOT_ENTRYPOINT}
 	if [ -n "${UBOOT_ENTRYSYMBOL}" ]; then
@@ -36,6 +37,6 @@ do_uboot_mkimage() {
 			awk '$3=="${UBOOT_ENTRYSYMBOL}" {print "0x"$1;exit}'`
 	fi
 
-	uboot-mkimage -A ${UBOOT_ARCH} -O linux -T ${UBOOT_MKIMAGE_KERNEL_TYPE} -C "${linux_comp}" -a ${UBOOT_LOADADDRESS} -e $ENTRYPOINT -n "${DISTRO_NAME}/${PV}/${MACHINE}" -d linux.bin ${B}/arch/${ARCH}/boot/uImage
+	uboot-mkimage -A ${UBOOT_ARCH} -O linux -T ${UBOOT_MKIMAGE_KERNEL_TYPE} -C "$linux_comp" -a ${UBOOT_LOADADDRESS} -e $ENTRYPOINT -n "${DISTRO_NAME}/${PV}/${MACHINE}" -d linux.bin ${B}/arch/${ARCH}/boot/uImage
 	rm -f linux.bin
 }
-- 
2.49.0



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

* [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (8 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 09/21] kernel-uboot.bbclass: do not require the kernel build folder AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02 18:51   ` [OE-core] " Bruce Ashfield
  2025-06-02  7:56 ` [PATCH v6 11/21] kernel-fitimage: refactor order in its AdrianF
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Extend the kernel-uboot.bbclass to build and deploy the kernel binary
intended for inclusion in a FIT image.
Note that the kernel used in a FIT image is a stripped (and optionally
compressed) vmlinux ELF binary - not a self-extracting format like
zImage, which is already available in the deploy directory if needed
separately.

The kernel.bbclass inherits all classes listed in the KERNEL_CLASSES
variable. By default, KERNEL_CLASSES is defined as:
  KERNEL_CLASSES ?= " kernel-uimage "
Since kernel-uimage.bbclass inherits from kernel-uboot.bbclass, the
creation and deployment of the kernel binary for inclusion in a FIT
image happens automatically by default.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel-uboot.bbclass | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/meta/classes-recipe/kernel-uboot.bbclass b/meta/classes-recipe/kernel-uboot.bbclass
index d2a63524ece..e26ba6f0aa9 100644
--- a/meta/classes-recipe/kernel-uboot.bbclass
+++ b/meta/classes-recipe/kernel-uboot.bbclass
@@ -56,4 +56,13 @@ uboot_prep_kimage() {
 	fi
 
 	printf "$linux_comp" > "$output_dir/linux_comp"
-}
\ No newline at end of file
+}
+
+kernel_do_deploy:append() {
+	# Provide the kernel artifacts to post processing recipes e.g. for creating a FIT image
+	uboot_prep_kimage "$deployDir"
+	# For x86 a setup.bin needs to be include"d in a fitImage as well
+	if [ -e ${KERNEL_OUTPUT_DIR}/setup.bin ]; then
+		install -D "${B}/${KERNEL_OUTPUT_DIR}/setup.bin" "$deployDir/"
+	fi
+}
-- 
2.49.0



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

* [PATCH v6 11/21] kernel-fitimage: refactor order in its
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (9 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation AdrianF
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

When the ITS file is created, the mandatory properties are written first
before the optional properties are written.
This is not really useful for the current implementation. But it is a
preparation for a new Python-based implementation that will expect
mandatory properties first. This change makes it possible to run the
tests with both the old and the new implementation.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel-fitimage.bbclass | 20 ++++++++++----------
 meta/lib/oeqa/selftest/cases/fitimage.py    |  4 ++--
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/meta/classes-recipe/kernel-fitimage.bbclass b/meta/classes-recipe/kernel-fitimage.bbclass
index f5f02f30f0a..8e66f326766 100644
--- a/meta/classes-recipe/kernel-fitimage.bbclass
+++ b/meta/classes-recipe/kernel-fitimage.bbclass
@@ -128,11 +128,11 @@ fitimage_emit_section_kernel() {
 	cat << EOF >> $1
                 kernel-$2 {
                         description = "Linux kernel";
-                        data = /incbin/("$3");
                         type = "${UBOOT_MKIMAGE_KERNEL_TYPE}";
+                        compression = "$4";
+                        data = /incbin/("$3");
                         arch = "${UBOOT_ARCH}";
                         os = "linux";
-                        compression = "$4";
                         load = <${UBOOT_LOADADDRESS}>;
                         entry = <$ENTRYPOINT>;
                         hash-1 {
@@ -177,10 +177,10 @@ fitimage_emit_section_dtb() {
 	cat << EOF >> $1
                 fdt-$2 {
                         description = "Flattened Device Tree blob";
-                        data = /incbin/("$3");
                         type = "flat_dt";
-                        arch = "${UBOOT_ARCH}";
                         compression = "none";
+                        data = /incbin/("$3");
+                        arch = "${UBOOT_ARCH}";
                         $dtb_loadline
                         hash-1 {
                                 algo = "$dtb_csum";
@@ -215,10 +215,10 @@ fitimage_emit_section_boot_script() {
         cat << EOF >> $1
                 bootscr-$2 {
                         description = "U-boot script";
-                        data = /incbin/("$3");
                         type = "script";
-                        arch = "${UBOOT_ARCH}";
                         compression = "none";
+                        data = /incbin/("$3");
+                        arch = "${UBOOT_ARCH}";
                         hash-1 {
                                 algo = "$bootscr_csum";
                         };
@@ -252,11 +252,11 @@ fitimage_emit_section_setup() {
 	cat << EOF >> $1
                 setup-$2 {
                         description = "Linux setup.bin";
-                        data = /incbin/("$3");
                         type = "x86_setup";
+                        compression = "none";
+                        data = /incbin/("$3");
                         arch = "${UBOOT_ARCH}";
                         os = "linux";
-                        compression = "none";
                         load = <0x00090000>;
                         entry = <0x00090000>;
                         hash-1 {
@@ -301,11 +301,11 @@ fitimage_emit_section_ramdisk() {
 	cat << EOF >> $1
                 ramdisk-$2 {
                         description = "${INITRAMFS_IMAGE}";
-                        data = /incbin/("$3");
                         type = "ramdisk";
+                        compression = "none";
+                        data = /incbin/("$3");
                         arch = "${UBOOT_ARCH}";
                         os = "linux";
-                        compression = "none";
                         $ramdisk_loadline
                         $ramdisk_entryline
                         hash-1 {
diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index fcba28b7b76..9c353b6ed13 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -513,11 +513,11 @@ class KernelFitImageTests(FitImageTestCase):
         its_field_check = [
             'description = "%s";' % bb_vars['FIT_DESC'],
             'description = "Linux kernel";',
-            'data = /incbin/("linux.bin");',
             'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";',
+            # 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', defined based on files in TMPDIR, not ideal...
+            'data = /incbin/("linux.bin");',
             'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";',
             'os = "linux";',
-            # 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', defined based on files in TMPDIR, not ideal...
             'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;',
             'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;',
         ]
-- 
2.49.0



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

* [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (10 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 11/21] kernel-fitimage: refactor order in its AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02 18:12   ` [OE-core] " Bruce Ashfield
  2025-06-02  7:56 ` [PATCH v6 13/21] maintainers: add myself for linux-yocto-fitimage AdrianF
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

The new recipe linux-yocto-fitimage.bb and the new
kernel-fit-image.bbclass are intended to become successors of the
kernel-fitimage.bbclass.

Instead of injecting the FIT image related build steps into the kernel
recipe, the new recipe takes the kernel artifacts from the kernel recipe
and creates the FIT image as an independent task.

This solves some basic problems:
* sstate does not work well when a fitImage contains an initramfs. The
  kernel is rebuilt from scratch if the build runs from an empty TMPDIR.
* A fitImage kernel is not available as a package, but all other kernel
  image types are.
* The task dependencies in the kernel are very complex and difficult to
  debug if something goes wrong. As a separate, downstream recipe, this
  is now much easier.

The recipe takes the kernel artifacts from the deploy folder. There was
also a test implementation passing the kernel artifacts via sysroot
directory. This requires changes on the kernel.bbclass to make it
copying the artifacts also to the sysroot directory while the same
artifacts are already in the sstate-cached deploy directory.

The long story about this issue is here:
[YOCTO #12912]

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel-fit-image.bbclass  | 187 ++++++
 meta/classes/multilib.bbclass                 |   1 +
 meta/lib/oe/fitimage.py                       | 547 ++++++++++++++++++
 .../linux/linux-yocto-fitimage.bb             |  13 +
 4 files changed, 748 insertions(+)
 create mode 100644 meta/classes-recipe/kernel-fit-image.bbclass
 create mode 100644 meta/lib/oe/fitimage.py
 create mode 100644 meta/recipes-kernel/linux/linux-yocto-fitimage.bb

diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass
new file mode 100644
index 00000000000..6d80cd4bb47
--- /dev/null
+++ b/meta/classes-recipe/kernel-fit-image.bbclass
@@ -0,0 +1,187 @@
+
+inherit kernel-arch kernel-artifact-names uboot-config deploy
+require conf/image-fitimage.conf
+
+S = "${WORKDIR}/sources"
+UNPACKDIR = "${S}"
+
+PACKAGE_ARCH = "${MACHINE_ARCH}"
+
+DEPENDS += "\
+    u-boot-tools-native dtc-native \
+    ${@'kernel-signing-keys-native' if d.getVar('FIT_GENERATE_KEYS') == '1' else ''} \
+"
+
+python () {
+    image = d.getVar('INITRAMFS_IMAGE')
+    if image and d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1':
+        if d.getVar('INITRAMFS_MULTICONFIG'):
+            mc = d.getVar('BB_CURRENT_MC')
+            d.appendVarFlag('do_compile', 'mcdepends', ' mc:' + mc + ':${INITRAMFS_MULTICONFIG}:${INITRAMFS_IMAGE}:do_image_complete')
+        else:
+            d.appendVarFlag('do_compile', 'depends', ' ${INITRAMFS_IMAGE}:do_image_complete')
+
+    #check if there are any dtb providers
+    providerdtb = d.getVar("PREFERRED_PROVIDER_virtual/dtb")
+    if providerdtb:
+        d.appendVarFlag('do_compile', 'depends', ' virtual/dtb:do_populate_sysroot')
+        d.setVar('EXTERNAL_KERNEL_DEVICETREE', "${RECIPE_SYSROOT}/boot/devicetree")
+}
+
+do_configure[noexec] = "1"
+
+UBOOT_MKIMAGE_KERNEL_TYPE ?= "kernel"
+KERNEL_IMAGEDEST ?= "/boot"
+
+python do_compile() {
+    import shutil
+    import oe.fitimage
+
+    itsfile = "fit-image.its"
+    fitname = "fitImage"
+    kernel_deploydir = d.getVar('DEPLOY_DIR_IMAGE')
+    kernel_deploysubdir = d.getVar('KERNEL_DEPLOYSUBDIR')
+    if kernel_deploysubdir:
+        kernel_deploydir = os.path.join(kernel_deploydir, kernel_deploysubdir)
+
+    # Collect all the its nodes before the its file is generated and mkimage gets executed
+    root_node = oe.fitimage.ItsNodeRootKernel(
+        d.getVar("FIT_DESC"), d.getVar("FIT_ADDRESS_CELLS"),
+        d.getVar('HOST_PREFIX'), d.getVar('UBOOT_ARCH'),  d.getVar("FIT_CONF_PREFIX"),
+        oe.types.boolean(d.getVar('UBOOT_SIGN_ENABLE')), d.getVar("UBOOT_SIGN_KEYDIR"),
+        d.getVar("UBOOT_MKIMAGE"), d.getVar("UBOOT_MKIMAGE_DTCOPTS"),
+        d.getVar("UBOOT_MKIMAGE_SIGN"), d.getVar("UBOOT_MKIMAGE_SIGN_ARGS"),
+        d.getVar('FIT_HASH_ALG'), d.getVar('FIT_SIGN_ALG'), d.getVar('FIT_PAD_ALG'),
+        d.getVar('UBOOT_SIGN_KEYNAME'),
+        oe.types.boolean(d.getVar('FIT_SIGN_INDIVIDUAL')), d.getVar('UBOOT_SIGN_IMG_KEYNAME')
+    )
+
+    # Prepare a kernel image section.
+    shutil.copyfile(os.path.join(kernel_deploydir, "linux.bin"), "linux.bin")
+    with open(os.path.join(kernel_deploydir, "linux_comp")) as linux_comp_f:
+        linux_comp = linux_comp_f.read()
+    root_node.fitimage_emit_section_kernel("kernel-1", "linux.bin", linux_comp,
+        d.getVar('UBOOT_LOADADDRESS'), d.getVar('UBOOT_ENTRYPOINT'),
+        d.getVar('UBOOT_MKIMAGE_KERNEL_TYPE'), d.getVar("UBOOT_ENTRYSYMBOL"))
+
+    # Prepare a DTB image section
+    kernel_devicetree = d.getVar('KERNEL_DEVICETREE')
+    external_kernel_devicetree = d.getVar("EXTERNAL_KERNEL_DEVICETREE")
+    if kernel_devicetree:
+        for dtb in kernel_devicetree.split():
+            # In deploy_dir the DTBs are without sub-directories also with KERNEL_DTBVENDORED = "1"
+            dtb_name = os.path.basename(dtb)
+
+            # Skip DTB if it's also provided in EXTERNAL_KERNEL_DEVICETREE directory
+            if external_kernel_devicetree:
+                ext_dtb_path = os.path.join(external_kernel_devicetree, dtb_name)
+                if os.path.exists(ext_dtb_path) and os.path.getsize(ext_dtb_path) > 0:
+                    continue
+
+            # Copy the dtb or dtbo file into the FIT image assembly directory
+            shutil.copyfile(os.path.join(kernel_deploydir, dtb_name), dtb_name)
+            root_node.fitimage_emit_section_dtb(dtb_name, dtb_name,
+                d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"))
+
+    if external_kernel_devicetree:
+        # iterate over all .dtb and .dtbo files in the external kernel devicetree directory
+        # and copy them to the FIT image assembly directory
+        for dtb_name in sorted(os.listdir(external_kernel_devicetree)):
+            if dtb_name.endswith('.dtb') or dtb_name.endswith('.dtbo'):
+                dtb_path = os.path.join(external_kernel_devicetree, dtb_name)
+
+                # For symlinks, add a configuration node that refers to the DTB image node to which the symlink points
+                symlink_target = oe.fitimage.symlink_points_below(dtb_name, external_kernel_devicetree)
+                if symlink_target:
+                    root_node.fitimage_emit_section_dtb_alias(dtb_name, symlink_target, True)
+                # For real DTB files add an image node and a configuration node
+                else:
+                    shutil.copyfile(dtb_path, dtb_name)
+                    root_node.fitimage_emit_section_dtb(dtb_name, dtb_name,
+                        d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"), True)
+
+    # Prepare a u-boot script section
+    fit_uboot_env = d.getVar("FIT_UBOOT_ENV")
+    if fit_uboot_env:
+        root_node.fitimage_emit_section_boot_script("bootscr-"+fit_uboot_env , fit_uboot_env)
+
+    # Prepare a setup section (For x86)
+    setup_bin_path = os.path.join(kernel_deploydir, "setup.bin")
+    if os.path.exists(setup_bin_path):
+        shutil.copyfile(setup_bin_path, "setup.bin")
+        root_node.fitimage_emit_section_setup("setup-1", "setup.bin")
+
+    # Prepare a ramdisk section.
+    initramfs_image = d.getVar('INITRAMFS_IMAGE')
+    if initramfs_image and d.getVar("INITRAMFS_IMAGE_BUNDLE") != '1':
+        # Find and use the first initramfs image archive type we find
+        found = False
+        for img in d.getVar("FIT_SUPPORTED_INITRAMFS_FSTYPES").split():
+            initramfs_path = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), "%s.%s" % (d.getVar('INITRAMFS_IMAGE_NAME'), img))
+            if os.path.exists(initramfs_path):
+                bb.note("Found initramfs image: " + initramfs_path)
+                found = True
+                root_node.fitimage_emit_section_ramdisk("ramdisk-1", initramfs_path,
+                    initramfs_image,
+                    d.getVar("UBOOT_RD_LOADADDRESS"),
+                    d.getVar("UBOOT_RD_ENTRYPOINT"))
+                break
+            else:
+                bb.note("Did not find initramfs image: " + initramfs_path)
+
+        if not found:
+            bb.fatal("Could not find a valid initramfs type for %s, the supported types are: %s" % (d.getVar('INITRAMFS_IMAGE_NAME'), d.getVar('FIT_SUPPORTED_INITRAMFS_FSTYPES')))
+
+    # Generate the configuration section
+    root_node.fitimage_emit_section_config(d.getVar("FIT_CONF_DEFAULT_DTB"))
+
+    # Write the its file
+    root_node.write_its_file(itsfile)
+
+    # Assemble the FIT image
+    root_node.run_mkimage_assemble(itsfile, fitname)
+
+    # Sign the FIT image if required
+    root_node.run_mkimage_sign(fitname)
+}
+do_compile[depends] += "virtual/kernel:do_deploy"
+
+do_install() {
+    install -d "${D}/${KERNEL_IMAGEDEST}"
+    install -m 0644 "${B}/fitImage" "${D}/${KERNEL_IMAGEDEST}/fitImage"
+}
+
+FILES:${PN} = "${KERNEL_IMAGEDEST}"
+
+
+do_deploy() {
+    deploy_dir="${DEPLOYDIR}"
+    if [ -n "${KERNEL_DEPLOYSUBDIR}" ]; then
+        deploy_dir="${DEPLOYDIR}/${KERNEL_DEPLOYSUBDIR}"
+    fi
+    install -d "$deploy_dir"
+    install -m 0644 "${B}/fitImage" "$deploy_dir/fitImage"
+    install -m 0644 "${B}/fit-image.its" "$deploy_dir/fit-image.its"
+
+    if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
+        ln -snf fit-image.its "$deploy_dir/fitImage-its-${KERNEL_FIT_NAME}.its"
+        if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
+            ln -snf fit-image.its "$deploy_dir/fitImage-its-${KERNEL_FIT_LINK_NAME}"
+        fi
+    fi
+
+    if [ -n "${INITRAMFS_IMAGE}" ]; then
+        ln -snf fit-image-its "$deploy_dir/fitImage-its-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.its"
+        if [ -n "${KERNEL_FIT_LINK_NAME}" ]; then
+            ln -snf fit-image.its "$deploy_dir/fitImage-its-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_LINK_NAME}"
+        fi
+
+        if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
+            ln -snf fitImage "$deploy_dir/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}${KERNEL_FIT_BIN_EXT}"
+            if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
+                ln -snf fitImage "$deploy_dir/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_LINK_NAME}"
+            fi
+        fi
+    fi
+}
+addtask deploy after do_compile before do_build
diff --git a/meta/classes/multilib.bbclass b/meta/classes/multilib.bbclass
index a4151658a62..b959bbd93c0 100644
--- a/meta/classes/multilib.bbclass
+++ b/meta/classes/multilib.bbclass
@@ -21,6 +21,7 @@ python multilib_virtclass_handler () {
     bpn = d.getVar("BPN")
     if ("virtual/kernel" in provides
             or bb.data.inherits_class('module-base', d)
+            or bb.data.inherits_class('kernel-fit-image', d)
             or bpn in non_ml_recipes):
         raise bb.parse.SkipRecipe("We shouldn't have multilib variants for %s" % bpn)
 
diff --git a/meta/lib/oe/fitimage.py b/meta/lib/oe/fitimage.py
new file mode 100644
index 00000000000..f3037991558
--- /dev/null
+++ b/meta/lib/oe/fitimage.py
@@ -0,0 +1,547 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# This file contains common functions for the fitimage generation
+
+import os
+import shlex
+import subprocess
+import bb
+
+from oeqa.utils.commands import runCmd
+
+class ItsNode:
+    INDENT_SIZE = 8
+
+    def __init__(self, name, parent_node, sub_nodes=None, properties=None):
+        self.name = name
+        self.parent_node = parent_node
+
+        self.sub_nodes = []
+        if sub_nodes:
+            self.sub_nodes = sub_nodes
+
+        self.properties = {}
+        if properties:
+            self.properties = properties
+
+        if parent_node:
+            parent_node.add_sub_node(self)
+
+    def add_sub_node(self, sub_node):
+        self.sub_nodes.append(sub_node)
+
+    def add_property(self, key, value):
+        self.properties[key] = value
+
+    def emit(self, f, indent):
+        indent_str_name = " " * indent
+        indent_str_props = " " * (indent + self.INDENT_SIZE)
+        f.write("%s%s {\n" % (indent_str_name, self.name))
+        for key, value in self.properties.items():
+            bb.debug(1, "key: %s, value: %s" % (key, str(value)))
+            # Single integer: <0x12ab>
+            if isinstance(value, int):
+                f.write(indent_str_props + key + ' = <0x%x>;\n' % value)
+            # list of strings: "string1", "string2" or integers: <0x12ab 0x34cd>
+            elif isinstance(value, list):
+                if len(value) == 0:
+                    f.write(indent_str_props + key + ' = "";\n')
+                elif isinstance(value[0], int):
+                    list_entries = ' '.join('0x%x' % entry for entry in value)
+                    f.write(indent_str_props + key + ' = <%s>;\n' % list_entries)
+                else:
+                    list_entries = ', '.join('"%s"' % entry for entry in value)
+                    f.write(indent_str_props + key + ' = %s;\n' % list_entries)
+            elif isinstance(value, str):
+                # path: /incbin/("path/to/file")
+                if key in ["data"] and value.startswith('/incbin/('):
+                    f.write(indent_str_props + key + ' = %s;\n' % value)
+                # Integers which are already string formatted
+                elif value.startswith("<") and value.endswith(">"):
+                    f.write(indent_str_props + key + ' = %s;\n' % value)
+                else:
+                    f.write(indent_str_props + key + ' = "%s";\n' % value)
+            else:
+                bb.fatal("%s has unexpexted data type." % str(value))
+        for sub_node in self.sub_nodes:
+            sub_node.emit(f, indent + self.INDENT_SIZE)
+        f.write(indent_str_name + '};\n')
+
+class ItsNodeImages(ItsNode):
+    def __init__(self, parent_node):
+        super().__init__("images", parent_node)
+
+class ItsNodeConfigurations(ItsNode):
+    def __init__(self, parent_node):
+        super().__init__("configurations", parent_node)
+
+class ItsNodeHash(ItsNode):
+    def __init__(self, name, parent_node, algo, opt_props=None):
+        properties = {
+            "algo": algo
+        }
+        if opt_props:
+            properties.update(opt_props)
+        super().__init__(name, parent_node, None, properties)
+
+class ItsImageSignature(ItsNode):
+    def __init__(self, name, parent_node, algo, keyname, opt_props=None):
+        properties = {
+            "algo": algo,
+            "key-name-hint": keyname
+        }
+        if opt_props:
+            properties.update(opt_props)
+        super().__init__(name, parent_node, None, properties)
+
+class ItsNodeImage(ItsNode):
+    def __init__(self, name, parent_node, description, type, compression, sub_nodes=None, opt_props=None):
+        properties = {
+            "description": description,
+            "type": type,
+            "compression": compression,
+        }
+        if opt_props:
+            properties.update(opt_props)
+        super().__init__(name, parent_node, sub_nodes, properties)
+
+class ItsNodeDtb(ItsNodeImage):
+    def __init__(self, name, parent_node, description, type, compression,
+                 sub_nodes=None, opt_props=None, compatible=None):
+        super().__init__(name, parent_node, description, type, compression, sub_nodes, opt_props)
+        self.compatible = compatible
+
+class ItsNodeDtbAlias(ItsNode):
+    """Additional Configuration Node for a DTB
+
+    Symlinks pointing to a DTB file are handled by an addtitional
+    configuration node referring to another DTB image node.
+    """
+    def __init__(self, name, alias_name, compatible=None):
+        super().__init__(name, parent_node=None, sub_nodes=None, properties=None)
+        self.alias_name = alias_name
+        self.compatible = compatible
+
+class ItsNodeConfigurationSignature(ItsNode):
+    def __init__(self, name, parent_node, algo, keyname, opt_props=None):
+        properties = {
+            "algo": algo,
+            "key-name-hint": keyname
+        }
+        if opt_props:
+            properties.update(opt_props)
+        super().__init__(name, parent_node, None, properties)
+
+class ItsNodeConfiguration(ItsNode):
+    def __init__(self, name, parent_node, description, sub_nodes=None, opt_props=None):
+        properties = {
+            "description": description,
+        }
+        if opt_props:
+            properties.update(opt_props)
+        super().__init__(name, parent_node, sub_nodes, properties)
+
+class ItsNodeRootKernel(ItsNode):
+    """Create FIT images for the kernel
+
+    Currently only a single kernel (no less or more) can be added to the FIT
+    image along with 0 or more device trees and 0 or 1 ramdisk.
+
+    If a device tree included in the FIT image, the default configuration is the
+    firt DTB. If there is no dtb present than the default configuation the kernel.
+    """
+    def __init__(self, description, address_cells, host_prefix, arch, conf_prefix,
+                 sign_enable=False, sign_keydir=None,
+                 mkimage=None, mkimage_dtcopts=None,
+                 mkimage_sign=None, mkimage_sign_args=None,
+                 hash_algo=None, sign_algo=None, pad_algo=None,
+                 sign_keyname_conf=None,
+                 sign_individual=False, sign_keyname_img=None):
+        props = {
+            "description": description,
+            "#address-cells": f"<{address_cells}>"
+        }
+        super().__init__("/", None, None, props)
+        self.images = ItsNodeImages(self)
+        self.configurations = ItsNodeConfigurations(self)
+
+        self._host_prefix = host_prefix
+        self._arch = arch
+        self._conf_prefix = conf_prefix
+
+        # Signature related properties
+        self._sign_enable = sign_enable
+        self._sign_keydir = sign_keydir
+        self._mkimage = mkimage
+        self._mkimage_dtcopts = mkimage_dtcopts
+        self._mkimage_sign = mkimage_sign
+        self._mkimage_sign_args = mkimage_sign_args
+        self._hash_algo = hash_algo
+        self._sign_algo = sign_algo
+        self._pad_algo = pad_algo
+        self._sign_keyname_conf = sign_keyname_conf
+        self._sign_individual = sign_individual
+        self._sign_keyname_img = sign_keyname_img
+        self._sanitize_sign_config()
+
+        self._dtbs = []
+        self._dtb_alias = []
+        self._kernel = None
+        self._ramdisk = None
+        self._bootscr = None
+        self._setup = None
+
+    def _sanitize_sign_config(self):
+        if self._sign_enable:
+            if not self._hash_algo:
+                bb.fatal("FIT image signing is enabled but no hash algorithm is provided.")
+            if not self._sign_algo:
+                bb.fatal("FIT image signing is enabled but no signature algorithm is provided.")
+            if not self._pad_algo:
+                bb.fatal("FIT image signing is enabled but no padding algorithm is provided.")
+            if not self._sign_keyname_conf:
+                bb.fatal("FIT image signing is enabled but no configuration key name is provided.")
+            if self._sign_individual and not self._sign_keyname_img:
+                bb.fatal("FIT image signing is enabled for individual images but no image key name is provided.")
+
+    def write_its_file(self, itsfile):
+        with open(itsfile, 'w') as f:
+            f.write("/dts-v1/;\n\n")
+            self.emit(f, 0)
+
+    def its_add_node_image(self, image_id, description, image_type, compression, opt_props):
+        image_node = ItsNodeImage(
+            image_id,
+            self.images,
+            description,
+            image_type,
+            compression,
+            opt_props=opt_props
+        )
+        if self._hash_algo:
+            ItsNodeHash(
+                "hash-1",
+                image_node,
+                self._hash_algo
+            )
+        if self._sign_individual:
+            ItsImageSignature(
+                "signature-1",
+                image_node,
+                f"{self._hash_algo},{self._sign_algo}",
+                self._sign_keyname_img
+            )
+        return image_node
+
+    def its_add_node_dtb(self, image_id, description, image_type, compression, opt_props, compatible):
+        dtb_node = ItsNodeDtb(
+            image_id,
+            self.images,
+            description,
+            image_type,
+            compression,
+            opt_props=opt_props,
+            compatible=compatible
+        )
+        if self._hash_algo:
+            ItsNodeHash(
+                "hash-1",
+                dtb_node,
+                self._hash_algo
+            )
+        if self._sign_individual:
+            ItsImageSignature(
+                "signature-1",
+                dtb_node,
+                f"{self._hash_algo},{self._sign_algo}",
+                self._sign_keyname_img
+            )
+        return dtb_node
+
+    def fitimage_emit_section_kernel(self, kernel_id, kernel_path, compression,
+        load, entrypoint, mkimage_kernel_type, entrysymbol=None):
+        """Emit the fitImage ITS kernel section"""
+        if self._kernel:
+            bb.fatal("Kernel section already exists in the ITS file.")
+        if entrysymbol:
+            result = subprocess.run([self._host_prefix + "nm", "vmlinux"], capture_output=True, text=True)
+            for line in result.stdout.splitlines():
+                parts = line.split()
+                if len(parts) == 3 and parts[2] == entrysymbol:
+                    entrypoint = "<0x%s>" % parts[0]
+                    break
+        kernel_node = self.its_add_node_image(
+            kernel_id,
+            "Linux kernel",
+            mkimage_kernel_type,
+            compression,
+            {
+                "data": '/incbin/("' + kernel_path + '")',
+                "arch": self._arch,
+                "os": "linux",
+                "load": f"<{load}>",
+                "entry": f"<{entrypoint}>"
+            }
+        )
+        self._kernel = kernel_node
+
+    def fitimage_emit_section_dtb(self, dtb_id, dtb_path, dtb_loadaddress=None,
+                                  dtbo_loadaddress=None, add_compatible=False):
+        """Emit the fitImage ITS DTB section"""
+        load=None
+        dtb_ext = os.path.splitext(dtb_path)[1]
+        if dtb_ext == ".dtbo":
+            if dtbo_loadaddress:
+                load = dtbo_loadaddress
+        elif dtb_loadaddress:
+            load = dtb_loadaddress
+
+        opt_props = {
+            "data": '/incbin/("' + dtb_path + '")',
+            "arch": self._arch
+        }
+        if load:
+            opt_props["load"] = f"<{load}>"
+
+        # Preserve the DTB's compatible string to be added to the configuration node
+        compatible = None
+        if add_compatible:
+            compatible = get_compatible_from_dtb(dtb_path)
+
+        dtb_node = self.its_add_node_dtb(
+            "fdt-" + dtb_id,
+            "Flattened Device Tree blob",
+            "flat_dt",
+            "none",
+            opt_props,
+            compatible
+        )
+        self._dtbs.append(dtb_node)
+
+    def fitimage_emit_section_dtb_alias(self, dtb_alias_id, dtb_path, add_compatible=False):
+        """Add a configuration node referring to another DTB"""
+        # Preserve the DTB's compatible string to be added to the configuration node
+        compatible = None
+        if add_compatible:
+            compatible = get_compatible_from_dtb(dtb_path)
+
+        dtb_id = os.path.basename(dtb_path)
+        dtb_alias_node = ItsNodeDtbAlias("fdt-" + dtb_id, dtb_alias_id, compatible)
+        self._dtb_alias.append(dtb_alias_node)
+        bb.warn(f"compatible: {compatible}, dtb_alias_id: {dtb_alias_id}, dtb_id: {dtb_id}, dtb_path: {dtb_path}")
+
+    def fitimage_emit_section_boot_script(self, bootscr_id, bootscr_path):
+        """Emit the fitImage ITS u-boot script section"""
+        if self._bootscr:
+            bb.fatal("U-boot script section already exists in the ITS file.")
+        bootscr_node = self.its_add_node_image(
+            bootscr_id,
+            "U-boot script",
+            "script",
+            "none",
+            {
+                "data": '/incbin/("' + bootscr_path + '")',
+                "arch": self._arch,
+                "type": "script"
+            }
+        )
+        self._bootscr = bootscr_node
+
+    def fitimage_emit_section_setup(self, setup_id, setup_path):
+        """Emit the fitImage ITS setup section"""
+        if self._setup:
+            bb.fatal("Setup section already exists in the ITS file.")
+        load = "<0x00090000>"
+        entry = "<0x00090000>"
+        setup_node = self.its_add_node_image(
+            setup_id,
+            "Linux setup.bin",
+            "x86_setup",
+            "none",
+            {
+                "data": '/incbin/("' + setup_path + '")',
+                "arch": self._arch,
+                "os": "linux",
+                "load": load,
+                "entry": entry
+            }
+        )
+        self._setup = setup_node
+
+    def fitimage_emit_section_ramdisk(self, ramdisk_id, ramdisk_path, description="ramdisk", load=None, entry=None):
+        """Emit the fitImage ITS ramdisk section"""
+        if self._ramdisk:
+            bb.fatal("Ramdisk section already exists in the ITS file.")
+        opt_props = {
+            "data": '/incbin/("' + ramdisk_path + '")',
+            "type": "ramdisk",
+            "arch": self._arch,
+            "os": "linux"
+        }
+        if load:
+            opt_props["load"] = f"<{load}>"
+        if entry:
+            opt_props["entry"] = f"<{entry}>"
+
+        ramdisk_node = self.its_add_node_image(
+            ramdisk_id,
+            description,
+            "ramdisk",
+            "none",
+            opt_props
+        )
+        self._ramdisk = ramdisk_node
+
+    def _fitimage_emit_one_section_config(self, conf_node_name, dtb=None):
+        """Emit the fitImage ITS configuration section"""
+        opt_props = {}
+        conf_desc = []
+        sign_entries = []
+
+        if self._kernel:
+            conf_desc.append("Linux kernel")
+            opt_props["kernel"] = self._kernel.name
+            if self._sign_enable:
+                sign_entries.append("kernel")
+
+        if dtb:
+            conf_desc.append("FDT blob")
+            opt_props["fdt"] = dtb.name
+            if dtb.compatible:
+                opt_props["compatible"] = dtb.compatible
+            if self._sign_enable:
+                sign_entries.append("fdt")
+
+        if self._ramdisk:
+            conf_desc.append("ramdisk")
+            opt_props["ramdisk"] = self._ramdisk.name
+            if self._sign_enable:
+                sign_entries.append("ramdisk")
+
+        if self._bootscr:
+            conf_desc.append("u-boot script")
+            opt_props["bootscr"] = self._bootscr.name
+            if self._sign_enable:
+                sign_entries.append("bootscr")
+
+        if self._setup:
+            conf_desc.append("setup")
+            opt_props["setup"] = self._setup.name
+            if self._sign_enable:
+                sign_entries.append("setup")
+
+        # First added configuration is the default configuration
+        default_flag = "0"
+        if len(self.configurations.sub_nodes) == 0:
+            default_flag = "1"
+
+        conf_node = ItsNodeConfiguration(
+            conf_node_name,
+            self.configurations,
+            f"{default_flag} {', '.join(conf_desc)}",
+            opt_props=opt_props
+        )
+        if self._hash_algo:
+            ItsNodeHash(
+                "hash-1",
+                conf_node,
+                self._hash_algo
+            )
+        if self._sign_enable:
+            ItsNodeConfigurationSignature(
+                "signature-1",
+                conf_node,
+                f"{self._hash_algo},{self._sign_algo}",
+                self._sign_keyname_conf,
+                opt_props={
+                    "padding": self._pad_algo,
+                    "sign-images": sign_entries
+                }
+            )
+
+    def fitimage_emit_section_config(self, default_dtb_image=None):
+        if self._dtbs:
+            for dtb in self._dtbs:
+                dtb_name = dtb.name
+                if dtb.name.startswith("fdt-"):
+                    dtb_name = dtb.name[len("fdt-"):]
+                self._fitimage_emit_one_section_config(self._conf_prefix + dtb_name, dtb)
+            for dtb in self._dtb_alias:
+                self._fitimage_emit_one_section_config(self._conf_prefix + dtb.alias_name, dtb)
+        else:
+            # Currently exactly one kernel is supported.
+            self._fitimage_emit_one_section_config(self._conf_prefix + "1")
+
+        default_conf = self.configurations.sub_nodes[0].name
+        if default_dtb_image and self._dtbs:
+            default_conf = self._conf_prefix + default_dtb_image
+        self.configurations.add_property('default', default_conf)
+
+    def run_mkimage_assemble(self, itsfile, fitfile):
+        cmd = [
+            self._mkimage,
+            '-f', itsfile,
+            fitfile
+        ]
+        if self._mkimage_dtcopts:
+            cmd.insert(1, '-D')
+            cmd.insert(2, self._mkimage_dtcopts)
+        try:
+            subprocess.run(cmd, check=True, capture_output=True)
+        except subprocess.CalledProcessError as e:
+            bb.fatal(f"Command '{' '.join(cmd)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\nitsflile: {os.path.abspath(itsfile)}")
+
+    def run_mkimage_sign(self, fitfile):
+        if not self._sign_enable:
+            bb.debug(1, "FIT image signing is disabled. Skipping signing.")
+            return
+
+        # Some sanity checks because mkimage exits with 0 also without needed keys
+        sign_key_path = os.path.join(self._sign_keydir, self._sign_keyname_conf)
+        if not os.path.exists(sign_key_path + '.key') or not os.path.exists(sign_key_path + '.crt'):
+            bb.fatal("%s.key or .crt does not exist" % sign_key_path)
+        if self._sign_individual:
+            sign_key_img_path = os.path.join(self._sign_keydir, self._sign_keyname_img)
+            if not os.path.exists(sign_key_img_path + '.key') or not os.path.exists(sign_key_img_path + '.crt'):
+                bb.fatal("%s.key or .crt does not exist" % sign_key_img_path)
+
+        cmd = [
+            self._mkimage_sign,
+            '-F',
+            '-k', self._sign_keydir,
+            '-r', fitfile
+        ]
+        if self._mkimage_dtcopts:
+            cmd.extend(['-D', self._mkimage_dtcopts])
+        if self._mkimage_sign_args:
+            cmd.extend(shlex.split(self._mkimage_sign_args))
+        try:
+            subprocess.run(cmd, check=True, capture_output=True)
+        except subprocess.CalledProcessError as e:
+            bb.fatal(f"Command '{' '.join(cmd)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}")
+
+
+def symlink_points_below(file_or_symlink, expected_parent_dir):
+    """returns symlink destination if it points below directory"""
+    file_path = os.path.join(expected_parent_dir, file_or_symlink)
+    if not os.path.islink(file_path):
+        return None
+
+    realpath = os.path.relpath(os.path.realpath(file_path), expected_parent_dir)
+    if realpath.startswith(".."):
+        return None
+
+    return realpath
+
+def get_compatible_from_dtb(dtb_path, fdtget_path="fdtget"):
+    compatible = None
+    cmd = [fdtget_path, "-t", "s", dtb_path, "/", "compatible"]
+    try:
+        ret = subprocess.run(cmd, check=True, capture_output=True, text=True)
+        compatible = ret.stdout.strip().split()
+    except subprocess.CalledProcessError:
+        compatible = None
+    return compatible
diff --git a/meta/recipes-kernel/linux/linux-yocto-fitimage.bb b/meta/recipes-kernel/linux/linux-yocto-fitimage.bb
new file mode 100644
index 00000000000..6ce1960a871
--- /dev/null
+++ b/meta/recipes-kernel/linux/linux-yocto-fitimage.bb
@@ -0,0 +1,13 @@
+SUMMARY = "The Linux kernel as a FIT image (optionally with initramfs)"
+SECTION = "kernel"
+
+# If an initramfs is included in the FIT image more licenses apply.
+# But also the kernel uses more than one license (see Documentation/process/license-rules.rst)
+LICENSE = "GPL-2.0-with-Linux-syscall-note"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-with-Linux-syscall-note;md5=0bad96c422c41c3a94009dcfe1bff992"
+
+inherit linux-kernel-base kernel-fit-image
+
+# Set the version of this recipe to the version of the included kernel
+# (without taking the long way around via PV)
+PKGV = "${@get_kernelversion_file("${STAGING_KERNEL_BUILDDIR}")}"
-- 
2.49.0



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

* [PATCH v6 13/21] maintainers: add myself for linux-yocto-fitimage
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (11 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 14/21] oe-selftest: fitimage: add tests for fitimage.py AdrianF
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/conf/distro/include/maintainers.inc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/meta/conf/distro/include/maintainers.inc b/meta/conf/distro/include/maintainers.inc
index 470e63af079..5165024f503 100644
--- a/meta/conf/distro/include/maintainers.inc
+++ b/meta/conf/distro/include/maintainers.inc
@@ -472,6 +472,7 @@ RECIPE_MAINTAINER:pn-libxxf86vm = "Unassigned <unassigned@yoctoproject.org>"
 RECIPE_MAINTAINER:pn-libyaml = "Wang Mingyu <wangmy@fujitsu.com>"
 RECIPE_MAINTAINER:pn-lighttpd = "Unassigned <unassigned@yoctoproject.org>"
 RECIPE_MAINTAINER:pn-linux-dummy = "Unassigned <unassigned@yoctoproject.org>"
+RECIPE_MAINTAINER:pn-linux-yocto-fitimage = "Adrian Freihofer <adrian.freihofer@siemens.com>"
 RECIPE_MAINTAINER:pn-linux-firmware = "Otavio Salvador <otavio.salvador@ossystems.com.br>"
 RECIPE_MAINTAINER:pn-linux-libc-headers = "Bruce Ashfield <bruce.ashfield@gmail.com>"
 RECIPE_MAINTAINER:pn-linux-yocto = "Bruce Ashfield <bruce.ashfield@gmail.com>"
-- 
2.49.0



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

* [PATCH v6 14/21] oe-selftest: fitimage: add tests for fitimage.py
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (12 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 13/21] maintainers: add myself for linux-yocto-fitimage AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 15/21] oe-selftest: fitimage: support new FIT recipe as well AdrianF
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Having the FIT image generator code as a separate class, which is
essentially independent of BitBake, also allows testing the code
separately from BitBake. Take advantage of this enables testing more
use cases with significantly faster tests.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 179 ++++++++++++++++++++++-
 1 file changed, 175 insertions(+), 4 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 9c353b6ed13..3d6a5a582f0 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -4,13 +4,36 @@
 # SPDX-License-Identifier: MIT
 #
 
-from oeqa.selftest.case import OESelftestTestCase
-from oeqa.utils.commands import runCmd, bitbake, get_bb_vars
 import os
 import re
 import shlex
 import logging
 import pprint
+import tempfile
+
+import oe.fitimage
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_vars, get_bb_var
+
+
+class BbVarsMockGenKeys:
+    def __init__(self, keydir, gen_keys="0", sign_enabled="0", keyname="", sign_ind="0", img_keyname=""):
+        self.bb_vars = {
+            'FIT_GENERATE_KEYS': gen_keys,
+            'FIT_KEY_GENRSA_ARGS': "-F4",
+            'FIT_KEY_REQ_ARGS': "-batch -new",
+            'FIT_KEY_SIGN_PKCS': "-x509",
+            'FIT_SIGN_INDIVIDUAL': sign_ind,
+            'FIT_SIGN_NUMBITS': "2048",
+            'UBOOT_SIGN_ENABLE': sign_enabled,
+            'UBOOT_SIGN_IMG_KEYNAME': img_keyname,
+            'UBOOT_SIGN_KEYDIR': keydir,
+            'UBOOT_SIGN_KEYNAME': keyname,
+        }
+
+    def getVar(self, var):
+        return self.bb_vars[var]
 
 class FitImageTestCase(OESelftestTestCase):
     """Test functions usable for testing kernel-fitimage.bbclass and uboot-sign.bbclass
@@ -369,8 +392,7 @@ class FitImageTestCase(OESelftestTestCase):
         # Verify the FIT image
         self._check_fitimage(bb_vars, fitimage_path, uboot_tools_bindir)
 
-
-class KernelFitImageTests(FitImageTestCase):
+class KernelFitImageBase(FitImageTestCase):
     """Test cases for the kernel-fitimage bbclass"""
 
     def _fit_get_bb_vars(self, additional_vars=[]):
@@ -696,6 +718,8 @@ class KernelFitImageTests(FitImageTestCase):
             self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." %
                              (num_signatures, a_comment))
 
+class KernelFitImageTests(KernelFitImageBase):
+    """Test cases for the kernel-fitimage bbclass"""
 
     def test_fit_image(self):
         """
@@ -732,6 +756,52 @@ FIT_CONF_PREFIX = "foo-"
         bb_vars = self._fit_get_bb_vars()
         self._test_fitimage(bb_vars)
 
+    def test_get_compatible_from_dtb(self):
+        """Test the oe.fitimage.get_compatible_from_dtb function
+
+        1. bitbake bbb-dtbs-as-ext
+        2. Check if symlink_points_below returns the path to the DTB
+        3. Check if the expected compatible string is found by get_compatible_from_dtb()
+        """
+        DTB_RECIPE = "bbb-dtbs-as-ext"
+        DTB_FILE = "am335x-bonegreen-ext.dtb"
+        DTB_SYMLINK = "am335x-bonegreen-ext-alias.dtb"
+        DTBO_FILE = "BBORG_RELAY-00A2.dtbo"
+        EXPECTED_COMP = ["ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"]
+
+        config = """
+DISTRO="poky"
+MACHINE = "beaglebone-yocto"
+"""
+        self.write_config(config)
+
+        # Provide the fdtget command called by get_compatible_from_dtb
+        dtc_bindir = FitImageTestCase._setup_native('dtc-native')
+        fdtget_path = os.path.join(dtc_bindir, "fdtget")
+        self.assertExists(fdtget_path)
+
+        # bitbake an external DTB with a symlink to it and a DTB overlay
+        bitbake(DTB_RECIPE)
+        deploy_dir_image = get_bb_var("DEPLOY_DIR_IMAGE", DTB_RECIPE)
+        devicetree_dir = os.path.join(deploy_dir_image, "devicetree")
+        dtb_path = os.path.join(devicetree_dir, DTB_FILE)
+        dtb_alias_path = os.path.join(devicetree_dir, DTB_SYMLINK)
+        dtbo_file = os.path.join(devicetree_dir, DTBO_FILE)
+        self.assertExists(dtb_path)
+        self.assertExists(dtb_alias_path)
+        self.assertExists(dtbo_file)
+
+        # Test symlink_points_below
+        linked_dtb = oe.fitimage.symlink_points_below(dtb_alias_path, devicetree_dir)
+        self.assertEqual(linked_dtb, DTB_FILE)
+
+        # Check if get_compatible_from_dtb finds the expected compatible string in the DTBs
+        comp = oe.fitimage.get_compatible_from_dtb(dtb_path, fdtget_path)
+        self.assertEqual(comp, EXPECTED_COMP)
+        comp_alias = oe.fitimage.get_compatible_from_dtb(dtb_alias_path, fdtget_path)
+        self.assertEqual(comp_alias, EXPECTED_COMP)
+        # The alias is a symlink, therefore the compatible string is equal
+        self.assertEqual(comp_alias, comp)
 
     def test_fit_image_ext_dtb_dtbo(self):
         """
@@ -939,6 +1009,107 @@ FIT_HASH_ALG = "sha256"
         self._gen_signing_key(bb_vars)
         self._test_fitimage(bb_vars)
 
+class FitImagePyTests(KernelFitImageBase):
+    """Test cases for the fitimage.py module without calling bitbake"""
+
+    def _test_fitimage_py(self, bb_vars_overrides=None):
+        topdir = os.path.join(os.environ['BUILDDIR'])
+        fitimage_its_path = os.path.join(topdir, self._testMethodName + '.its')
+
+        # Provide variables without calling bitbake
+        bb_vars = {
+            # image-fitimage.conf
+            'FIT_DESC': "Kernel fitImage for a dummy distro",
+            'FIT_HASH_ALG': "sha256",
+            'FIT_SIGN_ALG': "rsa2048",
+            'FIT_PAD_ALG': "pkcs-1.5",
+            'FIT_GENERATE_KEYS': "0",
+            'FIT_SIGN_NUMBITS': "2048",
+            'FIT_KEY_GENRSA_ARGS': "-F4",
+            'FIT_KEY_REQ_ARGS': "-batch -new",
+            'FIT_KEY_SIGN_PKCS': "-x509",
+            'FIT_SIGN_INDIVIDUAL': "0",
+            'FIT_CONF_PREFIX': "conf-",
+            'FIT_SUPPORTED_INITRAMFS_FSTYPES': "cpio.lz4 cpio.lzo cpio.lzma cpio.xz cpio.zst cpio.gz ext2.gz cpio",
+            'FIT_CONF_DEFAULT_DTB': "",
+            'FIT_ADDRESS_CELLS': "1",
+            'FIT_UBOOT_ENV': "",
+            # kernel.bbclass
+            'UBOOT_ENTRYPOINT': "0x20008000",
+            'UBOOT_LOADADDRESS': "0x20008000",
+            'INITRAMFS_IMAGE': "",
+            'INITRAMFS_IMAGE_BUNDLE': "",
+            # kernel-uboot.bbclass
+            'FIT_KERNEL_COMP_ALG': "gzip",
+            'FIT_KERNEL_COMP_ALG_EXTENSION': ".gz",
+            'UBOOT_MKIMAGE_KERNEL_TYPE': "kernel",
+            # uboot-config.bbclass
+            'UBOOT_MKIMAGE_DTCOPTS': "",
+            'UBOOT_MKIMAGE': "uboot-mkimage",
+            'UBOOT_MKIMAGE_SIGN': "uboot-mkimage",
+            'UBOOT_MKIMAGE_SIGN_ARGS': "",
+            'UBOOT_SIGN_ENABLE': "0",
+            'UBOOT_SIGN_KEYDIR': None,
+            'UBOOT_SIGN_KEYNAME': None,
+            'UBOOT_SIGN_IMG_KEYNAME': None,
+            # others
+            'MACHINE': "qemux86-64",
+            'UBOOT_ARCH': "x86",
+            'HOST_PREFIX': "x86_64-poky-linux-"
+        }
+        if bb_vars_overrides:
+            bb_vars.update(bb_vars_overrides)
+
+        root_node = oe.fitimage.ItsNodeRootKernel(
+            bb_vars["FIT_DESC"], bb_vars["FIT_ADDRESS_CELLS"],
+            bb_vars['HOST_PREFIX'], bb_vars['UBOOT_ARCH'],  bb_vars["FIT_CONF_PREFIX"],
+            oe.types.boolean(bb_vars['UBOOT_SIGN_ENABLE']), bb_vars["UBOOT_SIGN_KEYDIR"],
+            bb_vars["UBOOT_MKIMAGE"], bb_vars["UBOOT_MKIMAGE_DTCOPTS"],
+            bb_vars["UBOOT_MKIMAGE_SIGN"], bb_vars["UBOOT_MKIMAGE_SIGN_ARGS"],
+            bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG'], bb_vars['FIT_PAD_ALG'],
+            bb_vars['UBOOT_SIGN_KEYNAME'],
+            oe.types.boolean(bb_vars['FIT_SIGN_INDIVIDUAL']), bb_vars['UBOOT_SIGN_IMG_KEYNAME']
+        )
+
+        root_node.fitimage_emit_section_kernel("kernel-1", "linux.bin", "none",
+            bb_vars.get('UBOOT_LOADADDRESS'), bb_vars.get('UBOOT_ENTRYPOINT'),
+            bb_vars.get('UBOOT_MKIMAGE_KERNEL_TYPE'), bb_vars.get("UBOOT_ENTRYSYMBOL")
+        )
+
+        dtb_files, _ = FitImageTestCase._get_dtb_files(bb_vars)
+        for dtb in dtb_files:
+            root_node.fitimage_emit_section_dtb(dtb, os.path.join("a-dir", dtb),
+                bb_vars.get("UBOOT_DTB_LOADADDRESS"), bb_vars.get("UBOOT_DTBO_LOADADDRESS"))
+
+        if bb_vars.get('FIT_UBOOT_ENV'):
+            root_node.fitimage_emit_section_boot_script(
+                "bootscr-" + bb_vars['FIT_UBOOT_ENV'], bb_vars['FIT_UBOOT_ENV'])
+
+        if bb_vars['MACHINE'] == "qemux86-64": # Not really the right if
+            root_node.fitimage_emit_section_setup("setup-1", "setup1.bin")
+
+        if bb_vars.get('INITRAMFS_IMAGE') and bb_vars.get("INITRAMFS_IMAGE_BUNDLE") != "1":
+            root_node.fitimage_emit_section_ramdisk("ramdisk-1", "a-dir/a-initramfs-1",
+                "core-image-minimal-initramfs",
+                bb_vars.get("UBOOT_RD_LOADADDRESS"), bb_vars.get("UBOOT_RD_ENTRYPOINT"))
+
+        root_node.fitimage_emit_section_config(bb_vars['FIT_CONF_DEFAULT_DTB'])
+        root_node.write_its_file(fitimage_its_path)
+
+        self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+        self.logger.debug("Checking its: %s" % fitimage_its_path)
+        self._check_its_file(bb_vars, fitimage_its_path)
+
+    def test_fitimage_py_default(self):
+        self._test_fitimage_py()
+
+    def test_fitimage_py_default_dtb(self):
+        bb_vars_overrides = {
+            'KERNEL_DEVICETREE': "one.dtb two.dtb three.dtb",
+            'FIT_CONF_DEFAULT_DTB': "two.dtb"
+        }
+        self._test_fitimage_py(bb_vars_overrides)
+
 
 class UBootFitImageTests(FitImageTestCase):
     """Test cases for the uboot-sign bbclass"""
-- 
2.49.0



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

* [PATCH v6 15/21] oe-selftest: fitimage: support new FIT recipe as well
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (13 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 14/21] oe-selftest: fitimage: add tests for fitimage.py AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 16/21] oe-selftest: fitimage: run all tests for both FIT implementations AdrianF
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Enable all existing tests to be compatible with FIT images generated
either by the new linux-yocto-fitimage recipe or the legacy
kernel-fitimage.bbclass approach.

- Make the following configurations optional:
  - KERNEL_IMAGETYPES += "fitImage"
  - KERNEL_CLASSES = "kernel-fitimage"
- Allow the tests to specify which kernel recipe should be used for the
  build (e.g., linux-yocto, linux-yocto-fitimage, etc.)

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 58 +++++++++++++++---------
 1 file changed, 37 insertions(+), 21 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 3d6a5a582f0..20fedee18bf 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -413,6 +413,7 @@ class KernelFitImageBase(FitImageTestCase):
             'INITRAMFS_IMAGE_BUNDLE',
             'INITRAMFS_IMAGE_NAME',
             'INITRAMFS_IMAGE',
+            'KERNEL_DEPLOYSUBDIR',
             'KERNEL_DEVICETREE',
             'KERNEL_FIT_LINK_NAME',
             'MACHINE',
@@ -429,10 +430,21 @@ class KernelFitImageBase(FitImageTestCase):
             'UBOOT_SIGN_KEYDIR',
             'UBOOT_SIGN_KEYNAME',
         }
-        bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/kernel")
+        bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), self.kernel_recipe)
         self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4))
         return bb_vars
 
+    def _config_add_kernel_classes(self, config):
+        config += '# Use kernel-fitimage.bbclass for the creation of the fitImage' + os.linesep
+        config += 'KERNEL_IMAGETYPES += " fitImage "' + os.linesep
+        config += 'KERNEL_CLASSES = " kernel-fitimage "' + os.linesep
+        return config
+
+    @property
+    def kernel_recipe(self):
+        # virtual/kernel does not work with SRC_URI:append:pn-%s
+        return "linux-yocto"
+
     def _config_add_uboot_env(self, config):
         """Generate an u-boot environment
 
@@ -446,7 +458,7 @@ class KernelFitImageBase(FitImageTestCase):
         config += '# Add an u-boot script to the fitImage' + os.linesep
         config += 'FIT_UBOOT_ENV = "%s"' % fit_uenv_file + os.linesep
         config += 'FILESEXTRAPATHS:prepend := "${TOPDIR}/%s:"' % test_files_dir + os.linesep
-        config += 'SRC_URI:append:pn-linux-yocto = " file://${FIT_UBOOT_ENV}"' + os.linesep
+        config += 'SRC_URI:append:pn-%s = " file://${FIT_UBOOT_ENV}"' % self.kernel_recipe + os.linesep
 
         if not os.path.isdir(test_files_dir):
             os.makedirs(test_files_dir)
@@ -458,7 +470,7 @@ class KernelFitImageBase(FitImageTestCase):
 
     def _bitbake_fit_image(self, bb_vars):
         """Bitbake the kernel and return the paths to the its file and the FIT image"""
-        bitbake("virtual/kernel")
+        bitbake(self.kernel_recipe)
 
         # Find the right its file and the final fitImage and check if both files are available
         deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE']
@@ -477,8 +489,13 @@ class KernelFitImageBase(FitImageTestCase):
             fitimage_name = "fitImage"  # or fitImage-${KERNEL_IMAGE_LINK_NAME}${KERNEL_IMAGE_BIN_EXT}
         else:
             self.fail('Invalid configuration: INITRAMFS_IMAGE_BUNDLE = "1" and not INITRAMFS_IMAGE')
-        fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_its_name))
-        fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_name))
+        kernel_deploysubdir = bb_vars['KERNEL_DEPLOYSUBDIR']
+        if kernel_deploysubdir:
+            fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, kernel_deploysubdir, fitimage_its_name))
+            fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, kernel_deploysubdir, fitimage_name))
+        else:
+            fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_its_name))
+            fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_name))
         return (fitimage_its_path, fitimage_path)
 
     def _get_req_its_paths(self, bb_vars):
@@ -677,6 +694,7 @@ class KernelFitImageBase(FitImageTestCase):
         uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME']
         uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME']
         deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE']
+        kernel_deploysubdir = bb_vars['KERNEL_DEPLOYSUBDIR']
         fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL']
         fit_hash_alg_len = FitImageTestCase.MKIMAGE_HASH_LENGTHS[fit_hash_alg]
         fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[fit_sign_alg]
@@ -690,6 +708,8 @@ class KernelFitImageBase(FitImageTestCase):
                 self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section)
                 dtb_file_name = section.replace(bb_vars['FIT_CONF_PREFIX'], '')
                 dtb_path = os.path.join(deploy_dir_image, dtb_file_name)
+                if kernel_deploysubdir:
+                    dtb_path = os.path.join(deploy_dir_image, kernel_deploysubdir, dtb_file_name)
                 # External devicetrees created by devicetree.bbclass are in a subfolder and have priority
                 dtb_path_ext = os.path.join(deploy_dir_image, "devicetree", dtb_file_name)
                 if os.path.exists(dtb_path_ext):
@@ -735,10 +755,7 @@ class KernelFitImageTests(KernelFitImageBase):
         Author:      Usama Arif <usama.arif@arm.com>
         """
         config = """
-# Enable creation of fitImage
 KERNEL_IMAGETYPE = "Image"
-KERNEL_IMAGETYPES += " fitImage "
-KERNEL_CLASSES = " kernel-fitimage "
 
 # RAM disk variables including load address and entrypoint for kernel and RAM disk
 IMAGE_FSTYPES += "cpio.gz"
@@ -752,6 +769,7 @@ UBOOT_ENTRYPOINT = "0x80080000"
 FIT_DESC = "A model description"
 FIT_CONF_PREFIX = "foo-"
 """
+        config = self._config_add_kernel_classes(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
         self._test_fitimage(bb_vars)
@@ -813,11 +831,10 @@ MACHINE = "beaglebone-yocto"
         config = """
 # Enable creation of fitImage
 MACHINE = "beaglebone-yocto"
-KERNEL_IMAGETYPES += " fitImage "
-KERNEL_CLASSES = " kernel-fitimage "
 # Add a devicetree overlay which does not need kernel sources
 PREFERRED_PROVIDER_virtual/dtb = "bbb-dtbs-as-ext"
 """
+        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
@@ -841,14 +858,13 @@ PREFERRED_PROVIDER_virtual/dtb = "bbb-dtbs-as-ext"
         config = """
 # Enable creation of fitImage
 MACHINE = "beaglebone-yocto"
-KERNEL_IMAGETYPES += " fitImage "
-KERNEL_CLASSES = " kernel-fitimage "
 UBOOT_SIGN_ENABLE = "1"
 UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
 UBOOT_SIGN_KEYNAME = "dev"
 UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
 FIT_CONF_DEFAULT_DTB = "am335x-bonegreen.dtb"
 """
+        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
 
@@ -890,8 +906,6 @@ FIT_CONF_DEFAULT_DTB = "am335x-bonegreen.dtb"
         config = """
 # Enable creation of fitImage
 MACHINE = "beaglebone-yocto"
-KERNEL_IMAGETYPES += " fitImage "
-KERNEL_CLASSES = " kernel-fitimage "
 UBOOT_SIGN_ENABLE = "1"
 FIT_GENERATE_KEYS = "1"
 UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
@@ -900,6 +914,7 @@ UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
 FIT_SIGN_INDIVIDUAL = "1"
 UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
 """
+        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
@@ -930,8 +945,6 @@ MACHINE = "beaglebone-yocto"
 INITRAMFS_IMAGE = "core-image-minimal-initramfs"
 INITRAMFS_SCRIPTS = ""
 UBOOT_MACHINE = "am335x_evm_defconfig"
-KERNEL_CLASSES = " kernel-fitimage "
-KERNEL_IMAGETYPES = "fitImage"
 UBOOT_SIGN_ENABLE = "1"
 UBOOT_SIGN_KEYNAME = "beaglebonekey"
 UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
@@ -949,6 +962,7 @@ KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
 FIT_KERNEL_COMP_ALG = "none"
 FIT_HASH_ALG = "sha256"
 """
+        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
 
@@ -986,8 +1000,6 @@ INITRAMFS_IMAGE_BUNDLE = "1"
 INITRAMFS_IMAGE = "core-image-minimal-initramfs"
 INITRAMFS_SCRIPTS = ""
 UBOOT_MACHINE = "am335x_evm_defconfig"
-KERNEL_CLASSES = " kernel-fitimage "
-KERNEL_IMAGETYPES = "fitImage"
 UBOOT_SIGN_ENABLE = "1"
 UBOOT_SIGN_KEYNAME = "beaglebonekey"
 UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
@@ -1003,12 +1015,14 @@ KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
 FIT_KERNEL_COMP_ALG = "none"
 FIT_HASH_ALG = "sha256"
 """
+        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
         self._gen_signing_key(bb_vars)
         self._test_fitimage(bb_vars)
 
+
 class FitImagePyTests(KernelFitImageBase):
     """Test cases for the fitimage.py module without calling bitbake"""
 
@@ -1114,6 +1128,8 @@ class FitImagePyTests(KernelFitImageBase):
 class UBootFitImageTests(FitImageTestCase):
     """Test cases for the uboot-sign bbclass"""
 
+    BOOTLOADER_RECIPE = "virtual/bootloader"
+
     def _fit_get_bb_vars(self, additional_vars=[]):
         """Get bb_vars as needed by _test_sign_fit_image
 
@@ -1155,13 +1171,13 @@ class UBootFitImageTests(FitImageTestCase):
             'UBOOT_SIGN_KEYDIR',
             'UBOOT_SIGN_KEYNAME',
         }
-        bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), "virtual/bootloader")
+        bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), UBootFitImageTests.BOOTLOADER_RECIPE)
         self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4))
         return bb_vars
 
     def _bitbake_fit_image(self, bb_vars):
         """Bitbake the bootloader and return the paths to the its file and the FIT image"""
-        bitbake("virtual/bootloader")
+        bitbake(UBootFitImageTests.BOOTLOADER_RECIPE)
 
         deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE']
         machine = bb_vars['MACHINE']
@@ -1675,7 +1691,7 @@ FIT_SIGN_INDIVIDUAL = "1"
         bb_vars = self._fit_get_bb_vars()
         self._gen_signing_key(bb_vars)
 
-        bitbake("virtual/bootloader")
+        bitbake(UBootFitImageTests.BOOTLOADER_RECIPE)
 
         # Just check the DTB of u-boot since there is no u-boot FIT image
         self._check_kernel_dtb(bb_vars)
-- 
2.49.0



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

* [PATCH v6 16/21] oe-selftest: fitimage: run all tests for both FIT implementations
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (14 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 15/21] oe-selftest: fitimage: support new FIT recipe as well AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 17/21] oe-selftest: fitimage refactor classes AdrianF
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Run all existing tests for kernel-fitimage.bbclass also with the new
linux-yocto-fitimage recipe.
Executing each test for both implementations helps ensure functional
compatibility and consistency between them.

This change will naturally double the test duration for FIT image-related
tests, as each test now runs against both implementations. However, the
goal is to eventually deprecate kernel-fitimage.bbclass, at which point
the duplicate tests can be removed.
Additionally, since the new implementation makes significantly more
efficient use of the sstate cache compared to the old one, the overall
test execution time may still be improved.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 20fedee18bf..bb88ac9444c 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -1022,6 +1022,17 @@ FIT_HASH_ALG = "sha256"
         self._gen_signing_key(bb_vars)
         self._test_fitimage(bb_vars)
 
+class KernelFitImageRecipeTests(KernelFitImageTests):
+    """Test cases for the linux-yocto-fitimage recipe"""
+
+    @property
+    def kernel_recipe(self):
+        return "linux-yocto-fitimage"
+
+    def _config_add_kernel_classes(self, config):
+        config += '# Avoid naming clashes in the deploy folder with kernel-fitimage.bbclass artifacts' + os.linesep
+        config += 'KERNEL_DEPLOYSUBDIR = "linux-yocto-fitimage"' + os.linesep
+        return config
 
 class FitImagePyTests(KernelFitImageBase):
     """Test cases for the fitimage.py module without calling bitbake"""
-- 
2.49.0



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

* [PATCH v6 17/21] oe-selftest: fitimage refactor classes
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (15 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 16/21] oe-selftest: fitimage: run all tests for both FIT implementations AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 18/21] kernel-fitimage: re-write its code in Python AdrianF
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Make the new KernelFitImageRecipeTests class the one that contains the
code, and keep the KernelFitImageTests class as the one that just adds
back the same tests. This will make it easier to delete the tests later,
which will hopefully become obsolete when the kernel-fitimage.bbclass
class is no longer needed.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index bb88ac9444c..fc66f22f2d2 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -393,7 +393,7 @@ class FitImageTestCase(OESelftestTestCase):
         self._check_fitimage(bb_vars, fitimage_path, uboot_tools_bindir)
 
 class KernelFitImageBase(FitImageTestCase):
-    """Test cases for the kernel-fitimage bbclass"""
+    """Test cases for the linux-yocto-fitimage recipe"""
 
     def _fit_get_bb_vars(self, additional_vars=[]):
         """Retrieve BitBake variables specific to the test case.
@@ -435,15 +435,13 @@ class KernelFitImageBase(FitImageTestCase):
         return bb_vars
 
     def _config_add_kernel_classes(self, config):
-        config += '# Use kernel-fitimage.bbclass for the creation of the fitImage' + os.linesep
-        config += 'KERNEL_IMAGETYPES += " fitImage "' + os.linesep
-        config += 'KERNEL_CLASSES = " kernel-fitimage "' + os.linesep
+        config += '# Avoid naming clashes in the deploy folder with kernel-fitimage.bbclass artifacts' + os.linesep
+        config += 'KERNEL_DEPLOYSUBDIR = "linux-yocto-fitimage"' + os.linesep
         return config
 
     @property
     def kernel_recipe(self):
-        # virtual/kernel does not work with SRC_URI:append:pn-%s
-        return "linux-yocto"
+        return "linux-yocto-fitimage"
 
     def _config_add_uboot_env(self, config):
         """Generate an u-boot environment
@@ -738,7 +736,7 @@ class KernelFitImageBase(FitImageTestCase):
             self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." %
                              (num_signatures, a_comment))
 
-class KernelFitImageTests(KernelFitImageBase):
+class KernelFitImageRecipeTests(KernelFitImageBase):
     """Test cases for the kernel-fitimage bbclass"""
 
     def test_fit_image(self):
@@ -1022,16 +1020,18 @@ FIT_HASH_ALG = "sha256"
         self._gen_signing_key(bb_vars)
         self._test_fitimage(bb_vars)
 
-class KernelFitImageRecipeTests(KernelFitImageTests):
-    """Test cases for the linux-yocto-fitimage recipe"""
+class KernelFitImageTests(KernelFitImageRecipeTests):
+    """Test cases for the kernel-fitimage.bbclass"""
 
     @property
     def kernel_recipe(self):
-        return "linux-yocto-fitimage"
+        # virtual/kernel does not work with SRC_URI:append:pn-%s
+        return "linux-yocto"
 
     def _config_add_kernel_classes(self, config):
-        config += '# Avoid naming clashes in the deploy folder with kernel-fitimage.bbclass artifacts' + os.linesep
-        config += 'KERNEL_DEPLOYSUBDIR = "linux-yocto-fitimage"' + os.linesep
+        config += '# Use kernel-fitimage.bbclass for the creation of the fitImage' + os.linesep
+        config += 'KERNEL_IMAGETYPES += " fitImage "' + os.linesep
+        config += 'KERNEL_CLASSES = " kernel-fitimage "' + os.linesep
         return config
 
 class FitImagePyTests(KernelFitImageBase):
-- 
2.49.0



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

* [PATCH v6 18/21] kernel-fitimage: re-write its code in Python
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (16 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 17/21] oe-selftest: fitimage refactor classes AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 19/21] oe-selftest: fitimage: remove kernel-fitimage tests AdrianF
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Rewrite the kernel-fitimage.bbclass file in Python. This allows the
reuse of the new fitimage Python library and a clear alignment of the
two implementations.

Actually, the motivation for an implementation in Python was different.
During the transition from the kernel-fitimage.bbclass to the new
linux-yocto-fitimage.bb, the existing shell code was rewritten in Python
so that the fitimage.py library could be extracted. The new
kernel-fit-image.bbclass and linux-yocto-fitimage.bb were then developed
on this basis.
This approach makes it possible to run the same tests for all different
implementations:
- kernel-fitimage.bbclass in Shell
- kernel-fitimage.bbclass in Python
- linux-yocto-fitimage.bb

Changing the commit order now enables a smooth transition. The two
implementations can coexist. Maintenance and testing should be feasible
for a few months with reasonable effort as they share most of the code.
But of course, the goal is to remove the kernel-fitimage.bbclass as soon
as possible.

This commit opens the path for different strategies going forward:
- Just replace the old implementations with the new one and ignoring
  this commit.
- Add the new implementation and keep the old implementation without any
  change.
- Add the new implementation and this commit and support the old
  architecture sharing most of the code with the new architecture and
  implementatiion.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel-fitimage.bbclass | 864 ++++----------------
 meta/classes-recipe/kernel-uboot.bbclass    |  45 +
 2 files changed, 224 insertions(+), 685 deletions(-)

diff --git a/meta/classes-recipe/kernel-fitimage.bbclass b/meta/classes-recipe/kernel-fitimage.bbclass
index 8e66f326766..a253b210ef5 100644
--- a/meta/classes-recipe/kernel-fitimage.bbclass
+++ b/meta/classes-recipe/kernel-fitimage.bbclass
@@ -56,680 +56,174 @@ python __anonymous () {
         d.setVar('EXTERNAL_KERNEL_DEVICETREE', "${RECIPE_SYSROOT}/boot/devicetree")
 }
 
-#
-# Emit the fitImage ITS header
-#
-# $1 ... .its filename
-fitimage_emit_fit_header() {
-	cat << EOF >> $1
-/dts-v1/;
-
-/ {
-        description = "${FIT_DESC}";
-        #address-cells = <${FIT_ADDRESS_CELLS}>;
-EOF
-}
-
-#
-# Emit the fitImage section bits
-#
-# $1 ... .its filename
-# $2 ... Section bit type: imagestart - image section start
-#                          confstart  - configuration section start
-#                          sectend    - section end
-#                          fitend     - fitimage end
-#
-fitimage_emit_section_maint() {
-	case $2 in
-	imagestart)
-		cat << EOF >> $1
-
-        images {
-EOF
-	;;
-	confstart)
-		cat << EOF >> $1
-
-        configurations {
-EOF
-	;;
-	sectend)
-		cat << EOF >> $1
-	};
-EOF
-	;;
-	fitend)
-		cat << EOF >> $1
-};
-EOF
-	;;
-	esac
-}
-
-#
-# Emit the fitImage ITS kernel section
-#
-# $1 ... .its filename
-# $2 ... Image counter
-# $3 ... Path to kernel image
-# $4 ... Compression type
-fitimage_emit_section_kernel() {
-
-	kernel_csum="${FIT_HASH_ALG}"
-	kernel_sign_algo="${FIT_SIGN_ALG}"
-	kernel_sign_keyname="${UBOOT_SIGN_IMG_KEYNAME}"
-
-	ENTRYPOINT="${UBOOT_ENTRYPOINT}"
-	if [ -n "${UBOOT_ENTRYSYMBOL}" ]; then
-		ENTRYPOINT=`${HOST_PREFIX}nm vmlinux | \
-			awk '$3=="${UBOOT_ENTRYSYMBOL}" {print "0x"$1;exit}'`
-	fi
-
-	cat << EOF >> $1
-                kernel-$2 {
-                        description = "Linux kernel";
-                        type = "${UBOOT_MKIMAGE_KERNEL_TYPE}";
-                        compression = "$4";
-                        data = /incbin/("$3");
-                        arch = "${UBOOT_ARCH}";
-                        os = "linux";
-                        load = <${UBOOT_LOADADDRESS}>;
-                        entry = <$ENTRYPOINT>;
-                        hash-1 {
-                                algo = "$kernel_csum";
-                        };
-                };
-EOF
-
-	if [ "${UBOOT_SIGN_ENABLE}" = "1" -a "${FIT_SIGN_INDIVIDUAL}" = "1" -a -n "$kernel_sign_keyname" ] ; then
-		sed -i '$ d' $1
-		cat << EOF >> $1
-                        signature-1 {
-                                algo = "$kernel_csum,$kernel_sign_algo";
-                                key-name-hint = "$kernel_sign_keyname";
-                        };
-                };
-EOF
-	fi
-}
-
-#
-# Emit the fitImage ITS DTB section
-#
-# $1 ... .its filename
-# $2 ... Image counter
-# $3 ... Path to DTB image
-fitimage_emit_section_dtb() {
-
-	dtb_csum="${FIT_HASH_ALG}"
-	dtb_sign_algo="${FIT_SIGN_ALG}"
-	dtb_sign_keyname="${UBOOT_SIGN_IMG_KEYNAME}"
-
-	dtb_loadline=""
-	dtb_ext=${DTB##*.}
-	if [ "${dtb_ext}" = "dtbo" ]; then
-		if [ -n "${UBOOT_DTBO_LOADADDRESS}" ]; then
-			dtb_loadline="load = <${UBOOT_DTBO_LOADADDRESS}>;"
-		fi
-	elif [ -n "${UBOOT_DTB_LOADADDRESS}" ]; then
-		dtb_loadline="load = <${UBOOT_DTB_LOADADDRESS}>;"
-	fi
-	cat << EOF >> $1
-                fdt-$2 {
-                        description = "Flattened Device Tree blob";
-                        type = "flat_dt";
-                        compression = "none";
-                        data = /incbin/("$3");
-                        arch = "${UBOOT_ARCH}";
-                        $dtb_loadline
-                        hash-1 {
-                                algo = "$dtb_csum";
-                        };
-                };
-EOF
-
-	if [ "${UBOOT_SIGN_ENABLE}" = "1" -a "${FIT_SIGN_INDIVIDUAL}" = "1" -a -n "$dtb_sign_keyname" ] ; then
-		sed -i '$ d' $1
-		cat << EOF >> $1
-                        signature-1 {
-                                algo = "$dtb_csum,$dtb_sign_algo";
-                                key-name-hint = "$dtb_sign_keyname";
-                        };
-                };
-EOF
-	fi
-}
-
-#
-# Emit the fitImage ITS u-boot script section
-#
-# $1 ... .its filename
-# $2 ... Image counter
-# $3 ... Path to boot script image
-fitimage_emit_section_boot_script() {
-
-	bootscr_csum="${FIT_HASH_ALG}"
-	bootscr_sign_algo="${FIT_SIGN_ALG}"
-	bootscr_sign_keyname="${UBOOT_SIGN_IMG_KEYNAME}"
-
-        cat << EOF >> $1
-                bootscr-$2 {
-                        description = "U-boot script";
-                        type = "script";
-                        compression = "none";
-                        data = /incbin/("$3");
-                        arch = "${UBOOT_ARCH}";
-                        hash-1 {
-                                algo = "$bootscr_csum";
-                        };
-                };
-EOF
-
-	if [ "${UBOOT_SIGN_ENABLE}" = "1" -a "${FIT_SIGN_INDIVIDUAL}" = "1" -a -n "$bootscr_sign_keyname" ] ; then
-		sed -i '$ d' $1
-		cat << EOF >> $1
-                        signature-1 {
-                                algo = "$bootscr_csum,$bootscr_sign_algo";
-                                key-name-hint = "$bootscr_sign_keyname";
-                        };
-                };
-EOF
-	fi
-}
-
-#
-# Emit the fitImage ITS setup section
-#
-# $1 ... .its filename
-# $2 ... Image counter
-# $3 ... Path to setup image
-fitimage_emit_section_setup() {
-
-	setup_csum="${FIT_HASH_ALG}"
-	setup_sign_algo="${FIT_SIGN_ALG}"
-	setup_sign_keyname="${UBOOT_SIGN_IMG_KEYNAME}"
-
-	cat << EOF >> $1
-                setup-$2 {
-                        description = "Linux setup.bin";
-                        type = "x86_setup";
-                        compression = "none";
-                        data = /incbin/("$3");
-                        arch = "${UBOOT_ARCH}";
-                        os = "linux";
-                        load = <0x00090000>;
-                        entry = <0x00090000>;
-                        hash-1 {
-                                algo = "$setup_csum";
-                        };
-                };
-EOF
-
-	if [ "${UBOOT_SIGN_ENABLE}" = "1" -a "${FIT_SIGN_INDIVIDUAL}" = "1" -a -n "$setup_sign_keyname" ] ; then
-		sed -i '$ d' $1
-		cat << EOF >> $1
-                        signature-1 {
-                                algo = "$setup_csum,$setup_sign_algo";
-                                key-name-hint = "$setup_sign_keyname";
-                        };
-                };
-EOF
-	fi
-}
-
-#
-# Emit the fitImage ITS ramdisk section
-#
-# $1 ... .its filename
-# $2 ... Image counter
-# $3 ... Path to ramdisk image
-fitimage_emit_section_ramdisk() {
-
-	ramdisk_csum="${FIT_HASH_ALG}"
-	ramdisk_sign_algo="${FIT_SIGN_ALG}"
-	ramdisk_sign_keyname="${UBOOT_SIGN_IMG_KEYNAME}"
-	ramdisk_loadline=""
-	ramdisk_entryline=""
-
-	if [ -n "${UBOOT_RD_LOADADDRESS}" ]; then
-		ramdisk_loadline="load = <${UBOOT_RD_LOADADDRESS}>;"
-	fi
-	if [ -n "${UBOOT_RD_ENTRYPOINT}" ]; then
-		ramdisk_entryline="entry = <${UBOOT_RD_ENTRYPOINT}>;"
-	fi
-
-	cat << EOF >> $1
-                ramdisk-$2 {
-                        description = "${INITRAMFS_IMAGE}";
-                        type = "ramdisk";
-                        compression = "none";
-                        data = /incbin/("$3");
-                        arch = "${UBOOT_ARCH}";
-                        os = "linux";
-                        $ramdisk_loadline
-                        $ramdisk_entryline
-                        hash-1 {
-                                algo = "$ramdisk_csum";
-                        };
-                };
-EOF
-
-	if [ "${UBOOT_SIGN_ENABLE}" = "1" -a "${FIT_SIGN_INDIVIDUAL}" = "1" -a -n "$ramdisk_sign_keyname" ] ; then
-		sed -i '$ d' $1
-		cat << EOF >> $1
-                        signature-1 {
-                                algo = "$ramdisk_csum,$ramdisk_sign_algo";
-                                key-name-hint = "$ramdisk_sign_keyname";
-                        };
-                };
-EOF
-	fi
-}
-
-#
-# echoes symlink destination if it points below directory
-#
-# $1 ... file that's a potential symlink
-# $2 ... expected parent directory
-symlink_points_below() {
-	file="$2/$1"
-	dir=$2
-
-	if ! [ -L "$file" ]; then
-		return
-	fi
-
-	realpath="$(realpath --relative-to=$dir $file)"
-	if [ -z "${realpath%%../*}" ]; then
-		return
-	fi
-
-	echo "$realpath"
-}
-
-#
-# Emit the fitImage ITS configuration section
-#
-# $1 ... .its filename
-# $2 ... Linux kernel ID
-# $3 ... DTB image name
-# $4 ... ramdisk ID
-# $5 ... u-boot script ID
-# $6 ... config ID
-# $7 ... default flag
-# $8 ... default DTB image name
-fitimage_emit_section_config() {
-
-	conf_csum="${FIT_HASH_ALG}"
-	conf_sign_algo="${FIT_SIGN_ALG}"
-	conf_padding_algo="${FIT_PAD_ALG}"
-	if [ "${UBOOT_SIGN_ENABLE}" = "1" ] ; then
-		conf_sign_keyname="${UBOOT_SIGN_KEYNAME}"
-	fi
-
-	its_file="$1"
-	kernel_id="$2"
-	dtb_image="$3"
-	ramdisk_id="$4"
-	bootscr_id="$5"
-	config_id="$6"
-	default_flag="$7"
-	default_dtb_image="$8"
-
-	# Test if we have any DTBs at all
-	sep=""
-	conf_desc=""
-	conf_node="${FIT_CONF_PREFIX}"
-	kernel_line=""
-	fdt_line=""
-	ramdisk_line=""
-	bootscr_line=""
-	setup_line=""
-	default_line=""
-	compatible_line=""
-
-	dtb_image_sect=$(symlink_points_below $dtb_image "${EXTERNAL_KERNEL_DEVICETREE}")
-	if [ -z "$dtb_image_sect" ]; then
-		dtb_image_sect=$dtb_image
-	fi
-
-	dtb_path="${EXTERNAL_KERNEL_DEVICETREE}/${dtb_image_sect}"
-	if [ -f "$dtb_path" ] || [ -L "$dtb_path" ]; then
-		compat=$(fdtget -t s "$dtb_path" / compatible | sed 's/ /", "/g')
-		if [ -n "$compat" ]; then
-			compatible_line="compatible = \"$compat\";"
-		fi
-	fi
-
-	dtb_image=$(echo $dtb_image | tr '/' '_')
-	dtb_image_sect=$(echo "${dtb_image_sect}" | tr '/' '_')
-
-	# conf node name is selected based on dtb ID if it is present,
-	# otherwise its selected based on kernel ID
-	if [ -n "$dtb_image" ]; then
-		conf_node=$conf_node$dtb_image
-	else
-		conf_node=$conf_node$kernel_id
-	fi
-
-	if [ -n "$kernel_id" ]; then
-		conf_desc="Linux kernel"
-		sep=", "
-		kernel_line="kernel = \"kernel-$kernel_id\";"
-	fi
-
-	if [ -n "$dtb_image" ]; then
-		conf_desc="$conf_desc${sep}FDT blob"
-		sep=", "
-		fdt_line="fdt = \"fdt-$dtb_image_sect\";"
-	fi
-
-	if [ -n "$ramdisk_id" ]; then
-		conf_desc="$conf_desc${sep}ramdisk"
-		sep=", "
-		ramdisk_line="ramdisk = \"ramdisk-$ramdisk_id\";"
-	fi
-
-	if [ -n "$bootscr_id" ]; then
-		conf_desc="$conf_desc${sep}u-boot script"
-		sep=", "
-		bootscr_line="bootscr = \"bootscr-$bootscr_id\";"
-	fi
-
-	if [ -n "$config_id" ]; then
-		conf_desc="$conf_desc${sep}setup"
-		setup_line="setup = \"setup-$config_id\";"
-	fi
-
-	if [ "$default_flag" = "1" ]; then
-		# default node is selected based on dtb ID if it is present,
-		# otherwise its selected based on kernel ID
-		if [ -n "$dtb_image" ]; then
-			# Select default node as user specified dtb when
-			# multiple dtb exists.
-			if [ -n "$default_dtb_image" ]; then
-				default_line="default = \"${FIT_CONF_PREFIX}$default_dtb_image\";"
-			else
-				default_line="default = \"${FIT_CONF_PREFIX}$dtb_image\";"
-			fi
-		else
-			default_line="default = \"${FIT_CONF_PREFIX}$kernel_id\";"
-		fi
-	fi
-
-	cat << EOF >> $its_file
-                $default_line
-                $conf_node {
-                        description = "$default_flag $conf_desc";
-                        $compatible_line
-                        $kernel_line
-                        $fdt_line
-                        $ramdisk_line
-                        $bootscr_line
-                        $setup_line
-                        hash-1 {
-                                algo = "$conf_csum";
-                        };
-EOF
-
-	if [ -n "$conf_sign_keyname" ] ; then
-
-		sign_line="sign-images = "
-		sep=""
-
-		if [ -n "$kernel_id" ]; then
-			sign_line="$sign_line${sep}\"kernel\""
-			sep=", "
-		fi
-
-		if [ -n "$dtb_image" ]; then
-			sign_line="$sign_line${sep}\"fdt\""
-			sep=", "
-		fi
-
-		if [ -n "$ramdisk_id" ]; then
-			sign_line="$sign_line${sep}\"ramdisk\""
-			sep=", "
-		fi
-
-		if [ -n "$bootscr_id" ]; then
-			sign_line="$sign_line${sep}\"bootscr\""
-			sep=", "
-		fi
-
-		if [ -n "$config_id" ]; then
-			sign_line="$sign_line${sep}\"setup\""
-		fi
-
-		sign_line="$sign_line;"
-
-		cat << EOF >> $its_file
-                        signature-1 {
-                                algo = "$conf_csum,$conf_sign_algo";
-                                key-name-hint = "$conf_sign_keyname";
-                                padding = "$conf_padding_algo";
-                                $sign_line
-                        };
-EOF
-	fi
-
-	cat << EOF >> $its_file
-                };
-EOF
-}
-
-#
-# Assemble fitImage
-#
-# $1 ... .its filename
-# $2 ... fitImage name
-# $3 ... include ramdisk
-fitimage_assemble() {
-	kernelcount=1
-	dtbcount=""
-	DTBS=""
-	ramdiskcount=$3
-	setupcount=""
-	bootscr_id=""
-	default_dtb_image=""
-	rm -f $1 arch/${ARCH}/boot/$2
-
-	if [ -n "${UBOOT_SIGN_IMG_KEYNAME}" -a "${UBOOT_SIGN_KEYNAME}" = "${UBOOT_SIGN_IMG_KEYNAME}" ]; then
-		bbfatal "Keys used to sign images and configuration nodes must be different."
-	fi
-
-	fitimage_emit_fit_header $1
-
-	#
-	# Step 1: Prepare a kernel image section.
-	#
-	fitimage_emit_section_maint $1 imagestart
-
-	uboot_prep_kimage
-	fitimage_emit_section_kernel $1 $kernelcount linux.bin "$linux_comp"
-
-	#
-	# Step 2: Prepare a DTB image section
-	#
-
-	if [ -n "${KERNEL_DEVICETREE}" ]; then
-		dtbcount=1
-		for DTB in ${KERNEL_DEVICETREE}; do
-			if echo $DTB | grep -q '/dts/'; then
-				bbwarn "$DTB contains the full path to the the dts file, but only the dtb name should be used."
-				DTB=`basename $DTB | sed 's,\.dts$,.dtb,g'`
-			fi
-
-			# Skip ${DTB} if it's also provided in ${EXTERNAL_KERNEL_DEVICETREE}
-			if [ -n "${EXTERNAL_KERNEL_DEVICETREE}" ] && [ -s ${EXTERNAL_KERNEL_DEVICETREE}/${DTB} ]; then
-				continue
-			fi
-
-			DTB_PATH="${KERNEL_OUTPUT_DIR}/dts/$DTB"
-			if [ ! -e "$DTB_PATH" ]; then
-				DTB_PATH="${KERNEL_OUTPUT_DIR}/$DTB"
-			fi
-
-			# Strip off the path component from the filename
-			if "${@'false' if oe.types.boolean(d.getVar('KERNEL_DTBVENDORED')) else 'true'}"; then
-				DTB=`basename $DTB`
-			fi
-
-			# Set the default dtb image if it exists in the devicetree.
-			if [ "${FIT_CONF_DEFAULT_DTB}" = "$DTB" ];then
-				default_dtb_image=$(echo "$DTB" | tr '/' '_')
-			fi
-
-			DTB=$(echo "$DTB" | tr '/' '_')
-
-			# Skip DTB if we've picked it up previously
-			echo "$DTBS" | tr ' ' '\n' | grep -xq "$DTB" && continue
-
-			DTBS="$DTBS $DTB"
-			DTB=$(echo $DTB | tr '/' '_')
-			fitimage_emit_section_dtb $1 $DTB $DTB_PATH
-		done
-	fi
-
-	if [ -n "${EXTERNAL_KERNEL_DEVICETREE}" ]; then
-		dtbcount=1
-		for DTB in $(find "${EXTERNAL_KERNEL_DEVICETREE}" -name '*.dtb' -printf '%P\n' | sort) \
-		$(find "${EXTERNAL_KERNEL_DEVICETREE}" -name '*.dtbo' -printf '%P\n' | sort); do
-			# Set the default dtb image if it exists in the devicetree.
-			if [ ${FIT_CONF_DEFAULT_DTB} = $DTB ];then
-				default_dtb_image=$(echo "$DTB" | tr '/' '_')
-			fi
-
-			DTB=$(echo "$DTB" | tr '/' '_')
-
-			# Skip DTB/DTBO if we've picked it up previously
-			echo "$DTBS" | tr ' ' '\n' | grep -xq "$DTB" && continue
-
-			DTBS="$DTBS $DTB"
-
-			# Also skip if a symlink. We'll later have each config section point at it
-			[ $(symlink_points_below $DTB "${EXTERNAL_KERNEL_DEVICETREE}") ] && continue
-
-			DTB=$(echo $DTB | tr '/' '_')
-			fitimage_emit_section_dtb $1 $DTB "${EXTERNAL_KERNEL_DEVICETREE}/$DTB"
-		done
-	fi
-
-	if [ -n "${FIT_CONF_DEFAULT_DTB}" ] && [ -z $default_dtb_image ]; then 
-		bbwarn "${FIT_CONF_DEFAULT_DTB} is not available in the list of device trees."
-	fi
-
-	#
-	# Step 3: Prepare a u-boot script section
-	#
-
-	if [ -n "${FIT_UBOOT_ENV}" ]; then
-		cp ${UNPACKDIR}/${FIT_UBOOT_ENV} ${B}
-		bootscr_id="${FIT_UBOOT_ENV}"
-		fitimage_emit_section_boot_script $1 "$bootscr_id" ${FIT_UBOOT_ENV}
-	fi
-
-	#
-	# Step 4: Prepare a setup section. (For x86)
-	#
-	if [ -e ${KERNEL_OUTPUT_DIR}/setup.bin ]; then
-		setupcount=1
-		fitimage_emit_section_setup $1 $setupcount ${KERNEL_OUTPUT_DIR}/setup.bin
-	fi
-
-	#
-	# Step 5: Prepare a ramdisk section.
-	#
-	if [ "x${ramdiskcount}" = "x1" ] && [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
-		# Find and use the first initramfs image archive type we find
-		found=
-		for img in ${FIT_SUPPORTED_INITRAMFS_FSTYPES}; do
-			initramfs_path="${INITRAMFS_DEPLOY_DIR_IMAGE}/${INITRAMFS_IMAGE_NAME}.$img"
-			if [ -e "$initramfs_path" ]; then
-				bbnote "Found initramfs image: $initramfs_path"
-				found=true
-				fitimage_emit_section_ramdisk $1 "$ramdiskcount" "$initramfs_path"
-				break
-			else
-				bbnote "Did not find initramfs image: $initramfs_path"
-			fi
-		done
-
-		if [ -z "$found" ]; then
-			bbfatal "Could not find a valid initramfs type for ${INITRAMFS_IMAGE_NAME}, the supported types are: ${FIT_SUPPORTED_INITRAMFS_FSTYPES}"
-		fi
-	fi
-
-	fitimage_emit_section_maint $1 sectend
-
-	# Force the first Kernel and DTB in the default config
-	kernelcount=1
-	if [ -n "$dtbcount" ]; then
-		dtbcount=1
-	fi
-
-	#
-	# Step 6: Prepare a configurations section
-	#
-	fitimage_emit_section_maint $1 confstart
-
-	# kernel-fitimage.bbclass currently only supports a single kernel (no less or
-	# more) to be added to the FIT image along with 0 or more device trees and
-	# 0 or 1 ramdisk.
-	# It is also possible to include an initramfs bundle (kernel and rootfs in one binary)
-	# When the initramfs bundle is used ramdisk is disabled.
-	# If a device tree is to be part of the FIT image, then select
-	# the default configuration to be used is based on the dtbcount. If there is
-	# no dtb present than select the default configuation to be based on
-	# the kernelcount.
-	if [ -n "$DTBS" ]; then
-		i=1
-		for DTB in ${DTBS}; do
-			dtb_ext=${DTB##*.}
-			if [ "$dtb_ext" = "dtbo" ]; then
-				fitimage_emit_section_config $1 "" "$DTB" "" "$bootscr_id" "" "`expr $i = $dtbcount`" "$default_dtb_image"
-			else
-				fitimage_emit_section_config $1 $kernelcount "$DTB" "$ramdiskcount" "$bootscr_id" "$setupcount" "`expr $i = $dtbcount`" "$default_dtb_image"
-			fi
-			i=`expr $i + 1`
-		done
-	else
-		defaultconfigcount=1
-		fitimage_emit_section_config $1 $kernelcount "" "$ramdiskcount" "$bootscr_id"  "$setupcount" $defaultconfigcount "$default_dtb_image"
-	fi
-
-	fitimage_emit_section_maint $1 sectend
-
-	fitimage_emit_section_maint $1 fitend
-
-	#
-	# Step 7: Assemble the image
-	#
-	${UBOOT_MKIMAGE} \
-		${@'-D "${UBOOT_MKIMAGE_DTCOPTS}"' if len('${UBOOT_MKIMAGE_DTCOPTS}') else ''} \
-		-f $1 \
-		${KERNEL_OUTPUT_DIR}/$2
-
-	#
-	# Step 8: Sign the image
-	#
-	if [ "x${UBOOT_SIGN_ENABLE}" = "x1" ] ; then
-		${UBOOT_MKIMAGE_SIGN} \
-			${@'-D "${UBOOT_MKIMAGE_DTCOPTS}"' if len('${UBOOT_MKIMAGE_DTCOPTS}') else ''} \
-			-F -k "${UBOOT_SIGN_KEYDIR}" \
-			-r ${KERNEL_OUTPUT_DIR}/$2 \
-			${UBOOT_MKIMAGE_SIGN_ARGS}
-	fi
-}
-
-do_assemble_fitimage() {
-	if echo ${KERNEL_IMAGETYPES} | grep -wq "fitImage"; then
-		cd ${B}
-		fitimage_assemble fit-image.its fitImage-none ""
-		if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
-			ln -sf fitImage-none ${B}/${KERNEL_OUTPUT_DIR}/fitImage
-		fi
-	fi
+def fitimage_assemble(d, itsfile, fitname, ramdiskcount):
+    import shutil
+    import glob
+    import oe.fitimage
+
+    DTBS=""
+    default_dtb_image=""
+
+    for f in [itsfile, os.path.join("arch", d.getVar("ARCH"), "boot", fitname)]:
+        if os.path.exists(f):
+            os.remove(f)
+
+    root_node = oe.fitimage.ItsNodeRootKernel(
+        d.getVar("FIT_DESC"), d.getVar("FIT_ADDRESS_CELLS"),
+        d.getVar('HOST_PREFIX'), d.getVar('UBOOT_ARCH'),  d.getVar("FIT_CONF_PREFIX"),
+        d.getVar('UBOOT_SIGN_ENABLE') == "1", d.getVar("UBOOT_SIGN_KEYDIR"),
+        d.getVar("UBOOT_MKIMAGE"), d.getVar("UBOOT_MKIMAGE_DTCOPTS"),
+        d.getVar("UBOOT_MKIMAGE_SIGN"), d.getVar("UBOOT_MKIMAGE_SIGN_ARGS"),
+        d.getVar('FIT_HASH_ALG'), d.getVar('FIT_SIGN_ALG'), d.getVar('FIT_PAD_ALG'),
+        d.getVar('UBOOT_SIGN_KEYNAME'),
+        d.getVar('FIT_SIGN_INDIVIDUAL') == "1", d.getVar('UBOOT_SIGN_IMG_KEYNAME')
+    )
+
+    #
+    # Step 1: Prepare a kernel image section.
+    #
+    linux_comp = uboot_prep_kimage_py(d)
+    root_node.fitimage_emit_section_kernel("kernel-1", "linux.bin", linux_comp,
+        d.getVar('UBOOT_LOADADDRESS'), d.getVar('UBOOT_ENTRYPOINT'),
+        d.getVar('UBOOT_MKIMAGE_KERNEL_TYPE'), d.getVar("UBOOT_ENTRYSYMBOL"))
+
+    #
+    # Step 2: Prepare a DTB image section
+    #
+    kernel_devicetree = d.getVar('KERNEL_DEVICETREE')
+    external_kernel_devicetree = d.getVar("EXTERNAL_KERNEL_DEVICETREE")
+    if kernel_devicetree:
+        for DTB in kernel_devicetree.split():
+            if "/dts/" in DTB:
+                bb.warn(f"{DTB} contains the full path to the dts file, but only the dtb name should be used.")
+                DTB = os.path.basename(DTB).replace(".dts", ".dtb")
+
+            # Skip DTB if it's also provided in EXTERNAL_KERNEL_DEVICETREE
+            if external_kernel_devicetree:
+                ext_dtb_path = os.path.join(external_kernel_devicetree, DTB)
+                if os.path.exists(ext_dtb_path) and os.path.getsize(ext_dtb_path) > 0:
+                    continue
+
+            DTB_PATH = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), "dts", DTB)
+            if not os.path.exists(DTB_PATH):
+                DTB_PATH = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), DTB)
+
+            # Strip off the path component from the filename
+            if not oe.types.boolean(d.getVar("KERNEL_DTBVENDORED")):
+                DTB = os.path.basename(DTB)
+
+            # Set the default dtb image if it exists in the devicetree.
+            if d.getVar("FIT_CONF_DEFAULT_DTB") == DTB:
+                default_dtb_image = DTB.replace("/", "_")
+
+            DTB = DTB.replace("/", "_")
+
+            # Skip DTB if we've picked it up previously
+            if DTB in DTBS.split():
+                continue
+
+            DTBS += " " + DTB
+
+            root_node.fitimage_emit_section_dtb(DTB, DTB_PATH,
+                d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"))
+
+    if external_kernel_devicetree:
+        dtb_files = []
+        for ext in ['*.dtb', '*.dtbo']:
+            dtb_files.extend(sorted(glob.glob(os.path.join(external_kernel_devicetree, ext))))
+
+        for dtb_path in dtb_files:
+            dtb_name = os.path.relpath(dtb_path, external_kernel_devicetree)
+            dtb_name_underscore = dtb_name.replace('/', '_')
+
+            # Set the default dtb image if it exists in the devicetree.
+            if d.getVar("FIT_CONF_DEFAULT_DTB") == dtb_name:
+                default_dtb_image = dtb_name_underscore
+
+            # Skip DTB/DTBO if we've picked it up previously
+            if dtb_name_underscore in DTBS.split():
+                continue
+
+            DTBS += " " + dtb_name_underscore
+
+            # For symlinks, add a configuration node that refers to the DTB image node to which the symlink points
+            symlink_target = oe.fitimage.symlink_points_below(dtb_name, external_kernel_devicetree)
+            if symlink_target:
+                root_node.fitimage_emit_section_dtb_alias(dtb_name, symlink_target, True)
+            # For real DTB files add an image node and a configuration node
+            else:
+                root_node.fitimage_emit_section_dtb(dtb_name_underscore, dtb_path,
+                    d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"), True)
+
+    if d.getVar("FIT_CONF_DEFAULT_DTB") and not default_dtb_image:
+        bb.warn("%s is not available in the list of device trees." % d.getVar('FIT_CONF_DEFAULT_DTB'))
+
+    #
+    # Step 3: Prepare a u-boot script section
+    #
+    fit_uboot_env = d.getVar("FIT_UBOOT_ENV")
+    if fit_uboot_env:
+        unpack_dir = d.getVar("UNPACKDIR")
+        shutil.copy(os.path.join(unpack_dir, fit_uboot_env), fit_uboot_env)
+        root_node.fitimage_emit_section_boot_script("bootscr-"+fit_uboot_env , fit_uboot_env)
+
+    #
+    # Step 4: Prepare a setup section. (For x86)
+    #
+    setup_bin_path = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), "setup.bin")
+    if os.path.exists(setup_bin_path):
+        root_node.fitimage_emit_section_setup("setup-1", setup_bin_path)
+
+    #
+    # Step 5: Prepare a ramdisk section.
+    #
+    if ramdiskcount == 1 and d.getVar("INITRAMFS_IMAGE_BUNDLE") != "1":
+        # Find and use the first initramfs image archive type we find
+        found = False
+        for img in d.getVar("FIT_SUPPORTED_INITRAMFS_FSTYPES").split():
+            initramfs_path = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), "%s.%s" % (d.getVar('INITRAMFS_IMAGE_NAME'), img))
+            if os.path.exists(initramfs_path):
+                bb.note("Found initramfs image: " + initramfs_path)
+                found = True
+                root_node.fitimage_emit_section_ramdisk("ramdisk-%d" % ramdiskcount, initramfs_path,
+                    d.getVar('INITRAMFS_IMAGE'),
+                    d.getVar("UBOOT_RD_LOADADDRESS"),
+                    d.getVar("UBOOT_RD_ENTRYPOINT"))
+                break
+            else:
+                bb.note("Did not find initramfs image: " + initramfs_path)
+
+        if not found:
+            bb.fatal("Could not find a valid initramfs type for %s, the supported types are: %s" % (d.getVar('INITRAMFS_IMAGE_NAME'), d.getVar('FIT_SUPPORTED_INITRAMFS_FSTYPES')))
+
+    # Generate the configuration section
+    root_node.fitimage_emit_section_config(default_dtb_image)
+
+    #
+    # Write the ITS file
+    root_node.write_its_file(itsfile)
+
+    #
+    # Step 7: Assemble the image
+    #
+    fitfile = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), fitname)
+    root_node.run_mkimage_assemble(itsfile, fitfile)
+
+    #
+    # Step 8: Sign the image if required
+    #
+    root_node.run_mkimage_sign(fitfile)
+
+
+python do_assemble_fitimage() {
+    if "fitImage" in d.getVar("KERNEL_IMAGETYPES").split():
+        os.chdir(d.getVar("B"))
+        fitimage_assemble(d, "fit-image.its", "fitImage-none", "")
+        if d.getVar("INITRAMFS_IMAGE_BUNDLE") != "1":
+            link_name = os.path.join(d.getVar("B"), d.getVar("KERNEL_OUTPUT_DIR"), "fitImage")
+            if os.path.islink(link_name):
+                os.unlink(link_name)
+            os.symlink("fitImage-none", link_name)
 }
 
 addtask assemble_fitimage before do_install after do_compile
@@ -742,17 +236,17 @@ do_install:append() {
 	fi
 }
 
-do_assemble_fitimage_initramfs() {
-	if echo ${KERNEL_IMAGETYPES} | grep -wq "fitImage" && \
-		test -n "${INITRAMFS_IMAGE}" ; then
-		cd ${B}
-		if [ "${INITRAMFS_IMAGE_BUNDLE}" = "1" ]; then
-			fitimage_assemble fit-image-${INITRAMFS_IMAGE}.its fitImage-bundle ""
-			ln -sf fitImage-bundle ${B}/${KERNEL_OUTPUT_DIR}/fitImage
-		else
-			fitimage_assemble fit-image-${INITRAMFS_IMAGE}.its fitImage-${INITRAMFS_IMAGE} 1
-		fi
-	fi
+python do_assemble_fitimage_initramfs() {
+    if "fitImage" in d.getVar("KERNEL_IMAGETYPES").split() and d.getVar("INITRAMFS_IMAGE"):
+        os.chdir(d.getVar("B"))
+        if d.getVar("INITRAMFS_IMAGE_BUNDLE") == "1":
+            fitimage_assemble(d, "fit-image-%s.its" % d.getVar("INITRAMFS_IMAGE"), "fitImage-bundle", "")
+            link_name =  os.path.join(d.getVar("B"), d.getVar("KERNEL_OUTPUT_DIR"), "fitImage")
+            if os.path.islink(link_name):
+                os.unlink(link_name)
+            os.symlink("fitImage-bundle", link_name)
+        else:
+            fitimage_assemble(d, "fit-image-%s.its" % d.getVar("INITRAMFS_IMAGE"), "fitImage-%s" % d.getVar("INITRAMFS_IMAGE"), 1)
 }
 
 addtask assemble_fitimage_initramfs before do_deploy after do_bundle_initramfs
diff --git a/meta/classes-recipe/kernel-uboot.bbclass b/meta/classes-recipe/kernel-uboot.bbclass
index e26ba6f0aa9..678d851fd30 100644
--- a/meta/classes-recipe/kernel-uboot.bbclass
+++ b/meta/classes-recipe/kernel-uboot.bbclass
@@ -66,3 +66,48 @@ kernel_do_deploy:append() {
 		install -D "${B}/${KERNEL_OUTPUT_DIR}/setup.bin" "$deployDir/"
 	fi
 }
+
+def uboot_prep_kimage_py(d):
+    import subprocess
+
+    arch = d.getVar('ARCH')
+    initramfs_image_bundle = d.getVar('INITRAMFS_IMAGE_BUNDLE')
+    fit_kernel_comp_alg = d.getVar('FIT_KERNEL_COMP_ALG') or 'gzip'
+    fit_kernel_comp_alg_extension = d.getVar('FIT_KERNEL_COMP_ALG_EXTENSION') or '.gz'
+    kernel_objcopy = d.getVar('KERNEL_OBJCOPY')
+
+    vmlinux_path = ""
+    linux_suffix = ""
+    linux_comp = "none"
+
+    if os.path.exists(f'arch/{arch}/boot/compressed/vmlinux'):
+        vmlinux_path = f'arch/{arch}/boot/compressed/vmlinux'
+    elif os.path.exists(f'arch/{arch}/boot/vmlinuz.bin'):
+        if os.path.exists('linux.bin'):
+            os.remove('linux.bin')
+        os.link(f'arch/{arch}/boot/vmlinuz.bin', 'linux.bin')
+    else:
+        vmlinux_path = 'vmlinux'
+        # Use vmlinux.initramfs for linux.bin when INITRAMFS_IMAGE_BUNDLE set
+        # As per the implementation in kernel.bbclass.
+        # See do_bundle_initramfs function
+        if initramfs_image_bundle == '1' and os.path.exists('vmlinux.initramfs'):
+            vmlinux_path = 'vmlinux.initramfs'
+        linux_suffix = fit_kernel_comp_alg_extension
+        linux_comp = fit_kernel_comp_alg
+
+    if vmlinux_path:
+        subprocess.run([kernel_objcopy.strip(), '-O', 'binary', '-R', '.note', '-R', '.comment', '-S', os.path.abspath(vmlinux_path), 'linux.bin'], check=True)
+        # if ret.returncode != 0:
+        # bb.fatal(f"Error: stderr: {ret.stderr.decode('utf-8')}   stdout: {ret.stdout.decode('utf-8')}, vmlinux_path: {os.path.abspath(vmlinux_path)}, pwd: {os.getcwd()}, args: {ret.args}")
+
+    if linux_comp != "none":
+        if linux_comp == "gzip":
+            subprocess.run(['gzip', '-9', 'linux.bin'], check=True)
+        elif linux_comp == "lzo":
+            subprocess.run(['lzop', '-9', 'linux.bin'], check=True)
+        elif linux_comp == "lzma":
+            subprocess.run(['xz', '--format=lzma', '-f', '-6', 'linux.bin'], check=True)
+        os.rename(f'linux.bin{linux_suffix}', 'linux.bin')
+
+    return linux_comp
-- 
2.49.0



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

* [PATCH v6 19/21] oe-selftest: fitimage: remove kernel-fitimage tests
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (17 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 18/21] kernel-fitimage: re-write its code in Python AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02  7:56 ` [PATCH v6 20/21] kernel.bbclass: remove support for type fitImage AdrianF
  2025-06-02  7:56 ` [PATCH v6 21/21] kernel-fitimage.bbclass: remove it AdrianF
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

Remove the test cases for the kernel-fitimage.bbclass.

The _config_add_kernel_classes function is no longer needed since the
kernel-fitimage.bbclass is no longer handled.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/lib/oeqa/selftest/cases/fitimage.py | 25 ------------------------
 1 file changed, 25 deletions(-)

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index fc66f22f2d2..1c8a5d1124c 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -434,11 +434,6 @@ class KernelFitImageBase(FitImageTestCase):
         self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4))
         return bb_vars
 
-    def _config_add_kernel_classes(self, config):
-        config += '# Avoid naming clashes in the deploy folder with kernel-fitimage.bbclass artifacts' + os.linesep
-        config += 'KERNEL_DEPLOYSUBDIR = "linux-yocto-fitimage"' + os.linesep
-        return config
-
     @property
     def kernel_recipe(self):
         return "linux-yocto-fitimage"
@@ -767,7 +762,6 @@ UBOOT_ENTRYPOINT = "0x80080000"
 FIT_DESC = "A model description"
 FIT_CONF_PREFIX = "foo-"
 """
-        config = self._config_add_kernel_classes(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
         self._test_fitimage(bb_vars)
@@ -832,7 +826,6 @@ MACHINE = "beaglebone-yocto"
 # Add a devicetree overlay which does not need kernel sources
 PREFERRED_PROVIDER_virtual/dtb = "bbb-dtbs-as-ext"
 """
-        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
@@ -862,7 +855,6 @@ UBOOT_SIGN_KEYNAME = "dev"
 UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
 FIT_CONF_DEFAULT_DTB = "am335x-bonegreen.dtb"
 """
-        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
 
@@ -912,7 +904,6 @@ UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
 FIT_SIGN_INDIVIDUAL = "1"
 UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
 """
-        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
@@ -960,7 +951,6 @@ KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
 FIT_KERNEL_COMP_ALG = "none"
 FIT_HASH_ALG = "sha256"
 """
-        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
 
@@ -1013,27 +1003,12 @@ KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
 FIT_KERNEL_COMP_ALG = "none"
 FIT_HASH_ALG = "sha256"
 """
-        config = self._config_add_kernel_classes(config)
         config = self._config_add_uboot_env(config)
         self.write_config(config)
         bb_vars = self._fit_get_bb_vars()
         self._gen_signing_key(bb_vars)
         self._test_fitimage(bb_vars)
 
-class KernelFitImageTests(KernelFitImageRecipeTests):
-    """Test cases for the kernel-fitimage.bbclass"""
-
-    @property
-    def kernel_recipe(self):
-        # virtual/kernel does not work with SRC_URI:append:pn-%s
-        return "linux-yocto"
-
-    def _config_add_kernel_classes(self, config):
-        config += '# Use kernel-fitimage.bbclass for the creation of the fitImage' + os.linesep
-        config += 'KERNEL_IMAGETYPES += " fitImage "' + os.linesep
-        config += 'KERNEL_CLASSES = " kernel-fitimage "' + os.linesep
-        return config
-
 class FitImagePyTests(KernelFitImageBase):
     """Test cases for the fitimage.py module without calling bitbake"""
 
-- 
2.49.0



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

* [PATCH v6 20/21] kernel.bbclass: remove support for type fitImage
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (18 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 19/21] oe-selftest: fitimage: remove kernel-fitimage tests AdrianF
@ 2025-06-02  7:56 ` AdrianF
  2025-06-02 18:59   ` [OE-core] " Bruce Ashfield
  2025-06-02  7:56 ` [PATCH v6 21/21] kernel-fitimage.bbclass: remove it AdrianF
  20 siblings, 1 reply; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

kernel.bbclass is no longer involved in FIT image creation. Whether a
FIT image is built now depends entirely on whether the linux-yocto-fitimage
recipe (or any other recipe capable of producing a FIT image) is selected
for the build.
As a result, specifying the kernel image type "fitImage" in
KERNEL_IMAGETYPE or KERNEL_IMAGETYPES is no longer necessary and gets
removed.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel.bbclass | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass
index 36ce659762e..599996ceb0b 100644
--- a/meta/classes-recipe/kernel.bbclass
+++ b/meta/classes-recipe/kernel.bbclass
@@ -84,6 +84,10 @@ python __anonymous () {
         types = (alttype + ' ' + types).strip()
     d.setVar('KERNEL_IMAGETYPES', types)
 
+    # Since kernel-fitimage.bbclass got replaced by kernel-fit-image.bbclass
+    if "fitImage" in types:
+        bb.error("fitImage is no longer supported as a KERNEL_IMAGETYPE(S)")
+
     # KERNEL_IMAGETYPES may contain a mixture of image types supported directly
     # by the kernel build system and types which are created by post-processing
     # the output of the kernel build system (e.g. compressing vmlinux ->
@@ -477,17 +481,10 @@ kernel_do_install() {
 	install -d ${D}/${KERNEL_IMAGEDEST}
 
 	#
-	# When including an initramfs bundle inside a FIT image, the fitImage is created after the install task
-	# by do_assemble_fitimage_initramfs.
-	# This happens after the generation of the initramfs bundle (done by do_bundle_initramfs).
-	# So, at the level of the install task we should not try to install the fitImage. fitImage is still not
-	# generated yet.
-	# After the generation of the fitImage, the deploy task copies the fitImage from the build directory to
-	# the deploy folder.
+	# bundle_initramfs runs after do_install before do_deploy. do_deploy does what's needed therefore.
 	#
-
 	for imageType in ${KERNEL_IMAGETYPES} ; do
-		if [ $imageType != "fitImage" ] || [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ] ; then
+		if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ] ; then
 			install -m 0644 ${KERNEL_OUTPUT_DIR}/$imageType ${D}/${KERNEL_IMAGEDEST}/$imageType-${KERNEL_VERSION}
 		fi
 	done
@@ -843,11 +840,8 @@ kernel_do_deploy() {
 		fi
 	fi
 
-	if [ ! -z "${INITRAMFS_IMAGE}" -a x"${INITRAMFS_IMAGE_BUNDLE}" = x1 ]; then
+	if [ -n "${INITRAMFS_IMAGE}" ] && [ "${INITRAMFS_IMAGE_BUNDLE}" = "1" ]; then
 		for imageType in ${KERNEL_IMAGETYPES} ; do
-			if [ "$imageType" = "fitImage" ] ; then
-				continue
-			fi
 			initramfsBaseName=$imageType-${INITRAMFS_NAME}
 			install -m 0644 ${KERNEL_OUTPUT_DIR}/$imageType.initramfs $deployDir/$initramfsBaseName${KERNEL_IMAGE_BIN_EXT}
 			if [ -n "${INITRAMFS_LINK_NAME}" ] ; then
-- 
2.49.0



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

* [PATCH v6 21/21] kernel-fitimage.bbclass: remove it
  2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
                   ` (19 preceding siblings ...)
  2025-06-02  7:56 ` [PATCH v6 20/21] kernel.bbclass: remove support for type fitImage AdrianF
@ 2025-06-02  7:56 ` AdrianF
  20 siblings, 0 replies; 28+ messages in thread
From: AdrianF @ 2025-06-02  7:56 UTC (permalink / raw)
  To: openembedded-core; +Cc: marex, a.fatoum, Adrian Freihofer

From: Adrian Freihofer <adrian.freihofer@siemens.com>

The integration of the FIT image-related build steps into the kernel
recipe has proven to be not very good. The new implementation with
kernel-fit-image.bbclass fixes some design issues:

* sstate does not work well when a fitImage contains an initramfs. The
  kernel is rebuilt from scratch if the build runs from an empty TMPDIR.
* A fitImage kernel is not available as a package, but all other kernel
  image types are.
* The task dependencies in the kernel are very complex and difficult to
  debug if something goes wrong. As a separate, downstream recipe, this
  is now much easier.

The long storry about this issue is here:
[YOCTO #12912]

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta/classes-recipe/kernel-fitimage.bbclass | 289 --------------------
 meta/classes-recipe/kernel-uboot.bbclass    |  45 ---
 2 files changed, 334 deletions(-)
 delete mode 100644 meta/classes-recipe/kernel-fitimage.bbclass

diff --git a/meta/classes-recipe/kernel-fitimage.bbclass b/meta/classes-recipe/kernel-fitimage.bbclass
deleted file mode 100644
index a253b210ef5..00000000000
--- a/meta/classes-recipe/kernel-fitimage.bbclass
+++ /dev/null
@@ -1,289 +0,0 @@
-#
-# Copyright OpenEmbedded Contributors
-#
-# SPDX-License-Identifier: MIT
-#
-
-inherit kernel-uboot kernel-artifact-names uboot-config
-require conf/image-fitimage.conf
-
-def get_fit_replacement_type(d):
-    kerneltypes = d.getVar('KERNEL_IMAGETYPES') or ""
-    replacementtype = ""
-    if 'fitImage' in kerneltypes.split():
-        uarch = d.getVar("UBOOT_ARCH")
-        if uarch == "arm64":
-            replacementtype = "Image"
-        elif uarch == "riscv":
-            replacementtype = "Image"
-        elif uarch == "mips":
-            replacementtype = "vmlinuz.bin"
-        elif uarch == "x86":
-            replacementtype = "bzImage"
-        elif uarch == "microblaze":
-            replacementtype = "linux.bin"
-        else:
-            replacementtype = "zImage"
-    return replacementtype
-
-KERNEL_IMAGETYPE_REPLACEMENT ?= "${@get_fit_replacement_type(d)}"
-DEPENDS:append = " \
-    ${@'u-boot-tools-native dtc-native' if 'fitImage' in (d.getVar('KERNEL_IMAGETYPES') or '').split() else ''} \
-    ${@'kernel-signing-keys-native' if d.getVar('FIT_GENERATE_KEYS') == '1' else ''} \
-"
-
-python __anonymous () {
-    # Override KERNEL_IMAGETYPE_FOR_MAKE variable, which is internal
-    # to kernel.bbclass . We have to override it, since we pack zImage
-    # (at least for now) into the fitImage .
-    typeformake = d.getVar("KERNEL_IMAGETYPE_FOR_MAKE") or ""
-    if 'fitImage' in typeformake.split():
-        d.setVar('KERNEL_IMAGETYPE_FOR_MAKE', typeformake.replace('fitImage', d.getVar('KERNEL_IMAGETYPE_REPLACEMENT')))
-
-    image = d.getVar('INITRAMFS_IMAGE')
-    if image and not bb.utils.to_boolean(d.getVar('INITRAMFS_IMAGE_BUNDLE')):
-        if d.getVar('INITRAMFS_MULTICONFIG'):
-            mc = d.getVar('BB_CURRENT_MC')
-            d.appendVarFlag('do_assemble_fitimage_initramfs', 'mcdepends', ' mc:' + mc + ':${INITRAMFS_MULTICONFIG}:${INITRAMFS_IMAGE}:do_image_complete')
-        else:
-            d.appendVarFlag('do_assemble_fitimage_initramfs', 'depends', ' ${INITRAMFS_IMAGE}:do_image_complete')
-
-    #check if there are any dtb providers
-    providerdtb = d.getVar("PREFERRED_PROVIDER_virtual/dtb")
-    if providerdtb:
-        d.appendVarFlag('do_assemble_fitimage', 'depends', ' virtual/dtb:do_populate_sysroot')
-        d.appendVarFlag('do_assemble_fitimage_initramfs', 'depends', ' virtual/dtb:do_populate_sysroot')
-        d.setVar('EXTERNAL_KERNEL_DEVICETREE', "${RECIPE_SYSROOT}/boot/devicetree")
-}
-
-def fitimage_assemble(d, itsfile, fitname, ramdiskcount):
-    import shutil
-    import glob
-    import oe.fitimage
-
-    DTBS=""
-    default_dtb_image=""
-
-    for f in [itsfile, os.path.join("arch", d.getVar("ARCH"), "boot", fitname)]:
-        if os.path.exists(f):
-            os.remove(f)
-
-    root_node = oe.fitimage.ItsNodeRootKernel(
-        d.getVar("FIT_DESC"), d.getVar("FIT_ADDRESS_CELLS"),
-        d.getVar('HOST_PREFIX'), d.getVar('UBOOT_ARCH'),  d.getVar("FIT_CONF_PREFIX"),
-        d.getVar('UBOOT_SIGN_ENABLE') == "1", d.getVar("UBOOT_SIGN_KEYDIR"),
-        d.getVar("UBOOT_MKIMAGE"), d.getVar("UBOOT_MKIMAGE_DTCOPTS"),
-        d.getVar("UBOOT_MKIMAGE_SIGN"), d.getVar("UBOOT_MKIMAGE_SIGN_ARGS"),
-        d.getVar('FIT_HASH_ALG'), d.getVar('FIT_SIGN_ALG'), d.getVar('FIT_PAD_ALG'),
-        d.getVar('UBOOT_SIGN_KEYNAME'),
-        d.getVar('FIT_SIGN_INDIVIDUAL') == "1", d.getVar('UBOOT_SIGN_IMG_KEYNAME')
-    )
-
-    #
-    # Step 1: Prepare a kernel image section.
-    #
-    linux_comp = uboot_prep_kimage_py(d)
-    root_node.fitimage_emit_section_kernel("kernel-1", "linux.bin", linux_comp,
-        d.getVar('UBOOT_LOADADDRESS'), d.getVar('UBOOT_ENTRYPOINT'),
-        d.getVar('UBOOT_MKIMAGE_KERNEL_TYPE'), d.getVar("UBOOT_ENTRYSYMBOL"))
-
-    #
-    # Step 2: Prepare a DTB image section
-    #
-    kernel_devicetree = d.getVar('KERNEL_DEVICETREE')
-    external_kernel_devicetree = d.getVar("EXTERNAL_KERNEL_DEVICETREE")
-    if kernel_devicetree:
-        for DTB in kernel_devicetree.split():
-            if "/dts/" in DTB:
-                bb.warn(f"{DTB} contains the full path to the dts file, but only the dtb name should be used.")
-                DTB = os.path.basename(DTB).replace(".dts", ".dtb")
-
-            # Skip DTB if it's also provided in EXTERNAL_KERNEL_DEVICETREE
-            if external_kernel_devicetree:
-                ext_dtb_path = os.path.join(external_kernel_devicetree, DTB)
-                if os.path.exists(ext_dtb_path) and os.path.getsize(ext_dtb_path) > 0:
-                    continue
-
-            DTB_PATH = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), "dts", DTB)
-            if not os.path.exists(DTB_PATH):
-                DTB_PATH = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), DTB)
-
-            # Strip off the path component from the filename
-            if not oe.types.boolean(d.getVar("KERNEL_DTBVENDORED")):
-                DTB = os.path.basename(DTB)
-
-            # Set the default dtb image if it exists in the devicetree.
-            if d.getVar("FIT_CONF_DEFAULT_DTB") == DTB:
-                default_dtb_image = DTB.replace("/", "_")
-
-            DTB = DTB.replace("/", "_")
-
-            # Skip DTB if we've picked it up previously
-            if DTB in DTBS.split():
-                continue
-
-            DTBS += " " + DTB
-
-            root_node.fitimage_emit_section_dtb(DTB, DTB_PATH,
-                d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"))
-
-    if external_kernel_devicetree:
-        dtb_files = []
-        for ext in ['*.dtb', '*.dtbo']:
-            dtb_files.extend(sorted(glob.glob(os.path.join(external_kernel_devicetree, ext))))
-
-        for dtb_path in dtb_files:
-            dtb_name = os.path.relpath(dtb_path, external_kernel_devicetree)
-            dtb_name_underscore = dtb_name.replace('/', '_')
-
-            # Set the default dtb image if it exists in the devicetree.
-            if d.getVar("FIT_CONF_DEFAULT_DTB") == dtb_name:
-                default_dtb_image = dtb_name_underscore
-
-            # Skip DTB/DTBO if we've picked it up previously
-            if dtb_name_underscore in DTBS.split():
-                continue
-
-            DTBS += " " + dtb_name_underscore
-
-            # For symlinks, add a configuration node that refers to the DTB image node to which the symlink points
-            symlink_target = oe.fitimage.symlink_points_below(dtb_name, external_kernel_devicetree)
-            if symlink_target:
-                root_node.fitimage_emit_section_dtb_alias(dtb_name, symlink_target, True)
-            # For real DTB files add an image node and a configuration node
-            else:
-                root_node.fitimage_emit_section_dtb(dtb_name_underscore, dtb_path,
-                    d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"), True)
-
-    if d.getVar("FIT_CONF_DEFAULT_DTB") and not default_dtb_image:
-        bb.warn("%s is not available in the list of device trees." % d.getVar('FIT_CONF_DEFAULT_DTB'))
-
-    #
-    # Step 3: Prepare a u-boot script section
-    #
-    fit_uboot_env = d.getVar("FIT_UBOOT_ENV")
-    if fit_uboot_env:
-        unpack_dir = d.getVar("UNPACKDIR")
-        shutil.copy(os.path.join(unpack_dir, fit_uboot_env), fit_uboot_env)
-        root_node.fitimage_emit_section_boot_script("bootscr-"+fit_uboot_env , fit_uboot_env)
-
-    #
-    # Step 4: Prepare a setup section. (For x86)
-    #
-    setup_bin_path = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), "setup.bin")
-    if os.path.exists(setup_bin_path):
-        root_node.fitimage_emit_section_setup("setup-1", setup_bin_path)
-
-    #
-    # Step 5: Prepare a ramdisk section.
-    #
-    if ramdiskcount == 1 and d.getVar("INITRAMFS_IMAGE_BUNDLE") != "1":
-        # Find and use the first initramfs image archive type we find
-        found = False
-        for img in d.getVar("FIT_SUPPORTED_INITRAMFS_FSTYPES").split():
-            initramfs_path = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), "%s.%s" % (d.getVar('INITRAMFS_IMAGE_NAME'), img))
-            if os.path.exists(initramfs_path):
-                bb.note("Found initramfs image: " + initramfs_path)
-                found = True
-                root_node.fitimage_emit_section_ramdisk("ramdisk-%d" % ramdiskcount, initramfs_path,
-                    d.getVar('INITRAMFS_IMAGE'),
-                    d.getVar("UBOOT_RD_LOADADDRESS"),
-                    d.getVar("UBOOT_RD_ENTRYPOINT"))
-                break
-            else:
-                bb.note("Did not find initramfs image: " + initramfs_path)
-
-        if not found:
-            bb.fatal("Could not find a valid initramfs type for %s, the supported types are: %s" % (d.getVar('INITRAMFS_IMAGE_NAME'), d.getVar('FIT_SUPPORTED_INITRAMFS_FSTYPES')))
-
-    # Generate the configuration section
-    root_node.fitimage_emit_section_config(default_dtb_image)
-
-    #
-    # Write the ITS file
-    root_node.write_its_file(itsfile)
-
-    #
-    # Step 7: Assemble the image
-    #
-    fitfile = os.path.join(d.getVar("KERNEL_OUTPUT_DIR"), fitname)
-    root_node.run_mkimage_assemble(itsfile, fitfile)
-
-    #
-    # Step 8: Sign the image if required
-    #
-    root_node.run_mkimage_sign(fitfile)
-
-
-python do_assemble_fitimage() {
-    if "fitImage" in d.getVar("KERNEL_IMAGETYPES").split():
-        os.chdir(d.getVar("B"))
-        fitimage_assemble(d, "fit-image.its", "fitImage-none", "")
-        if d.getVar("INITRAMFS_IMAGE_BUNDLE") != "1":
-            link_name = os.path.join(d.getVar("B"), d.getVar("KERNEL_OUTPUT_DIR"), "fitImage")
-            if os.path.islink(link_name):
-                os.unlink(link_name)
-            os.symlink("fitImage-none", link_name)
-}
-
-addtask assemble_fitimage before do_install after do_compile
-
-SYSROOT_DIRS:append = " /sysroot-only"
-do_install:append() {
-	if echo ${KERNEL_IMAGETYPES} | grep -wq "fitImage" && \
-		[ "${UBOOT_SIGN_ENABLE}" = "1" ]; then
-		install -D ${B}/${KERNEL_OUTPUT_DIR}/fitImage-none ${D}/sysroot-only/fitImage
-	fi
-}
-
-python do_assemble_fitimage_initramfs() {
-    if "fitImage" in d.getVar("KERNEL_IMAGETYPES").split() and d.getVar("INITRAMFS_IMAGE"):
-        os.chdir(d.getVar("B"))
-        if d.getVar("INITRAMFS_IMAGE_BUNDLE") == "1":
-            fitimage_assemble(d, "fit-image-%s.its" % d.getVar("INITRAMFS_IMAGE"), "fitImage-bundle", "")
-            link_name =  os.path.join(d.getVar("B"), d.getVar("KERNEL_OUTPUT_DIR"), "fitImage")
-            if os.path.islink(link_name):
-                os.unlink(link_name)
-            os.symlink("fitImage-bundle", link_name)
-        else:
-            fitimage_assemble(d, "fit-image-%s.its" % d.getVar("INITRAMFS_IMAGE"), "fitImage-%s" % d.getVar("INITRAMFS_IMAGE"), 1)
-}
-
-addtask assemble_fitimage_initramfs before do_deploy after do_bundle_initramfs
-
-kernel_do_deploy[vardepsexclude] = "DATETIME"
-kernel_do_deploy:append() {
-	# Update deploy directory
-	if echo ${KERNEL_IMAGETYPES} | grep -wq "fitImage"; then
-
-		if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
-			bbnote "Copying fit-image.its source file..."
-			install -m 0644 ${B}/fit-image.its "$deployDir/fitImage-its-${KERNEL_FIT_NAME}.its"
-			if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
-				ln -snf fitImage-its-${KERNEL_FIT_NAME}.its "$deployDir/fitImage-its-${KERNEL_FIT_LINK_NAME}"
-			fi
-
-			bbnote "Copying linux.bin file..."
-			install -m 0644 ${B}/linux.bin $deployDir/fitImage-linux.bin-${KERNEL_FIT_NAME}${KERNEL_FIT_BIN_EXT}
-			if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
-				ln -snf fitImage-linux.bin-${KERNEL_FIT_NAME}${KERNEL_FIT_BIN_EXT} "$deployDir/fitImage-linux.bin-${KERNEL_FIT_LINK_NAME}"
-			fi
-		fi
-
-		if [ -n "${INITRAMFS_IMAGE}" ]; then
-			bbnote "Copying fit-image-${INITRAMFS_IMAGE}.its source file..."
-			install -m 0644 ${B}/fit-image-${INITRAMFS_IMAGE}.its "$deployDir/fitImage-its-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.its"
-			if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
-				ln -snf fitImage-its-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.its "$deployDir/fitImage-its-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_LINK_NAME}"
-			fi
-
-			if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
-				bbnote "Copying fitImage-${INITRAMFS_IMAGE} file..."
-				install -m 0644 ${B}/${KERNEL_OUTPUT_DIR}/fitImage-${INITRAMFS_IMAGE} "$deployDir/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}${KERNEL_FIT_BIN_EXT}"
-				if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
-					ln -snf fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}${KERNEL_FIT_BIN_EXT} "$deployDir/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_LINK_NAME}"
-				fi
-			fi
-		fi
-	fi
-}
diff --git a/meta/classes-recipe/kernel-uboot.bbclass b/meta/classes-recipe/kernel-uboot.bbclass
index 678d851fd30..e26ba6f0aa9 100644
--- a/meta/classes-recipe/kernel-uboot.bbclass
+++ b/meta/classes-recipe/kernel-uboot.bbclass
@@ -66,48 +66,3 @@ kernel_do_deploy:append() {
 		install -D "${B}/${KERNEL_OUTPUT_DIR}/setup.bin" "$deployDir/"
 	fi
 }
-
-def uboot_prep_kimage_py(d):
-    import subprocess
-
-    arch = d.getVar('ARCH')
-    initramfs_image_bundle = d.getVar('INITRAMFS_IMAGE_BUNDLE')
-    fit_kernel_comp_alg = d.getVar('FIT_KERNEL_COMP_ALG') or 'gzip'
-    fit_kernel_comp_alg_extension = d.getVar('FIT_KERNEL_COMP_ALG_EXTENSION') or '.gz'
-    kernel_objcopy = d.getVar('KERNEL_OBJCOPY')
-
-    vmlinux_path = ""
-    linux_suffix = ""
-    linux_comp = "none"
-
-    if os.path.exists(f'arch/{arch}/boot/compressed/vmlinux'):
-        vmlinux_path = f'arch/{arch}/boot/compressed/vmlinux'
-    elif os.path.exists(f'arch/{arch}/boot/vmlinuz.bin'):
-        if os.path.exists('linux.bin'):
-            os.remove('linux.bin')
-        os.link(f'arch/{arch}/boot/vmlinuz.bin', 'linux.bin')
-    else:
-        vmlinux_path = 'vmlinux'
-        # Use vmlinux.initramfs for linux.bin when INITRAMFS_IMAGE_BUNDLE set
-        # As per the implementation in kernel.bbclass.
-        # See do_bundle_initramfs function
-        if initramfs_image_bundle == '1' and os.path.exists('vmlinux.initramfs'):
-            vmlinux_path = 'vmlinux.initramfs'
-        linux_suffix = fit_kernel_comp_alg_extension
-        linux_comp = fit_kernel_comp_alg
-
-    if vmlinux_path:
-        subprocess.run([kernel_objcopy.strip(), '-O', 'binary', '-R', '.note', '-R', '.comment', '-S', os.path.abspath(vmlinux_path), 'linux.bin'], check=True)
-        # if ret.returncode != 0:
-        # bb.fatal(f"Error: stderr: {ret.stderr.decode('utf-8')}   stdout: {ret.stdout.decode('utf-8')}, vmlinux_path: {os.path.abspath(vmlinux_path)}, pwd: {os.getcwd()}, args: {ret.args}")
-
-    if linux_comp != "none":
-        if linux_comp == "gzip":
-            subprocess.run(['gzip', '-9', 'linux.bin'], check=True)
-        elif linux_comp == "lzo":
-            subprocess.run(['lzop', '-9', 'linux.bin'], check=True)
-        elif linux_comp == "lzma":
-            subprocess.run(['xz', '--format=lzma', '-f', '-6', 'linux.bin'], check=True)
-        os.rename(f'linux.bin{linux_suffix}', 'linux.bin')
-
-    return linux_comp
-- 
2.49.0



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

* Re: [OE-core] [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation
  2025-06-02  7:56 ` [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation AdrianF
@ 2025-06-02 18:12   ` Bruce Ashfield
  0 siblings, 0 replies; 28+ messages in thread
From: Bruce Ashfield @ 2025-06-02 18:12 UTC (permalink / raw)
  To: adrian.freihofer; +Cc: openembedded-core, marex, a.fatoum

In message: [OE-core] [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation
on 02/06/2025 Adrian Freihofer via lists.openembedded.org wrote:

> From: Adrian Freihofer <adrian.freihofer@siemens.com>
> 
> The new recipe linux-yocto-fitimage.bb and the new
> kernel-fit-image.bbclass are intended to become successors of the
> kernel-fitimage.bbclass.
> 
> Instead of injecting the FIT image related build steps into the kernel
> recipe, the new recipe takes the kernel artifacts from the kernel recipe
> and creates the FIT image as an independent task.
> 
> This solves some basic problems:
> * sstate does not work well when a fitImage contains an initramfs. The
>   kernel is rebuilt from scratch if the build runs from an empty TMPDIR.
> * A fitImage kernel is not available as a package, but all other kernel
>   image types are.
> * The task dependencies in the kernel are very complex and difficult to
>   debug if something goes wrong. As a separate, downstream recipe, this
>   is now much easier.
> 
> The recipe takes the kernel artifacts from the deploy folder. There was
> also a test implementation passing the kernel artifacts via sysroot
> directory. This requires changes on the kernel.bbclass to make it
> copying the artifacts also to the sysroot directory while the same
> artifacts are already in the sstate-cached deploy directory.
> 
> The long story about this issue is here:
> [YOCTO #12912]
> 
> Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> ---
>  meta/classes-recipe/kernel-fit-image.bbclass  | 187 ++++++
>  meta/classes/multilib.bbclass                 |   1 +
>  meta/lib/oe/fitimage.py                       | 547 ++++++++++++++++++
>  .../linux/linux-yocto-fitimage.bb             |  13 +
>  4 files changed, 748 insertions(+)
>  create mode 100644 meta/classes-recipe/kernel-fit-image.bbclass
>  create mode 100644 meta/lib/oe/fitimage.py
>  create mode 100644 meta/recipes-kernel/linux/linux-yocto-fitimage.bb
> 
> diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass
> new file mode 100644
> index 00000000000..6d80cd4bb47
> --- /dev/null
> +++ b/meta/classes-recipe/kernel-fit-image.bbclass
> @@ -0,0 +1,187 @@
> +
> +inherit kernel-arch kernel-artifact-names uboot-config deploy
> +require conf/image-fitimage.conf
> +
> +S = "${WORKDIR}/sources"
> +UNPACKDIR = "${S}"
> +
> +PACKAGE_ARCH = "${MACHINE_ARCH}"
> +
> +DEPENDS += "\
> +    u-boot-tools-native dtc-native \
> +    ${@'kernel-signing-keys-native' if d.getVar('FIT_GENERATE_KEYS') == '1' else ''} \
> +"
> +
> +python () {
> +    image = d.getVar('INITRAMFS_IMAGE')
> +    if image and d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1':
> +        if d.getVar('INITRAMFS_MULTICONFIG'):
> +            mc = d.getVar('BB_CURRENT_MC')
> +            d.appendVarFlag('do_compile', 'mcdepends', ' mc:' + mc + ':${INITRAMFS_MULTICONFIG}:${INITRAMFS_IMAGE}:do_image_complete')
> +        else:
> +            d.appendVarFlag('do_compile', 'depends', ' ${INITRAMFS_IMAGE}:do_image_complete')
> +
> +    #check if there are any dtb providers
> +    providerdtb = d.getVar("PREFERRED_PROVIDER_virtual/dtb")
> +    if providerdtb:
> +        d.appendVarFlag('do_compile', 'depends', ' virtual/dtb:do_populate_sysroot')
> +        d.setVar('EXTERNAL_KERNEL_DEVICETREE', "${RECIPE_SYSROOT}/boot/devicetree")
> +}
> +
> +do_configure[noexec] = "1"
> +
> +UBOOT_MKIMAGE_KERNEL_TYPE ?= "kernel"
> +KERNEL_IMAGEDEST ?= "/boot"
> +
> +python do_compile() {
> +    import shutil
> +    import oe.fitimage
> +
> +    itsfile = "fit-image.its"
> +    fitname = "fitImage"
> +    kernel_deploydir = d.getVar('DEPLOY_DIR_IMAGE')
> +    kernel_deploysubdir = d.getVar('KERNEL_DEPLOYSUBDIR')
> +    if kernel_deploysubdir:
> +        kernel_deploydir = os.path.join(kernel_deploydir, kernel_deploysubdir)
> +
> +    # Collect all the its nodes before the its file is generated and mkimage gets executed
> +    root_node = oe.fitimage.ItsNodeRootKernel(
> +        d.getVar("FIT_DESC"), d.getVar("FIT_ADDRESS_CELLS"),
> +        d.getVar('HOST_PREFIX'), d.getVar('UBOOT_ARCH'),  d.getVar("FIT_CONF_PREFIX"),
> +        oe.types.boolean(d.getVar('UBOOT_SIGN_ENABLE')), d.getVar("UBOOT_SIGN_KEYDIR"),
> +        d.getVar("UBOOT_MKIMAGE"), d.getVar("UBOOT_MKIMAGE_DTCOPTS"),
> +        d.getVar("UBOOT_MKIMAGE_SIGN"), d.getVar("UBOOT_MKIMAGE_SIGN_ARGS"),
> +        d.getVar('FIT_HASH_ALG'), d.getVar('FIT_SIGN_ALG'), d.getVar('FIT_PAD_ALG'),
> +        d.getVar('UBOOT_SIGN_KEYNAME'),
> +        oe.types.boolean(d.getVar('FIT_SIGN_INDIVIDUAL')), d.getVar('UBOOT_SIGN_IMG_KEYNAME')
> +    )
> +
> +    # Prepare a kernel image section.
> +    shutil.copyfile(os.path.join(kernel_deploydir, "linux.bin"), "linux.bin")
> +    with open(os.path.join(kernel_deploydir, "linux_comp")) as linux_comp_f:
> +        linux_comp = linux_comp_f.read()
> +    root_node.fitimage_emit_section_kernel("kernel-1", "linux.bin", linux_comp,
> +        d.getVar('UBOOT_LOADADDRESS'), d.getVar('UBOOT_ENTRYPOINT'),
> +        d.getVar('UBOOT_MKIMAGE_KERNEL_TYPE'), d.getVar("UBOOT_ENTRYSYMBOL"))
> +
> +    # Prepare a DTB image section
> +    kernel_devicetree = d.getVar('KERNEL_DEVICETREE')
> +    external_kernel_devicetree = d.getVar("EXTERNAL_KERNEL_DEVICETREE")
> +    if kernel_devicetree:
> +        for dtb in kernel_devicetree.split():
> +            # In deploy_dir the DTBs are without sub-directories also with KERNEL_DTBVENDORED = "1"
> +            dtb_name = os.path.basename(dtb)
> +
> +            # Skip DTB if it's also provided in EXTERNAL_KERNEL_DEVICETREE directory
> +            if external_kernel_devicetree:
> +                ext_dtb_path = os.path.join(external_kernel_devicetree, dtb_name)
> +                if os.path.exists(ext_dtb_path) and os.path.getsize(ext_dtb_path) > 0:
> +                    continue
> +
> +            # Copy the dtb or dtbo file into the FIT image assembly directory
> +            shutil.copyfile(os.path.join(kernel_deploydir, dtb_name), dtb_name)
> +            root_node.fitimage_emit_section_dtb(dtb_name, dtb_name,
> +                d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"))
> +
> +    if external_kernel_devicetree:
> +        # iterate over all .dtb and .dtbo files in the external kernel devicetree directory
> +        # and copy them to the FIT image assembly directory
> +        for dtb_name in sorted(os.listdir(external_kernel_devicetree)):
> +            if dtb_name.endswith('.dtb') or dtb_name.endswith('.dtbo'):
> +                dtb_path = os.path.join(external_kernel_devicetree, dtb_name)
> +
> +                # For symlinks, add a configuration node that refers to the DTB image node to which the symlink points
> +                symlink_target = oe.fitimage.symlink_points_below(dtb_name, external_kernel_devicetree)
> +                if symlink_target:
> +                    root_node.fitimage_emit_section_dtb_alias(dtb_name, symlink_target, True)
> +                # For real DTB files add an image node and a configuration node
> +                else:
> +                    shutil.copyfile(dtb_path, dtb_name)
> +                    root_node.fitimage_emit_section_dtb(dtb_name, dtb_name,
> +                        d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"), True)
> +
> +    # Prepare a u-boot script section
> +    fit_uboot_env = d.getVar("FIT_UBOOT_ENV")
> +    if fit_uboot_env:
> +        root_node.fitimage_emit_section_boot_script("bootscr-"+fit_uboot_env , fit_uboot_env)
> +
> +    # Prepare a setup section (For x86)
> +    setup_bin_path = os.path.join(kernel_deploydir, "setup.bin")
> +    if os.path.exists(setup_bin_path):
> +        shutil.copyfile(setup_bin_path, "setup.bin")
> +        root_node.fitimage_emit_section_setup("setup-1", "setup.bin")
> +
> +    # Prepare a ramdisk section.
> +    initramfs_image = d.getVar('INITRAMFS_IMAGE')
> +    if initramfs_image and d.getVar("INITRAMFS_IMAGE_BUNDLE") != '1':
> +        # Find and use the first initramfs image archive type we find
> +        found = False
> +        for img in d.getVar("FIT_SUPPORTED_INITRAMFS_FSTYPES").split():
> +            initramfs_path = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), "%s.%s" % (d.getVar('INITRAMFS_IMAGE_NAME'), img))
> +            if os.path.exists(initramfs_path):
> +                bb.note("Found initramfs image: " + initramfs_path)
> +                found = True
> +                root_node.fitimage_emit_section_ramdisk("ramdisk-1", initramfs_path,
> +                    initramfs_image,
> +                    d.getVar("UBOOT_RD_LOADADDRESS"),
> +                    d.getVar("UBOOT_RD_ENTRYPOINT"))
> +                break
> +            else:
> +                bb.note("Did not find initramfs image: " + initramfs_path)
> +
> +        if not found:
> +            bb.fatal("Could not find a valid initramfs type for %s, the supported types are: %s" % (d.getVar('INITRAMFS_IMAGE_NAME'), d.getVar('FIT_SUPPORTED_INITRAMFS_FSTYPES')))
> +
> +    # Generate the configuration section
> +    root_node.fitimage_emit_section_config(d.getVar("FIT_CONF_DEFAULT_DTB"))
> +
> +    # Write the its file
> +    root_node.write_its_file(itsfile)
> +
> +    # Assemble the FIT image
> +    root_node.run_mkimage_assemble(itsfile, fitname)
> +
> +    # Sign the FIT image if required
> +    root_node.run_mkimage_sign(fitname)
> +}
> +do_compile[depends] += "virtual/kernel:do_deploy"
> +
> +do_install() {
> +    install -d "${D}/${KERNEL_IMAGEDEST}"
> +    install -m 0644 "${B}/fitImage" "${D}/${KERNEL_IMAGEDEST}/fitImage"
> +}
> +
> +FILES:${PN} = "${KERNEL_IMAGEDEST}"
> +
> +
> +do_deploy() {
> +    deploy_dir="${DEPLOYDIR}"
> +    if [ -n "${KERNEL_DEPLOYSUBDIR}" ]; then
> +        deploy_dir="${DEPLOYDIR}/${KERNEL_DEPLOYSUBDIR}"
> +    fi
> +    install -d "$deploy_dir"
> +    install -m 0644 "${B}/fitImage" "$deploy_dir/fitImage"
> +    install -m 0644 "${B}/fit-image.its" "$deploy_dir/fit-image.its"
> +
> +    if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
> +        ln -snf fit-image.its "$deploy_dir/fitImage-its-${KERNEL_FIT_NAME}.its"
> +        if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
> +            ln -snf fit-image.its "$deploy_dir/fitImage-its-${KERNEL_FIT_LINK_NAME}"
> +        fi
> +    fi
> +
> +    if [ -n "${INITRAMFS_IMAGE}" ]; then
> +        ln -snf fit-image-its "$deploy_dir/fitImage-its-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}.its"
> +        if [ -n "${KERNEL_FIT_LINK_NAME}" ]; then
> +            ln -snf fit-image.its "$deploy_dir/fitImage-its-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_LINK_NAME}"
> +        fi
> +
> +        if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ]; then
> +            ln -snf fitImage "$deploy_dir/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_NAME}${KERNEL_FIT_BIN_EXT}"
> +            if [ -n "${KERNEL_FIT_LINK_NAME}" ] ; then
> +                ln -snf fitImage "$deploy_dir/fitImage-${INITRAMFS_IMAGE_NAME}-${KERNEL_FIT_LINK_NAME}"
> +            fi
> +        fi
> +    fi
> +}
> +addtask deploy after do_compile before do_build
> diff --git a/meta/classes/multilib.bbclass b/meta/classes/multilib.bbclass
> index a4151658a62..b959bbd93c0 100644
> --- a/meta/classes/multilib.bbclass
> +++ b/meta/classes/multilib.bbclass
> @@ -21,6 +21,7 @@ python multilib_virtclass_handler () {
>      bpn = d.getVar("BPN")
>      if ("virtual/kernel" in provides
>              or bb.data.inherits_class('module-base', d)
> +            or bb.data.inherits_class('kernel-fit-image', d)
>              or bpn in non_ml_recipes):
>          raise bb.parse.SkipRecipe("We shouldn't have multilib variants for %s" % bpn)
>  
> diff --git a/meta/lib/oe/fitimage.py b/meta/lib/oe/fitimage.py
> new file mode 100644
> index 00000000000..f3037991558
> --- /dev/null
> +++ b/meta/lib/oe/fitimage.py
> @@ -0,0 +1,547 @@
> +#
> +# Copyright OpenEmbedded Contributors
> +#
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# This file contains common functions for the fitimage generation
> +
> +import os
> +import shlex
> +import subprocess
> +import bb
> +
> +from oeqa.utils.commands import runCmd
> +
> +class ItsNode:
> +    INDENT_SIZE = 8
> +
> +    def __init__(self, name, parent_node, sub_nodes=None, properties=None):
> +        self.name = name
> +        self.parent_node = parent_node
> +
> +        self.sub_nodes = []
> +        if sub_nodes:
> +            self.sub_nodes = sub_nodes
> +
> +        self.properties = {}
> +        if properties:
> +            self.properties = properties
> +
> +        if parent_node:
> +            parent_node.add_sub_node(self)
> +
> +    def add_sub_node(self, sub_node):
> +        self.sub_nodes.append(sub_node)
> +
> +    def add_property(self, key, value):
> +        self.properties[key] = value
> +
> +    def emit(self, f, indent):
> +        indent_str_name = " " * indent
> +        indent_str_props = " " * (indent + self.INDENT_SIZE)
> +        f.write("%s%s {\n" % (indent_str_name, self.name))
> +        for key, value in self.properties.items():
> +            bb.debug(1, "key: %s, value: %s" % (key, str(value)))
> +            # Single integer: <0x12ab>
> +            if isinstance(value, int):
> +                f.write(indent_str_props + key + ' = <0x%x>;\n' % value)
> +            # list of strings: "string1", "string2" or integers: <0x12ab 0x34cd>
> +            elif isinstance(value, list):
> +                if len(value) == 0:
> +                    f.write(indent_str_props + key + ' = "";\n')
> +                elif isinstance(value[0], int):
> +                    list_entries = ' '.join('0x%x' % entry for entry in value)
> +                    f.write(indent_str_props + key + ' = <%s>;\n' % list_entries)
> +                else:
> +                    list_entries = ', '.join('"%s"' % entry for entry in value)
> +                    f.write(indent_str_props + key + ' = %s;\n' % list_entries)
> +            elif isinstance(value, str):
> +                # path: /incbin/("path/to/file")
> +                if key in ["data"] and value.startswith('/incbin/('):
> +                    f.write(indent_str_props + key + ' = %s;\n' % value)
> +                # Integers which are already string formatted
> +                elif value.startswith("<") and value.endswith(">"):
> +                    f.write(indent_str_props + key + ' = %s;\n' % value)
> +                else:
> +                    f.write(indent_str_props + key + ' = "%s";\n' % value)
> +            else:
> +                bb.fatal("%s has unexpexted data type." % str(value))
> +        for sub_node in self.sub_nodes:
> +            sub_node.emit(f, indent + self.INDENT_SIZE)
> +        f.write(indent_str_name + '};\n')
> +
> +class ItsNodeImages(ItsNode):
> +    def __init__(self, parent_node):
> +        super().__init__("images", parent_node)
> +
> +class ItsNodeConfigurations(ItsNode):
> +    def __init__(self, parent_node):
> +        super().__init__("configurations", parent_node)
> +
> +class ItsNodeHash(ItsNode):
> +    def __init__(self, name, parent_node, algo, opt_props=None):
> +        properties = {
> +            "algo": algo
> +        }
> +        if opt_props:
> +            properties.update(opt_props)
> +        super().__init__(name, parent_node, None, properties)
> +
> +class ItsImageSignature(ItsNode):
> +    def __init__(self, name, parent_node, algo, keyname, opt_props=None):
> +        properties = {
> +            "algo": algo,
> +            "key-name-hint": keyname
> +        }
> +        if opt_props:
> +            properties.update(opt_props)
> +        super().__init__(name, parent_node, None, properties)
> +
> +class ItsNodeImage(ItsNode):
> +    def __init__(self, name, parent_node, description, type, compression, sub_nodes=None, opt_props=None):
> +        properties = {
> +            "description": description,
> +            "type": type,
> +            "compression": compression,
> +        }
> +        if opt_props:
> +            properties.update(opt_props)
> +        super().__init__(name, parent_node, sub_nodes, properties)
> +
> +class ItsNodeDtb(ItsNodeImage):
> +    def __init__(self, name, parent_node, description, type, compression,
> +                 sub_nodes=None, opt_props=None, compatible=None):
> +        super().__init__(name, parent_node, description, type, compression, sub_nodes, opt_props)
> +        self.compatible = compatible
> +
> +class ItsNodeDtbAlias(ItsNode):
> +    """Additional Configuration Node for a DTB
> +
> +    Symlinks pointing to a DTB file are handled by an addtitional
> +    configuration node referring to another DTB image node.
> +    """
> +    def __init__(self, name, alias_name, compatible=None):
> +        super().__init__(name, parent_node=None, sub_nodes=None, properties=None)
> +        self.alias_name = alias_name
> +        self.compatible = compatible
> +
> +class ItsNodeConfigurationSignature(ItsNode):
> +    def __init__(self, name, parent_node, algo, keyname, opt_props=None):
> +        properties = {
> +            "algo": algo,
> +            "key-name-hint": keyname
> +        }
> +        if opt_props:
> +            properties.update(opt_props)
> +        super().__init__(name, parent_node, None, properties)
> +
> +class ItsNodeConfiguration(ItsNode):
> +    def __init__(self, name, parent_node, description, sub_nodes=None, opt_props=None):
> +        properties = {
> +            "description": description,
> +        }
> +        if opt_props:
> +            properties.update(opt_props)
> +        super().__init__(name, parent_node, sub_nodes, properties)
> +
> +class ItsNodeRootKernel(ItsNode):
> +    """Create FIT images for the kernel
> +
> +    Currently only a single kernel (no less or more) can be added to the FIT
> +    image along with 0 or more device trees and 0 or 1 ramdisk.
> +
> +    If a device tree included in the FIT image, the default configuration is the
> +    firt DTB. If there is no dtb present than the default configuation the kernel.
> +    """
> +    def __init__(self, description, address_cells, host_prefix, arch, conf_prefix,
> +                 sign_enable=False, sign_keydir=None,
> +                 mkimage=None, mkimage_dtcopts=None,
> +                 mkimage_sign=None, mkimage_sign_args=None,
> +                 hash_algo=None, sign_algo=None, pad_algo=None,
> +                 sign_keyname_conf=None,
> +                 sign_individual=False, sign_keyname_img=None):
> +        props = {
> +            "description": description,
> +            "#address-cells": f"<{address_cells}>"
> +        }
> +        super().__init__("/", None, None, props)
> +        self.images = ItsNodeImages(self)
> +        self.configurations = ItsNodeConfigurations(self)
> +
> +        self._host_prefix = host_prefix
> +        self._arch = arch
> +        self._conf_prefix = conf_prefix
> +
> +        # Signature related properties
> +        self._sign_enable = sign_enable
> +        self._sign_keydir = sign_keydir
> +        self._mkimage = mkimage
> +        self._mkimage_dtcopts = mkimage_dtcopts
> +        self._mkimage_sign = mkimage_sign
> +        self._mkimage_sign_args = mkimage_sign_args
> +        self._hash_algo = hash_algo
> +        self._sign_algo = sign_algo
> +        self._pad_algo = pad_algo
> +        self._sign_keyname_conf = sign_keyname_conf
> +        self._sign_individual = sign_individual
> +        self._sign_keyname_img = sign_keyname_img
> +        self._sanitize_sign_config()
> +
> +        self._dtbs = []
> +        self._dtb_alias = []
> +        self._kernel = None
> +        self._ramdisk = None
> +        self._bootscr = None
> +        self._setup = None
> +
> +    def _sanitize_sign_config(self):
> +        if self._sign_enable:
> +            if not self._hash_algo:
> +                bb.fatal("FIT image signing is enabled but no hash algorithm is provided.")
> +            if not self._sign_algo:
> +                bb.fatal("FIT image signing is enabled but no signature algorithm is provided.")
> +            if not self._pad_algo:
> +                bb.fatal("FIT image signing is enabled but no padding algorithm is provided.")
> +            if not self._sign_keyname_conf:
> +                bb.fatal("FIT image signing is enabled but no configuration key name is provided.")
> +            if self._sign_individual and not self._sign_keyname_img:
> +                bb.fatal("FIT image signing is enabled for individual images but no image key name is provided.")
> +
> +    def write_its_file(self, itsfile):
> +        with open(itsfile, 'w') as f:
> +            f.write("/dts-v1/;\n\n")
> +            self.emit(f, 0)
> +
> +    def its_add_node_image(self, image_id, description, image_type, compression, opt_props):
> +        image_node = ItsNodeImage(
> +            image_id,
> +            self.images,
> +            description,
> +            image_type,
> +            compression,
> +            opt_props=opt_props
> +        )
> +        if self._hash_algo:
> +            ItsNodeHash(
> +                "hash-1",
> +                image_node,
> +                self._hash_algo
> +            )
> +        if self._sign_individual:
> +            ItsImageSignature(
> +                "signature-1",
> +                image_node,
> +                f"{self._hash_algo},{self._sign_algo}",
> +                self._sign_keyname_img
> +            )
> +        return image_node
> +
> +    def its_add_node_dtb(self, image_id, description, image_type, compression, opt_props, compatible):
> +        dtb_node = ItsNodeDtb(
> +            image_id,
> +            self.images,
> +            description,
> +            image_type,
> +            compression,
> +            opt_props=opt_props,
> +            compatible=compatible
> +        )
> +        if self._hash_algo:
> +            ItsNodeHash(
> +                "hash-1",
> +                dtb_node,
> +                self._hash_algo
> +            )
> +        if self._sign_individual:
> +            ItsImageSignature(
> +                "signature-1",
> +                dtb_node,
> +                f"{self._hash_algo},{self._sign_algo}",
> +                self._sign_keyname_img
> +            )
> +        return dtb_node
> +
> +    def fitimage_emit_section_kernel(self, kernel_id, kernel_path, compression,
> +        load, entrypoint, mkimage_kernel_type, entrysymbol=None):
> +        """Emit the fitImage ITS kernel section"""
> +        if self._kernel:
> +            bb.fatal("Kernel section already exists in the ITS file.")
> +        if entrysymbol:
> +            result = subprocess.run([self._host_prefix + "nm", "vmlinux"], capture_output=True, text=True)
> +            for line in result.stdout.splitlines():
> +                parts = line.split()
> +                if len(parts) == 3 and parts[2] == entrysymbol:
> +                    entrypoint = "<0x%s>" % parts[0]
> +                    break
> +        kernel_node = self.its_add_node_image(
> +            kernel_id,
> +            "Linux kernel",
> +            mkimage_kernel_type,
> +            compression,
> +            {
> +                "data": '/incbin/("' + kernel_path + '")',
> +                "arch": self._arch,
> +                "os": "linux",
> +                "load": f"<{load}>",
> +                "entry": f"<{entrypoint}>"
> +            }
> +        )
> +        self._kernel = kernel_node
> +
> +    def fitimage_emit_section_dtb(self, dtb_id, dtb_path, dtb_loadaddress=None,
> +                                  dtbo_loadaddress=None, add_compatible=False):
> +        """Emit the fitImage ITS DTB section"""
> +        load=None
> +        dtb_ext = os.path.splitext(dtb_path)[1]
> +        if dtb_ext == ".dtbo":
> +            if dtbo_loadaddress:
> +                load = dtbo_loadaddress
> +        elif dtb_loadaddress:
> +            load = dtb_loadaddress
> +
> +        opt_props = {
> +            "data": '/incbin/("' + dtb_path + '")',
> +            "arch": self._arch
> +        }
> +        if load:
> +            opt_props["load"] = f"<{load}>"
> +
> +        # Preserve the DTB's compatible string to be added to the configuration node
> +        compatible = None
> +        if add_compatible:
> +            compatible = get_compatible_from_dtb(dtb_path)
> +
> +        dtb_node = self.its_add_node_dtb(
> +            "fdt-" + dtb_id,
> +            "Flattened Device Tree blob",
> +            "flat_dt",
> +            "none",
> +            opt_props,
> +            compatible
> +        )
> +        self._dtbs.append(dtb_node)
> +
> +    def fitimage_emit_section_dtb_alias(self, dtb_alias_id, dtb_path, add_compatible=False):
> +        """Add a configuration node referring to another DTB"""
> +        # Preserve the DTB's compatible string to be added to the configuration node
> +        compatible = None
> +        if add_compatible:
> +            compatible = get_compatible_from_dtb(dtb_path)
> +
> +        dtb_id = os.path.basename(dtb_path)
> +        dtb_alias_node = ItsNodeDtbAlias("fdt-" + dtb_id, dtb_alias_id, compatible)
> +        self._dtb_alias.append(dtb_alias_node)
> +        bb.warn(f"compatible: {compatible}, dtb_alias_id: {dtb_alias_id}, dtb_id: {dtb_id}, dtb_path: {dtb_path}")
> +
> +    def fitimage_emit_section_boot_script(self, bootscr_id, bootscr_path):
> +        """Emit the fitImage ITS u-boot script section"""
> +        if self._bootscr:
> +            bb.fatal("U-boot script section already exists in the ITS file.")
> +        bootscr_node = self.its_add_node_image(
> +            bootscr_id,
> +            "U-boot script",
> +            "script",
> +            "none",
> +            {
> +                "data": '/incbin/("' + bootscr_path + '")',
> +                "arch": self._arch,
> +                "type": "script"
> +            }
> +        )
> +        self._bootscr = bootscr_node
> +
> +    def fitimage_emit_section_setup(self, setup_id, setup_path):
> +        """Emit the fitImage ITS setup section"""
> +        if self._setup:
> +            bb.fatal("Setup section already exists in the ITS file.")
> +        load = "<0x00090000>"
> +        entry = "<0x00090000>"
> +        setup_node = self.its_add_node_image(
> +            setup_id,
> +            "Linux setup.bin",
> +            "x86_setup",
> +            "none",
> +            {
> +                "data": '/incbin/("' + setup_path + '")',
> +                "arch": self._arch,
> +                "os": "linux",
> +                "load": load,
> +                "entry": entry
> +            }
> +        )
> +        self._setup = setup_node
> +
> +    def fitimage_emit_section_ramdisk(self, ramdisk_id, ramdisk_path, description="ramdisk", load=None, entry=None):
> +        """Emit the fitImage ITS ramdisk section"""
> +        if self._ramdisk:
> +            bb.fatal("Ramdisk section already exists in the ITS file.")
> +        opt_props = {
> +            "data": '/incbin/("' + ramdisk_path + '")',
> +            "type": "ramdisk",
> +            "arch": self._arch,
> +            "os": "linux"
> +        }
> +        if load:
> +            opt_props["load"] = f"<{load}>"
> +        if entry:
> +            opt_props["entry"] = f"<{entry}>"
> +
> +        ramdisk_node = self.its_add_node_image(
> +            ramdisk_id,
> +            description,
> +            "ramdisk",
> +            "none",
> +            opt_props
> +        )
> +        self._ramdisk = ramdisk_node
> +
> +    def _fitimage_emit_one_section_config(self, conf_node_name, dtb=None):
> +        """Emit the fitImage ITS configuration section"""
> +        opt_props = {}
> +        conf_desc = []
> +        sign_entries = []
> +
> +        if self._kernel:
> +            conf_desc.append("Linux kernel")
> +            opt_props["kernel"] = self._kernel.name
> +            if self._sign_enable:
> +                sign_entries.append("kernel")
> +
> +        if dtb:
> +            conf_desc.append("FDT blob")
> +            opt_props["fdt"] = dtb.name
> +            if dtb.compatible:
> +                opt_props["compatible"] = dtb.compatible
> +            if self._sign_enable:
> +                sign_entries.append("fdt")
> +
> +        if self._ramdisk:
> +            conf_desc.append("ramdisk")
> +            opt_props["ramdisk"] = self._ramdisk.name
> +            if self._sign_enable:
> +                sign_entries.append("ramdisk")
> +
> +        if self._bootscr:
> +            conf_desc.append("u-boot script")
> +            opt_props["bootscr"] = self._bootscr.name
> +            if self._sign_enable:
> +                sign_entries.append("bootscr")
> +
> +        if self._setup:
> +            conf_desc.append("setup")
> +            opt_props["setup"] = self._setup.name
> +            if self._sign_enable:
> +                sign_entries.append("setup")
> +
> +        # First added configuration is the default configuration
> +        default_flag = "0"
> +        if len(self.configurations.sub_nodes) == 0:
> +            default_flag = "1"
> +
> +        conf_node = ItsNodeConfiguration(
> +            conf_node_name,
> +            self.configurations,
> +            f"{default_flag} {', '.join(conf_desc)}",
> +            opt_props=opt_props
> +        )
> +        if self._hash_algo:
> +            ItsNodeHash(
> +                "hash-1",
> +                conf_node,
> +                self._hash_algo
> +            )
> +        if self._sign_enable:
> +            ItsNodeConfigurationSignature(
> +                "signature-1",
> +                conf_node,
> +                f"{self._hash_algo},{self._sign_algo}",
> +                self._sign_keyname_conf,
> +                opt_props={
> +                    "padding": self._pad_algo,
> +                    "sign-images": sign_entries
> +                }
> +            )
> +
> +    def fitimage_emit_section_config(self, default_dtb_image=None):
> +        if self._dtbs:
> +            for dtb in self._dtbs:
> +                dtb_name = dtb.name
> +                if dtb.name.startswith("fdt-"):
> +                    dtb_name = dtb.name[len("fdt-"):]
> +                self._fitimage_emit_one_section_config(self._conf_prefix + dtb_name, dtb)
> +            for dtb in self._dtb_alias:
> +                self._fitimage_emit_one_section_config(self._conf_prefix + dtb.alias_name, dtb)
> +        else:
> +            # Currently exactly one kernel is supported.
> +            self._fitimage_emit_one_section_config(self._conf_prefix + "1")
> +
> +        default_conf = self.configurations.sub_nodes[0].name
> +        if default_dtb_image and self._dtbs:
> +            default_conf = self._conf_prefix + default_dtb_image
> +        self.configurations.add_property('default', default_conf)
> +
> +    def run_mkimage_assemble(self, itsfile, fitfile):
> +        cmd = [
> +            self._mkimage,
> +            '-f', itsfile,
> +            fitfile
> +        ]
> +        if self._mkimage_dtcopts:
> +            cmd.insert(1, '-D')
> +            cmd.insert(2, self._mkimage_dtcopts)
> +        try:
> +            subprocess.run(cmd, check=True, capture_output=True)
> +        except subprocess.CalledProcessError as e:
> +            bb.fatal(f"Command '{' '.join(cmd)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\nitsflile: {os.path.abspath(itsfile)}")
> +
> +    def run_mkimage_sign(self, fitfile):
> +        if not self._sign_enable:
> +            bb.debug(1, "FIT image signing is disabled. Skipping signing.")
> +            return
> +
> +        # Some sanity checks because mkimage exits with 0 also without needed keys
> +        sign_key_path = os.path.join(self._sign_keydir, self._sign_keyname_conf)
> +        if not os.path.exists(sign_key_path + '.key') or not os.path.exists(sign_key_path + '.crt'):
> +            bb.fatal("%s.key or .crt does not exist" % sign_key_path)
> +        if self._sign_individual:
> +            sign_key_img_path = os.path.join(self._sign_keydir, self._sign_keyname_img)
> +            if not os.path.exists(sign_key_img_path + '.key') or not os.path.exists(sign_key_img_path + '.crt'):
> +                bb.fatal("%s.key or .crt does not exist" % sign_key_img_path)
> +
> +        cmd = [
> +            self._mkimage_sign,
> +            '-F',
> +            '-k', self._sign_keydir,
> +            '-r', fitfile
> +        ]
> +        if self._mkimage_dtcopts:
> +            cmd.extend(['-D', self._mkimage_dtcopts])
> +        if self._mkimage_sign_args:
> +            cmd.extend(shlex.split(self._mkimage_sign_args))
> +        try:
> +            subprocess.run(cmd, check=True, capture_output=True)
> +        except subprocess.CalledProcessError as e:
> +            bb.fatal(f"Command '{' '.join(cmd)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}")
> +
> +
> +def symlink_points_below(file_or_symlink, expected_parent_dir):
> +    """returns symlink destination if it points below directory"""
> +    file_path = os.path.join(expected_parent_dir, file_or_symlink)
> +    if not os.path.islink(file_path):
> +        return None
> +
> +    realpath = os.path.relpath(os.path.realpath(file_path), expected_parent_dir)
> +    if realpath.startswith(".."):
> +        return None
> +
> +    return realpath
> +
> +def get_compatible_from_dtb(dtb_path, fdtget_path="fdtget"):
> +    compatible = None
> +    cmd = [fdtget_path, "-t", "s", dtb_path, "/", "compatible"]
> +    try:
> +        ret = subprocess.run(cmd, check=True, capture_output=True, text=True)
> +        compatible = ret.stdout.strip().split()
> +    except subprocess.CalledProcessError:
> +        compatible = None
> +    return compatible
> diff --git a/meta/recipes-kernel/linux/linux-yocto-fitimage.bb b/meta/recipes-kernel/linux/linux-yocto-fitimage.bb
> new file mode 100644
> index 00000000000..6ce1960a871
> --- /dev/null
> +++ b/meta/recipes-kernel/linux/linux-yocto-fitimage.bb
> @@ -0,0 +1,13 @@
> +SUMMARY = "The Linux kernel as a FIT image (optionally with initramfs)"
> +SECTION = "kernel"
> +
> +# If an initramfs is included in the FIT image more licenses apply.
> +# But also the kernel uses more than one license (see Documentation/process/license-rules.rst)
> +LICENSE = "GPL-2.0-with-Linux-syscall-note"
> +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/GPL-2.0-with-Linux-syscall-note;md5=0bad96c422c41c3a94009dcfe1bff992"
> +
> +inherit linux-kernel-base kernel-fit-image
> +
> +# Set the version of this recipe to the version of the included kernel
> +# (without taking the long way around via PV)
> +PKGV = "${@get_kernelversion_file("${STAGING_KERNEL_BUILDDIR}")}"

At least for this part of the series, I have no concerns. Now that this
won't need to be updated to match the kernel, the maintenance is low.

I'll have a look at the other parts shortly as well.

Bruce

> -- 
> 2.49.0
> 

> 
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#217695): https://lists.openembedded.org/g/openembedded-core/message/217695
> Mute This Topic: https://lists.openembedded.org/mt/113424429/1050810
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
> 



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

* Re: [OE-core] [PATCH v6 01/21] devicetree: minor improvements
  2025-06-02  7:56 ` [PATCH v6 01/21] devicetree: minor improvements AdrianF
@ 2025-06-02 18:31   ` Bruce Ashfield
  0 siblings, 0 replies; 28+ messages in thread
From: Bruce Ashfield @ 2025-06-02 18:31 UTC (permalink / raw)
  To: adrian.freihofer; +Cc: openembedded-core, marex, a.fatoum

In message: [OE-core] [PATCH v6 01/21] devicetree: minor improvements
on 02/06/2025 Adrian Freihofer via lists.openembedded.org wrote:

> From: Adrian Freihofer <adrian.freihofer@siemens.com>
> 
> - Do not use the ${} bitbake syntax for shell internal variables
> - Fix shellcheck SC2045 warning:
>   Iterating over ls output is fragile. Use globs.
> - Improve error handling for dtc. Print the output, not only the exit
>   value.
> 
> Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> ---
>  meta/classes-recipe/devicetree.bbclass | 20 ++++++++++++++------
>  1 file changed, 14 insertions(+), 6 deletions(-)
> 
> diff --git a/meta/classes-recipe/devicetree.bbclass b/meta/classes-recipe/devicetree.bbclass
> index 1806cb62cbf..0d0583c13a0 100644
> --- a/meta/classes-recipe/devicetree.bbclass
> +++ b/meta/classes-recipe/devicetree.bbclass
> @@ -109,7 +109,11 @@ def devicetree_compile(dtspath, includes, d):
>          ppargs.append("-I{0}".format(i))
>      ppargs += ["-o", "{0}.pp".format(dts), dtspath]
>      bb.note("Running {0}".format(" ".join(ppargs)))
> -    subprocess.run(ppargs, check = True)
> +    try:
> +        subprocess.run(ppargs, check=True, capture_output=True)
> +    except subprocess.CalledProcessError as e:
> +        bb.fatal(f"Command '{' '.join(ppargs)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\ndtspath: {os.path.abspath(dtspath)}")
> +
>  
>      # determine if the file is an overlay or not (using the preprocessed file)
>      isoverlay = devicetree_source_is_overlay("{0}.pp".format(dts))
> @@ -125,7 +129,11 @@ def devicetree_compile(dtspath, includes, d):
>      dtcargs += ["-o", "{0}.{1}".format(dtname, "dtbo" if isoverlay else "dtb")]
>      dtcargs += ["-I", "dts", "-O", "dtb", "{0}.pp".format(dts)]
>      bb.note("Running {0}".format(" ".join(dtcargs)))
> -    subprocess.run(dtcargs, check = True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
> +    try:
> +        subprocess.run(dtcargs, check=True, capture_output=True)
> +    except subprocess.CalledProcessError as e:
> +        bb.fatal(f"Command '{' '.join(dtcargs)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\ndtname: {dtname}")
> +
>  
>  python devicetree_do_compile() {
>      import re
> @@ -144,14 +152,14 @@ python devicetree_do_compile() {
>  }
>  
>  devicetree_do_install() {
> -    for DTB_FILE in `ls *.dtb *.dtbo`; do
> -        install -Dm 0644 ${B}/${DTB_FILE} ${D}/boot/devicetree/${DTB_FILE}
> +    for dtb_file in *.dtb *.dtbo; do
> +        install -Dm 0644 "${B}/$dtb_file" "${D}/boot/devicetree/$dtb_file"
>      done
>  }
>  
>  devicetree_do_deploy() {
> -    for DTB_FILE in `ls *.dtb *.dtbo`; do
> -        install -Dm 0644 ${B}/${DTB_FILE} ${DEPLOYDIR}/devicetree/${DTB_FILE}
> +    for dtb_file in *.dtb *.dtbo; do

The behaviour on no dtb and dtbo files is different between
the two.

Is that a situation we should detect and throw a useful error
message ? It may end up not being possible to happen, but since
this patch is about improving the handling of the other commands,
I thought it might be relevant.

Bruce

> +        install -Dm 0644 "${B}/$dtb_file" "${DEPLOYDIR}/devicetree/$dtb_file"
>      done
>  }
>  addtask deploy before do_build after do_install
> -- 
> 2.49.0
> 

> 
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#217689): https://lists.openembedded.org/g/openembedded-core/message/217689
> Mute This Topic: https://lists.openembedded.org/mt/113424423/1050810
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
> 



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

* Re: [OE-core] [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary
  2025-06-02  7:56 ` [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary AdrianF
@ 2025-06-02 18:51   ` Bruce Ashfield
  2025-06-02 19:44     ` Freihofer, Adrian
  0 siblings, 1 reply; 28+ messages in thread
From: Bruce Ashfield @ 2025-06-02 18:51 UTC (permalink / raw)
  To: adrian.freihofer; +Cc: openembedded-core, marex, a.fatoum



In message: [OE-core] [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary
on 02/06/2025 Adrian Freihofer via lists.openembedded.org wrote:

> From: Adrian Freihofer <adrian.freihofer@siemens.com>
> 
> Extend the kernel-uboot.bbclass to build and deploy the kernel binary
> intended for inclusion in a FIT image.
> Note that the kernel used in a FIT image is a stripped (and optionally
> compressed) vmlinux ELF binary - not a self-extracting format like
> zImage, which is already available in the deploy directory if needed
> separately.
> 
> The kernel.bbclass inherits all classes listed in the KERNEL_CLASSES
> variable. By default, KERNEL_CLASSES is defined as:
>   KERNEL_CLASSES ?= " kernel-uimage "
> Since kernel-uimage.bbclass inherits from kernel-uboot.bbclass, the
> creation and deployment of the kernel binary for inclusion in a FIT
> image happens automatically by default.
> 
> Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> ---
>  meta/classes-recipe/kernel-uboot.bbclass | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/meta/classes-recipe/kernel-uboot.bbclass b/meta/classes-recipe/kernel-uboot.bbclass
> index d2a63524ece..e26ba6f0aa9 100644
> --- a/meta/classes-recipe/kernel-uboot.bbclass
> +++ b/meta/classes-recipe/kernel-uboot.bbclass
> @@ -56,4 +56,13 @@ uboot_prep_kimage() {
>  	fi
>  
>  	printf "$linux_comp" > "$output_dir/linux_comp"
> -}
> \ No newline at end of file
> +}
> +
> +kernel_do_deploy:append() {
> +	# Provide the kernel artifacts to post processing recipes e.g. for creating a FIT image
> +	uboot_prep_kimage "$deployDir"
> +	# For x86 a setup.bin needs to be include"d in a fitImage as well
> +	if [ -e ${KERNEL_OUTPUT_DIR}/setup.bin ]; then
> +		install -D "${B}/${KERNEL_OUTPUT_DIR}/setup.bin" "$deployDir/"
> +	fi

Isn't this adding extra steps / overhead to a user of kernel-uboot
bbclass that isn't building or using a fitimage ?

Bruce

> +}
> -- 
> 2.49.0
> 

> 
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#217701): https://lists.openembedded.org/g/openembedded-core/message/217701
> Mute This Topic: https://lists.openembedded.org/mt/113424435/1050810
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
> 



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

* Re: [OE-core] [PATCH v6 20/21] kernel.bbclass: remove support for type fitImage
  2025-06-02  7:56 ` [PATCH v6 20/21] kernel.bbclass: remove support for type fitImage AdrianF
@ 2025-06-02 18:59   ` Bruce Ashfield
  0 siblings, 0 replies; 28+ messages in thread
From: Bruce Ashfield @ 2025-06-02 18:59 UTC (permalink / raw)
  To: adrian.freihofer; +Cc: openembedded-core, marex, a.fatoum



In message: [OE-core] [PATCH v6 20/21] kernel.bbclass: remove support for type fitImage
on 02/06/2025 Adrian Freihofer via lists.openembedded.org wrote:

> From: Adrian Freihofer <adrian.freihofer@siemens.com>
> 
> kernel.bbclass is no longer involved in FIT image creation. Whether a
> FIT image is built now depends entirely on whether the linux-yocto-fitimage
> recipe (or any other recipe capable of producing a FIT image) is selected
> for the build.
> As a result, specifying the kernel image type "fitImage" in
> KERNEL_IMAGETYPE or KERNEL_IMAGETYPES is no longer necessary and gets
> removed.
> 
> Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> ---
>  meta/classes-recipe/kernel.bbclass | 20 +++++++-------------
>  1 file changed, 7 insertions(+), 13 deletions(-)
> 
> diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass
> index 36ce659762e..599996ceb0b 100644
> --- a/meta/classes-recipe/kernel.bbclass
> +++ b/meta/classes-recipe/kernel.bbclass
> @@ -84,6 +84,10 @@ python __anonymous () {
>          types = (alttype + ' ' + types).strip()
>      d.setVar('KERNEL_IMAGETYPES', types)
>  
> +    # Since kernel-fitimage.bbclass got replaced by kernel-fit-image.bbclass
> +    if "fitImage" in types:
> +        bb.error("fitImage is no longer supported as a KERNEL_IMAGETYPE(S)")

Can this be a little be more specific ? Indicating the migration
path (the new recipe) would help someone seeing the message.

> +
>      # KERNEL_IMAGETYPES may contain a mixture of image types supported directly
>      # by the kernel build system and types which are created by post-processing
>      # the output of the kernel build system (e.g. compressing vmlinux ->
> @@ -477,17 +481,10 @@ kernel_do_install() {
>  	install -d ${D}/${KERNEL_IMAGEDEST}
>  
>  	#
> -	# When including an initramfs bundle inside a FIT image, the fitImage is created after the install task
> -	# by do_assemble_fitimage_initramfs.
> -	# This happens after the generation of the initramfs bundle (done by do_bundle_initramfs).
> -	# So, at the level of the install task we should not try to install the fitImage. fitImage is still not
> -	# generated yet.
> -	# After the generation of the fitImage, the deploy task copies the fitImage from the build directory to
> -	# the deploy folder.
> +	# bundle_initramfs runs after do_install before do_deploy. do_deploy does what's needed therefore.
>  	#
> -
>  	for imageType in ${KERNEL_IMAGETYPES} ; do
> -		if [ $imageType != "fitImage" ] || [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ] ; then
> +		if [ "${INITRAMFS_IMAGE_BUNDLE}" != "1" ] ; then
>  			install -m 0644 ${KERNEL_OUTPUT_DIR}/$imageType ${D}/${KERNEL_IMAGEDEST}/$imageType-${KERNEL_VERSION}
>  		fi
>  	done
> @@ -843,11 +840,8 @@ kernel_do_deploy() {
>  		fi
>  	fi
>  
> -	if [ ! -z "${INITRAMFS_IMAGE}" -a x"${INITRAMFS_IMAGE_BUNDLE}" = x1 ]; then
> +	if [ -n "${INITRAMFS_IMAGE}" ] && [ "${INITRAMFS_IMAGE_BUNDLE}" = "1" ]; then

Minor 'nit, but the above change is unrelated to the fit image rework :)

Bruce

>  		for imageType in ${KERNEL_IMAGETYPES} ; do
> -			if [ "$imageType" = "fitImage" ] ; then
> -				continue
> -			fi
>  			initramfsBaseName=$imageType-${INITRAMFS_NAME}
>  			install -m 0644 ${KERNEL_OUTPUT_DIR}/$imageType.initramfs $deployDir/$initramfsBaseName${KERNEL_IMAGE_BIN_EXT}
>  			if [ -n "${INITRAMFS_LINK_NAME}" ] ; then
> -- 
> 2.49.0
> 

> 
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#217705): https://lists.openembedded.org/g/openembedded-core/message/217705
> Mute This Topic: https://lists.openembedded.org/mt/113424439/1050810
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
> 



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

* Re: [OE-core] [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary
  2025-06-02 18:51   ` [OE-core] " Bruce Ashfield
@ 2025-06-02 19:44     ` Freihofer, Adrian
  2025-06-02 19:53       ` Bruce Ashfield
  0 siblings, 1 reply; 28+ messages in thread
From: Freihofer, Adrian @ 2025-06-02 19:44 UTC (permalink / raw)
  To: Bruce Ashfield
  Cc: openembedded-core@lists.openembedded.org, marex@denx.de,
	a.fatoum@pengutronix.de

Hi Bruce

On Mon, 2025-06-02 at 14:51 -0400, Bruce Ashfield wrote:
>
>
> In message: [OE-core] [PATCH v6 10/21] kernel-uboot.bbclass: deploy
> the vmlinux kernel binary
> on 02/06/2025 Adrian Freihofer via lists.openembedded.org wrote:
>
> > From: Adrian Freihofer <adrian.freihofer@siemens.com>
> >
> > Extend the kernel-uboot.bbclass to build and deploy the kernel
> > binary
> > intended for inclusion in a FIT image.
> > Note that the kernel used in a FIT image is a stripped (and
> > optionally
> > compressed) vmlinux ELF binary - not a self-extracting format like
> > zImage, which is already available in the deploy directory if
> > needed
> > separately.
> >
> > The kernel.bbclass inherits all classes listed in the
> > KERNEL_CLASSES
> > variable. By default, KERNEL_CLASSES is defined as:
> >   KERNEL_CLASSES ?= " kernel-uimage "
> > Since kernel-uimage.bbclass inherits from kernel-uboot.bbclass, the
> > creation and deployment of the kernel binary for inclusion in a FIT
> > image happens automatically by default.
> >
> > Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> > ---
> >  meta/classes-recipe/kernel-uboot.bbclass | 11 ++++++++++-
> >  1 file changed, 10 insertions(+), 1 deletion(-)
> >
> > diff --git a/meta/classes-recipe/kernel-uboot.bbclass
> > b/meta/classes-recipe/kernel-uboot.bbclass
> > index d2a63524ece..e26ba6f0aa9 100644
> > --- a/meta/classes-recipe/kernel-uboot.bbclass
> > +++ b/meta/classes-recipe/kernel-uboot.bbclass
> > @@ -56,4 +56,13 @@ uboot_prep_kimage() {
> >     fi
> >
> >     printf "$linux_comp" > "$output_dir/linux_comp"
> > -}
> > \ No newline at end of file
> > +}
> > +
> > +kernel_do_deploy:append() {
> > +   # Provide the kernel artifacts to post processing recipes
> > e.g. for creating a FIT image
> > +   uboot_prep_kimage "$deployDir"
> > +   # For x86 a setup.bin needs to be include"d in a fitImage
> > as well
> > +   if [ -e ${KERNEL_OUTPUT_DIR}/setup.bin ]; then
> > +           install -D "${B}/${KERNEL_OUTPUT_DIR}/setup.bin"
> > "$deployDir/"
> > +   fi
>
> Isn't this adding extra steps / overhead to a user of kernel-uboot
> bbclass that isn't building or using a fitimage ?

Yes, the vmlinux is generated and deployed. For some x86 machines the
setup.bin gets deployed as well.

We could argue that this default is good, because all the different use
cases just work (for little overhead). For those who do not like this
overhead it is possible to add
  KERNEL_CLASSES = ""

But we could also argue that uImage and fitImage should be completely
opt-in and changing the default (in kernel.bbclass) from
  KERNEL_CLASSES ?= " kernel-uimage "
to
  KERNEL_CLASSES ?= ""
would be better.

Or we could leave the uImage exactly as it is, and for the fitImage,
introduce a new bbclass called 'kernel-export-vmlinux.bbclass'. This
new class would be enabled by adding:
  KERNEL_CLASSES = "kernel-export-vmlinux"


Any preferences?

Thank you for the re-view.
Adrian

>
> Bruce
>
> > +}
> > --
> > 2.49.0
> >
>
> >
> > -=-=-=-=-=-=-=-=-=-=-=-
> > Links: You receive all messages sent to this group.
> > View/Reply Online (#217701):
> > https://lists.openembedded.org/g/openembedded-core/message/217701
> > Mute This Topic:
> > https://lists.openembedded.org/mt/113424435/1050810
> > Group Owner: openembedded-core+owner@lists.openembedded.org
> > Unsubscribe:
> > https://lists.openembedded.org/g/openembedded-core/unsub
> >  [bruce.ashfield@gmail.com]
> > -=-=-=-=-=-=-=-=-=-=-=-
> >
>


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

* Re: [OE-core] [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary
  2025-06-02 19:44     ` Freihofer, Adrian
@ 2025-06-02 19:53       ` Bruce Ashfield
  0 siblings, 0 replies; 28+ messages in thread
From: Bruce Ashfield @ 2025-06-02 19:53 UTC (permalink / raw)
  To: Freihofer, Adrian
  Cc: openembedded-core@lists.openembedded.org, marex@denx.de,
	a.fatoum@pengutronix.de

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

On Mon, Jun 2, 2025 at 3:44 PM Freihofer, Adrian <
adrian.freihofer@siemens.com> wrote:

> Hi Bruce
>
> On Mon, 2025-06-02 at 14:51 -0400, Bruce Ashfield wrote:
> >
> >
> > In message: [OE-core] [PATCH v6 10/21] kernel-uboot.bbclass: deploy
> > the vmlinux kernel binary
> > on 02/06/2025 Adrian Freihofer via lists.openembedded.org wrote:
> >
> > > From: Adrian Freihofer <adrian.freihofer@siemens.com>
> > >
> > > Extend the kernel-uboot.bbclass to build and deploy the kernel
> > > binary
> > > intended for inclusion in a FIT image.
> > > Note that the kernel used in a FIT image is a stripped (and
> > > optionally
> > > compressed) vmlinux ELF binary - not a self-extracting format like
> > > zImage, which is already available in the deploy directory if
> > > needed
> > > separately.
> > >
> > > The kernel.bbclass inherits all classes listed in the
> > > KERNEL_CLASSES
> > > variable. By default, KERNEL_CLASSES is defined as:
> > >   KERNEL_CLASSES ?= " kernel-uimage "
> > > Since kernel-uimage.bbclass inherits from kernel-uboot.bbclass, the
> > > creation and deployment of the kernel binary for inclusion in a FIT
> > > image happens automatically by default.
> > >
> > > Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
> > > ---
> > >  meta/classes-recipe/kernel-uboot.bbclass | 11 ++++++++++-
> > >  1 file changed, 10 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/meta/classes-recipe/kernel-uboot.bbclass
> > > b/meta/classes-recipe/kernel-uboot.bbclass
> > > index d2a63524ece..e26ba6f0aa9 100644
> > > --- a/meta/classes-recipe/kernel-uboot.bbclass
> > > +++ b/meta/classes-recipe/kernel-uboot.bbclass
> > > @@ -56,4 +56,13 @@ uboot_prep_kimage() {
> > >     fi
> > >
> > >     printf "$linux_comp" > "$output_dir/linux_comp"
> > > -}
> > > \ No newline at end of file
> > > +}
> > > +
> > > +kernel_do_deploy:append() {
> > > +   # Provide the kernel artifacts to post processing recipes
> > > e.g. for creating a FIT image
> > > +   uboot_prep_kimage "$deployDir"
> > > +   # For x86 a setup.bin needs to be include"d in a fitImage
> > > as well
> > > +   if [ -e ${KERNEL_OUTPUT_DIR}/setup.bin ]; then
> > > +           install -D "${B}/${KERNEL_OUTPUT_DIR}/setup.bin"
> > > "$deployDir/"
> > > +   fi
> >
> > Isn't this adding extra steps / overhead to a user of kernel-uboot
> > bbclass that isn't building or using a fitimage ?
>
> Yes, the vmlinux is generated and deployed. For some x86 machines the
> setup.bin gets deployed as well.
>
> We could argue that this default is good, because all the different use
> cases just work (for little overhead). For those who do not like this
> overhead it is possible to add
>   KERNEL_CLASSES = ""
>
> But we could also argue that uImage and fitImage should be completely
> opt-in and changing the default (in kernel.bbclass) from
>   KERNEL_CLASSES ?= " kernel-uimage "
> to
>   KERNEL_CLASSES ?= ""
> would be better.
>
> Or we could leave the uImage exactly as it is, and for the fitImage,
> introduce a new bbclass called 'kernel-export-vmlinux.bbclass'. This
> new class would be enabled by adding:
>   KERNEL_CLASSES = "kernel-export-vmlinux"
>
>
> Any preferences?
>

I prefer #3, with the explicit export kernel class.

option #1 might be hard to find, we could argue that if you are having
such an issue with space / time, you'd dig in and find out how to disable
the processing. But I still prefer something obvious

#2 I do like opt-in, but leaving the default as it currently is makes sense.

Bruce



>
> Thank you for the re-view.
> Adrian
>
> >
> > Bruce
> >
> > > +}
> > > --
> > > 2.49.0
> > >
> >
> > >
> > > -=-=-=-=-=-=-=-=-=-=-=-
> > > Links: You receive all messages sent to this group.
> > > View/Reply Online (#217701):
> > > https://lists.openembedded.org/g/openembedded-core/message/217701
> > > Mute This Topic:
> > > https://lists.openembedded.org/mt/113424435/1050810
> > > Group Owner: openembedded-core+owner@lists.openembedded.org
> > > Unsubscribe:
> > > https://lists.openembedded.org/g/openembedded-core/unsub
> > >  [bruce.ashfield@gmail.com]
> > > -=-=-=-=-=-=-=-=-=-=-=-
> > >
> >
>
>

-- 
- Thou shalt not follow the NULL pointer, for chaos and madness await thee
at its end
- "Use the force Harry" - Gandalf, Star Trek II

[-- Attachment #2: Type: text/html, Size: 7107 bytes --]

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

end of thread, other threads:[~2025-06-02 19:53 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-02  7:56 [PATCH v6 00/21] FIT image improvements AdrianF
2025-06-02  7:56 ` [PATCH v6 01/21] devicetree: minor improvements AdrianF
2025-06-02 18:31   ` [OE-core] " Bruce Ashfield
2025-06-02  7:56 ` [PATCH v6 02/21] oe-selftest: add new ext dtb recipe AdrianF
2025-06-02  7:56 ` [PATCH v6 03/21] oe-selftest: fitimage: test external dtb AdrianF
2025-06-02  7:56 ` [PATCH v6 04/21] oe-selftest: fitimage: test FIT_CONF_PREFIX AdrianF
2025-06-02  7:56 ` [PATCH v6 05/21] oe-selftest: fitimage: test FIT_CONF_DEFAULT_DTB AdrianF
2025-06-02  7:56 ` [PATCH v6 06/21] kernel-signing-keys-native: refactor key generation into a new recipe AdrianF
2025-06-02  7:56 ` [PATCH v6 07/21] maintainers: add myself for kernel-signing-keys-native AdrianF
2025-06-02  7:56 ` [PATCH v6 08/21] oe-selftest: fitimage: cleanup FIT_GENERATE_KEYS AdrianF
2025-06-02  7:56 ` [PATCH v6 09/21] kernel-uboot.bbclass: do not require the kernel build folder AdrianF
2025-06-02  7:56 ` [PATCH v6 10/21] kernel-uboot.bbclass: deploy the vmlinux kernel binary AdrianF
2025-06-02 18:51   ` [OE-core] " Bruce Ashfield
2025-06-02 19:44     ` Freihofer, Adrian
2025-06-02 19:53       ` Bruce Ashfield
2025-06-02  7:56 ` [PATCH v6 11/21] kernel-fitimage: refactor order in its AdrianF
2025-06-02  7:56 ` [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation AdrianF
2025-06-02 18:12   ` [OE-core] " Bruce Ashfield
2025-06-02  7:56 ` [PATCH v6 13/21] maintainers: add myself for linux-yocto-fitimage AdrianF
2025-06-02  7:56 ` [PATCH v6 14/21] oe-selftest: fitimage: add tests for fitimage.py AdrianF
2025-06-02  7:56 ` [PATCH v6 15/21] oe-selftest: fitimage: support new FIT recipe as well AdrianF
2025-06-02  7:56 ` [PATCH v6 16/21] oe-selftest: fitimage: run all tests for both FIT implementations AdrianF
2025-06-02  7:56 ` [PATCH v6 17/21] oe-selftest: fitimage refactor classes AdrianF
2025-06-02  7:56 ` [PATCH v6 18/21] kernel-fitimage: re-write its code in Python AdrianF
2025-06-02  7:56 ` [PATCH v6 19/21] oe-selftest: fitimage: remove kernel-fitimage tests AdrianF
2025-06-02  7:56 ` [PATCH v6 20/21] kernel.bbclass: remove support for type fitImage AdrianF
2025-06-02 18:59   ` [OE-core] " Bruce Ashfield
2025-06-02  7:56 ` [PATCH v6 21/21] kernel-fitimage.bbclass: remove it AdrianF

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.