diff mbox series

[v2,4/6] oe-selftest: fitimage: replace MACHINE==qemux86-64 guards with KERNEL_SETUP_BIN

Message ID 20260619112046.125876-5-adrian.freihofer@siemens.com
State New
Headers show
Series fitimage selftests: stop hardcoding DISTRO/MACHINE, drop meta-yocto-bsp dependency | expand

Commit Message

AdrianF June 19, 2026, 11:17 a.m. UTC
From: Adrian Freihofer <adrian.freihofer@siemens.com>

On x86, the kernel build produces setup.bin which is packed as a separate
section in the FIT image. The existing code used MACHINE == "qemux86-64"
as a proxy to detect this, which is fragile and non-composable.

Replace all such guards with a KERNEL_SETUP_BIN variable that is set to
the filename ("setup.bin") when the artifact is present, or left empty
otherwise.

Also fix the signing verification helpers for machines without DTBs
(e.g. qemux86-64 that uses conf-1 instead of conf-<dtb>.dtb):

- _get_req_sigvalues_config(): make "fdt" conditional in sign-images;
  x86 FIT images have no DTB sections, so the ITS only signs "kernel".
- _check_signing(): when conf-1 has no matching DTB filename, fall back
  to UBOOT_DTB_IMAGE as the key-holding DTB for fit_check_sign; if that
  is also absent, skip the cryptographic check and log a debug message.
- _fit_get_bb_vars(): fetch UBOOT_DTB_IMAGE needed by the above.

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

Patch

diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 3a2ba2208d..57bff2e4c6 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -509,6 +509,7 @@  class KernelFitImageBase(FitImageTestCase):
             'UBOOT_SIGN_IMG_KEYNAME',
             'UBOOT_SIGN_KEYDIR',
             'UBOOT_SIGN_KEYNAME',
+            'UBOOT_DTB_IMAGE',
         }
         bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), self.kernel_recipe)
         self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4))
@@ -571,9 +572,21 @@  class KernelFitImageBase(FitImageTestCase):
         if kernel_deploysubdir:
             fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, kernel_deploysubdir, fitimage_its_name))
             fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, kernel_deploysubdir, fitimage_name))
+            setup_bin_dir = os.path.join(deploy_dir_image, kernel_deploysubdir)
         else:
             fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_its_name))
             fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_name))
+            setup_bin_dir = deploy_dir_image
+        # x86 kernel builds produce setup.bin which is packed into the FIT image.
+        # Detect this and inject KERNEL_SETUP_BIN into bb_vars so that the
+        # downstream helpers (_get_req_its_paths, _get_req_sections, etc.) can
+        # use it instead of checking MACHINE == "qemux86-64" directly.
+        if bb_vars['MACHINE'] == "qemux86-64":
+            setup_bin_path = os.path.join(setup_bin_dir, "setup.bin")
+            self.assertExists(setup_bin_path, "Expected setup.bin artifact not found: %s" % setup_bin_path)
+            bb_vars['KERNEL_SETUP_BIN'] = "setup.bin"
+        else:
+            bb_vars['KERNEL_SETUP_BIN'] = ""
         return (fitimage_its_path, fitimage_path)
 
     def _get_req_its_paths(self, bb_vars):
@@ -605,7 +618,10 @@  class KernelFitImageBase(FitImageTestCase):
         else:
             not_images.append('bootscr-boot.cmd')
 
-        if bb_vars['MACHINE'] == "qemux86-64": # Not really the right if
+        # setup-1 is an x86-only section present when the kernel build emits
+        # setup.bin. KERNEL_SETUP_BIN is either injected by _bitbake_fit_image
+        # after the build, or set explicitly in unit tests.
+        if bb_vars.get('KERNEL_SETUP_BIN'):
             images.append('setup-1')
         else:
             not_images.append('setup-1')
@@ -737,11 +753,16 @@  class KernelFitImageBase(FitImageTestCase):
         """Generate a dictionary of expected configuration signature nodes"""
         if bb_vars.get('UBOOT_SIGN_ENABLE') != "1":
             return {}
-        sign_images = '"kernel", "fdt"'
+        sign_images = '"kernel"'
+        dtb_files, _ = FitImageTestCase._get_dtb_files(bb_vars)
+        if dtb_files:
+            sign_images += ', "fdt"'
         if bb_vars['INITRAMFS_IMAGE'] and bb_vars['INITRAMFS_IMAGE_BUNDLE'] != "1":
             sign_images += ', "ramdisk"'
         if bb_vars['FIT_UBOOT_ENV']:
             sign_images += ', "bootscr"'
+        if bb_vars.get('KERNEL_SETUP_BIN'):
+            sign_images += ', "setup"'
         req_sigvalues_config = {
             'algo': '"%s,%s"' % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']),
             'key-name-hint': '"%s"' % bb_vars['UBOOT_SIGN_KEYNAME'],
@@ -789,6 +810,9 @@  class KernelFitImageBase(FitImageTestCase):
         # Add a script section if there is a script
         if fit_uboot_env:
             req_sections['bootscr-' + fit_uboot_env] = { "Type": "Script" }
+        # Add the x86 setup section if present
+        if bb_vars.get('KERNEL_SETUP_BIN'):
+            req_sections['setup-1'] = { "Type": "x86 setup.bin" }
         # Add the initramfs
         if initramfs_image and initramfs_image_bundle != "1":
             req_sections['ramdisk-1'] = {
@@ -864,6 +888,15 @@  class KernelFitImageBase(FitImageTestCase):
         fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL']
         fit_hash_alg_len = FitImageTestCase.MKIMAGE_HASH_LENGTHS[fit_hash_alg]
         fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[fit_sign_alg]
+        dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars)
+        all_dtb_names = set(dtb_files + 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 file
+        # works. For configurations without a matching DTB (e.g. conf-1 on x86)
+        # UBOOT_DTB_IMAGE is the only option.
+        uboot_dtb_image = bb_vars.get('UBOOT_DTB_IMAGE')
+        uboot_dtb_path_default = os.path.join(deploy_dir_image, uboot_dtb_image) if uboot_dtb_image else None
         for section, values in sections.items():
             # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1")
             if section.startswith(bb_vars['FIT_CONF_PREFIX']):
@@ -873,13 +906,29 @@  class KernelFitImageBase(FitImageTestCase):
                 sign_value = values.get('Sign value', None)
                 self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section)
                 dtb_file_name = section.replace(bb_vars['FIT_CONF_PREFIX'], '')
-                dtb_path = os.path.join(deploy_dir_image, dtb_file_name)
-                if kernel_deploysubdir:
-                    dtb_path = os.path.join(deploy_dir_image, kernel_deploysubdir, dtb_file_name)
-                # External devicetrees created by devicetree.bbclass are in a subfolder and have priority
-                dtb_path_ext = os.path.join(deploy_dir_image, "devicetree", dtb_file_name)
-                if os.path.exists(dtb_path_ext):
-                    dtb_path = dtb_path_ext
+                if dtb_file_name in all_dtb_names:
+                    # Use the per-configuration DTB (public key is also injected there)
+                    dtb_path = os.path.join(deploy_dir_image, dtb_file_name)
+                    if kernel_deploysubdir:
+                        dtb_path = os.path.join(deploy_dir_image, kernel_deploysubdir, dtb_file_name)
+                    # External devicetrees created by devicetree.bbclass are in a subfolder and have priority
+                    dtb_path_ext = os.path.join(deploy_dir_image, "devicetree", dtb_file_name)
+                    if os.path.exists(dtb_path_ext):
+                        dtb_path = dtb_path_ext
+                elif uboot_dtb_path_default:
+                    # No per-config DTB (e.g. conf-1 on x86): use the U-Boot DTB
+                    # which always gets the public key injected via -K in concat_dtb
+                    dtb_path = uboot_dtb_path_default
+                else:
+                    # No key-holding DTB available (e.g. qemux86-64 without
+                    # UBOOT_DTB_BINARY). The algo/key-name/value checks above
+                    # already ran; the cryptographic fit_check_sign step is not
+                    # possible without a DTB containing the public key.
+                    self.logger.debug(
+                        "Skipping fit_check_sign for section %s: no UBOOT_DTB_IMAGE "
+                        "available to carry the public key on MACHINE=%s"
+                        % (section, bb_vars.get('MACHINE', '?')))
+                    continue
                 self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, dtb_path, section)
             else:
                 # Image nodes always need a hash which gets indirectly signed by the config signature
@@ -1247,7 +1296,10 @@  class FitImagePyTests(KernelFitImageBase):
             # others
             'MACHINE': "qemux86-64",
             'UBOOT_ARCH': "x86",
-            'HOST_PREFIX': "x86_64-poky-linux-"
+            'HOST_PREFIX': "x86_64-poky-linux-",
+            # 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',
         }
         if bb_vars_overrides:
             bb_vars.update(bb_vars_overrides)
@@ -1286,8 +1338,8 @@  class FitImagePyTests(KernelFitImageBase):
             root_node.fitimage_emit_section_boot_script(
                 "bootscr-" + bb_vars['FIT_UBOOT_ENV'], bb_vars['FIT_UBOOT_ENV'])
 
-        if bb_vars['MACHINE'] == "qemux86-64": # Not really the right if
-            root_node.fitimage_emit_section_setup("setup-1", "setup1.bin")
+        if bb_vars.get('KERNEL_SETUP_BIN'):
+            root_node.fitimage_emit_section_setup("setup-1", bb_vars['KERNEL_SETUP_BIN'])
 
         if bb_vars.get('INITRAMFS_IMAGE') and bb_vars.get("INITRAMFS_IMAGE_BUNDLE") != "1":
             root_node.fitimage_emit_section_ramdisk("ramdisk-1", "a-dir/a-initramfs-1",