From patchwork Tue Oct 20 12:52:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 101403 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.6670.1792500754836944368 for ; Tue, 20 Oct 2026 05:52:35 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=kTe8p25n; spf=pass (domain: gmail.com, ip: 209.85.221.44, mailfrom: vgharaee@gmail.com) Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-48e1d0e5dafso478340f8f.1 for ; Tue, 20 Oct 2026 05:52:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792500753; x=1793105553; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=0DMyeWQj4RNOUtPgy6jPZh7Vpm+V/DXd81JWMP/CxDM=; b=kTe8p25nsULKurhTv/Wx1I+Gcm138aCGUgnm7vHosFFMkAbHuPExJICgFM27Yz41UL WpxC+HXclUueaCL2oJ/f98yUqQWQW0Qof+r9TcEBGUPVzsc/wYOk4KlYnLNPuYOsS/Ps EhDpIuaJtGSz4jKcoB/GdYJh06zsmLeldSR2BxaZTYAhICL5y/uJI2wjAiGZdf3sAWUd N28n6d2KF1HhqrEWUZoIVj4Xzo3b2jdIT43Vj18pCAQC5g7o0cX5GQ3Oadh2H5qCiKLA luzKMICHtSuH7FXL+z2ebTKXlyfFxrU5F9mCkYpLw5KsBT2lPYWQaWoldUaKubE2pFgK iLDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792500753; x=1793105553; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=0DMyeWQj4RNOUtPgy6jPZh7Vpm+V/DXd81JWMP/CxDM=; b=plarti6Xh6uxmOqKk1d9ZkpHmSYKz4hJBYrbIf8VAyZYaz5P8Arvv+MuVeJpBKUvZc DWR34aBrzpBKjNFaU8ydZI4VziDYgogwMRhFfJkVci15XFfoVsSkfZvoRaZSOoa2QEPR bfEcChx3FWLAHsvGiDtTYB57a/9NBgOlSTKsVj0oE7EeNfAhGqdkqPwmss9HEakOuXcm /nESInXi72YJfOz6djpO97HA5ETzQLKOnARWntdRmvgPeLdmrsObK4Lqh0oNQXi46hgt 7Ek6epD3URqi6iVFNxEgPzVZDAJ/48PXOrKgfjIBEbZvnLbMFlR9UJorbFnt4u8ji2j6 g2bQ== X-Gm-Message-State: AFq9FYIt1Y5EC8WvCYBCG1wZU1WdkNLxywJUdHdhp/mHQ8ascPZnHpzD bzM58XLHPgTg6mOKNhM14b6rkiS8lZemVsKErDN6VXXyeWft8IbgJWDk3jr/Gg== X-Gm-Gg: AYBFou0xoffbIDDATELixdse/WPCVsCOfVa5FzJvHop4c8xEx1D0IGlV2cPmTsbonMp LQK6mpTtXa/P/LafAG6Oq2sksBMnzjE6h49/kfW/0mxeSn3jC05gRPvVppRLubB+eSX9C2wQKnk 4iyKNy16hgiaczXMUTJ8aOsHXRtN/vh5n71DNqNhdkCVp2Lay+eXfau5mUFZSxP91PBjW7GWCo5 IF+1mjcBsR9jdarMmzF7FxK59GkgB4d49AE4TcLd8GK50ku2iyqhDNJXdSYG47xt5BynzCpB3MC Z5YPxwzVpCvPuNSf6fFmqCroiYwz+0JwecHt+RUZon80kvbZYtIH1ZnPKgcnqR6387N5xdUMceG 5r6RO42MPsmGVmg7Vk6arFCzXU+BOxvlwGEPfSZJTdVGL4xK53cyr3e+a4J4xrBWxFoytRd/b6f 5pVxxp57jm9GPBM5OfuNU9F1tEcm3ZcXk147GxbBd1FtJkpdJ0BczFQyxVtxsrLzCG7kQy7YiEm 9YShYukV/ZdkWiskjQE20OHJynf4sE5PCNwM2Pu X-Received: by 2002:a5d:5e86:0:b0:48e:adc:b97d with SMTP id ffacd0b85a97d-48e1e7a7a2emr4638120f8f.8.1792500752832; Tue, 20 Oct 2026 05:52:32 -0700 (PDT) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.31]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-48e1d80fdbbsm6335698f8f.17.2026.10.20.05.52.31 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 20 Oct 2026 05:52:32 -0700 (PDT) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH 1/4] oe.fitimage: Support multiple kernel nodes and configuration selection Date: Tue, 20 Oct 2026 14:52:30 +0200 Message-ID: <20261020125233.69997-2-vgharaee@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261020125233.69997-1-vgharaee@gmail.com> References: <20261020125233.69997-1-vgharaee@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Tue, 20 Oct 2026 13:03:48 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248280 From: Vahid Gharaee Signed-off-by: Vahid Gharaee --- meta/lib/oe/fitimage.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/meta/lib/oe/fitimage.py b/meta/lib/oe/fitimage.py index 484e5c5ee0..91ccc31d46 100644 --- a/meta/lib/oe/fitimage.py +++ b/meta/lib/oe/fitimage.py @@ -193,6 +193,7 @@ class ItsNodeRootKernel(ItsNode): self._dtbs = [] self._dtb_alias = [] self._kernel = None + self._kernels = {} self._ramdisk = None self._bootscr = None self._setup = None @@ -268,8 +269,8 @@ class ItsNodeRootKernel(ItsNode): 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 kernel_id in self._kernels: + bb.fatal("Kernel section '%s' already exists in the ITS file." % kernel_id) if entrysymbol: result = subprocess.run([self._host_prefix + "nm", "vmlinux"], capture_output=True, text=True) for line in result.stdout.splitlines(): @@ -294,7 +295,10 @@ class ItsNodeRootKernel(ItsNode): compression, opt_props, ) - self._kernel = kernel_node + self._kernels[kernel_id] = kernel_node + + if self._kernel is None: + self._kernel = kernel_node def fitimage_emit_section_dtb(self, dtb_id, dtb_path, dtb_loadaddress=None, dtbo_loadaddress=None, add_compatible=False): @@ -425,7 +429,7 @@ class ItsNodeRootKernel(ItsNode): self._loadables.append(loadable_node) - def _fitimage_emit_one_section_config(self, conf_node_name, dtb=None): + def _fitimage_emit_one_section_config(self, conf_node_name, dtb=None, kernel=None): """Emit the fitImage ITS configuration section""" opt_props = {} conf_desc = [] @@ -436,9 +440,11 @@ class ItsNodeRootKernel(ItsNode): if dtb and os.path.splitext(dtb.name)[1] == ".dtbo": include_kernel = 0 - if self._kernel and include_kernel: + selected_kernel = kernel if kernel is not None else self._kernel + + if selected_kernel and include_kernel: conf_desc.append("Linux kernel") - opt_props["kernel"] = self._kernel.name + opt_props["kernel"] = selected_kernel.name if self._sign_enable: sign_entries.append("kernel") From patchwork Tue Oct 20 12:52:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 101404 Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.6671.1792500756863575644 for ; Tue, 20 Oct 2026 05:52:37 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=MoLrml/c; spf=pass (domain: gmail.com, ip: 209.85.128.45, mailfrom: vgharaee@gmail.com) Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-4a1d484b4a9so40908205e9.0 for ; Tue, 20 Oct 2026 05:52:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792500755; x=1793105555; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=pmy/HQ2L/+JdDDbW4oH3apECpFF0tT6iV04eFC08gKE=; b=MoLrml/c8JDl9BXWnwuY8ar/FlTNyfDZGENKE3jOGt3x6POCoeq/Wn3jLV2BKAoAi6 0zsa7OTm7Jf1Qc24tkQ6CjalgUbHdieHUHpiVCH4iveUpcq0S5VNTifVrrrZs1waP549 nHF4jXMLhBKjMb+C6Wj+PEiFmMusc7a7zykY0ZetHE0wjYJd9WKIWY9iXhueE0QflR+o Ak5cYMI4PcNVR8x6Pqk3PdpcXpnhZCSG50y9O8ViX7YSO2QDU5tDntqHw7EY/k/hbrma QPn+7qe6tXu7lL9Dl9Jl2jPxP2Zz/5nctjMQUc2xpdxWyY8834F+7q+/M2ve2SKDzNOC rMag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792500755; x=1793105555; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=pmy/HQ2L/+JdDDbW4oH3apECpFF0tT6iV04eFC08gKE=; b=1rXzJf1HTeB4fJbrQ7VBRDEtvdxaeW+mlxE9UYwGMZ0sMZAnmecwIhURmlBxqhIakh c1GNUDjuGusEuvEyUsio1EbGTj+XrmmRhXtHJkTT5PXDng3X51GS10Lolbi0+tqQSEzT oXihaixqRVt9hCC7x9UF4ye2fx04JhEB9xiPyHBNnNXYlARBL0NcjE1ryRHt6DsdfIFn fZWh8St/f+8erA4eKyDnO5cf2FFXP0I3KImmXPidqwsHjWF2l1btTL+8O9Q498u3/c2t ovHgB7Bf1V92SH6+gUSRne3L3PALM4Cw+/JG2hSjA/zGJNouV+brctgkq3wDAvMZKn6d +9Sw== X-Gm-Message-State: AFq9FYJ8fzwLvjzSQMYMttffjRl5DEUna3sJ9T4EDxLa5PuGnP8OJtwe tUaGuokEwzjnmZ5n+FNby5j9dMj1bIKBA8dUXob+mX7tuDQAjn4w8FrVvv1RBg== X-Gm-Gg: AYBFou0bAzmW5ie/4rKLqFXwdZGgDlhIbQ299ine7CVQmYLnpQrjm1pqLgsfv24NHeb iCo+wH7lfDN0CUL1YV0C2laqforRGDnZgC8GuLk71Jof2heEPhnxVSzhQvrwphq041K+OwOBfwj T807cSEIBREOm5UCAWgOWqYeX/JYxuQ49IEx/zLWBPpGWiedOhiP9XMlZgOSKHrctjY6m6aBhIA sSw6yWIJByL12EAeEOmu4Y/Je6SJDLYYciWWHh7iD2G4q1B11x+KMXXlxU8IafBA1Ryev87eCfB QKn96QC7ofBo3tghIe2SyZdjbM1unONNTyF7dDpV8mrbJR7V3Ytv09Stn5rNhs86LUsEZKNP0Fn 8V5r6qDD2RuCAUdgK2+r8fGiFbRCGXrhkhc9MrmIyhIWu2DdFvM0IfMhf47Id3F0ilBvv5Itkv6 kw8KxRnY7oAmwlS6JmIZbhQzUbE6tB/pM+54tZqm0riX5fG6VJEH1fOC9EnAtXPnRoYS4CRufu8 fVo2XVxVm7Krlu8RNCfqcqbV4qrJMrkz0IYPdw= X-Received: by 2002:a05:600c:6215:b0:4a1:d41b:5060 with SMTP id 5b1f17b1804b1-4a1d762b705mr202181905e9.22.1792500754869; Tue, 20 Oct 2026 05:52:34 -0700 (PDT) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.31]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-48e1d80fdbbsm6335698f8f.17.2026.10.20.05.52.34 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 20 Oct 2026 05:52:34 -0700 (PDT) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH 2/4] oe.fitimage: add new config interface variables for multi-kernel image, to fitimage config file Date: Tue, 20 Oct 2026 14:52:31 +0200 Message-ID: <20261020125233.69997-3-vgharaee@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261020125233.69997-1-vgharaee@gmail.com> References: <20261020125233.69997-1-vgharaee@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Tue, 20 Oct 2026 13:04:08 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248281 From: Vahid Gharaee Signed-off-by: Vahid Gharaee --- meta/conf/image-fitimage.conf | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/meta/conf/image-fitimage.conf b/meta/conf/image-fitimage.conf index a06d30e494..21d54ce18d 100644 --- a/meta/conf/image-fitimage.conf +++ b/meta/conf/image-fitimage.conf @@ -102,3 +102,58 @@ FIT_UBOOT_ENV ?= "" # arch, os) if the corresponding flag is not set, while others (load address # and entry point) will be omitted in the final FIT. FIT_LOADABLES ?= "" + +# Allow user to insert additional kernel images, e.g. a second kernel built by +# another recipe or multiconfig, or a prebuilt kernel. The default kernel +# (kernel-1) from virtual/kernel is always included. Each entry is used as the +# image node name and must not collide with other image nodes, e.g. kernel-1. +# Entries can be configured through variable flags: +# FIT_KERNEL_FILENAME[id] - kernel file, relative to FIT_KERNEL_DEPLOYDIR +# (default: linux.bin, mandatory for prebuilt +# kernels) +# FIT_KERNEL_DEPLOYDIR[id] - directory containing the kernel file +# (default: ${DEPLOY_DIR_IMAGE}) +# FIT_KERNEL_RECIPE[id] - recipe whose do_deploy provides the kernel +# (default: none, the file must already exist). +# Without FIT_KERNEL_MULTICONFIG the kernel file +# must be deployed by this recipe. +# FIT_KERNEL_MULTICONFIG[id] - multiconfig building FIT_KERNEL_RECIPE[id] +# (default recipe: virtual/kernel), requires +# FIT_KERNEL_DEPLOYDIR[id] +# FIT_KERNEL_COMPRESSION[id] - default: for linux.bin deployed by +# FIT_KERNEL_RECIPE or FIT_KERNEL_MULTICONFIG the +# content of linux_comp next to it, else none +# FIT_KERNEL_LOADADDRESS[id] - default: ${UBOOT_LOADADDRESS} +# FIT_KERNEL_ENTRYPOINT[id] - default: ${UBOOT_ENTRYPOINT} +# FIT_KERNEL_TYPE[id] - default: ${UBOOT_MKIMAGE_KERNEL_TYPE} +# FIT_KERNEL_DESCRIPTION[id] - default: "Linux kernel" +# Example, a kernel from the "rt" multiconfig with the same MACHINE: +# FIT_KERNELS = "kernel-rt" +# FIT_KERNEL_MULTICONFIG[kernel-rt] = "rt" +# FIT_KERNEL_DEPLOYDIR[kernel-rt] = "${TOPDIR}/tmp-rt/deploy/images/${MACHINE}" +# Example, a second kernel recipe with KERNEL_PACKAGE_NAME = "kernel-rt": +# FIT_KERNELS = "kernel-rt" +# FIT_KERNEL_RECIPE[kernel-rt] = "linux-yocto-rt" +# FIT_KERNEL_DEPLOYDIR[kernel-rt] = "${DEPLOY_DIR_IMAGE}/kernel-rt" +# Additional kernels are only referenced by configurations from FIT_CONFIGS. +FIT_KERNELS ?= "" + +# Allow user to add configurations in addition to the ones generated per DTB +# (or conf-1 without DTBs). Each entry results in a ${FIT_CONF_PREFIX} +# configuration node which can be configured through variable flags: +# FIT_CONFIG_KERNEL[id] - kernel image node, e.g. one of FIT_KERNELS +# (default: kernel-1) +# FIT_CONFIG_DTB[id] - DTB name as in KERNEL_DEVICETREE or a file name in +# EXTERNAL_KERNEL_DEVICETREE (default: none) +# The ramdisk, u-boot script, setup and loadables are referenced by all +# configurations. +# Example: +# FIT_CONFIGS = "rt" +# FIT_CONFIG_KERNEL[rt] = "kernel-rt" +# FIT_CONFIG_DTB[rt] = "am335x-boneblack.dtb" +FIT_CONFIGS ?= "" + +# Allow user to select the default configuration by name without +# FIT_CONF_PREFIX, e.g. "rt" for "conf-rt". Takes precedence over +# FIT_CONF_DEFAULT_DTB. +FIT_CONF_DEFAULT ?= "" From patchwork Tue Oct 20 12:52:32 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 101405 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.6672.1792500759180210707 for ; Tue, 20 Oct 2026 05:52:39 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=FP+XjzNt; spf=pass (domain: gmail.com, ip: 209.85.221.48, mailfrom: vgharaee@gmail.com) Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-48df4e9d2d4so2052013f8f.3 for ; Tue, 20 Oct 2026 05:52:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792500757; x=1793105557; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=Ur4FDr53MhQSk/biYrkocJe0cgRJrjFAQC3VssK4WVA=; b=FP+XjzNturhkL7PHolFSBE6N+Q/UZOWS7cNM9mv6okaH/ggJmFnKSJGtymIJj3D5P1 T46xJGj9xlsRYzPlhTQNB9N3k98S+G+FkYsvLkkdMOmtOtKpC1grjIz4Nnoh0GWXSeVl D1E0lK3aOZytq1TVqBH7Bb9hZ+A1m2yu9XhrHE0Is3jSFtsPd4wrZaEdGTs2Nooqjchh fvxrunbFCoxq1iXdSlUPhyvJHK2uXWZyYJ4PACX5bjuZinynmjdehXEOnc9Il3RdwLL1 6N/Tr6WtGG1uCJEAUa67KiXyXTd30PTx9SHPIcHkkngAqvBs8xlh+S2e3dVjwgM/WELm 45Ig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792500757; x=1793105557; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=Ur4FDr53MhQSk/biYrkocJe0cgRJrjFAQC3VssK4WVA=; b=Zh0cfyx6750JN0+z7mbg8xQnK1s0ZWf5hgu+SvwZbwW2gteTo9KqMn8ALivwTS/JZd fV/U8wvMt/lLBiyWGPDJE0YrX0XbiO4a93nRGTj+IZliLMIyNtYKBM51vw4p+D4Fg7ok cLJmiTcKg0Gu+LZ4WH2Z4C+BI0zXKNRleqVJ7VGAzRQYyaklz4gXo26/GI9MJAgeW2St Cf7mhKxDxzBJB5ytjknqrxNqtL450uOxJxUnGDTlDA2v7neOISyY99Egs5DoFfWwieyb vSYQ6L0lsjDf/Xxo5Y3XB6prVkVh1YiwDIyAV/7iAzJc6XxR8TXAJ5vBYPAa69awSZyH NCMA== X-Gm-Message-State: AFq9FYJxRxu6+xsIIt4GHoPXllrD0fz7dVAJ0OlV2lYWj3lV/Y6Hkbaq f4ATm5IuT6thn20g+/p64boHd9L41zIywN4mmQKrNLrwjDDgaRHIjpvbnKSK7Q== X-Gm-Gg: AYBFou3fNQtJMUdxDhGMktajVv8lftpVoBDibkZM9szclQ8XMa7HOvVOI0FJGNtA9PY oM7Lx4oJSM5z25/fAdFwA0KcP4wcKUrRa2sOaBNRPje2gSUjZ5Z8IsCBvnIFPTo18ZX06Qdzk7S mgLOfO0276yb+lUah6Af3i+Zr6lcmElSoY6bQl30exy6xNX/dapxELtMNVChT+c1Di7tJz5lYXE wksjh45hpwZg+WSa7Tk1YTIsfmnJR2zE3n1F+l9E1EdYBSajUyt4TSnQ+diC6+j5X6F3H9gVrT3 o45cUDTM4Hu/umfQMkXvD0/SKeJXwQ54Po9nqbpiVPOIZWJlmkVKJ5DU4TUC4rTTlv3biQYQWDw dMk6/wWX+g9YzOCvJkA3rrq//m9FTF9ad6A+/PmE/CJA3OKHvLgXJ39P8HmSkbx2VPBRnqYfmvv kbu6Nt/KJQUSRQw55B/pEthjmGIRXCbXorl5+4/mGTFMzRuj7U14RSa3EuudV++DqVsTO+io4JF gC2/RDRIbaOVs3J9oazkXB9tmXdFObqMT728LM= X-Received: by 2002:a05:6000:4287:b0:48c:7306:18e9 with SMTP id ffacd0b85a97d-48e07fa891fmr19344084f8f.43.1792500757080; Tue, 20 Oct 2026 05:52:37 -0700 (PDT) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.31]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-48e1d80fdbbsm6335698f8f.17.2026.10.20.05.52.36 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 20 Oct 2026 05:52:36 -0700 (PDT) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH 3/4] oe.selftest: adding tests for multi kernel/config fitimage. AI-Generated: Uses GitHub copilot (Claude Opus 5.5) Date: Tue, 20 Oct 2026 14:52:32 +0200 Message-ID: <20261020125233.69997-4-vgharaee@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261020125233.69997-1-vgharaee@gmail.com> References: <20261020125233.69997-1-vgharaee@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Tue, 20 Oct 2026 13:04:28 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248282 From: Vahid Gharaee Signed-off-by: Vahid Gharaee --- meta/lib/oeqa/selftest/cases/fitimage.py | 156 ++++++++++++++++++++++- 1 file changed, 155 insertions(+), 1 deletion(-) diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 5d77e6da09..cd099a7ce4 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -461,6 +461,30 @@ class FitImageTestCase(OESelftestTestCase): loadables += var_loadables.split() return loadables + @staticmethod + def _get_flag_values(bb_vars, var, items): + """Map items to the per-item values of a synthetic "a ? b" flag variable""" + values = (bb_vars.get(var) or "").split("?") + return {item: values[index].strip() if index < len(values) else "" for index, item in enumerate(items)} + + @staticmethod + def _get_extra_configs(bb_vars): + """Return a list of (conf, kernel, dtb image) tuples for FIT_CONFIGS""" + configs = (bb_vars.get('FIT_CONFIGS') or "").split() + kernels = FitImageTestCase._get_flag_values(bb_vars, 'FIT_CONFIG_KERNEL', configs) + dtbs = FitImageTestCase._get_flag_values(bb_vars, 'FIT_CONFIG_DTB', configs) + _, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars) + extra_configs = [] + for conf in configs: + dtb = dtbs[conf] + if dtb in dtb_symlinks: + # Symlinks of test-dtbs-as-ext point to the DTB without "-alias" + dtb = dtb.replace("-alias", "") + elif dtb: + dtb = FitImageTestCase._apply_dtbvendored(bb_vars, dtb) + extra_configs.append((conf, kernels[conf] or "kernel-1", dtb)) + return extra_configs + def _is_req_dict_in_dict(self, found_dict, req_dict): """ Check if all key-value pairs in the required dictionary are present in the found dictionary. @@ -670,11 +694,16 @@ class KernelFitImageBase(FitImageTestCase): internal_used = { 'DEPLOY_DIR_IMAGE', 'FIT_CONF_DEFAULT_DTB', + 'FIT_CONF_DEFAULT', 'FIT_CONF_MAPPINGS', 'FIT_CONF_PREFIX', + 'FIT_CONFIGS', + 'FIT_CONFIG_DTB', + 'FIT_CONFIG_KERNEL', 'FIT_DESC', 'FIT_HASH_ALG', 'FIT_KERNEL_COMP_ALG', + 'FIT_KERNELS', 'FIT_LOADABLES', 'FIT_LOADABLE_ENTRYPOINT', 'FIT_LOADABLE_LOADADDRESS', @@ -835,6 +864,8 @@ class KernelFitImageBase(FitImageTestCase): if loadables: images += loadables + images += (bb_vars.get('FIT_KERNELS') or "").split() + # configuration nodes (one per DTB, symlink, and mappings) configurations = [] if dtb_files: @@ -884,6 +915,8 @@ class KernelFitImageBase(FitImageTestCase): else: configurations = [bb_vars['FIT_CONF_PREFIX'] + '1'] + configurations += [bb_vars['FIT_CONF_PREFIX'] + conf for conf, _, _ in FitImageTestCase._get_extra_configs(bb_vars)] + # Create a list of paths for all image and configuration nodes req_its_paths = [] for image in images: @@ -926,8 +959,12 @@ class KernelFitImageBase(FitImageTestCase): if uboot_rd_entrypoint: its_field_check.append("entry = <%s>;" % uboot_rd_entrypoint) + fit_conf_default = bb_vars.get('FIT_CONF_DEFAULT') fit_conf_default_dtb = bb_vars.get('FIT_CONF_DEFAULT_DTB') - if fit_conf_default_dtb: + if fit_conf_default: + fit_conf_prefix = bb_vars.get('FIT_CONF_PREFIX', "conf-") + its_field_check.append('default = "' + fit_conf_prefix + fit_conf_default + '";') + elif fit_conf_default_dtb: fit_conf_prefix = bb_vars.get('FIT_CONF_PREFIX', "conf-") its_field_check.append('default = "' + fit_conf_prefix + self._apply_dtbvendored(bb_vars, fit_conf_default_dtb) + '";') @@ -951,6 +988,12 @@ class KernelFitImageBase(FitImageTestCase): if initramfs_image and initramfs_image_bundle != "1": its_field_check.append('ramdisk = "ramdisk-1";') + # Additional configurations are emitted after the generated ones + for _, kernel, dtb in FitImageTestCase._get_extra_configs(bb_vars): + its_field_check.append('kernel = "%s";' % kernel) + if dtb: + its_field_check.append('fdt = "fdt-%s";' % dtb) + return its_field_check def _get_req_sigvalues_config(self, bb_vars): @@ -1062,6 +1105,15 @@ class KernelFitImageBase(FitImageTestCase): if loadables: req_sections[conf_name]['Loadables'] = ",".join(loadables) + # Additional kernels and configurations + for kernel in (bb_vars.get('FIT_KERNELS') or "").split(): + req_sections[kernel] = { "Type": "Kernel Image" } + for conf, kernel, dtb in FitImageTestCase._get_extra_configs(bb_vars): + conf_name = bb_vars['FIT_CONF_PREFIX'] + conf + req_sections[conf_name] = { "Kernel": kernel } + if dtb: + req_sections[conf_name]['FDT'] = 'fdt-' + dtb + # Add signing related properties if needed if uboot_sign_enable == "1": for section in req_sections: @@ -1208,6 +1260,32 @@ FIT_LOADABLE_TYPE[loadable2] = "firmware" self._gen_atf_tee_dummy_images(bb_vars) self._test_fitimage(bb_vars) + def test_fit_image_multiple_kernels(self): + """ + Summary: Check FIT_KERNELS, FIT_CONFIGS and FIT_CONF_DEFAULT + Expected: An additional kernel image node and a configuration + referring to it are present and the configuration is + the default one. + Product: oe-core + """ + machine = self._get_machine_or_skip() + config = """ +KERNEL_IMAGETYPE = "Image" +UBOOT_LOADADDRESS = "0x80080000" +UBOOT_ENTRYPOINT = "0x80080000" +# Use the linux.bin of virtual/kernel also as second kernel to avoid building another kernel +FIT_KERNELS = "kernel-2" +FIT_KERNEL_RECIPE[kernel-2] = "virtual/kernel" +FIT_CONFIGS = "second" +FIT_CONFIG_KERNEL[second] = "kernel-2" +FIT_CONF_DEFAULT = "second" +""" + config = FitImageTestCase._config_add_machine_settings(config, machine, keys=["KERNEL_DEVICETREE"]) + config = self._config_add_kernel_classes(config) + self.write_config(config) + 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 @@ -1501,11 +1579,14 @@ class FitImagePyTests(KernelFitImageBase): # image-fitimage.conf 'FIT_ADDRESS_CELLS': "1", 'FIT_CONF_DEFAULT_DTB': "", + 'FIT_CONF_DEFAULT': "", 'FIT_CONF_MAPPINGS': "", 'FIT_CONF_PREFIX': "conf-", + 'FIT_CONFIGS': "", 'FIT_DESC': "Kernel fitImage for a dummy distro", 'FIT_GENERATE_KEYS': "0", 'FIT_HASH_ALG': "sha256", + 'FIT_KERNELS': "", 'FIT_KEY_GENRSA_ARGS': "-F4", 'FIT_KEY_REQ_ARGS': "-batch -new", 'FIT_KEY_SIGN_PKCS': "-x509", @@ -1566,6 +1647,11 @@ class FitImagePyTests(KernelFitImageBase): bb_vars.get('UBOOT_MKIMAGE_KERNEL_TYPE'), bb_vars.get("UBOOT_ENTRYSYMBOL") ) + for kernel in bb_vars['FIT_KERNELS'].split(): + root_node.fitimage_emit_section_kernel(kernel, "linux-%s.bin" % kernel, "none", + bb_vars.get('UBOOT_LOADADDRESS'), bb_vars.get('UBOOT_ENTRYPOINT'), + bb_vars.get('UBOOT_MKIMAGE_KERNEL_TYPE')) + dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars) for dtb in dtb_files: root_node.fitimage_emit_section_dtb(dtb, os.path.join("a-dir", dtb), @@ -1596,9 +1682,19 @@ class FitImagePyTests(KernelFitImageBase): "loadable-type") root_node.fitimage_emit_section_config(bb_vars['FIT_CONF_DEFAULT_DTB'], bb_vars.get('FIT_CONF_MAPPINGS')) + configs = bb_vars['FIT_CONFIGS'].split() + conf_kernels = FitImageTestCase._get_flag_values(bb_vars, 'FIT_CONFIG_KERNEL', configs) + conf_dtbs = FitImageTestCase._get_flag_values(bb_vars, 'FIT_CONFIG_DTB', configs) + for conf in configs: + root_node.fitimage_emit_section_config_custom(conf, conf_kernels[conf], conf_dtbs[conf]) + if bb_vars['FIT_CONF_DEFAULT']: + root_node.fitimage_set_default_config(bb_vars['FIT_CONF_DEFAULT']) root_node.write_its_file(fitimage_its_path) self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) + with open(fitimage_its_path) as its_file: + default_lines = [line for line in its_file if line.strip().startswith("default = ")] + self.assertEqual(len(default_lines), 1, "Expected exactly one default configuration: %s" % default_lines) self.logger.debug("Checking its: %s" % fitimage_its_path) self._check_its_file(bb_vars, fitimage_its_path) @@ -1665,6 +1761,64 @@ class FitImagePyTests(KernelFitImageBase): } self._test_fitimage_py(bb_vars_overrides) + def test_fitimage_py_multiple_kernels(self): + """Test FIT_KERNELS, FIT_CONFIGS and FIT_CONF_DEFAULT""" + bb_vars_overrides = { + 'KERNEL_DEVICETREE': "one.dtb two.dtb", + 'FIT_CONF_DEFAULT_DTB': "one.dtb", + 'FIT_KERNELS': "kernel-rt", + 'FIT_CONFIGS': "rt rt-two default-two", + 'FIT_CONFIG_KERNEL': "kernel-rt ? kernel-rt ? ", + 'FIT_CONFIG_DTB': " ? two.dtb ? two.dtb", + 'FIT_CONF_DEFAULT': "rt-two", + } + self._test_fitimage_py(bb_vars_overrides) + + def test_fitimage_py_multiple_kernels_unknown_kernel_error(self): + """Test that FIT_CONFIG_KERNEL referring to an unknown kernel causes a fatal error""" + bb_vars_overrides = { + 'FIT_CONFIGS': "rt", + 'FIT_CONFIG_KERNEL': "kernel-rt", + } + with self.assertRaises(BBHandledException): + self._test_fitimage_py(bb_vars_overrides) + + def test_fitimage_py_conf_default_unknown_error(self): + """Test that FIT_CONF_DEFAULT referring to an unknown configuration causes a fatal error""" + bb_vars_overrides = { + 'FIT_CONF_DEFAULT': "nonexistent", + } + with self.assertRaises(BBHandledException): + self._test_fitimage_py(bb_vars_overrides) + + def test_fitimage_py_multiple_kernels_dtb_alias(self): + """Test FIT_CONFIG_DTB referring to an external DTB symlink""" + bb_vars_overrides = { + 'PREFERRED_PROVIDER_virtual/dtb': "test-dtbs-as-ext", + 'FIT_KERNELS': "kernel-rt", + 'FIT_CONFIGS': "rt", + 'FIT_CONFIG_KERNEL': "kernel-rt", + 'FIT_CONFIG_DTB': "test-ext-alias.dtb", + } + self._test_fitimage_py(bb_vars_overrides) + + def test_fitimage_py_multiple_kernels_kernel_1_error(self): + """Test that a FIT_KERNELS entry colliding with kernel-1 causes a fatal error""" + bb_vars_overrides = { + 'FIT_KERNELS': "kernel-1", + } + with self.assertRaises(BBHandledException): + self._test_fitimage_py(bb_vars_overrides) + + def test_fitimage_py_multiple_kernels_image_collision_error(self): + """Test that a FIT_KERNELS entry colliding with another image node causes a fatal error""" + bb_vars_overrides = { + 'FIT_KERNELS': "my-loadable", + 'FIT_LOADABLES': "my-loadable", + } + with self.assertRaises(BBHandledException): + self._test_fitimage_py(bb_vars_overrides) + class UBootFitImageTests(FitImageTestCase): """Test cases for the uboot-sign bbclass""" From patchwork Tue Oct 20 12:52:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 101406 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.6673.1792500761235600899 for ; Tue, 20 Oct 2026 05:52:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20251104 header.b=ACYo4bDs; spf=pass (domain: gmail.com, ip: 209.85.221.44, mailfrom: vgharaee@gmail.com) Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-48af4d4e61fso1972475f8f.2 for ; Tue, 20 Oct 2026 05:52:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792500759; x=1793105559; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=n4X/tNcJq3ugmSTOKwLQFXC43bPs4CCgvZT23f8uMbw=; b=ACYo4bDsaAugfKM2XTQ5UmAOI3nVGT4uWUCzJ8mEjgdzi60W17jKtI/yqFbQPjO06b JNpsu+YL5wuIRxV7MEj+MvXva7UeBd25KAq6HYNvKDrm9h5jl90i+TJ5QNlB6W5Q4ih/ 86ve3rnbNf1oFu4UpfQm0amtP8ORnEY/j6JIE04kacchRbtcxJc9WTjKrAELd4f5Blbn 46QOGXpu3PL88G0zubkaost8aRyjk7Dd3vEu8JuNPpg27WUVkNW9tkX1DDwyaUQGvpAP EfJa0o/p8Xz1TNb1/mBt9IUVzCz1J3B8eRRN9uZyZnLTEu/a+bOJ6dX1Ltw9MOEb9rbH tPuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792500759; x=1793105559; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=n4X/tNcJq3ugmSTOKwLQFXC43bPs4CCgvZT23f8uMbw=; b=dyGkML3rex4BZRBeQkZz8eg1rGJ+tF7iUGTOTjb1lMA0CAknzIdSmXOeMQimAzplHC i8mx+VDFy6kv0/4GIYeMZiT7ki4h+3k9ms2dhIty5Zj+UxRtdzt0bEcfCbH2Ds6AICoF V4lkRstkwYafPTQYfiS5Zt7Whc+CHmppcygp+iAzNPooJ4PlqZEel4vHf9Vwu1ptF6Mc lRSiFzVWN1yR5CEqMgmTnCkKq6HnyTmMTvzP94l0vhFhB/Jgc5+nMUSLTP6Hnc2ZTID9 rFuJgqJkUs5uKlgXWx5sPlEg0YzWAIOlJ4Xyf/ZDMfl/AvqDZ/nUZV+/ZWRqHwLIDLay HpzA== X-Gm-Message-State: AFq9FYJDwwfTNNU8k0/BJZB8Ek0bfoY1Yyh3hSDvM8h2TbgI+GUFKf9J VgEvfjimDjiKjTR6TuWaljAImIHlBS6xsqvaYbU/YYq9Vyvhfjhjqlhf6MkoLA== X-Gm-Gg: AYBFou0IhjSEFJYcoJ6cX79CV9I6tuf7z/3DkDD/jN4fy2QB6iDYD9zPRRpz4FX6X6o KliZgF0tG/qjctBkwpJK1KNhiUG8Aea1GxPxXbR0xGLvdxdzkygST80J93MQpOIe7Btf/UumthN DJwzpgndfwVGXDspuHzHzeNL4zt4qPB+9Fbpw+YQPJykVraRlfxza1Nqonhee6Ae3TNw/aM7zPl SSr7HZaIbsrDK+LF6S4tbVVAn6mjcmyEp5MmSI5t9PrSid1V1J348HkO2jivhWtR169o2P5wcyX 4Myafa2zqo1cKoMFPzq/2iyIw2IFkDpivMfqg967TzRhcJU9N77PasZYze2tZ7TMFkw6vEJsrav W9qqYPNbKgMAB7qmHP+3jLK0NYHq7qI4UBmi8HgY0brTEUr5zUOP9ni9njPYxfLW4UeysbdfYsn 33JOybxTcr+RyB5LgeN1MB7kZ5i5GGBaJ818BMcJvgTnUqyNn+ehpoN7BjI0RhxNTMlmjAOhuyu VJYj3yC4xhk6Ei0j++DUl+o X-Received: by 2002:adf:f28a:0:b0:48e:44d:5a9d with SMTP id ffacd0b85a97d-48e07fa99a7mr16005870f8f.45.1792500759104; Tue, 20 Oct 2026 05:52:39 -0700 (PDT) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.31]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-48e1d80fdbbsm6335698f8f.17.2026.10.20.05.52.38 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 20 Oct 2026 05:52:38 -0700 (PDT) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH 4/4] oe.fitimage: Add custom configurations and harden multi-kernel handling Date: Tue, 20 Oct 2026 14:52:33 +0200 Message-ID: <20261020125233.69997-5-vgharaee@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261020125233.69997-1-vgharaee@gmail.com> References: <20261020125233.69997-1-vgharaee@gmail.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by lin-tor-korg-lkml-alma10-1 with HTTPS for ; Tue, 20 Oct 2026 13:04:38 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/248283 From: Vahid Gharaee One can now add configs that point to any kernel node and, optionally, a DTB with fitimage_emit_section_config_custom(). The default config still can be chosen by name with fitimage_set_default_config(). The default configuration is now stored and written to the ITS once, in write_its_file(). Before, fitimage_set_default_config() had to overwrite the property that fitimage_emit_section_config() had already set. DTBs and DTB symlink aliases are now looked up by the id they were added with. Before, the lookup depended on the "fdt-" node name or the alias name. Tested with oe-selftest -r fitimage.FitImagePyTests. Signed-off-by: Vahid Gharaee --- meta/classes-recipe/kernel-fit-image.bbclass | 129 +++++++++++++++++-- meta/lib/oe/fitimage.py | 66 +++++++++- 2 files changed, 175 insertions(+), 20 deletions(-) diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass index 7b2bcd4673..4abdab0a4a 100644 --- a/meta/classes-recipe/kernel-fit-image.bbclass +++ b/meta/classes-recipe/kernel-fit-image.bbclass @@ -29,20 +29,52 @@ python () { d.appendVarFlag('do_compile', 'depends', ' virtual/dtb:do_populate_sysroot') d.setVar('EXTERNAL_KERNEL_DEVICETREE', "${RECIPE_SYSROOT}/boot/devicetree") - # Parse loadables config - loadables = d.getVar('FIT_LOADABLES') - if loadables: - # Check that the loadables configuration variables are not set directly - # directly, then assign them a synthetic value for testability purposes - for v in ['FIT_LOADABLE_ARCH', 'FIT_LOADABLE_COMPRESSION', - 'FIT_LOADABLE_DESCRIPTION', 'FIT_LOADABLE_ENTRYPOINT', - 'FIT_LOADABLE_FILENAME', 'FIT_LOADABLE_LOADADDRESS', - 'FIT_LOADABLE_OS', 'FIT_LOADABLE_TYPE']: - if d.getVar(v): - raise bb.parse.SkipRecipe("You cannot use %s as a variable, you can only set flags." % v) - - synt_value = " ? ".join([ d.getVarFlag(v, loadable) or "" for loadable in loadables.split() ]) - d.setVar(v, synt_value) + # Parse loadables, additional kernels and additional configurations + for items_var, flag_vars in ( + ('FIT_LOADABLES', ['FIT_LOADABLE_ARCH', 'FIT_LOADABLE_COMPRESSION', + 'FIT_LOADABLE_DESCRIPTION', 'FIT_LOADABLE_ENTRYPOINT', + 'FIT_LOADABLE_FILENAME', 'FIT_LOADABLE_LOADADDRESS', + 'FIT_LOADABLE_OS', 'FIT_LOADABLE_TYPE']), + ('FIT_KERNELS', ['FIT_KERNEL_COMPRESSION', 'FIT_KERNEL_DEPLOYDIR', + 'FIT_KERNEL_DESCRIPTION', 'FIT_KERNEL_ENTRYPOINT', + 'FIT_KERNEL_FILENAME', 'FIT_KERNEL_LOADADDRESS', + 'FIT_KERNEL_MULTICONFIG', 'FIT_KERNEL_RECIPE', + 'FIT_KERNEL_TYPE']), + ('FIT_CONFIGS', ['FIT_CONFIG_DTB', 'FIT_CONFIG_KERNEL'])): + items = d.getVar(items_var) + if items: + # Check that the configuration variables are not set directly, + # then assign them a synthetic value for testability purposes + for v in flag_vars: + if d.getVar(v): + raise bb.parse.SkipRecipe("You cannot use %s as a variable, you can only set flags." % v) + + synt_value = " ? ".join([ d.getVarFlag(v, item, False) or "" for item in items.split() ]) + d.setVar(v, synt_value) + + # Additional kernels may be deployed by another recipe, by a recipe of + # another multiconfig or be provided as prebuilt files + kernels = (d.getVar('FIT_KERNELS') or "").split() + if "kernel-1" in kernels: + bb.fatal("FIT_KERNELS must not contain kernel-1, it is reserved for virtual/kernel") + duplicates = sorted(set(k for k in kernels if kernels.count(k) > 1)) + if duplicates: + bb.fatal("FIT_KERNELS contains duplicate entries: %s" % ' '.join(duplicates)) + for kernel in kernels: + recipe = d.getVarFlag('FIT_KERNEL_RECIPE', kernel) + mc = d.getVarFlag('FIT_KERNEL_MULTICONFIG', kernel) + if mc: + if not d.getVarFlag('FIT_KERNEL_DEPLOYDIR', kernel): + bb.fatal("FIT_KERNEL_DEPLOYDIR[%s] must be set if FIT_KERNEL_MULTICONFIG[%s] is set" % (kernel, kernel)) + d.appendVarFlag('do_compile', 'mcdepends', ' mc:%s:%s:%s:do_deploy' % + (d.getVar('BB_CURRENT_MC'), mc, recipe or 'virtual/kernel')) + elif recipe: + d.appendVarFlag('do_compile', 'depends', ' %s:do_deploy' % recipe) + else: + if not d.getVarFlag('FIT_KERNEL_FILENAME', kernel): + bb.fatal("FIT_KERNEL_FILENAME[%s] must be set if neither FIT_KERNEL_RECIPE[%s] nor FIT_KERNEL_MULTICONFIG[%s] is set" % (kernel, kernel, kernel)) + kernel_path = fit_kernel_path(d, kernel) + d.appendVarFlag('do_compile', 'file-checksums', ' %s:%s' % (kernel_path, os.path.exists(kernel_path))) } do_configure[noexec] = "1" @@ -62,6 +94,30 @@ def apply_dtbvendored(d,dtb): mapped_name = dtb.replace('/', '_') return mapped_name +def fit_kernel_path(d, kernel): + filename = d.getVarFlag('FIT_KERNEL_FILENAME', kernel) or "linux.bin" + deploydir = d.getVarFlag('FIT_KERNEL_DEPLOYDIR', kernel) or d.getVar('DEPLOY_DIR_IMAGE') + return os.path.join(deploydir, filename) + +def fit_kernel_deployed_files(d, recipe): + """Return the real paths of all files deployed by do_deploy of recipe (PN or PROVIDES) in this multiconfig""" + import bb.runqueue + mc = d.getVar('BB_CURRENT_MC') or "" + taskdepdata = d.getVar('BB_TASKDEPDATA', False) + pns = set(dep.pn for tid, dep in taskdepdata.items() + if dep.taskname == "do_deploy" and bb.runqueue.mc_from_tid(tid) == mc and + (dep.pn == recipe or recipe in dep.provides)) + if len(pns) != 1: + bb.fatal("Could not identify the do_deploy dependency providing %s (found: %s)" % (recipe, ' '.join(sorted(pns)) or "none")) + pn = pns.pop() + for arch in d.getVar('SSTATE_ARCHS').split(): + manifest = os.path.join(d.getVar('SSTATE_MANIFESTS'), "manifest-%s-%s.deploy" % (arch, pn)) + if os.path.exists(manifest): + with open(manifest) as f: + return set(os.path.realpath(line) for line in f.read().splitlines() if line and not line.endswith('/')) + bb.fatal("No deploy manifest found for %s" % pn) +fit_kernel_deployed_files[vardepsexclude] = "BB_TASKDEPDATA SSTATE_ARCHS" + python do_compile() { import shutil import oe.fitimage @@ -95,6 +151,39 @@ python do_compile() { d.getVar('UBOOT_LOADADDRESS'), d.getVar('UBOOT_ENTRYPOINT'), d.getVar('UBOOT_MKIMAGE_KERNEL_TYPE'), d.getVar("UBOOT_ENTRYSYMBOL")) + # Prepare additional kernel image sections + for kernel in d.getVar('FIT_KERNELS').split(): + recipe = d.getVarFlag('FIT_KERNEL_RECIPE', kernel) + mc = d.getVarFlag('FIT_KERNEL_MULTICONFIG', kernel) + kernel_path = fit_kernel_path(d, kernel) + if not os.path.exists(kernel_path): + bb.fatal("File for kernel %s not found at %s" % (kernel, kernel_path)) + # Artifacts of another multiconfig cannot be verified, its FIT_KERNEL_DEPLOYDIR is trusted + deployed = None + if recipe and not mc: + deployed = fit_kernel_deployed_files(d, recipe) + if os.path.realpath(kernel_path) not in deployed: + bb.fatal("File for kernel %s at %s is not deployed by %s" % (kernel, kernel_path, recipe)) + kernel_file = "linux-%s.bin" % kernel + shutil.copyfile(kernel_path, kernel_file) + + kernel_comp = d.getVarFlag('FIT_KERNEL_COMPRESSION', kernel) + if not kernel_comp: + kernel_comp = "none" + # linux_comp only describes linux.bin written by kernel-fit-extra-artifacts of the same recipe + if (recipe or mc) and os.path.basename(kernel_path) == "linux.bin": + kernel_comp_path = os.path.join(os.path.dirname(kernel_path), "linux_comp") + if not os.path.exists(kernel_comp_path) or \ + (deployed is not None and os.path.realpath(kernel_comp_path) not in deployed): + bb.fatal("%s not found or not deployed by the kernel recipe, set FIT_KERNEL_COMPRESSION[%s]" % (kernel_comp_path, kernel)) + with open(kernel_comp_path) as kernel_comp_f: + kernel_comp = kernel_comp_f.read().strip() + root_node.fitimage_emit_section_kernel(kernel, kernel_file, kernel_comp, + d.getVarFlag('FIT_KERNEL_LOADADDRESS', kernel) or d.getVar('UBOOT_LOADADDRESS'), + d.getVarFlag('FIT_KERNEL_ENTRYPOINT', kernel) or d.getVar('UBOOT_ENTRYPOINT'), + d.getVarFlag('FIT_KERNEL_TYPE', kernel) or d.getVar('UBOOT_MKIMAGE_KERNEL_TYPE'), + description=d.getVarFlag('FIT_KERNEL_DESCRIPTION', kernel) or "Linux kernel") + # Prepare a DTB image section kernel_devicetree = d.getVar('KERNEL_DEVICETREE') external_kernel_devicetree = d.getVar("EXTERNAL_KERNEL_DEVICETREE") @@ -210,6 +299,18 @@ python do_compile() { # Generate the configuration section root_node.fitimage_emit_section_config(default_dtb, d.getVar("FIT_CONF_MAPPINGS")) + # Generate additional configurations, e.g. referring to additional kernels + for conf in d.getVar('FIT_CONFIGS').split(): + conf_dtb = d.getVarFlag('FIT_CONFIG_DTB', conf) + if conf_dtb: + conf_dtb = apply_dtbvendored(d, conf_dtb) + root_node.fitimage_emit_section_config_custom(conf, + d.getVarFlag('FIT_CONFIG_KERNEL', conf), conf_dtb) + + fit_conf_default = d.getVar('FIT_CONF_DEFAULT') + if fit_conf_default: + root_node.fitimage_set_default_config(fit_conf_default) + # Write the its file root_node.write_its_file(itsfile) diff --git a/meta/lib/oe/fitimage.py b/meta/lib/oe/fitimage.py index 91ccc31d46..c062413f52 100644 --- a/meta/lib/oe/fitimage.py +++ b/meta/lib/oe/fitimage.py @@ -147,8 +147,11 @@ class ItsNodeConfiguration(ItsNode): 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. + One or more kernels can be added to the FIT image along with 0 or more + device trees and 0 or 1 ramdisk. The first kernel added is the default + kernel referenced by the automatically generated configurations. + Additional configurations referring to other kernels can be added with + fitimage_emit_section_config_custom(). 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. @@ -192,6 +195,9 @@ class ItsNodeRootKernel(ItsNode): self._dtbs = [] self._dtb_alias = [] + # DTB ids as passed to fitimage_emit_section_dtb(_alias) mapped to their nodes + self._dtbs_by_id = {} + self._default_conf = None self._kernel = None self._kernels = {} self._ramdisk = None @@ -213,11 +219,18 @@ class ItsNodeRootKernel(ItsNode): bb.fatal("FIT image signing is enabled for individual images but no image key name is provided.") def write_its_file(self, itsfile): + if self._default_conf: + self.configurations.add_property('default', self._default_conf) with open(itsfile, 'w') as f: f.write("/dts-v1/;\n\n") self.emit(f, 0) + def _check_image_id_unique(self, image_id): + if any(node.name == image_id for node in self.images.sub_nodes): + bb.fatal("Image node '%s' already exists in the ITS file." % image_id) + def its_add_node_image(self, image_id, description, image_type, compression, opt_props): + self._check_image_id_unique(image_id) image_node = ItsNodeImage( image_id, self.images, @@ -242,6 +255,7 @@ class ItsNodeRootKernel(ItsNode): return image_node def its_add_node_dtb(self, image_id, description, image_type, compression, opt_props, compatible): + self._check_image_id_unique(image_id) dtb_node = ItsNodeDtb( image_id, self.images, @@ -267,7 +281,7 @@ class ItsNodeRootKernel(ItsNode): return dtb_node def fitimage_emit_section_kernel(self, kernel_id, kernel_path, compression, - load, entrypoint, mkimage_kernel_type, entrysymbol=None): + load, entrypoint, mkimage_kernel_type, entrysymbol=None, description="Linux kernel"): """Emit the fitImage ITS kernel section""" if kernel_id in self._kernels: bb.fatal("Kernel section '%s' already exists in the ITS file." % kernel_id) @@ -290,7 +304,7 @@ class ItsNodeRootKernel(ItsNode): kernel_node = self.its_add_node_image( kernel_id, - "Linux kernel", + description, mkimage_kernel_type, compression, opt_props, @@ -303,6 +317,8 @@ class ItsNodeRootKernel(ItsNode): 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""" + if dtb_id in self._dtbs_by_id: + bb.fatal("DTB '%s' already exists in the ITS file." % dtb_id) load=None dtb_ext = os.path.splitext(dtb_path)[1] if dtb_ext == ".dtbo": @@ -332,9 +348,12 @@ class ItsNodeRootKernel(ItsNode): compatible ) self._dtbs.append(dtb_node) + self._dtbs_by_id[dtb_id] = 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""" + if dtb_alias_id in self._dtbs_by_id: + bb.fatal("DTB '%s' already exists in the ITS file." % dtb_alias_id) # Preserve the DTB's compatible string to be added to the configuration node compatible = None if add_compatible: @@ -343,6 +362,7 @@ class ItsNodeRootKernel(ItsNode): 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) + self._dtbs_by_id[dtb_alias_id] = dtb_alias_node def fitimage_emit_section_boot_script(self, bootscr_id, bootscr_path): """Emit the fitImage ITS u-boot script section""" @@ -567,13 +587,47 @@ class ItsNodeRootKernel(ItsNode): ) bb.fatal("FIT configuration mapping(s) not matched with any DTB: %s" % ', '.join(unused_conf)) else: - # Currently exactly one kernel is supported. + # Without DTBs, emit one configuration for the default kernel 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) + self._default_conf = default_conf + + def fitimage_emit_section_config_custom(self, conf_id, kernel_id=None, dtb_id=None): + """Emit an additional configuration node for a specific kernel and optional DTB + + kernel_id is the name of a kernel image node, the default kernel is used if not set. + dtb_id is the id passed to fitimage_emit_section_dtb or fitimage_emit_section_dtb_alias. + """ + conf_node_name = self._conf_prefix + conf_id + if any(conf.name == conf_node_name for conf in self.configurations.sub_nodes): + bb.fatal("FIT configuration '%s' already exists in the ITS file." % conf_node_name) + + kernel = None + if kernel_id: + kernel = self._kernels.get(kernel_id) + if not kernel: + bb.fatal("FIT configuration '%s' refers to unknown kernel '%s'. Available kernels: %s" % + (conf_node_name, kernel_id, ' '.join(self._kernels))) + + dtb = None + if dtb_id: + dtb = self._dtbs_by_id.get(dtb_id) + if not dtb: + bb.fatal("FIT configuration '%s' refers to unknown DTB '%s'. Available DTBs: %s" % + (conf_node_name, dtb_id, ' '.join(self._dtbs_by_id))) + + self._fitimage_emit_one_section_config(conf_node_name, dtb, kernel) + + def fitimage_set_default_config(self, conf_id): + """Select the default configuration, overriding the one chosen by fitimage_emit_section_config""" + conf_node_name = self._conf_prefix + conf_id + if not any(conf.name == conf_node_name for conf in self.configurations.sub_nodes): + bb.fatal("FIT default configuration '%s' does not exist. Available configurations: %s" % + (conf_node_name, ' '.join(conf.name for conf in self.configurations.sub_nodes))) + self._default_conf = conf_node_name def run_mkimage_assemble(self, itsfile, fitfile): cmd = [