Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions meta-balena-common/classes/kernel-balena.bbclass
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,56 @@ do_kernel_resin_checkconfig[vardeps] += "BALENA_CONFIGS BALENA_CONFIGS_DEPS"
do_kernel_resin_checkconfig[deptask] += "do_kernel_resin_reconfigure"
do_kernel_resin_checkconfig[dirs] += "${WORKDIR} ${B}"

python do_kernel_balena_merge_fragments() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @alexgg,

As you said this is only a part of the overall kernel and kernel module hostapp extension.

I see that there's a telling comment in meta-balena-raspberrypi that explain why we do this.

At first I thought that this was a re-edit of the config fragment mechanism of poky, so perhaps we should precise that poky's mechanism uses on SRC_URI and yours uses in FILESEXTRAPATHS. We could also explain why we do that. I suppose that it is so that we can have a kernel vendor not base on kernel-yocto and still have our own fragment added?

Also I'm wondering if we should merge this mechanism on its own or if we should wait for the rest of the feature? Is this whole change useful on its own?

@alexgg alexgg Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ycardaillac

I see that there's a telling comment in meta-balena-raspberrypi that explain why we do this.

It's also explained in the commit log.

At first I thought that this was a re-edit of the config fragment mechanism of poky, so perhaps we should precise that poky's mechanism uses on SRC_URI and yours uses in FILESEXTRAPATHS.

It is explained in the commit log.

We could also explain why we do that. I suppose that it is so that we can have a kernel vendor not base on kernel-yocto and still have our own fragment added?

The reasoning for the change is also in the commit log:

  • It lets recipes ship fragments by dropping them in ${THISDIR}/files/ (or any
    FILESEXTRAPATHS-reachable dir) without touching SRC_URI.
  • Allows sharing of kernel fragments across kernel siblings
  • Allow fragments to override BALENA_CONFIGS-injected values when needed.

Basically, it adds a mechanism that extensions can use to customize a kernel. Neither Yocto's default or the balenaOS kernel class allow this.

And indeed it's generic so it works independently of the kernel-yocto class.

Also I'm wondering if we should merge this mechanism on its own or if we should wait for the rest of the feature? Is this whole change useful on its own?

It's an independent mechanism to add kernel configuration so it stands on its own. There is no roadmap for extensions yet so there is no rest of the feature to wait for. Without it, extensions would just work, but they will have no clean interface to overriding kernel and modules. But extensions that won't need this would just work fine.

import glob
import os
import subprocess

S = d.getVar("S")
B = d.getVar("B")
base_config = os.path.join(B, ".config")

make_cmd = d.getVar("KERNEL_MAKE_CMD") or "make"
make_opts = d.getVar("EXTRA_OEMAKE") or ""
arch = d.getVar("ARCH")
if not arch:
bb.fatal("kernel-balena: ARCH variable not set")

fragments = []
seen = set()
filesextrapaths = d.getVar("FILESEXTRAPATHS") or ""
for path in filesextrapaths.split(":"):
if not path:
continue
for cfg in sorted(glob.glob(os.path.join(path, "*.cfg"))):
if cfg not in seen:
seen.add(cfg)
fragments.append(cfg)

if not fragments:
return

bb.note("Merging %d kernel fragment(s):" % len(fragments))
for f in fragments:
bb.note(" %s" % f)

merge_script = os.path.join(S, "scripts", "kconfig", "merge_config.sh")
cmd = " ".join([merge_script, "-m", "-O", B, base_config] + fragments)
ret = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if ret.returncode != 0:
bb.fatal("merge_config.sh failed:\n%s" % ret.stderr)
if ret.stderr:
bb.note(ret.stderr)

cmd = "%s %s -C %s O=%s ARCH=%s olddefconfig" % (make_cmd, make_opts, S, B, arch)
bb.note("Running olddefconfig")
ret = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if ret.returncode != 0:
bb.fatal("olddefconfig failed:\n%s" % ret.stderr)
}
addtask kernel_balena_merge_fragments after do_kernel_resin_injectconfig before do_compile
do_kernel_balena_merge_fragments[dirs] += "${B}"

do_configure:append () {
if [ -f "${DEPLOY_DIR_IMAGE}/balena-keys/kmod.crt" ]; then
install -d certs
Expand Down Expand Up @@ -1195,6 +1245,11 @@ do_deploy:prepend () {

# copy to deploy dir latest .config and Module.symvers (after kernel modules have been built)
do_deploy:append () {
install -m 0644 ${D}/boot/Module.symvers-* ${DEPLOYDIR}/Module.symvers
install -m 0644 ${D}/boot/config-* ${DEPLOYDIR}/.config
deployDir="${DEPLOYDIR}"
if [ -n "${KERNEL_DEPLOYSUBDIR}" ]; then
deployDir="${DEPLOYDIR}/${KERNEL_DEPLOYSUBDIR}"
mkdir -p "$deployDir"
fi
install -m 0644 ${D}/boot/Module.symvers-* "$deployDir/Module.symvers"
install -m 0644 ${D}/boot/config-* "$deployDir/.config"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ LIC_FILES_CHKSUM = "file://${BALENA_COREBASE}/COPYING.Apache-2.0;md5=89aea4e17d9
PACKAGES = "${PN}"
FILES:${PN} = "/boot"

KERNEL_INITRAMFS_PROVIDER ?= "virtual/kernel"
KERNEL_INITRAMFS_DEPLOY_DIR ?= "${DEPLOY_DIR_IMAGE}"

do_install() {
mkdir -p ${D}/boot
for type in ${KERNEL_IMAGETYPE}; do
install -m 0644 ${DEPLOY_DIR_IMAGE}/${type}-initramfs-${MACHINE}.bin ${D}/boot/${type}
if [ -f "${DEPLOY_DIR_IMAGE}/${type}.initramfs.sig" ]; then
install -m 0644 ${DEPLOY_DIR_IMAGE}/${type}.initramfs ${D}/boot/${type}
install -m 0644 "${DEPLOY_DIR_IMAGE}/${type}.initramfs.sig" "${D}/boot/${type}.sig"
install -m 0644 ${KERNEL_INITRAMFS_DEPLOY_DIR}/${type}-initramfs-${MACHINE}.bin ${D}/boot/${type}
if [ -f "${KERNEL_INITRAMFS_DEPLOY_DIR}/${type}.initramfs.sig" ]; then
install -m 0644 ${KERNEL_INITRAMFS_DEPLOY_DIR}/${type}.initramfs ${D}/boot/${type}
install -m 0644 "${KERNEL_INITRAMFS_DEPLOY_DIR}/${type}.initramfs.sig" "${D}/boot/${type}.sig"
fi
done
}
do_install[depends] += "virtual/kernel:do_deploy"
do_install[depends] += "${KERNEL_INITRAMFS_PROVIDER}:do_deploy"

PACKAGE_ARCH = "${MACHINE_ARCH}"
5 changes: 4 additions & 1 deletion meta-balena-kirkstone/classes/kernel-balena-noimage.bbclass
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
# the initramfs bundled kernel image
python __anonymous() {
kernel_image_type = d.getVar('KERNEL_IMAGETYPE')
d.appendVar('PACKAGE_EXCLUDE', ' kernel-image-%s-*' % kernel_image_type.lower())
kernel_package_name = d.getVar('KERNEL_PACKAGE_NAME') or "kernel"
d.appendVar('PACKAGE_EXCLUDE',
' %s-image-%s-*' % (kernel_package_name, kernel_image_type.lower()))
d.setVar('RRECOMMENDS:%s-base' % kernel_package_name, '')
}
5 changes: 4 additions & 1 deletion meta-balena-scarthgap/classes/kernel-balena-noimage.bbclass
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
# the initramfs bundled kernel image
python __anonymous() {
kernel_image_type = d.getVar('KERNEL_IMAGETYPE')
d.appendVar('PACKAGE_EXCLUDE', ' kernel-image-%s-*' % kernel_image_type.lower())
kernel_package_name = d.getVar('KERNEL_PACKAGE_NAME') or "kernel"
d.appendVar('PACKAGE_EXCLUDE',
' %s-image-%s-*' % (kernel_package_name, kernel_image_type.lower()))
d.setVar('RRECOMMENDS:%s-base' % kernel_package_name, '')
}
Loading