From patchwork Thu Nov 5 16:48:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 103162 Received: from mail-wm1-f50.google.com (mail-wm1-f50.google.com [209.85.128.50]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.631.1793897300020588161 for ; Thu, 05 Nov 2026 08:48:20 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20260707 header.b=HipSNrJL; spf=pass (domain: gmail.com, ip: 209.85.128.50, mailfrom: vgharaee@gmail.com) Received: by mail-wm1-f50.google.com with SMTP id 5b1f17b1804b1-4a2153c8525so14582485e9.3 for ; Thu, 05 Nov 2026 08:48:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793897298; x=1794502098; 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=Gr2z0lSEdi/qlkh17yOTTROqhkNmkqALo8HOCCZw8bs=; b=HipSNrJL8eP5WZxKdnkrLwSMvJqZ2J8Ls6bSiz2vuGJNDS0eZu/9+FL4oPA9TFPBNd bJjxkc7yHB8y8Kc6zmnBj6xOYl5KaekYlmG8ozYT+ZeauEBKViGxZi+KGwb32czlaOb1 tlKFSid0nPO/Y2LBWwR/hFsQJSDyCJm5OFyCDQO93joy1MqjXo3rHGkdb8wKvJEanXh6 e2Gi7EdeQ7d+mu7M5Se+QvqY+FRrivrZiLaurAddOQ7KDZwUQV3MriAPlASkfoYWVyWm X3sK54vhhexJ+rJ+Vtc1Sqx4RcN5VpdWwTyQue2ZwF47LyFh6hScTF1zmk+DQLkH5aaC c0zA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793897298; x=1794502098; 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=Gr2z0lSEdi/qlkh17yOTTROqhkNmkqALo8HOCCZw8bs=; b=u3SG+BPXaGbZq9yIffWiVDrWV65XL54HnW9byZbg/XYHAOkzhsi5Y1qCRDAVbKEMVO SnseY4lp1m2tQErt45r2Im/dp/ZvsGD2KWM4Wbs98N1JHp54nqfeAni28oCZy0jbdEjE RzLvy4n1OZxgSXGkMMyTuh087VMAZo3RtNM4Qcwccw7sDQ71ctX9XIgNHzleNE0GxA4k 7lMlCKn6n/IOOfsP5ZBSwALkn8qf54MTszNm76BPu8xudSf56Hbt9W6dEshgcW2kDOHg f3XXbS62xDs2J3pLR9VQzDWkbEDlipdy/GVtYIbv+DPnjhYroGZrCIq9Dszr0ygdSonS z/0w== X-Gm-Message-State: APuU4Z+AOS5hc3fzP0C7lJPMCiRncv1zAHLc11Tqm4BFg8KDKCF37pZG L4nwZHx/l8XqAjUQmIz6H0F1DihiNBu1yZumt8iox/1EECdItTwpeeJkuaetsQ== X-Gm-Gg: AYBFou3n6BbXLsZu8NGT0+q+n48nyQMieC44o04h8WEzSS6JzlDwGAESUUA2FrbOvPP vAI6cpTpkzyWqhaqvfDI8yrPX671XFcFHkV0nl9DK6FWyDVEy1Omjc1am1BairBWSc0o7mWI6wC 0qyz869/AE2oMPz/e5pt5OlJnxxsMxzeR35U2DWmLkOiRp3E2cpjgMXBh2rTuagXnw+Ld6yPBTq ihuFF87QB7TAJF9J8SiyEWH4p9h2zyDHlotTuRY3wLVW9SEnYkHz66NNQbaoAww+j1/I9DwVjdJ 2wYj0Yh4x3kRz6ICw6VQ0uOqYVViwpHF2gVE+b1JMto3oUgvzEhxefFwmWHJnGNsgtVll47Bez2 nOVSuGgC784q9NKzJW25xvIODxboMR01fZaKxXBh+QrcUHehSlaFZ0wJrOpyR40byoloey5ELxA qTwCUu4Z8II1Zn2UbbBNpzjOePeeVWHN9gCRqmaSXsZPUpUZn6qFU+6kdI/sJj+EvVRvPcQfsjV B5xqBRS/C6P1th4dni2hnCaTqLK8vQnqN8WcqVd4rIU2gLn0wujJer7cNqn8uEgNRc6fitZ6V7T COltRgpoAVpwXtKKEBs//R24R+cpc2z/VsrLn4HNfKbQewEPCc3pLgxjAj/a8xuQ41rBsQ== X-Received: by 2002:a05:600c:3b8f:b0:4a6:42cc:bf66 with SMTP id 5b1f17b1804b1-4ac16acfdd5mr456708145e9.11.1793897298255; Thu, 05 Nov 2026 08:48:18 -0800 (PST) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4b63863d490sm42536975e9.2.2026.11.05.08.48.17 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Nov 2026 08:48:17 -0800 (PST) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH v4 1/6] oe.fitimage: Support multiple kernel nodes and configuration selection Date: Thu, 5 Nov 2026 17:48:21 +0100 Message-ID: <20261105164826.1340446-2-vgharaee@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261105164826.1340446-1-vgharaee@gmail.com> References: <20261105164826.1340446-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 ; Thu, 05 Nov 2026 19:57:58 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/249572 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 Thu Nov 5 16:48:22 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 103161 Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.554.1793897302483771753 for ; Thu, 05 Nov 2026 08:48:22 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20260707 header.b=KWG0G+Yv; spf=pass (domain: gmail.com, ip: 209.85.128.41, mailfrom: vgharaee@gmail.com) Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-4b467581b98so15218685e9.0 for ; Thu, 05 Nov 2026 08:48:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793897301; x=1794502101; 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=mFloBapDVcd3+/2Z7S2vb+KqfQUEwJ4Xpx4+fHx4jMw=; b=KWG0G+Yv0nZwNQ10psTvqYTVXKET3Pi64Tz0ardRnLKp26WOhKw8awz2QlfyY9TsUd fO2zCOEAG8Je+HWSWTSu70OakXYwXCsez2gjtQ9XbdQZYaFfdfPMHhSronfmHtEKt3WB ZUQvNGV+QLZcyaQklvii4jzotECMPzw54QQgTmzuHdvDmm4yqqGHoOf1cMzl9lKd1WlW SsaEPYkH+r6IRTwjkJIZXGMVV3WJ3UluP+viKAI8MtjrNwSP0MYsumSujsi9dgAAEPxD YzdH1NGVLYVzxNM1UcwrqwetQqrExZxI501SBAT06o95pm+ulzsZAG1iRuElpvyOWsXy bG2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793897301; x=1794502101; 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=mFloBapDVcd3+/2Z7S2vb+KqfQUEwJ4Xpx4+fHx4jMw=; b=uDosn34xpMizeo22BzQEKuOeOzB0FJHy8IbRh4ZzrzUupHaNvZh8vhlP3mhiJBA0Fy p0LMh0a+DcO2iOI+3ULaE7Jec5jFR8QRrp4UuMJ517D7IZKIekc7TJd2GYYVNBmK+Rhe pWrOkVTmUkWlm/5WXtNen6OzZi4D+d05n/lD0GzoJPBYvDuoNVssdV3QkfSI2Rt4Tjpu LneyjW6rkNFClnnWVGhnOFZOWduxdXJMBtmThdkWvzSvaerPLsGgGrIgLX0eoxrx3/T8 GywP3dhzCNyksxFE3m43nXHP6cDpgFn1jFz3DVrIygGXtgFVIUTL+xHEI/Sz4aBpSM3L oGsQ== X-Gm-Message-State: APuU4Z8tKBIeLw9+XJDdx6U3Jbfh+jWnP9Xh3wuy0qrF9PucgWWgqLM+ 8MXJ8r0i/veUpG+QfEBPTkNYBXJwnnbHY7x990r8K0SwEl8W5NjSu7iIGZi+EQ== X-Gm-Gg: AYBFou0+1cbZDrBxVmgxZmMszB+ihHjSMyfZfhUnltD/GoTEyHabnebpNgNmoqkppoE heZpwUZHSz/xpYEaSWeRBjZipbntvNHQE90pDtbuRv/8Rv7n4frIATK84kS4gzoj0jofAx/ZOmQ owhVtlIfPFCwytzPtkDZBKmeVwqU7jHP2WhNZS9JsE1GlAz6YBLvQoi0SSoe/1o6PcvFIS8dEPA JEWCCY3hNaQAY0K5vkzj/9EYZ9OWHpzglz1DSYlDJQ3ni8xRIFUC2u+dDYvACq/OlPVZYKbT/i8 PE/yRjGDhi/B7z05Q/IXwchSjBTiqBuk18szOUF9XlzOTXnrjHQZr1HQIZvVUu/sA0Bfx5SF9sP zWY3dRY8KeSHQU3+sUTj+4ZetgAF2QjrU0gRbHJbHbV9vwgKV5jW6W+J+Wivztgy8aCFLJxYGXn NtdnOws+WmDpqcX36FUC7ZpquPMjfoaDcar7/Xj/8pH3nANyMF9Enck1eWpieJ7rYLTV3TEqfWl weOb8Knj0qj0lmoetjk9W67ibC+idnGLbnkOdQSrRQSSujF9KXyaNZcVIsIEJO3mwVhk6HXrinA B/4iE8n1mWrMjOgho6C0/lRjkUInenNNnQZ93ldH99dz+Lu6gRzyD+Bd9ppIrpTYDWVRItYknYH qrDlx8QL6Yg9efQ== X-Received: by 2002:a05:600c:3b1a:b0:4a8:b8b4:6226 with SMTP id 5b1f17b1804b1-4ac18c73d17mr435071245e9.28.1793897300650; Thu, 05 Nov 2026 08:48:20 -0800 (PST) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4b63863d490sm42536975e9.2.2026.11.05.08.48.19 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Nov 2026 08:48:20 -0800 (PST) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH v4 2/6] oe.fitimage: add metadata for multiple kernels Date: Thu, 5 Nov 2026 17:48:22 +0100 Message-ID: <20261105164826.1340446-3-vgharaee@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261105164826.1340446-1-vgharaee@gmail.com> References: <20261105164826.1340446-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 ; Thu, 05 Nov 2026 19:57:58 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/249573 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 Thu Nov 5 16:48:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 103157 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.633.1793897304700127406 for ; Thu, 05 Nov 2026 08:48:25 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20260707 header.b=mjMa99Mf; spf=pass (domain: gmail.com, ip: 209.85.128.42, mailfrom: vgharaee@gmail.com) Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-4a5087a5348so16405085e9.0 for ; Thu, 05 Nov 2026 08:48:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793897303; x=1794502103; 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=yqArQJrXu/3ruyqwO7InglEIi9Wi9COdxz3EdAaSSSk=; b=mjMa99MfTNxHRRAbUp/7kG/XNZQwuSBrNpOWfH0xNEJN6XtaEkjE5ekG+vLyw2hJBQ fxALg8BQL6MJYdeJsPvUbWFZUPmDphUj1Y275B7WgBLfl/u4dZCL9Hhn3sRFor6DfgmX dHQFkoN2HEPgIfcKZmxhOxBxQoEr50H+WFVXJbEFxgRrIVXMmbd6i/mjIfkYS/8r7J9N RnJA7LprZAeHWmx3sZT75OfGzxboOoYpNVJfSChojcpnKGUQ2JbEjf/GnNCFGZ2OdDsO uuA0PDcGegYW2l5zFqW4A9F2lvvzMcg/DIjQIoT8xcgGwraaS35ZLlMMD0KbT/geSGhC UxIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793897303; x=1794502103; 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=yqArQJrXu/3ruyqwO7InglEIi9Wi9COdxz3EdAaSSSk=; b=xl65jXeleSiIp8RCxYVSwNmej4YylMO9KoKba3xuNKsdjbtbc+sypn5fRZ9yqCR2Wk ETA68l38007HG1zDv1+5wMukrFKg825I4rRWEGJx88L1KwevtcshObX3bjZwBs2mDu4f tcB1KgoAIHVI9CWySvhluxy5kq6TraZhtxz5RgAZsi4rYWDzye4133fr8prbOxqgPM+j 57L720S8VndQcRyWLFcDhLKnkyobmrtDXNWcXyJO0N402dLkrd0b0jzhqiUapyBZ57e5 FMuINHTpcDgKL+DlmxRhxD4NBgOoIAvZYLKQnjv/vqfUj9U9VOc/W07zVMB0sYHNEzUf 7utg== X-Gm-Message-State: APuU4Z8htD3Pvs4/Nx+ByJN+AEkdH+uIrbD4l+hc+8Ujgcfj2Qub7Uu8 gBddyEJTZo74Hl2MjIdDWDA1qoaWxh2ECaEY3GVQylF0n7X68Faog3QJlTotlg== X-Gm-Gg: AYBFou3tkpucQ3ulViXDQzLIZ9f1Db4viVJueq7SaEvGMfi1RYjMzUebbeWlgiTXWcN 5TenbdVxNxYXcrfaJ4j/HR01pF9kEcfGBnO3Kqv78zyPcrN1ii8sm4MWwAB39BVOsVYeMjH0Ss3 CjeyIh4tH/xxLg9sN1p230v5mwAAaZSjcGpNzsZmRkW+VQrn+hqAJNxuDnf32BouR0hgQt8FvXk 38rMgJTRdyiL6CefJsaInCOvpgWpdcYkWrJFj69cswaEZLdSGBqfEW8tqJymz7QNu+VWD8Ul1Ev Z2Z6uokSOUFXT1kybjQdAmDV9Zc8T/QB9a5WMO6kNMdFd42D0bDwHM55q6OTreH3ZI/0ZB4nlMB qMCOWfPIR1xMOSBTBpSsJyoRZYCpMWIq2sQG72yD6oN8N8uSxIt4cMs3Vyi1x9YFpySdKxbU6Lg P6gBJm8zWBDCZE8u3V7XCLwXcaEE96BCio5UQRUFRc9OwXPEXlxOjMnCtNp0LFOj0G4s7ASZkcH U0rIOk4FkVJXltaoqDSF8Y89I9TQB6TcUapT1XyQJXQNz5urkUs24o9lln5v1Ky4ndxSm/HpQGp uFDzzfzTsxsb+37lXbBblpj8M37BHJLYQvHtzG9qdT5jNfeo3lUdB/zD9emRr8To94V1 X-Received: by 2002:a05:600c:1c0f:b0:4a1:ac66:6059 with SMTP id 5b1f17b1804b1-4ac19233381mr435384635e9.26.1793897302723; Thu, 05 Nov 2026 08:48:22 -0800 (PST) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4b63863d490sm42536975e9.2.2026.11.05.08.48.21 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Nov 2026 08:48:22 -0800 (PST) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH v4 3/6] oe.selftest: Add tests for multi-kernel FIT config Date: Thu, 5 Nov 2026 17:48:23 +0100 Message-ID: <20261105164826.1340446-4-vgharaee@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261105164826.1340446-1-vgharaee@gmail.com> References: <20261105164826.1340446-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 ; Thu, 05 Nov 2026 19:57:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/249574 From: Vahid Gharaee Add selftests for FIT images containing multiple kernel nodes and configurations. AI-Generated: Uses GitHub copilot (Claude Opus 5.5) Signed-off-by: Vahid Gharaee --- meta/lib/oeqa/selftest/cases/fitimage.py | 163 ++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 1fad53b16c..40431184de 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -461,6 +461,39 @@ class FitImageTestCase(OESelftestTestCase): loadables += var_loadables.split() return loadables + @staticmethod + def _get_flag_values(bb_vars, var, items): + """Return per-item values from BitBake flags or synthetic test values.""" + values = bb_vars.get(var) or {} + + if isinstance(values, dict): + return {item: values.get(item) or "" for item in items} + + # Support synthetic values used by the Python-only test fixtures. + values = values.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. @@ -672,11 +705,14 @@ class KernelFitImageBase(FitImageTestCase): internal_used = { 'DEPLOY_DIR_IMAGE', 'FIT_CONF_DEFAULT_DTB', + 'FIT_CONF_DEFAULT', 'FIT_CONF_MAPPINGS', 'FIT_CONF_PREFIX', + 'FIT_CONFIGS', 'FIT_DESC', 'FIT_HASH_ALG', 'FIT_KERNEL_COMP_ALG', + 'FIT_KERNELS', 'FIT_LOADABLES', 'FIT_OS', 'FIT_SIGN_ALG', @@ -837,6 +873,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: @@ -886,6 +924,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: @@ -928,8 +968,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) + '";') @@ -953,6 +997,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): @@ -1064,6 +1114,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: @@ -1212,6 +1271,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(flags={'FIT_CONFIG_KERNEL': ['second'], 'FIT_CONFIG_DTB': ['second'],}) + self._test_fitimage(bb_vars) + def test_get_compatible_from_dtb(self): """Test the oe.fitimage.get_compatible_from_dtb function @@ -1505,11 +1590,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", @@ -1570,6 +1658,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), @@ -1600,9 +1693,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) @@ -1669,6 +1772,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 Thu Nov 5 16:48:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 103159 Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.635.1793897306593405018 for ; Thu, 05 Nov 2026 08:48:27 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20260707 header.b=rJYnhKQO; spf=pass (domain: gmail.com, ip: 209.85.128.53, mailfrom: vgharaee@gmail.com) Received: by mail-wm1-f53.google.com with SMTP id 5b1f17b1804b1-49d05d51553so13909385e9.2 for ; Thu, 05 Nov 2026 08:48:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793897305; x=1794502105; 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=wLo4MO57mFeEdguzm54nlZUyTtxgfw9IFek1ZO55BmY=; b=rJYnhKQOZLrBPWXFxyeaerjnZT3RkELpWsd54XCqFz0A5ufsGD3zgc1L6QoMEc4abY kFJq/oDJkeMZkamHwxGnZTgklojXfVfg8JESX2hkFi5hG2GAkl+c9d1/ERUyZjzm7pfu 1qESCj/fXEAch8TRA9JAMpdDcjQBw8ITNQIuMpFXNZZtBu94ZDh0tJp0908nye6Ld0+m O66HpJCBw+WUJGjqXNACVQJCKFlPPQ765v8emHyDsox6fwHA1RXyFiUQ7IkGhSqL38QG rvp67u+gbW25P56fGYPlxriRjy84BHCKcOBWGUPuNss3K5fhHe9T4DdlQ5jYMNos9i1f 2hJg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793897305; x=1794502105; 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=wLo4MO57mFeEdguzm54nlZUyTtxgfw9IFek1ZO55BmY=; b=fsFZEoPnorbEoqfE1VeoHbf7sGAx/6uR5q90AAWzUR8RJe+B5kjJA2e1ttTtZ6v6eL qxcOu98ZxuFRI/Z/rh2hdtJ+ZJrK1Jf87g17yHXCwJtCR9bNBnJ4EMFsT0j69AERUUAp Bd/vuIEoLRRInFVxEDKjTU46uP0XW/4mAB6cCtVKsYN2d9wQxuY7FesLeRNMYiAejNxv HjL/nayyZ2Xws3cFrwzR6CSCaKT/JD9bL5lqEuG46hFkdilhSBp2vgOG8LBEAderBd1M J4Py7EOHxp3bPdw526Ugjoy55mtTHgd3y1DLtpSffGAgrYBAgTAhL80OhcdytaYeB7Dw f1+w== X-Gm-Message-State: APuU4Z+vUf0x06kkp94F4JgWZd8X238iUqIyOmDKl32FPz505aqV3Owc Dxm9+4YObxxjJ2GUH2g1tQmJDWf/MsRndJyL3kLpypcVPy28yLBhij/fotUVuQ== X-Gm-Gg: AYBFou3Kh1+K5JDvSzznUsEBmsjjo7st3cRNGzkWRtBGs8IiYT+oxpzqLPx7o0SRuaf 0tudIR3r3jXFhIB0bgnciu1fHujpKQkY1rmi0a8Hu2VVYgHLRPPG5/BcTbHZpq+UjTkkgehjqkb XTfG8h3hvj/yZyBxRFUSonO9ApGKgL6O+8lzpX+VECtQR/8qBEY8vMDXBKxOjbVE/b586SX73go 8IC5gk7NDkXZ3ZJeNEVunyPG601wzpxGfaT2rKgHlIs+qk7sDFI4pffA/MDG3nIQgB7lX6JPaWZ VXODJOxyRc70U1RZySIaZ2p60K6/mbl7k8Sfd3H2L/VNoTESlFFIu0LU3XG72Oc1af2MhIet2BH vPVtoZwt5QD4s3YPDNPcReFp18rk/yOBJok6v4F6uQly7n9lCx06xHcAraNRn1Rj5b/y1aCmbUM 4IyrnC0iDwDhBitAQVKWUSoJBjvSt173qccK6hpzxe5/3ho75UuNnToAa2lPtEq1iWDxOU0lSWQ 2tK4MSUglKVwOvjKDAHWPJP5NdGj14OmS7f6iVceEHarWbbRoG4ds+/7886Ol7RAFaVLqpJ+gM9 xEsxMWW1xHbprMEl1UzBi/8I2/+PYCrvZLQuHpHCBrPxwi751/RBjYylpC/MhkUxKqSP X-Received: by 2002:a05:600c:8b46:b0:4a0:4a8:d444 with SMTP id 5b1f17b1804b1-4ac18c73238mr440835575e9.33.1793897304616; Thu, 05 Nov 2026 08:48:24 -0800 (PST) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4b63863d490sm42536975e9.2.2026.11.05.08.48.23 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Nov 2026 08:48:24 -0800 (PST) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH v4 4/6] oe.fitimage: Add custom configurations and harden multi-kernel handling Date: Thu, 5 Nov 2026 17:48:24 +0100 Message-ID: <20261105164826.1340446-5-vgharaee@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261105164826.1340446-1-vgharaee@gmail.com> References: <20261105164826.1340446-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 ; Thu, 05 Nov 2026 19:57:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/249575 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. Reject explicit kernel selection for FIT overlays and validate configuration generation order. 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 | 85 ++++++++++-- meta/lib/oeqa/selftest/cases/fitimage.py | 37 ++++++ 3 files changed, 227 insertions(+), 24 deletions(-) diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass index abf7327c97..457654926c 100644 --- a/meta/classes-recipe/kernel-fit-image.bbclass +++ b/meta/classes-recipe/kernel-fit-image.bbclass @@ -29,19 +29,53 @@ 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: - # Only flags are supported. Add the vars to vardeps so their flags are - # part of the signature. - loadable_vars = ['FIT_LOADABLE_ARCH', 'FIT_LOADABLE_COMPRESSION', - 'FIT_LOADABLE_DESCRIPTION', 'FIT_LOADABLE_ENTRYPOINT', - 'FIT_LOADABLE_FILENAME', 'FIT_LOADABLE_LOADADDRESS', - 'FIT_LOADABLE_OS', 'FIT_LOADABLE_TYPE'] - for v in loadable_vars: - if d.getVar(v): - raise bb.parse.SkipRecipe("You cannot use %s as a variable, you can only set flags." % v) - d.appendVarFlag('do_compile', 'vardeps', ' ' + ' '.join(loadable_vars)) + # 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: + # Only variable flags are supported, not direct assignments. + 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 + ) + + # Include variable flags in the do_compile task signature. + d.appendVarFlag('do_compile', 'vardeps', ' ' + ' '.join(flag_vars)) + + # 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" @@ -61,6 +95,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 @@ -94,6 +152,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") @@ -209,6 +300,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..55dfdc1737 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,7 +195,9 @@ class ItsNodeRootKernel(ItsNode): self._dtbs = [] self._dtb_alias = [] - self._kernel = None + # DTB ids as passed to fitimage_emit_section_dtb(_alias) mapped to their nodes + self._dtbs_by_id = {} + self._default_conf = None self._kernels = {} self._ramdisk = None self._bootscr = None @@ -213,11 +218,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 +254,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 +280,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,19 +303,18 @@ class ItsNodeRootKernel(ItsNode): kernel_node = self.its_add_node_image( kernel_id, - "Linux kernel", + description, mkimage_kernel_type, compression, opt_props, ) 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): """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 +344,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 +358,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""" @@ -440,7 +456,7 @@ class ItsNodeRootKernel(ItsNode): if dtb and os.path.splitext(dtb.name)[1] == ".dtbo": include_kernel = 0 - selected_kernel = kernel if kernel is not None else self._kernel + selected_kernel = kernel if kernel is not None else next(iter(self._kernels.values()), None) if selected_kernel and include_kernel: conf_desc.append("Linux kernel") @@ -510,6 +526,9 @@ class ItsNodeRootKernel(ItsNode): ) def fitimage_emit_section_config(self, default_dtb_image=None, mappings=None): + if self.configurations.sub_nodes: + bb.fatal("Standard FIT configurations must be emitted before custom configurations") + if self._dtbs: dtb_extra_confs = [] dtb_confs = {} @@ -567,13 +586,57 @@ 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") + # The caller must emit standard configurations before custom ones. + # The first configuration is the default unless overridden by + # default_dtb_image or a later call to fitimage_set_default_config(). 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))) + + if kernel_id and dtb and os.path.splitext(dtb.name)[1] == ".dtbo": + bb.fatal( + "FIT configuration '%s' cannot select kernel '%s' " + "with device tree overlay '%s'" % + (conf_node_name, kernel_id, dtb_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 = [ diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 40431184de..1b99d6bac8 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -1580,6 +1580,43 @@ KERNEL_DTBVENDORED = "1" class FitImagePyTests(KernelFitImageBase): """Test cases for the fitimage.py module without calling bitbake""" + def test_custom_config_rejects_kernel_with_overlay(self): + """An overlay configuration must not silently ignore an explicit kernel.""" + root = oe.fitimage.ItsNodeRootKernel( + "Test FIT", "1", "x86_64-poky-linux-", "x86", + "linux", "conf-", False, None, + "uboot-mkimage", "", "uboot-mkimage", "", + "sha256", "rsa2048", "pkcs-1.5", + None, False, None + ) + + root.fitimage_emit_section_kernel( + "kernel-1", "linux.bin", "none", + "0x20008000", "0x20008000", "kernel" + ) + + root.fitimage_emit_section_dtb( + "test-overlay.dtbo", "a-dir/test-overlay.dtbo" + ) + + with self.assertRaises(bb.BBHandledException): + root.fitimage_emit_section_config_custom( + "invalid", "kernel-1", "test-overlay.dtbo" + ) + + # A regular DTB may explicitly select a kernel. + root.fitimage_emit_section_dtb( + "test-board.dtb", "a-dir/test-board.dtb" + ) + + root.fitimage_emit_section_config_custom( + "regular", "kernel-1", "test-board.dtb" + ) + + # An overlay without an explicit kernel is valid. + root.fitimage_emit_section_config_custom( + "overlay", None, "test-overlay.dtbo" + ) def _test_fitimage_py(self, bb_vars_overrides=None): topdir = os.path.join(os.environ['BUILDDIR']) From patchwork Thu Nov 5 16:48:25 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 103160 Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.638.1793897308338952880 for ; Thu, 05 Nov 2026 08:48:28 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20260707 header.b=iR+FUP1L; spf=pass (domain: gmail.com, ip: 209.85.128.41, mailfrom: vgharaee@gmail.com) Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-4a9a8d6cea8so10015015e9.1 for ; Thu, 05 Nov 2026 08:48:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793897306; x=1794502106; 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=Ecpd7VjXpy349c9YrAuofwBcNeqquH4xixQHGn0hwx4=; b=iR+FUP1Lk/b+2xHe5cT1/qAgTymNz8bJj7gbtxrpH5K40r5kD7r2gS35GwelT7/vOu J1Z64GvieprvxPChnz992SCJH0SDQ7El0dH5zx0kEJjW0tEKXL7xmlCVTD6H5epaUysq /jSWlSVxcA0zGtJt9MGzXas2IAkojinKSszVJ2jWUCxdlFgUlboqL+r6Wq3EqE++mANr KDzR/yJo6lZJKZiU3GCtCunUzqK3j29Qi7xwrx8B/YeMa2IXULhZ+9o3Gartl+nfbvaL eokZbFyl5DA2qrqgMtLxY+e68acbaFCTEK+FbteHNniLfgx3NUs0IChZZKdh3W+XEFDY 43Qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793897306; x=1794502106; 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=Ecpd7VjXpy349c9YrAuofwBcNeqquH4xixQHGn0hwx4=; b=BfUxVtpGI9aD3rTpYax8mK4Fr2FeKEQKbd6D9VZhOWFT4CBTNcHkEOIZ0NT73a7N01 qX/nGJrnYL7Ixy926pOXAoAEXt9uQmIQj114OoldfG/xyR7B14wHXE/V33gZ2M7n7+O7 ftZxgI6KhRkFROcEZzXYIaAIIvFMCKKOWMqDXBajIvBIN7Ro3dOfbdTyytqFk5X0+ZJI T2DZZpVzsWtTpvBAzT5O8c4OYULMMpjdwsKXNGdtRrXduAXMYRZX16+MuyXds2a+9s8f jUQ1th6xSx2clBDCHNbImzPBH3dxvsE7g2mRO1rvvTqJk3jGCgi7kmFKu/zuhSOpsVjc nx+Q== X-Gm-Message-State: APuU4Z84dZEnHPSc/J1C8K2ODuDcsghtl52seeMChXRmvUx1v2C4wG1w TNW8TRKWHFTm5SymwjqQARFD9N2RhBgKkONMKKMHkc+4pOR7AL8C8byHf45f+A== X-Gm-Gg: AYBFou2zdRStRqEIPG89PgQ3VSZl1a2Uq04pA1csIblyqoegyEVcxtnPJAbQuZzQ9nk qh3jOifS2AQLQvU2ENAN/M4k3/6l+XmUSz9un5JJfKFiJN/CXcAtVbNBWC4BlbVaO8/UKVgl5Vy K+hX2UeIiGp/S4QsJUtLMbIBLZkbzgAlCsn7LvNyY29CJKcFjixmwOJOSuS8taHoI4riOUSn4G6 UmiMkRVZd2qQ/xUxFwXhBTzXouk7IJcPTICHF3yTGOJyG0o/kziU0zqhjathc2B5XYjOxMs38Dv qbA4dZX7uhOIXEFeGoDpQ0oSyoIbx6Fsg9kJFX0LUlZXMth85zOeTt4wMbX+YD1OODmTgJ44WDV OivqKJ9WMz70CHAoKNa0pzQCYLjQtcyvK6rvMhaS33048rrEJ85opSRVhn0w5EzOgcYLYqxtOUo waKu+xbEgRCVP9f41Agc1/2uQBusU3L+LeOiso8YK2tPy/QYpu9uT1zqBo3qBYnD506GESHkuC1 2vgbIslinLHWpuS7i/gTYgtGi8TSRUEqG91qH12JFRHC7zKN8zU6QZbL1U+Hevf0qt8NEB1DmLf ZMv+ugvt9P4WaGe8btGY2fFzdTG6LAAeGUoajyUQLNsLu5TCstOyDVze71CJaIy2TreEoy+YUfN ofQw8zdmk0fkdZw== X-Received: by 2002:a05:600d:82e2:b0:4a1:c27d:e1a8 with SMTP id 5b1f17b1804b1-4b55d7d7a2amr62251835e9.10.1793897306572; Thu, 05 Nov 2026 08:48:26 -0800 (PST) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4b63863d490sm42536975e9.2.2026.11.05.08.48.25 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Nov 2026 08:48:25 -0800 (PST) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH v4 5/6] oe.selftest: Improve FIT DTB alias validation Date: Thu, 5 Nov 2026 17:48:25 +0100 Message-ID: <20261105164826.1340446-6-vgharaee@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261105164826.1340446-1-vgharaee@gmail.com> References: <20261105164826.1340446-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 ; Thu, 05 Nov 2026 19:57:58 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/249576 From: Vahid Gharaee Use explicit DTB alias targets instead of relying on FIT node naming conventions. Discover external DTBs and symlink aliases when generating expected FIT configurations, including vendored DTB names. Signed-off-by: Vahid Gharaee --- meta/lib/oeqa/selftest/cases/fitimage.py | 80 ++++++++++++++++-------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 1b99d6bac8..8ff75dd3da 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -432,6 +432,30 @@ class FitImageTestCase(OESelftestTestCase): mapped_name = dtb.replace('/', '_') return mapped_name + @staticmethod + def _get_test_dtb_files(): + return ( + ["test-ext.dtb", "test-overlay.dtbo"], + {"test-ext-alias.dtb": "test-ext.dtb"}, + ) + + @staticmethod + def _get_external_dtb_files(devicetree_dir): + dtbs = [] + aliases = {} + + for name in sorted(os.listdir(devicetree_dir)): + if not name.endswith((".dtb", ".dtbo")): + continue + + target = oe.fitimage.symlink_points_below(name, devicetree_dir) + if target: + aliases[name] = target + else: + dtbs.append(name) + + return dtbs, aliases + @staticmethod def _get_dtb_files(bb_vars): """Return a list of devicetree names @@ -442,15 +466,22 @@ class FitImageTestCase(OESelftestTestCase): """ kernel_devicetree = bb_vars.get('KERNEL_DEVICETREE') all_dtbs = [] - dtb_symlinks = [] + dtb_aliases = {} + if kernel_devicetree: all_dtbs += [FitImageTestCase._apply_dtbvendored(bb_vars, 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 == "test-dtbs-as-ext": - all_dtbs += ["test-ext.dtb", "test-overlay.dtbo"] - dtb_symlinks.append("test-ext-alias.dtb") - return (all_dtbs, dtb_symlinks) + + if bb_vars.get("PREFERRED_PROVIDER_virtual/dtb") == "test-dtbs-as-ext": + if bb_vars.get("_python_only_test"): + external_dtbs, external_aliases = FitImageTestCase._get_test_dtb_files() + else: + devicetree_dir = os.path.join(bb_vars["DEPLOY_DIR_IMAGE"], "devicetree") + external_dtbs, external_aliases = FitImageTestCase._get_external_dtb_files(devicetree_dir) + + all_dtbs.extend(external_dtbs) + dtb_aliases.update(external_aliases) + + return all_dtbs, dtb_aliases @staticmethod def _get_loadables(bb_vars): @@ -482,13 +513,12 @@ class FitImageTestCase(OESelftestTestCase): 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) + _, dtb_aliases = 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", "") + if dtb in dtb_aliases: + dtb = dtb_aliases[dtb] elif dtb: dtb = FitImageTestCase._apply_dtbvendored(bb_vars, dtb) extra_configs.append((conf, kernels[conf] or "kernel-1", dtb)) @@ -985,10 +1015,10 @@ class KernelFitImageBase(FitImageTestCase): if not dtb.endswith('.dtbo'): its_field_check.append('kernel = "kernel-1";') its_field_check.append('fdt = "fdt-%s";' % dtb) - for dtb in dtb_symlinks: + + for dtb, target in dtb_symlinks.items(): its_field_check.append('kernel = "kernel-1";') - # Works only for tests were the symlink is with -alias suffix - its_field_check.append('fdt = "fdt-%s";' % dtb.replace('-alias', '')) + its_field_check.append('fdt = "fdt-%s";' % target) if initramfs_image and initramfs_image_bundle != "1": its_field_check.append('ramdisk = "ramdisk-1";') @@ -1085,11 +1115,10 @@ class KernelFitImageBase(FitImageTestCase): req_sections[loadable]['Entry Point'] = entrypoint # Create a configuration section for each DTB if dtb_files: - for dtb in dtb_files + dtb_symlinks: + for dtb in dtb_files + list(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 - # test-dtbs-as-ext test recipe. Make the configuration node pointing to the real DTB. - real_dtb = dtb.replace("-alias", "") + # Resolve DTB aliases using their explicit target mapping. + real_dtb = dtb_symlinks.get(dtb, dtb) # dtb overlays do not refer to a kernel (yet?) if dtb.endswith('.dtbo'): req_sections[conf_name] = { @@ -1155,7 +1184,7 @@ class KernelFitImageBase(FitImageTestCase): fit_hash_alg_len = FitImageTestCase.MKIMAGE_HASH_LENGTHS[fit_hash_alg] fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[fit_sign_alg] dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars) - all_dtb_names = set(dtb_files + dtb_symlinks) + all_dtb_names = set(dtb_files) | set(dtb_symlinks) # The public key is always injected into UBOOT_DTB_IMAGE (-K flag in # uboot-sign.bbclass concat_dtb). For DTB-based configurations, the # same key is also injected into each per-configuration DTB, so either @@ -1621,7 +1650,7 @@ class FitImagePyTests(KernelFitImageBase): 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 @@ -1671,7 +1700,10 @@ class FitImagePyTests(KernelFitImageBase): # x86 kernels produce a setup.bin section; set to the file name to # enable it, or to "" / omit the key to suppress it. 'KERNEL_SETUP_BIN': 'setup1.bin', + # ... existing entries ... + '_python_only_test': True, } + if bb_vars_overrides: bb_vars.update(bb_vars_overrides) if logging.DEBUG >= self.logger.level: @@ -1700,15 +1732,12 @@ class FitImagePyTests(KernelFitImageBase): 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) + dtb_files, dtb_aliases = 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")) - for dtb_symlink in dtb_symlinks: - # For test purposes, assume each symlink points to a DTB with the same basename minus "-alias" - # In this case, "test-ext-alias.dtb" -> "test-ext.dtb" - dtb_target = dtb_symlink.replace("-alias", "") + for dtb_symlink, dtb_target in dtb_aliases.items(): root_node.fitimage_emit_section_dtb_alias(dtb_symlink, os.path.join("a-dir", dtb_target)) if bb_vars.get('FIT_UBOOT_ENV'): @@ -1921,6 +1950,7 @@ class UBootFitImageTests(FitImageTestCase): 'UBOOT_SIGN_IMG_KEYNAME', 'UBOOT_SIGN_KEYDIR', 'UBOOT_SIGN_KEYNAME', + 'EXTERNAL_KERNEL_DEVICETREE', } 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)) From patchwork Thu Nov 5 16:48:26 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vahid Gharaee X-Patchwork-Id: 103158 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.639.1793897310540453176 for ; Thu, 05 Nov 2026 08:48:30 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@gmail.com header.s=20260707 header.b=qhkHObYK; 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-4a2000d5fb1so13152985e9.1 for ; Thu, 05 Nov 2026 08:48:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793897309; x=1794502109; 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=fTo5JWhIlUvscC/8YJqwYSrCObxnEn2i4XgciURvDI8=; b=qhkHObYKRznGXCeeaJtKhT7rb1RxXpHoARgHyro7BbJVCReqAucFX9Re0C7CsATDSM bk/N+wgF8bBDkq0Qxib0AVGFcRNgaBur1K2JcSTUQeerDG0UYxYJGtfK0WWoivvJKo9G ifaHRfVJC2ZNiCv8fAIecoNZBB24wdh3F4A6idVrJV4rxH0df3Hib7jBQgXXjN3YJq6u 06f4MirQx18lzyUrcXnpoa2hOPuIpRo1LPp4a1H6T5uhvccuH4VW2eyU2vwEFBCbJKff PaRDvnymC6wWKbtaCGIs8EgMyf4nDaIW/F4tqn7i9WVK5gKa2AnnqFO1JoJQzufhHoJh SqBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793897309; x=1794502109; 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=fTo5JWhIlUvscC/8YJqwYSrCObxnEn2i4XgciURvDI8=; b=1HHHpUCPbqXBTIK8V7DcLZpMVYj3Ho7UQPTYFXKxtrGxNt2UOYYJVUBNZ0FEbSNdsB F/SNBgdWUAtg6Bn7/RDQgJGWQAKnXYESRDABcZStc8zrm0Kc71k69/vHUYpZ4g/nFsHF EUFAR4sIh9OBxPJlMGihrujuyZhOucjTg2sUJYmziZqWa6BcfU08yNlKk+pqBJUc7zUd yde9dy0BkGjB0KhOTh0cf15fF1Ji0jtrwt/uYNZiR5B+/yafJwX74xr2N89KctonTa6k 8pmJ2jbDMYMU4OUGwQ3AcotqN5R2SDevxryueNmyxRwYPi0/LLI3AKQYOVGK++QG6n8U 5Cew== X-Gm-Message-State: APuU4Z/G4u0lRJq9Qo8QFC8numbOLRiuDEF6uLeMm42EjS6nFk9k9UPW Z9nONtuiFsR88JwZWxlX4ROXNlfemFtgRn4ofa7PgJd25+0JaaQtYT/8kRsMrA== X-Gm-Gg: AYBFou2o54xBd27RY73LAIfZex7Hv30ZidlJDk5RY2xEHJgiq0fU41nkLQIfR02Vb2j MSmDTdcMw4u5N0NJJPIWR5zTYcD14CPjYcaxcyxa3kitJdoTz0nsKgHVicJDFP3Wb6Da7gBaZIC +JBj9arvxogZBzC4LsaC85LaJGk2vWRuoGsl7UXr2NHWZijxCDtbyy9Anz3LsJjnXf2Sf13fO02 p7ezc4ATUCFPHDc9mhvTVAoUe3TV1AbL6HxMhWQuSYfknFiQps7Rc3WcrF4SX4Cmgtbza1jd9cq hhSqSZZZBBXgGJ88XRB0qXqLaP+IThl5bh/F291Wyyf8E74y0yeJBDWVWp9C7VcbikbvfsTkWL7 787jkUMlcIeDGwUAWh3e+ZtFVOVAkBTTe6nVzuZtmCvH3bHKKLudanEuzhie3czi26wRVTBU5Dk xy/d0HmLJ/pbFmT3cLm+MCEIRdQzfPtjnge+0uGOuokmPui3mQ0DSzV6dSo0rdgWCyZ8h8ZGPzJ 4AXkR9xG28jee22wFqT9LQwPaPt3fUoEkZWpKEQ7zR134XnYyQFBkejbNt1OoLA+4Z9H5Zc9lhV vR4eOa32ZEgXZf1IB3teokB4w1NAnvTMIsq+kuYaRmB61uOOuRXzrqWn1Boo4PmP6EmqTA== X-Received: by 2002:a05:600c:64c5:b0:4a2:150f:1613 with SMTP id 5b1f17b1804b1-4ac17a367edmr446483805e9.7.1793897308700; Thu, 05 Nov 2026 08:48:28 -0800 (PST) Return-Path: Received: from vahid-mr.t-mobile.de ([88.128.94.1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4b63863d490sm42536975e9.2.2026.11.05.08.48.27 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Nov 2026 08:48:28 -0800 (PST) From: "Vahid Gharaee" To: openembedded-core@lists.openembedded.org Subject: [PATCH v4 6/6] kernel-fit-image: preserve vendored DTB aliases for external overrides Date: Thu, 5 Nov 2026 17:48:26 +0100 Message-ID: <20261105164826.1340446-7-vgharaee@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261105164826.1340446-1-vgharaee@gmail.com> References: <20261105164826.1340446-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 ; Thu, 05 Nov 2026 19:57:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/249577 From: Vahid Gharaee When an external DTB overrides a kernel-provided DTB, the FIT generation skips the kernel DTB. With KERNEL_DTBVENDORED enabled, this also drops the vendored configuration name. Preserve the vendored name by creating a configuration alias referencing the external DTB image. Resolve external symlinks to their actual DTB targets. Add regression tests for regular and symlinked external DTB overrides. Signed-off-by: Vahid Gharaee --- .../recipes-test/ext-dtb/test-dtbs-as-ext.bb | 11 ++ meta/classes-recipe/kernel-fit-image.bbclass | 7 ++ meta/lib/oeqa/selftest/cases/fitimage.py | 105 ++++++++++++++++-- 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/meta-selftest/recipes-test/ext-dtb/test-dtbs-as-ext.bb b/meta-selftest/recipes-test/ext-dtb/test-dtbs-as-ext.bb index 217ca36235..de994cebce 100644 --- a/meta-selftest/recipes-test/ext-dtb/test-dtbs-as-ext.bb +++ b/meta-selftest/recipes-test/ext-dtb/test-dtbs-as-ext.bb @@ -8,6 +8,7 @@ LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda inherit devicetree +TEST_EXTRA_DTB ?= "" COMPATIBLE_MACHINE = ".*" SRC_URI = "\ @@ -18,8 +19,18 @@ SRC_URI = "\ # Sym-links are handled as extra configuration nodes in FIT images. do_install:append() { ln -sf test-ext.dtb "${D}/boot/devicetree/test-ext-alias.dtb" + + if [ -n "${TEST_EXTRA_DTB}" ]; then + cp "${D}/boot/devicetree/test-ext.dtb" \ + "${D}/boot/devicetree/${TEST_EXTRA_DTB}" + fi } do_deploy:append() { ln -sf test-ext.dtb "${DEPLOYDIR}/devicetree/test-ext-alias.dtb" + + if [ -n "${TEST_EXTRA_DTB}" ]; then + cp "${DEPLOYDIR}/devicetree/test-ext.dtb" \ + "${DEPLOYDIR}/devicetree/${TEST_EXTRA_DTB}" + fi } diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass index 457654926c..74511b01d2 100644 --- a/meta/classes-recipe/kernel-fit-image.bbclass +++ b/meta/classes-recipe/kernel-fit-image.bbclass @@ -188,6 +188,7 @@ python do_compile() { # Prepare a DTB image section kernel_devicetree = d.getVar('KERNEL_DEVICETREE') external_kernel_devicetree = d.getVar("EXTERNAL_KERNEL_DEVICETREE") + external_dtb_aliases = {} if kernel_devicetree: for dtb in kernel_devicetree.split(): # In deploy_dir the DTBs are without sub-directories also with KERNEL_DTBVENDORED = "1" @@ -201,6 +202,8 @@ python do_compile() { 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: + if conf_name != dtb_name: + external_dtb_aliases[conf_name] = ext_dtb_path continue # Copy the dtb or dtbo file into the FIT image assembly directory @@ -225,6 +228,10 @@ python do_compile() { root_node.fitimage_emit_section_dtb(dtb_name, dtb_name, d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"), True) + for alias, dtb_path in external_dtb_aliases.items(): + symlink_target = oe.fitimage.symlink_points_below(os.path.basename(dtb_path), external_kernel_devicetree) + root_node.fitimage_emit_section_dtb_alias(alias, symlink_target or dtb_path) + # Prepare a u-boot script section fit_uboot_env = d.getVar("FIT_UBOOT_ENV") if fit_uboot_env: diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 8ff75dd3da..03c13d20a4 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py @@ -467,10 +467,9 @@ class FitImageTestCase(OESelftestTestCase): kernel_devicetree = bb_vars.get('KERNEL_DEVICETREE') all_dtbs = [] dtb_aliases = {} + external_aliases = {} + kernel_dtbs = kernel_devicetree.split() if kernel_devicetree else [] - if kernel_devicetree: - all_dtbs += [FitImageTestCase._apply_dtbvendored(bb_vars, dtb) for dtb in kernel_devicetree.split()] - if bb_vars.get("PREFERRED_PROVIDER_virtual/dtb") == "test-dtbs-as-ext": if bb_vars.get("_python_only_test"): external_dtbs, external_aliases = FitImageTestCase._get_test_dtb_files() @@ -481,6 +480,23 @@ class FitImageTestCase(OESelftestTestCase): all_dtbs.extend(external_dtbs) dtb_aliases.update(external_aliases) + for dtb in kernel_dtbs: + dtb_name = os.path.basename(dtb) + mapped_name = FitImageTestCase._apply_dtbvendored(bb_vars, dtb) + + if dtb_name in all_dtbs: + target = dtb_name + elif dtb_name in external_aliases: + target = external_aliases[dtb_name] + else: + target = None + + if target: + if mapped_name != target: + dtb_aliases[mapped_name] = target + else: + all_dtbs.append(mapped_name) + return all_dtbs, dtb_aliases @staticmethod @@ -517,10 +533,10 @@ class FitImageTestCase(OESelftestTestCase): extra_configs = [] for conf in configs: dtb = dtbs[conf] - if dtb in dtb_aliases: - dtb = dtb_aliases[dtb] - elif dtb: + if dtb: dtb = FitImageTestCase._apply_dtbvendored(bb_vars, dtb) + dtb = dtb_aliases.get(dtb, dtb) + extra_configs.append((conf, kernels[conf] or "kernel-1", dtb)) return extra_configs @@ -1390,6 +1406,58 @@ PREFERRED_PROVIDER_virtual/dtb = "test-dtbs-as-ext" self._gen_atf_tee_dummy_images(bb_vars) self._test_fitimage(bb_vars) + def test_fit_image_ext_dtb_vendored_override(self): + """Check that an external DTB overrides a vendored kernel DTB.""" + machine = self._get_machine_or_skip() + + if machine != "qemuarm64": + self.skipTest("This test requires the qemuarm64 foundation-v8.dtb") + + config = """ +KERNEL_DTBVENDORED = "1" +PREFERRED_PROVIDER_virtual/dtb = "test-dtbs-as-ext" +TEST_EXTRA_DTB = "foundation-v8.dtb" +FIT_CONF_DEFAULT_DTB = "arm/foundation-v8.dtb" +FIT_CONFIGS = "external-override" +FIT_CONFIG_DTB[external-override] = "arm/foundation-v8.dtb" +""" + config = self._config_add_machine_settings(config, machine, keys=["KERNEL_DEVICETREE"]) + 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_atf_tee_dummy_images(bb_vars) + + its_path, fit_path = self._bitbake_fit_image(bb_vars) + + self.assertExists(its_path) + self.assertExists(fit_path) + + self.logger.info("Regression test ITS: %s", its_path) + + with open(its_path, encoding="utf-8") as f: + its = f.read() + + self.assertIn("fdt-foundation-v8.dtb", its) + self.assertIn("conf-arm_foundation-v8.dtb", its) + self.assertIn("conf-external-override", its) + self.assertNotIn("fdt-arm_foundation-v8.dtb {", its) + # The vendored configuration must be the default. + self.assertIn('default = "conf-arm_foundation-v8.dtb";', its) + + # Both the vendored and custom configurations must reference + # the external DTB image. + for conf in ("conf-arm_foundation-v8.dtb", "conf-external-override"): + match = re.search(r'\b' + re.escape(conf) + r'\s*\{([^{}]*)', its) + self.assertIsNotNone(match, "Missing configuration: %s" % conf) + self.assertIn('fdt = "fdt-foundation-v8.dtb";', match.group(1)) + + self._check_its_file(bb_vars, its_path) + + uboot_tools_bindir = self._setup_native('u-boot-tools-native') + self._check_fitimage(bb_vars, fit_path, uboot_tools_bindir) def test_sign_fit_image_configurations(self): """ @@ -1627,17 +1695,17 @@ class FitImagePyTests(KernelFitImageBase): root.fitimage_emit_section_dtb( "test-overlay.dtbo", "a-dir/test-overlay.dtbo" ) - + with self.assertRaises(bb.BBHandledException): root.fitimage_emit_section_config_custom( "invalid", "kernel-1", "test-overlay.dtbo" ) - + # A regular DTB may explicitly select a kernel. root.fitimage_emit_section_dtb( "test-board.dtb", "a-dir/test-board.dtb" ) - + root.fitimage_emit_section_config_custom( "regular", "kernel-1", "test-board.dtb" ) @@ -1650,7 +1718,7 @@ class FitImagePyTests(KernelFitImageBase): 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 @@ -1775,6 +1843,23 @@ class FitImagePyTests(KernelFitImageBase): self.logger.debug("Checking its: %s" % fitimage_its_path) self._check_its_file(bb_vars, fitimage_its_path) + def test_fitimage_py_vendored_external_dtb_symlink_override(self): + bb_vars = { + "KERNEL_DEVICETREE": "arm/test-ext-alias.dtb", + "KERNEL_DTBVENDORED": "1", + "PREFERRED_PROVIDER_virtual/dtb": "test-dtbs-as-ext", + "_python_only_test": True, + } + + dtbs, aliases = FitImageTestCase._get_dtb_files(bb_vars) + + self.assertIn("test-ext.dtb", dtbs) + self.assertNotIn("arm_test-ext-alias.dtb", dtbs) + self.assertEqual(aliases["arm_test-ext-alias.dtb"],"test-ext.dtb") + + # Verify that the original external symlink alias is preserved + self.assertEqual(aliases["test-ext-alias.dtb"], "test-ext.dtb") + def test_fitimage_py_default(self): self._test_fitimage_py()