diff mbox series

[v7] kernel-fit-image: allow overriding DTB compatible string

Message ID 20251015080502.1704801-1-kavinaya@qti.qualcomm.com
State New
Headers show
Series [v7] kernel-fit-image: allow overriding DTB compatible string | expand

Commit Message

Kavinaya S Oct. 15, 2025, 8:05 a.m. UTC
The Linux kernel allows multiple DTBs to share the same compatible string.
For example:
qcs6490-rb3gen2.dtb
qcs6490-rb3gen2-vision-mezzanine.dtb
qcs6490-rb3gen2-industrial-mezzanine.dtb

All of these use the same base compatible string:
compatible = "qcom,qcs6490-rb3gen2", "qcom,qcm6490";

Since the latter two DTBs are overlays on top of the base DTB and do not
modify platform properties, they retain the same compatible string.

When these DTBs are bundled into a single fitImage, the bootloader cannot
distinguish between them due to identical compatible strings.

To address this, introduce a mechanism to override the compatible string
using a BitBake variable:
FIT_DTB_COMPATIBLE_OVERRIDE[<dtb_name>] = "<compatible_string>"

This overrides the compatible string generated resulting in unique entries in the
generated .its file. For example:
FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] = "qcom,qcs6490-rb3gen2-vision"
FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] = "qcom,qcs6490-rb3gen2-vision qcom,qcm6490"

Results in:

compatible = "qcom,qcs6490-rb3gen2-vision";
compatible = "qcom,qcs6490-rb3gen2-vision", "qcom,qcm6490";

Suggested-By: Alexander Kanavin <alex.kanavin@gmail.com>
Suggested-By: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Kavinaya S <kavinaya@qti.qualcomm.com>
---
 meta/classes-recipe/kernel-fit-image.bbclass |  7 ++-
 meta/conf/image-fitimage.conf                | 16 +++++++
 meta/lib/oe/fitimage.py                      | 16 +++++--
 meta/lib/oeqa/selftest/cases/fitimage.py     | 50 ++++++++++++++++++++
 4 files changed, 84 insertions(+), 5 deletions(-)

Comments

Adrian Freihofer Oct. 15, 2025, 5:13 p.m. UTC | #1
Thank you for the updated patch. It looks well integrated with the existing
code now.
I have only a few questions about some details now.

Am Mi., 15. Okt. 2025 um 10:05 Uhr schrieb Kavinaya S via
lists.openembedded.org <kavinaya=qti.qualcomm.com@lists.openembedded.org>:

> The Linux kernel allows multiple DTBs to share the same compatible string.
> For example:
> qcs6490-rb3gen2.dtb
> qcs6490-rb3gen2-vision-mezzanine.dtb
> qcs6490-rb3gen2-industrial-mezzanine.dtb
>
> All of these use the same base compatible string:
> compatible = "qcom,qcs6490-rb3gen2", "qcom,qcm6490";
>

What I don't quite understand yet is: Does this make sense from the kernel
or DTB perspective? Or are we circumventing problems that should be fixed
on the kernel or DTB side instead of overriding them on the Yocto side? I
mean, wouldn't it make more sense to assign a compatible string that is
useful for u-boot in the DTB without requiring FIT_DTB_COMPATIBLE_OVERRIDE?
>
>
> Since the latter two DTBs are overlays on top of the base DTB and do not
> modify platform properties, they retain the same compatible string.
>
> When these DTBs are bundled into a single fitImage, the bootloader cannot
> distinguish between them due to identical compatible strings.
>
> To address this, introduce a mechanism to override the compatible string
> using a BitBake variable:
> FIT_DTB_COMPATIBLE_OVERRIDE[<dtb_name>] = "<compatible_string>"
>
> This overrides the compatible string generated resulting in unique entries
> in the
> generated .its file. For example:
> FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] =
> "qcom,qcs6490-rb3gen2-vision"
> FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] =
> "qcom,qcs6490-rb3gen2-vision qcom,qcm6490"
>
> Results in:
>
> compatible = "qcom,qcs6490-rb3gen2-vision";
> compatible = "qcom,qcs6490-rb3gen2-vision", "qcom,qcm6490";
>

Would it be easier to understand these two examples like that?

For example:
FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] =
"qcom,qcs6490-rb3gen2-vision"
results in
compatible = "qcom,qcs6490-rb3gen2-vision";

or
FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] =
"qcom,qcs6490-rb3gen2-vision qcom,qcm6490"
results in
compatible = "qcom,qcs6490-rb3gen2-vision", "qcom,qcm6490";


> Suggested-By: Alexander Kanavin <alex.kanavin@gmail.com>
> Suggested-By: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: Kavinaya S <kavinaya@qti.qualcomm.com>
> ---
>  meta/classes-recipe/kernel-fit-image.bbclass |  7 ++-
>  meta/conf/image-fitimage.conf                | 16 +++++++
>  meta/lib/oe/fitimage.py                      | 16 +++++--
>  meta/lib/oeqa/selftest/cases/fitimage.py     | 50 ++++++++++++++++++++
>  4 files changed, 84 insertions(+), 5 deletions(-)
>
> diff --git a/meta/classes-recipe/kernel-fit-image.bbclass
> b/meta/classes-recipe/kernel-fit-image.bbclass
> index f04aee1807..0a6125b3b6 100644
> --- a/meta/classes-recipe/kernel-fit-image.bbclass
> +++ b/meta/classes-recipe/kernel-fit-image.bbclass
> @@ -84,8 +84,13 @@ python do_compile() {
>
>              # Copy the dtb or dtbo file into the FIT image assembly
> directory
>              shutil.copyfile(os.path.join(kernel_deploydir, dtb_name),
> dtb_name)
> +
> +            dtb_base = os.path.splitext(os.path.basename(dtb_name))[0]
> +            compatible_override_str =
> d.getVarFlag("FIT_DTB_COMPATIBLE_OVERRIDE", dtb_base) or ""
> +
>              root_node.fitimage_emit_section_dtb(dtb_name, dtb_name,
> -                d.getVar("UBOOT_DTB_LOADADDRESS"),
> d.getVar("UBOOT_DTBO_LOADADDRESS"))
> +                d.getVar("UBOOT_DTB_LOADADDRESS"),
> d.getVar("UBOOT_DTBO_LOADADDRESS"),
> +                compatible_override=compatible_override_str)
>
>      if external_kernel_devicetree:
>          # iterate over all .dtb and .dtbo files in the external kernel
> devicetree directory
> diff --git a/meta/conf/image-fitimage.conf b/meta/conf/image-fitimage.conf
> index 090ee148f4..9c51b28095 100644
> --- a/meta/conf/image-fitimage.conf
> +++ b/meta/conf/image-fitimage.conf
> @@ -65,3 +65,19 @@ FIT_ADDRESS_CELLS ?= "1"
>  # Machine configurations needing such a script file should include it in
> the
>  # SRC_URI of the kernel recipe and set the FIT_UBOOT_ENV parameter.
>  FIT_UBOOT_ENV ?= ""
> +
> +# For specific DTBs, add custom "compatible" strings when creating FIT
> images.
> +# Format:
> +#   FIT_DTB_COMPATIBLE_OVERRIDE[<dtb_name>] = "<compatible_string>"
> +#
> +# Result:
> +#   dtb_name.dtb {
> +#    ...
> +#    compatible = "compatible_string";
> +#   };
> +#   Example:
> +#   FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] =
> "qcom,qcs6490-rb3gen2-vision"
> +#   This will result in compatible string like:
> +#   compatible = "qcom,qcs6490-rb3gen2-vision";
> +#
> +#
> diff --git a/meta/lib/oe/fitimage.py b/meta/lib/oe/fitimage.py
> index f303799155..fc37ce7665 100644
> --- a/meta/lib/oe/fitimage.py
> +++ b/meta/lib/oe/fitimage.py
> @@ -289,7 +289,8 @@ class ItsNodeRootKernel(ItsNode):
>          self._kernel = kernel_node
>
>      def fitimage_emit_section_dtb(self, dtb_id, dtb_path,
> dtb_loadaddress=None,
> -                                  dtbo_loadaddress=None,
> add_compatible=False):
> +                                  dtbo_loadaddress=None,
> add_compatible=False,
> +                                  compatible_override=None):
>          """Emit the fitImage ITS DTB section"""
>          load=None
>          dtb_ext = os.path.splitext(dtb_path)[1]
> @@ -309,7 +310,10 @@ class ItsNodeRootKernel(ItsNode):
>          # Preserve the DTB's compatible string to be added to the
> configuration node
>          compatible = None
>          if add_compatible:
> -            compatible = get_compatible_from_dtb(dtb_path)
> +            if compatible_override:
> +                compatible = str(compatible_override).split()
>

Is this str conversion good or bad? The "if compatible_override:" ensures
it is not None. What else could be assigned to compatible_override? Or
would it be better to just throw the exception (probably something like
datatype x does not have a split() function) to the user and stop
immediately? (same question for the other, same change)


> +            else:
> +                compatible = get_compatible_from_dtb(dtb_path)
>
>          dtb_node = self.its_add_node_dtb(
>              "fdt-" + dtb_id,
> @@ -321,12 +325,16 @@ class ItsNodeRootKernel(ItsNode):
>          )
>          self._dtbs.append(dtb_node)
>
> -    def fitimage_emit_section_dtb_alias(self, dtb_alias_id, dtb_path,
> add_compatible=False):
> +    def fitimage_emit_section_dtb_alias(self, dtb_alias_id, dtb_path,
> add_compatible=False,
> +                                        compatible_override=None):
>          """Add a configuration node referring to another DTB"""
>          # Preserve the DTB's compatible string to be added to the
> configuration node
>          compatible = None
>          if add_compatible:
> -            compatible = get_compatible_from_dtb(dtb_path)
> +            if compatible_override:
> +                compatible = str(compatible_override).split()
> +            else:
> +                compatible = get_compatible_from_dtb(dtb_path)
>
>          dtb_id = os.path.basename(dtb_path)
>          dtb_alias_node = ItsNodeDtbAlias("fdt-" + dtb_id, dtb_alias_id,
> compatible)
> diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py
> b/meta/lib/oeqa/selftest/cases/fitimage.py
> index 195b9ee8b5..d32d7b6e2f 100644
> --- a/meta/lib/oeqa/selftest/cases/fitimage.py
> +++ b/meta/lib/oeqa/selftest/cases/fitimage.py
> @@ -819,6 +819,56 @@ MACHINE:forcevariable = "beaglebone-yocto"
>          # The alias is a symlink, therefore the compatible string is equal
>          self.assertEqual(comp_alias, comp)
>
> +    def test_fitimage_custom_compatible_in_its(self):
> +        """
> +        Verify that FIT_DTB_COMPATIBLE_OVERRIDE[...] is honored in the
> generated .its.
> +        This test:
> +        1) Selects beaglebone-yocto machine and a DTB that is part of its
> kernel.
> +        2) Sets FIT_DTB_COMPATIBLE_EXTENTION[am335x-bonegreen-ext] to a
> custom string.
> +        3) Runs do_assemble_fitimage to generate the FIT .its.
> +        4) Asserts the .its 'compatible = ...' includes custom compatible
> string.
> +
>
why an empty line?

> +        """
> +
> +        kernel_dtb = "am335x-bonegreen-ext.dtb"
> +        dtb_name = os.path.splitext(os.path.basename(kernel_dtb))[0]
> +
> +        config = f"""
> +DISTRO = "poky"
> +MACHINE = "beaglebone-yocto"
> +
> +# Ensure the FIT flow is active
> +KERNEL_CLASSES += "kernel-fit-extra-artifacts "
> +
> +# Ensure the selected DTB is built into the kernel deploy output
> +KERNEL_DEVICETREE = "{kernel_dtb}"
> +
> +# Original compatibles: "ti,am335x-bone-green", "ti,am335x-bone-black"
> +FIT_DTB_COMPATIBLE_OVERRIDE[{dtb_name}] = "subtypeA"
> +
>
>>
>> why an empty line?
> +"""
> +        self.write_config(config)
> +
> +        bitbake('virtual/kernel:do_deploy')
>
Why is this first bitbake call needed? It should be a dependency of
bitbake('linux-yocto-fitimage:do_deploy'),

> +        bitbake('linux-yocto-fitimage:do_deploy')
> +
> +        # Find the generated .its in DEPLOY_DIR_IMAGE
> +        deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
> +        self.assertTrue(deploy_dir_image and
> os.path.isdir(deploy_dir_image),
> +                    f"DEPLOY_DIR_IMAGE not found or invalid:
> {deploy_dir_image}")

+
> +        its_path = os.path.join(deploy_dir_image, 'fit-image.its')
> +        self.assertTrue(os.path.exists(its_path), f"Expected ITS file not
> found: {its_path}")
>
self.assertExists(its_path) ?

> +
> +        # Read the ITS content
> +        its_text = Path(its_path).read_text(encoding='utf-8',
> errors='ignore')
>

Missing import?
from pathlib import Path


> +
> +        # Assertions: extended compatibles must appear
> +        if "compatible" in its_text:
> +            self.assertIn('subtypeA', its_text)
> +        else:
> +            pass
> +
>

This test does not do much. It also does not re-use the existing test
functions for parsing its files.
Maybe we can merge it as it is and improve it later. See the last commit
from
https://git.yoctoproject.org/poky-contrib/log/?h=adrianf/fitimage-improvements.
It replaces the test witch a quicker implementation.

Regards,
Adrian



>      def test_fit_image_ext_dtb_dtbo(self):
>          """
>          Summary:     Check if FIT image and Image Tree Source (its) are
> created correctly.
> --
> 2.34.1
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#224881):
> https://lists.openembedded.org/g/openembedded-core/message/224881
> Mute This Topic: https://lists.openembedded.org/mt/115767371/4454582
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [
> adrian.freihofer@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>
Mathieu Dubois-Briand Oct. 16, 2025, 5:03 a.m. UTC | #2
On Wed Oct 15, 2025 at 10:05 AM CEST, Kavinaya S via lists.openembedded.org wrote:
> The Linux kernel allows multiple DTBs to share the same compatible string.
> For example:
> qcs6490-rb3gen2.dtb
> qcs6490-rb3gen2-vision-mezzanine.dtb
> qcs6490-rb3gen2-industrial-mezzanine.dtb
>
> All of these use the same base compatible string:
> compatible = "qcom,qcs6490-rb3gen2", "qcom,qcm6490";
>
> Since the latter two DTBs are overlays on top of the base DTB and do not
> modify platform properties, they retain the same compatible string.
>
> When these DTBs are bundled into a single fitImage, the bootloader cannot
> distinguish between them due to identical compatible strings.
>
> To address this, introduce a mechanism to override the compatible string
> using a BitBake variable:
> FIT_DTB_COMPATIBLE_OVERRIDE[<dtb_name>] = "<compatible_string>"
>
> This overrides the compatible string generated resulting in unique entries in the
> generated .its file. For example:
> FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] = "qcom,qcs6490-rb3gen2-vision"
> FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] = "qcom,qcs6490-rb3gen2-vision qcom,qcm6490"
>
> Results in:
>
> compatible = "qcom,qcs6490-rb3gen2-vision";
> compatible = "qcom,qcs6490-rb3gen2-vision", "qcom,qcm6490";
>
> Suggested-By: Alexander Kanavin <alex.kanavin@gmail.com>
> Suggested-By: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: Kavinaya S <kavinaya@qti.qualcomm.com>
> ---

Hi Kavinaya,

Thanks for the new version. However, I still got a similar issue:

ERROR: linux-yocto-6.16.9+git-r0 do_compile: Execution of '/srv/pokybuild/yocto-worker/oe-selftest-armhost/build/build-st-1723335/tmp/work/qemuarm64-poky-linux/linux-yocto/6.16.9+git/temp/run.do_compile.365769' failed with exit code 1
...
| make[2]: *** No rule to make target 'arch/arm64/boot/dts/am335x-bonegreen-ext.dtb'.  Stop.

https://autobuilder.yoctoproject.org/valkyrie/#/builders/23/builds/2701
https://autobuilder.yoctoproject.org/valkyrie/#/builders/35/builds/2549
https://autobuilder.yoctoproject.org/valkyrie/#/builders/48/builds/2463

Can you have a look at this please?

Thanks,
Mathieu
Kavinaya S Oct. 16, 2025, 3:29 p.m. UTC | #3
On Wed, Oct 15, 2025 at 10:43 PM, Adrian Freihofer wrote:

>
> What I don't quite understand yet is: Does this make sense from the kernel or
> DTB perspective? Or are we circumventing problems that should be fixed on the
> kernel or DTB side instead of overriding them on the Yocto side? I mean,
> wouldn't it make more sense to assign a compatible string that is useful for
> u-boot in the DTB without requiring FIT_DTB_COMPATIBLE_OVERRIDE?
> > 
> > 
> >
>

Hi Adrian,

Thank you for all your valuable reviews and inputs. After an internal discussion with our colleagues, we feel that the proposed change does not fully align with Qualcomm’s current approach to implementing DTB-only FIT in the format expected by our UEFI bootloader. Given this, we’ve decided to hold off on upstreaming the change for now. Our plan is to first implement the DTB-only FIT support within our meta layer, refine it further, and then propose a solution to oe-core that support both kernel+DTB FIT and DTB-only FIT formats effectively.

Thanks,
Kavinaya
Adrian Freihofer Oct. 20, 2025, 6:28 a.m. UTC | #4
Hi Kavinaya

It would be valuable to understand what gaps exist or what doesn't align
with Qualcomm's requirements. Perhaps others have encountered similar
issues, and we should consider improving the core functionality.
Here I attempted to organize the patches and prepare some test
improvements:
https://git.yoctoproject.org/poky-contrib/log/?h=adrianf/fitimage-improvements
I'm curious to hear whether your final solution will be a combination of
UKI and FIT images, or if that approach proved to be the wrong direction
entirely.

Thank you and regards,
Adrian




Am Do., 16. Okt. 2025 um 17:29 Uhr schrieb Kavinaya S via
lists.openembedded.org <kavinaya=qti.qualcomm.com@lists.openembedded.org>:

> On Wed, Oct 15, 2025 at 10:43 PM, Adrian Freihofer wrote:
>
> >
> > What I don't quite understand yet is: Does this make sense from the
> kernel or
> > DTB perspective? Or are we circumventing problems that should be fixed
> on the
> > kernel or DTB side instead of overriding them on the Yocto side? I mean,
> > wouldn't it make more sense to assign a compatible string that is useful
> for
> > u-boot in the DTB without requiring FIT_DTB_COMPATIBLE_OVERRIDE?
> > >
> > >
> > >
> >
>
> Hi Adrian,
>
> Thank you for all your valuable reviews and inputs. After an internal
> discussion with our colleagues, we feel that the proposed change does not
> fully align with Qualcomm’s current approach to implementing DTB-only FIT
> in the format expected by our UEFI bootloader. Given this, we’ve decided to
> hold off on upstreaming the change for now. Our plan is to first implement
> the DTB-only FIT support within our meta layer, refine it further, and then
> propose a solution to oe-core that support both kernel+DTB FIT and DTB-only
> FIT formats effectively.
>


> Thanks,
> Kavinaya
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#224972):
> https://lists.openembedded.org/g/openembedded-core/message/224972
> Mute This Topic: https://lists.openembedded.org/mt/115767371/4454582
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [
> adrian.freihofer@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>
Viswanath Kraleti Oct. 22, 2025, 12:46 p.m. UTC | #5
Hi Adrian,
          Thank you so much for being so accommodating.

After posting the initial set of patches, we at Qualcomm realized that
our case may not have been presented in a sufficiently generic manner.
The current changes don’t fully capture our needs and expectations. We
also felt the idea of modifying the kernel-fit-image implementation to
make it a dtb-only-fit-image, may not be the most appropriate
approach. It might be better for OE to support both options as
separate classes.

As we’re still finalizing the details, to avoid unnecessary
back-and-forth, we plan to share the following in the coming weeks,
which should help
ensure the right changes are made in the OE-Core layer:

1. Clear specification document outlining our requirements
2. Sample .its file
3. Reference implementation from the Qualcomm BSP layer

Thanks again for your support!

Best regards,
Viswanath

On Mon, Oct 20, 2025 at 11:58 AM Adrian Freihofer via
lists.openembedded.org
<adrian.freihofer=gmail.com@lists.openembedded.org> wrote:
>
> Hi Kavinaya
>
> It would be valuable to understand what gaps exist or what doesn't align with Qualcomm's requirements. Perhaps others have encountered similar issues, and we should consider improving the core functionality.
> Here I attempted to organize the patches and prepare some test improvements: https://git.yoctoproject.org/poky-contrib/log/?h=adrianf/fitimage-improvements
> I'm curious to hear whether your final solution will be a combination of UKI and FIT images, or if that approach proved to be the wrong direction entirely.
>
> Thank you and regards,
> Adrian
>
>
>
>
> Am Do., 16. Okt. 2025 um 17:29 Uhr schrieb Kavinaya S via lists.openembedded.org <kavinaya=qti.qualcomm.com@lists.openembedded.org>:
>>
>> On Wed, Oct 15, 2025 at 10:43 PM, Adrian Freihofer wrote:
>>
>> >
>> > What I don't quite understand yet is: Does this make sense from the kernel or
>> > DTB perspective? Or are we circumventing problems that should be fixed on the
>> > kernel or DTB side instead of overriding them on the Yocto side? I mean,
>> > wouldn't it make more sense to assign a compatible string that is useful for
>> > u-boot in the DTB without requiring FIT_DTB_COMPATIBLE_OVERRIDE?
>> > >
>> > >
>> > >
>> >
>>
>> Hi Adrian,
>>
>> Thank you for all your valuable reviews and inputs. After an internal discussion with our colleagues, we feel that the proposed change does not fully align with Qualcomm’s current approach to implementing DTB-only FIT in the format expected by our UEFI bootloader. Given this, we’ve decided to hold off on upstreaming the change for now. Our plan is to first implement the DTB-only FIT support within our meta layer, refine it further, and then propose a solution to oe-core that support both kernel+DTB FIT and DTB-only FIT formats effectively.
>
>
>>
>> Thanks,
>> Kavinaya
>>
>>
>>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#225096): https://lists.openembedded.org/g/openembedded-core/message/225096
> Mute This Topic: https://lists.openembedded.org/mt/115767371/5192326
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [viswanath.kraleti@oss.qualcomm.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
diff mbox series

Patch

diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass
index f04aee1807..0a6125b3b6 100644
--- a/meta/classes-recipe/kernel-fit-image.bbclass
+++ b/meta/classes-recipe/kernel-fit-image.bbclass
@@ -84,8 +84,13 @@  python do_compile() {
 
             # Copy the dtb or dtbo file into the FIT image assembly directory
             shutil.copyfile(os.path.join(kernel_deploydir, dtb_name), dtb_name)
+
+            dtb_base = os.path.splitext(os.path.basename(dtb_name))[0]
+            compatible_override_str = d.getVarFlag("FIT_DTB_COMPATIBLE_OVERRIDE", dtb_base) or ""
+
             root_node.fitimage_emit_section_dtb(dtb_name, dtb_name,
-                d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"))
+                d.getVar("UBOOT_DTB_LOADADDRESS"), d.getVar("UBOOT_DTBO_LOADADDRESS"),
+                compatible_override=compatible_override_str)
 
     if external_kernel_devicetree:
         # iterate over all .dtb and .dtbo files in the external kernel devicetree directory
diff --git a/meta/conf/image-fitimage.conf b/meta/conf/image-fitimage.conf
index 090ee148f4..9c51b28095 100644
--- a/meta/conf/image-fitimage.conf
+++ b/meta/conf/image-fitimage.conf
@@ -65,3 +65,19 @@  FIT_ADDRESS_CELLS ?= "1"
 # Machine configurations needing such a script file should include it in the
 # SRC_URI of the kernel recipe and set the FIT_UBOOT_ENV parameter.
 FIT_UBOOT_ENV ?= ""
+
+# For specific DTBs, add custom "compatible" strings when creating FIT images.
+# Format:
+#   FIT_DTB_COMPATIBLE_OVERRIDE[<dtb_name>] = "<compatible_string>"
+#
+# Result:
+#   dtb_name.dtb {
+#    ...
+#    compatible = "compatible_string";
+#   };
+#   Example:
+#   FIT_DTB_COMPATIBLE_OVERRIDE[qcs6490-rb3gen2-vision-mezzanine] = "qcom,qcs6490-rb3gen2-vision"
+#   This will result in compatible string like:
+#   compatible = "qcom,qcs6490-rb3gen2-vision";
+#
+#
diff --git a/meta/lib/oe/fitimage.py b/meta/lib/oe/fitimage.py
index f303799155..fc37ce7665 100644
--- a/meta/lib/oe/fitimage.py
+++ b/meta/lib/oe/fitimage.py
@@ -289,7 +289,8 @@  class ItsNodeRootKernel(ItsNode):
         self._kernel = kernel_node
 
     def fitimage_emit_section_dtb(self, dtb_id, dtb_path, dtb_loadaddress=None,
-                                  dtbo_loadaddress=None, add_compatible=False):
+                                  dtbo_loadaddress=None, add_compatible=False,
+                                  compatible_override=None):
         """Emit the fitImage ITS DTB section"""
         load=None
         dtb_ext = os.path.splitext(dtb_path)[1]
@@ -309,7 +310,10 @@  class ItsNodeRootKernel(ItsNode):
         # Preserve the DTB's compatible string to be added to the configuration node
         compatible = None
         if add_compatible:
-            compatible = get_compatible_from_dtb(dtb_path)
+            if compatible_override:
+                compatible = str(compatible_override).split()
+            else:
+                compatible = get_compatible_from_dtb(dtb_path)
 
         dtb_node = self.its_add_node_dtb(
             "fdt-" + dtb_id,
@@ -321,12 +325,16 @@  class ItsNodeRootKernel(ItsNode):
         )
         self._dtbs.append(dtb_node)
 
-    def fitimage_emit_section_dtb_alias(self, dtb_alias_id, dtb_path, add_compatible=False):
+    def fitimage_emit_section_dtb_alias(self, dtb_alias_id, dtb_path, add_compatible=False,
+                                        compatible_override=None):
         """Add a configuration node referring to another DTB"""
         # Preserve the DTB's compatible string to be added to the configuration node
         compatible = None
         if add_compatible:
-            compatible = get_compatible_from_dtb(dtb_path)
+            if compatible_override:
+                compatible = str(compatible_override).split()
+            else:
+                compatible = get_compatible_from_dtb(dtb_path)
 
         dtb_id = os.path.basename(dtb_path)
         dtb_alias_node = ItsNodeDtbAlias("fdt-" + dtb_id, dtb_alias_id, compatible)
diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 195b9ee8b5..d32d7b6e2f 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -819,6 +819,56 @@  MACHINE:forcevariable = "beaglebone-yocto"
         # The alias is a symlink, therefore the compatible string is equal
         self.assertEqual(comp_alias, comp)
 
+    def test_fitimage_custom_compatible_in_its(self):
+        """
+        Verify that FIT_DTB_COMPATIBLE_OVERRIDE[...] is honored in the generated .its.
+        This test:
+        1) Selects beaglebone-yocto machine and a DTB that is part of its kernel.
+        2) Sets FIT_DTB_COMPATIBLE_EXTENTION[am335x-bonegreen-ext] to a custom string.
+        3) Runs do_assemble_fitimage to generate the FIT .its.
+        4) Asserts the .its 'compatible = ...' includes custom compatible string.
+
+        """
+
+        kernel_dtb = "am335x-bonegreen-ext.dtb"
+        dtb_name = os.path.splitext(os.path.basename(kernel_dtb))[0]
+
+        config = f"""
+DISTRO = "poky"
+MACHINE = "beaglebone-yocto"
+
+# Ensure the FIT flow is active
+KERNEL_CLASSES += "kernel-fit-extra-artifacts "
+
+# Ensure the selected DTB is built into the kernel deploy output
+KERNEL_DEVICETREE = "{kernel_dtb}"
+
+# Original compatibles: "ti,am335x-bone-green", "ti,am335x-bone-black"
+FIT_DTB_COMPATIBLE_OVERRIDE[{dtb_name}] = "subtypeA"
+
+"""
+        self.write_config(config)
+
+        bitbake('virtual/kernel:do_deploy')
+        bitbake('linux-yocto-fitimage:do_deploy')
+
+        # Find the generated .its in DEPLOY_DIR_IMAGE
+        deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+        self.assertTrue(deploy_dir_image and os.path.isdir(deploy_dir_image),
+                    f"DEPLOY_DIR_IMAGE not found or invalid: {deploy_dir_image}")
+
+        its_path = os.path.join(deploy_dir_image, 'fit-image.its')
+        self.assertTrue(os.path.exists(its_path), f"Expected ITS file not found: {its_path}")
+
+        # Read the ITS content
+        its_text = Path(its_path).read_text(encoding='utf-8', errors='ignore')
+
+        # Assertions: extended compatibles must appear
+        if "compatible" in its_text:
+            self.assertIn('subtypeA', its_text)
+        else:
+            pass
+
     def test_fit_image_ext_dtb_dtbo(self):
         """
         Summary:     Check if FIT image and Image Tree Source (its) are created correctly.