Openembedded Core Discussions
 help / color / mirror / Atom feed
From: Bruce Ashfield <bruce.ashfield@gmail.com>
To: adrian.freihofer@siemens.com
Cc: openembedded-core@lists.openembedded.org, marex@denx.de,
	a.fatoum@pengutronix.de
Subject: Re: [OE-core] [PATCH v6 12/21] kernel-fit-image.bbclass: add a new FIT image implementation
Date: Mon, 2 Jun 2025 14:12:26 -0400	[thread overview]
Message-ID: <aD3pinBR+qWFYjZX@gmail.com> (raw)
In-Reply-To: <20250602075714.32122-13-adrian.freihofer@siemens.com>

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]
> -=-=-=-=-=-=-=-=-=-=-=-
> 



  reply	other threads:[~2025-06-02 18:12 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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   ` Bruce Ashfield [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aD3pinBR+qWFYjZX@gmail.com \
    --to=bruce.ashfield@gmail.com \
    --cc=a.fatoum@pengutronix.de \
    --cc=adrian.freihofer@siemens.com \
    --cc=marex@denx.de \
    --cc=openembedded-core@lists.openembedded.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox