diff mbox series

[RFC,4/4] kernel-fit-image: add bootconfig support

Message ID 20260319-bootconfig-v1-4-a4d467c3f0ba@valla.it
State New
Headers show
Series Add support for bootconfig on initramfs and FIT images | expand

Commit Message

Francesco Valla March 18, 2026, 11:30 p.m. UTC
Support for bootconfig is added to generated FITs in two forms, both
depending on the FIT_INCLUDE_BOOTCONFIG being set to 1 to be enabled:

  - if INITRAMFS_IMAGE is set, a .bootconfig image will be searched
    instead of the plain or compressed cpio archives;
  - if INITRAMFS_IMAGE is not set, a dummy initramfs will be generated
    and the bootconfig specified in BOOTCONFIG_SRC will be attached to
    it.

The case where both FIT_INCLUDE_BOOTCONFIG and INITRAMFS_IMAGE_BUNDLE
are set to '1' is not supported, as it wouldn't work as expected on the
target device.

Signed-off-by: Francesco Valla <francesco@valla.it>
---
 meta/classes-recipe/kernel-fit-image.bbclass | 44 +++++++++++++++++--
 meta/conf/image-fitimage.conf                |  3 ++
 meta/lib/oeqa/selftest/cases/fitimage.py     | 63 ++++++++++++++++++++++++----
 3 files changed, 98 insertions(+), 12 deletions(-)
diff mbox series

Patch

diff --git a/meta/classes-recipe/kernel-fit-image.bbclass b/meta/classes-recipe/kernel-fit-image.bbclass
index 367bc9bcfd3a4cd237e466269fa143941a02031a..9188723252912f2db6c9952798f66798cfe32b78 100644
--- a/meta/classes-recipe/kernel-fit-image.bbclass
+++ b/meta/classes-recipe/kernel-fit-image.bbclass
@@ -1,5 +1,5 @@ 
 
-inherit kernel-arch kernel-artifact-names uboot-config deploy
+inherit kernel-arch kernel-artifact-names uboot-config bootconfig-config deploy
 require conf/image-fitimage.conf
 
 S = "${UNPACKDIR}"
@@ -12,6 +12,7 @@  EXCLUDE_FROM_WORLD = "1"
 DEPENDS += "\
     u-boot-tools-native dtc-native \
     ${@'kernel-signing-keys-native' if d.getVar('FIT_GENERATE_KEYS') == '1' else ''} \
+    ${@'bootconfig-native' if (d.getVar('FIT_INCLUDE_BOOTCONFIG') == '1' and not d.getVar("INITRAMFS_IMAGE")) else ''} \
 "
 
 python () {
@@ -23,6 +24,9 @@  python () {
         else:
             d.appendVarFlag('do_compile', 'depends', ' ${INITRAMFS_IMAGE}:do_image_complete')
 
+    if not image and d.getVar('BOOTCONFIG_TASK') and d.getVar('FIT_INCLUDE_BOOTCONFIG') == '1':
+        d.appendVarFlag('do_compile', 'depends', ' ${BOOTCONFIG_TASK}')
+
     #check if there are any dtb providers
     providerdtb = d.getVar("PREFERRED_PROVIDER_virtual/dtb")
     if providerdtb:
@@ -54,8 +58,24 @@  FIT_KERNEL_SIGN_ENABLE ?= "${UBOOT_SIGN_ENABLE}"
 FIT_KERNEL_SIGN_KEYNAME ?= "${UBOOT_SIGN_KEYNAME}"
 FIT_KERNEL_SIGN_KEYDIR ?= "${UBOOT_SIGN_KEYDIR}"
 
+create_bootconfig_bin() {
+    if [ ! -e ${DEPLOY_DIR_IMAGE}/${BOOTCONFIG_SRC} ]; then
+        bbfatal "Did not find bootconfig source file: ${DEPLOY_DIR_IMAGE}/${BOOTCONFIG_SRC}"
+    fi
+
+    # Create empty file
+    dd if=/dev/zero of=bootconfig.bin count=0
+
+    # Add bootconfig to file
+    bootconfig -a ${DEPLOY_DIR_IMAGE}/${BOOTCONFIG_SRC} bootconfig.bin
+
+    # Re-read bootconfig
+    bootconfig -l bootconfig.bin
+}
+
 python do_compile() {
     import shutil
+    import subprocess
     import oe.fitimage
 
     itsfile = "fit-image.its"
@@ -135,10 +155,18 @@  python do_compile() {
 
     # Prepare a ramdisk section.
     initramfs_image = d.getVar('INITRAMFS_IMAGE')
-    if initramfs_image and d.getVar("INITRAMFS_IMAGE_BUNDLE") != '1':
+    if initramfs_image and d.getVar("INITRAMFS_IMAGE_BUNDLE") == '1':
+        bb.note("Initramfs is bundled with kernel image, not including it as ramdisk")
+        if d.getVar("FIT_INCLUDE_BOOTCONFIG") == '1':
+            bb.warn("FIT_INCLUDE_BOOTCONFIG not supported if INITRAMFS_IMAGE_BUNDLE is set. Bootconfig won't be included in the FIT image.")
+    elif initramfs_image:
         # Find and use the first initramfs image archive type we find
+        fstypes = d.getVar("FIT_SUPPORTED_INITRAMFS_FSTYPES").split()
+        if d.getVar("FIT_INCLUDE_BOOTCONFIG") == '1':
+            bb.note("Including initramfs with bootconfig")
+            fstypes = [ "%s.bootconfig" % f for f in fstypes ]
         found = False
-        for img in d.getVar("FIT_SUPPORTED_INITRAMFS_FSTYPES").split():
+        for img in fstypes:
             initramfs_path = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), "%s.%s" % (d.getVar('INITRAMFS_IMAGE_NAME'), img))
             if os.path.exists(initramfs_path):
                 bb.note("Found initramfs image: " + initramfs_path)
@@ -152,7 +180,15 @@  python do_compile() {
                 bb.note("Did not find initramfs image: " + initramfs_path)
 
         if not found:
-            bb.fatal("Could not find a valid initramfs type for %s, the supported types are: %s" % (d.getVar('INITRAMFS_IMAGE_NAME'), d.getVar('FIT_SUPPORTED_INITRAMFS_FSTYPES')))
+            bb.fatal("Could not find a valid initramfs type for %s, the supported types are: %s" % (d.getVar('INITRAMFS_IMAGE_NAME'), " ".join(fstypes)))
+    elif d.getVar("FIT_INCLUDE_BOOTCONFIG") == '1':
+        # Include bootconfig also without an initramfs, emitting a dedicated ramdisk section
+        bb.build.exec_func('create_bootconfig_bin', d)
+
+        root_node.fitimage_emit_section_ramdisk("ramdisk-1", "bootconfig.bin",
+            "bootconfig",
+            d.getVar("UBOOT_RD_LOADADDRESS"),
+            d.getVar("UBOOT_RD_ENTRYPOINT"))
 
     #
     # Prepare loadables sections
diff --git a/meta/conf/image-fitimage.conf b/meta/conf/image-fitimage.conf
index 2fdb816d556c786dcc2c14cb149446eaddc2ff4b..b2178853f388e320710b3c0117799145795bf8f4 100644
--- a/meta/conf/image-fitimage.conf
+++ b/meta/conf/image-fitimage.conf
@@ -37,6 +37,9 @@  FIT_CONF_PREFIX[doc] = "Prefix to use for FIT configuration node name"
 
 FIT_SUPPORTED_INITRAMFS_FSTYPES ?= "cpio.lz4 cpio.lzo cpio.lzma cpio.xz cpio.zst cpio.gz ext2.gz cpio"
 
+# Include bootconfig (either with or without initramfs)
+FIT_INCLUDE_BOOTCONFIG ?= "0"
+
 # Allow user to support special use cases where the kernel binary is
 # not included in the FIT image itself.
 # This is particularly useful for UKI-based setups, where the kernel
diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index 3541c07520a8ee3f212471430792c31334d54278..3d3f7cc036253c6ecffa86d844a7113504447912 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -419,12 +419,15 @@  class KernelFitImageBase(FitImageTestCase):
         Call the get_bb_vars function once and get all variables needed by the test case.
         """
         internal_used = {
+            'BOOTCONFIG_SRC',
+            'BOOTCONFIG_TASK',
             'DEPLOY_DIR_IMAGE',
             'FIT_CONF_DEFAULT_DTB',
             'FIT_CONF_MAPPINGS',
             'FIT_CONF_PREFIX',
             'FIT_DESC',
             'FIT_HASH_ALG',
+            'FIT_INCLUDE_BOOTCONFIG',
             'FIT_KERNEL_COMP_ALG',
             'FIT_LOADABLES',
             'FIT_LOADABLE_ENTRYPOINT',
@@ -528,6 +531,7 @@  class KernelFitImageBase(FitImageTestCase):
             ]
         """
         dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars)
+        fit_include_bootconfig = bb_vars['FIT_INCLUDE_BOOTCONFIG']
         fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL']
         fit_uboot_env = bb_vars['FIT_UBOOT_ENV']
         initramfs_image = bb_vars['INITRAMFS_IMAGE']
@@ -552,7 +556,8 @@  class KernelFitImageBase(FitImageTestCase):
         else:
             not_images.append('setup-1')
 
-        if initramfs_image and initramfs_image_bundle != "1":
+        if (initramfs_image and initramfs_image_bundle != "1") or \
+           (fit_include_bootconfig == "1" and not initramfs_image):
             images.append('ramdisk-1')
         else:
             not_images.append('ramdisk-1')
@@ -627,6 +632,7 @@  class KernelFitImageBase(FitImageTestCase):
         return (req_its_paths, not_req_its_paths)
 
     def _get_req_its_fields(self, bb_vars):
+        fit_include_bootconfig = bb_vars['FIT_INCLUDE_BOOTCONFIG']
         initramfs_image = bb_vars['INITRAMFS_IMAGE']
         initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE']
         uboot_rd_loadaddress = bb_vars.get('UBOOT_RD_LOADADDRESS')
@@ -643,7 +649,8 @@  class KernelFitImageBase(FitImageTestCase):
             'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;',
             'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;',
         ]
-        if initramfs_image and initramfs_image_bundle != "1":
+        if (initramfs_image and initramfs_image_bundle != "1") or \
+           (fit_include_bootconfig == "1" and not initramfs_image):
             its_field_check.append('type = "ramdisk";')
             if uboot_rd_loadaddress:
                 its_field_check.append("load = <%s>;" % uboot_rd_loadaddress)
@@ -666,11 +673,13 @@  class KernelFitImageBase(FitImageTestCase):
                 # Works only for tests were the symlink is with -alias suffix
                 its_field_check.append('fdt = "fdt-%s";' % dtb.replace('-alias', ''))
 
-            if initramfs_image and initramfs_image_bundle != "1":
+            if (initramfs_image and initramfs_image_bundle != "1") or \
+               (fit_include_bootconfig == "1" and not initramfs_image):
                 its_field_check.append('ramdisk = "ramdisk-1";')
         else:
             its_field_check.append('kernel = "kernel-1";')
-            if initramfs_image and initramfs_image_bundle != "1":
+            if (initramfs_image and initramfs_image_bundle != "1") or \
+               (fit_include_bootconfig == "1" and not initramfs_image):
                 its_field_check.append('ramdisk = "ramdisk-1";')
 
         return its_field_check
@@ -680,7 +689,8 @@  class KernelFitImageBase(FitImageTestCase):
         if bb_vars.get('UBOOT_SIGN_ENABLE') != "1":
             return {}
         sign_images = '"kernel", "fdt"'
-        if bb_vars['INITRAMFS_IMAGE'] and bb_vars['INITRAMFS_IMAGE_BUNDLE'] != "1":
+        if (bb_vars['INITRAMFS_IMAGE'] and bb_vars['INITRAMFS_IMAGE_BUNDLE'] != "1") or \
+           (bb_vars['FIT_INCLUDE_BOOTCONFIG'] == "1" and not bb_vars['INITRAMFS_IMAGE']):
             sign_images += ', "ramdisk"'
         if bb_vars['FIT_UBOOT_ENV']:
             sign_images += ', "bootscr"'
@@ -705,6 +715,7 @@  class KernelFitImageBase(FitImageTestCase):
         """Generate a dictionary of expected sections in the output of dumpimage"""
         dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars)
         fit_hash_alg = bb_vars['FIT_HASH_ALG']
+        fit_include_bootconfig = bb_vars['FIT_INCLUDE_BOOTCONFIG']
         fit_sign_alg = bb_vars['FIT_SIGN_ALG']
         fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL']
         fit_uboot_env = bb_vars['FIT_UBOOT_ENV']
@@ -732,7 +743,8 @@  class KernelFitImageBase(FitImageTestCase):
         if fit_uboot_env:
             req_sections['bootscr-' + fit_uboot_env] = { "Type": "Script" }
         # Add the initramfs
-        if initramfs_image and initramfs_image_bundle != "1":
+        if (initramfs_image and initramfs_image_bundle != "1") or \
+           (fit_include_bootconfig == "1" and not initramfs_image):
             req_sections['ramdisk-1'] = {
                 "Type": "RAMDisk Image",
                 "Load Address": bb_vars['UBOOT_RD_LOADADDRESS'],
@@ -762,7 +774,8 @@  class KernelFitImageBase(FitImageTestCase):
                         "Kernel": "kernel-1",
                         "FDT": 'fdt-' + real_dtb,
                     }
-                if initramfs_image and initramfs_image_bundle != "1":
+                if (initramfs_image and initramfs_image_bundle != "1") or \
+                   (fit_include_bootconfig == "1" and not initramfs_image):
                     req_sections[conf_name]['Init Ramdisk'] = "ramdisk-1"
                 if loadables:
                     req_sections[conf_name]['Loadables'] = ",".join(loadables)
@@ -771,7 +784,8 @@  class KernelFitImageBase(FitImageTestCase):
             req_sections[conf_name] = {
                 "Kernel": "kernel-1"
             }
-            if initramfs_image and initramfs_image_bundle != "1":
+            if (initramfs_image and initramfs_image_bundle != "1") or \
+               (fit_include_bootconfig == "1" and not initramfs_image):
                 req_sections[conf_name]['Init Ramdisk'] = "ramdisk-1"
             if loadables:
                 req_sections[conf_name]['Loadables'] = ",".join(loadables)
@@ -886,6 +900,36 @@  FIT_LOADABLE_TYPE[loadable1] = "firmware"
 FIT_LOADABLE_FILENAME[loadable2] = "linux.bin"
 FIT_LOADABLE_LOADADDRESS[loadable2] = "0x87000000"
 FIT_LOADABLE_TYPE[loadable2] = "firmware"
+"""
+        config = self._config_add_kernel_classes(config)
+        self.write_config(config)
+        bb_vars = self._fit_get_bb_vars()
+        self._test_fitimage(bb_vars)
+
+    def test_fit_image_bootconfig(self):
+        """
+        Summary:     Verifies the content of the ramdisk node in the FIT Image Tree Source (its)
+                     The FIT settings are set by the test case.
+        Expected:    1. The ITS is generated with bootconfig but without initramfs
+                     2. The ramdisk node is present in the its file with the
+                        expected load and entrypoint
+
+        Product:     oe-core
+        Author:      Francesco Valla <francesco@valla.it>
+        """
+
+        config = """
+KERNEL_IMAGETYPE = "Image"
+
+INITRAMFS_IMAGE = ""
+INITRAMFS_SCRIPTS = ""
+UBOOT_RD_LOADADDRESS = "0x88000000"
+UBOOT_RD_ENTRYPOINT = "0x88000000"
+UBOOT_LOADADDRESS = "0x80080000"
+UBOOT_ENTRYPOINT = "0x80080000"
+BOOTCONFIG_SRC = "bootconfig-test.txt"
+BOOTCONFIG_TASK = "bootconfig-test:do_deploy"
+FIT_INCLUDE_BOOTCONFIG = "1"
 """
         config = self._config_add_kernel_classes(config)
         self.write_config(config)
@@ -1156,6 +1200,7 @@  class FitImagePyTests(KernelFitImageBase):
             'FIT_CONF_PREFIX': "conf-",
             'FIT_DESC': "Kernel fitImage for a dummy distro",
             'FIT_GENERATE_KEYS': "0",
+            'FIT_INCLUDE_BOOTCONFIG': "0",
             'FIT_HASH_ALG': "sha256",
             'FIT_KEY_GENRSA_ARGS': "-F4",
             'FIT_KEY_REQ_ARGS': "-batch -new",
@@ -1173,6 +1218,8 @@  class FitImagePyTests(KernelFitImageBase):
             'UBOOT_LOADADDRESS': "0x20008000",
             'INITRAMFS_IMAGE': "",
             'INITRAMFS_IMAGE_BUNDLE': "",
+            'BOOTCONFIG_SRC': "",
+            'BOOTCONFIG_TASK': "",
             # kernel-uboot.bbclass
             'FIT_KERNEL_COMP_ALG': "gzip",
             'FIT_KERNEL_COMP_ALG_EXTENSION': ".gz",