* [PATCH RFC 2/4] image_types: add bootconfig conversion type
2026-03-18 23:30 [PATCH RFC 0/4] Add support for bootconfig on initramfs and FIT images Francesco Valla
2026-03-18 23:30 ` [PATCH RFC 1/4] bootconfig: add recipe Francesco Valla
@ 2026-03-18 23:30 ` Francesco Valla
2026-03-18 23:30 ` [PATCH RFC 3/4] recipes-test: add bootconfig-test recipe Francesco Valla
2026-03-18 23:30 ` [PATCH RFC 4/4] kernel-fit-image: add bootconfig support Francesco Valla
3 siblings, 0 replies; 7+ messages in thread
From: Francesco Valla @ 2026-03-18 23:30 UTC (permalink / raw)
To: openembedded-core
Cc: Adrian Freihofer, Francesco Castagnotto, Francesco Valla
Add the addition of a bootconfig block at the end of an image as a
conversion type; while a bootconfig is mostly used in conjuntion with an
initramfs (i.e.: a cpio image or one of its compressed forms), having it
as a generic conversion type simplifies the implemntation and is ready
for future modifications at kernel side.
The following configurations variables are introduced (in a separate
bbclass, for further usage by other components):
- BOOTCONFIG_SRC: source file to be used as bootconfig, to be found
inside DEPLOY_DIR_IMAGE;
- BOOTCONFIG_TASK: task deploying the bootconfig source file specified
in BOOTCONFIG_SRC to DEPLOY_DIR_IMAGE; an image using the bootconfig
modifier will depend on it.
Signed-off-by: Francesco Valla <francesco@valla.it>
---
meta/classes-recipe/bootconfig-config.bbclass | 12 ++++++++++++
meta/classes-recipe/image_types.bbclass | 15 +++++++++++++--
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/meta/classes-recipe/bootconfig-config.bbclass b/meta/classes-recipe/bootconfig-config.bbclass
new file mode 100644
index 0000000000000000000000000000000000000000..3d34fc47a3a684aef1ecdbd5ed52c904611bf95d
--- /dev/null
+++ b/meta/classes-recipe/bootconfig-config.bbclass
@@ -0,0 +1,12 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+# Name of the file to be used as bootconfig source, to be found inside DEPLOY_DIR_IMAGE
+BOOTCONFIG_SRC ?= ""
+
+# Task deploying the bootconfig source to DEPLOY_DIR_IMAGE
+# e.g.: BOOTCONFIG_TASK = "bootconfig-debug:do_deploy"
+BOOTCONFIG_TASK ?= ""
diff --git a/meta/classes-recipe/image_types.bbclass b/meta/classes-recipe/image_types.bbclass
index e6ef0ce11e40c0f8388fb5547ab03943004bde2f..98c6d9e836f5c80f78e6f6f32aa923de66feb836 100644
--- a/meta/classes-recipe/image_types.bbclass
+++ b/meta/classes-recipe/image_types.bbclass
@@ -264,9 +264,18 @@ IMAGE_CMD:f2fs () {
sload.f2fs -f ${IMAGE_ROOTFS} ${IMGDEPLOYDIR}/${IMAGE_NAME}.f2fs
}
+oe_bootconfig() {
+ local src_image=$1
+ if [ -z "${BOOTCONFIG_SRC}" ]; then
+ bbfatal "bootconfig image type requested but BOOTCONFIG_SRC is not set"
+ fi
+ cp ${src_image} ${src_image}.bootconfig
+ bootconfig -a ${DEPLOY_DIR_IMAGE}/${BOOTCONFIG_SRC} ${src_image}.bootconfig
+}
+
EXTRA_IMAGECMD = ""
-inherit siteinfo kernel-arch image-artifact-names
+inherit siteinfo kernel-arch image-artifact-names bootconfig-config
JFFS2_ENDIANNESS ?= "${@oe.utils.conditional('SITEINFO_ENDIANNESS', 'le', '-l', '-b', d)}"
JFFS2_ERASEBLOCK ?= "0x40000"
@@ -333,7 +342,7 @@ IMAGE_TYPES:append:x86-64 = " hddimg iso"
# CONVERSION_CMD/DEPENDS.
COMPRESSIONTYPES ?= ""
-CONVERSIONTYPES = "gz bz2 lzma xz lz4 lzo zip 7zip zst sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum bmap u-boot vmdk vhd vhdx vdi qcow2 base64 gzsync zsync ${COMPRESSIONTYPES}"
+CONVERSIONTYPES = "gz bz2 lzma xz lz4 lzo zip 7zip zst sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum bmap u-boot vmdk vhd vhdx vdi qcow2 base64 gzsync zsync bootconfig ${COMPRESSIONTYPES}"
CONVERSION_CMD:lzma = "lzma -k -f -7 ${IMAGE_NAME}.${type}"
CONVERSION_CMD:gz = "gzip -f -9 -n -c --rsyncable ${IMAGE_NAME}.${type} > ${IMAGE_NAME}.${type}.gz"
CONVERSION_CMD:bz2 = "pbzip2 -f -k ${IMAGE_NAME}.${type}"
@@ -360,6 +369,7 @@ CONVERSION_CMD:qcow2 = "qemu-img convert -O qcow2 ${IMAGE_NAME}.${type} ${IMAGE_
CONVERSION_CMD:base64 = "base64 ${IMAGE_NAME}.${type} > ${IMAGE_NAME}.${type}.base64"
CONVERSION_CMD:zsync = "zsyncmake_curl ${IMAGE_NAME}.${type}"
CONVERSION_CMD:gzsync = "zsyncmake_curl -z ${IMAGE_NAME}.${type}"
+CONVERSION_CMD:bootconfig = "oe_bootconfig ${IMAGE_NAME}.${type}"
CONVERSION_DEPENDS_lzma = "xz-native"
CONVERSION_DEPENDS_gz = "pigz-native"
CONVERSION_DEPENDS_bz2 = "pbzip2-native"
@@ -380,6 +390,7 @@ CONVERSION_DEPENDS_vhdx = "qemu-system-native"
CONVERSION_DEPENDS_vhd = "qemu-system-native"
CONVERSION_DEPENDS_zsync = "zsync-curl-native"
CONVERSION_DEPENDS_gzsync = "zsync-curl-native"
+CONVERSION_DEPENDS_bootconfig = "bootconfig-native ${BOOTCONFIG_TASK}"
RUNNABLE_IMAGE_TYPES ?= "ext2 ext3 ext4"
RUNNABLE_MACHINE_PATTERNS ?= "qemu"
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH RFC 4/4] kernel-fit-image: add bootconfig support
2026-03-18 23:30 [PATCH RFC 0/4] Add support for bootconfig on initramfs and FIT images Francesco Valla
` (2 preceding siblings ...)
2026-03-18 23:30 ` [PATCH RFC 3/4] recipes-test: add bootconfig-test recipe Francesco Valla
@ 2026-03-18 23:30 ` Francesco Valla
3 siblings, 0 replies; 7+ messages in thread
From: Francesco Valla @ 2026-03-18 23:30 UTC (permalink / raw)
To: openembedded-core
Cc: Adrian Freihofer, Francesco Castagnotto, Francesco Valla
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 --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",
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread