Engineering /Embedded Linux & Yocto

ReferenceWorking8 min read

What a Yocto BSP hand-over should actually contain

TL;DR

A usable Yocto BSP hand-over: a pinned manifest, vendored layers, a documented DISTRO/MACHINE, a reproducible build, a signed image with its SPDX SBOM, and the rationale for every kernel and U-Boot config choice.

View as Markdown

A Yocto BSP hand-over fails in a predictable way. Six months after the last invoice, someone runs the build on a fresh machine and it breaks: a layer moved, meta-openembedded advanced a branch, an SRC_URI 404s, the host GCC is now too new for a fetched tarball. The image that shipped is fine. The ability to rebuild it is gone. That is the thing a hand-over has to protect, and most hand-overs do not.

This page is the checklist we hold our own BSP deliveries to. It is deliberately about artefacts and their properties, not about a particular board.

The one property that matters: bit-for-bit rebuildability

Everything below is in service of a single test. Take the delivery, put it on a machine that has never seen the project, follow the written steps, and get an image whose package manifest matches the one that shipped. If that test passes, the BSP is maintainable. If it does not, you have bought a binary with source attached, not a BSP.

Yocto gives you the machinery for this — BB_HASHSERVE, hash-equivalence, buildhistory, reproducible-builds class — but none of it is on by a default that survives a vendor change. It has to be configured, and the configuration is part of the deliverable.

1. A pinned, self-contained source manifest

  • A repo manifest or a kas file that pins every layer to a commit SHA, not a branch. honister, kirkstone, scarthgap — a branch name is a moving target. kirkstone today is not kirkstone from the release date.
  • The BitBake and OE-Core revision pinned in the same file.
  • No layer fetched from a URL the client does not control. If a layer lives only on a vendor’s GitLab, it is a single point of failure with someone else’s uptime. Vendored into the client’s own Git, with the upstream URL and SHA recorded in the commit message so the provenance is not lost.
  • The conf/bblayers.conf BBLAYERS order documented, because layer priority changes which .bbappend wins.

The test: kas checkout (or repo sync) on an air-gapped mirror reproduces the exact tree. No “then update meta-freescale to the tip”.

2. Your own layer, and only your changes in it

There should be exactly one layer that contains the project’s work — meta-<project> — and it should be the only layer with local commits. Every change to a BSP or upstream recipe lives there as a .bbappend or a versioned recipe copy, never as an edit to meta-ti, meta-freescale, or poky.

Why this is a hand-over issue and not a style preference: when the client later moves from kirkstone to the next LTS, the migration work is reviewing one layer. If changes are smeared across five vendor layers, the migration is archaeology, and the estimate for it triples.

What belongs in meta-<project>:

  • The machine .conf (or a .bbappend to the vendor’s) with every deviation commented — why PREFERRED_VERSION_linux-* is pinned, why a MACHINE_FEATURE was removed.
  • The image recipe(s). One production image, one development image, and the delta between them stated explicitly (dev adds debug-tweaks, openssh-sftp, gdbserver — and production must be verified not to).
  • The distro config if the project defines its own DISTRO. It usually should: inheriting poky and overriding twelve variables in local.conf means the config only exists on the machine that has that local.conf.

3. local.conf is not part of the delivery — the distro config is

local.conf is per-developer scratch. Anything load-bearing that lives there will be lost. The hand-over must move every meaningful setting into version control:

  • DISTRO_FEATURES / DISTRO_FEATURES_remove — particularly systemd vs sysvinit, wayland/x11, pam, usrmerge.
  • IMAGE_FSTYPES and how they map to what the factory actually flashes (wic.gz, wic.bmap, a .swu for SWUpdate).
  • EXTRA_IMAGE_FEATURES, IMAGE_INSTALL:append.
  • The PACKAGE_CLASSES choice (package_rpm/ipk/deb) — this affects the on-target update mechanism and cannot be changed casually later.
  • Any PREMIRRORS / SSTATE_MIRRORS pointing at internal infrastructure.

4. The kernel: a defconfig fragment and a reason for every line

A kernel handed over as a 6,000-line .config is not maintainable, because nobody can tell an essential setting from an accident of make oldconfig.

  • defconfig plus fragments, wired through KERNEL_CONFIG_FRAGMENTS or a linux-*.bbappend. The fragment is small and every line is intentional.
  • A written rationale for the non-obvious ones: which CONFIG_ enables the Ethernet PHY, which sets the RT behaviour, which was needed for a USB gadget mode, which disables an unused subsystem to cut attack surface and boot time.
  • The kernel provenance: mainline version, the vendor SoC tree it is based on, the patch stack applied on top — as a quilt series or Git history, not a single squashed diff. When a CVE lands, someone needs to know whether the tree already carries the fix.
  • Out-of-tree modules identified, with their licence and their source. An out-of-tree Wi-Fi driver with no upstream is a maintenance liability that should be named in the hand-over, not discovered later.

5. Device tree: the board’s hardware description, reviewed

The device tree is where “the BSP works” and “the BSP is correct” diverge. A node can be missing and the board still boots.

  • The board .dts in meta-<project>, including via .dtsi, not patched into the vendor’s file in place.
  • Every pinmux group traceable to the schematic net name. A comment linking MX8MM_IOMUXC_SD2_CD_B_GPIO2_IO12 to the actual card-detect net saves the next engineer an afternoon with a multimeter.
  • Regulators modelled properly — regulator-always-on, regulator-boot-on and the supply chain to each peripheral. Half of “the peripheral randomly does not enumerate” bugs are a missing or lazy regulator description.
  • Overlays, if used, with the base-plus-overlay combination that the running system actually uses documented. Applied by U-Boot or by the kernel — state which.

6. U-Boot: the config, the environment, and the boot flow

  • The U-Boot defconfig and any board patches, same discipline as the kernel.
  • The boot environment as a file, not as whatever is currently in the SPI flash of the one golden board. The boot.cmd/boot.scr or the extlinux config, in version control.
  • The boot flow written out: ROM → SPL → U-Boot proper → kernel → init, with where each stage lives (eMMC boot partition, offset, GPT) and what the fallback path is if the primary kernel does not come up.
  • If secure boot is in play: which keys sign what, where the public keys are fused or stored, and — critically — a documented recovery path for a board whose signature check fails. A secure-boot BSP with no recovery story is a brick generator.

7. The signed image and its bill of materials

  • The exact image that was released, plus its signature and the public key to verify it.
  • buildhistory output committed for that build: the package list with versions, image size, dependency graph, and the diff against the previous release. This is what makes “what changed between v1.2 and v1.3” answerable in minutes.
  • An SBOM — Yocto emits SPDX 2.2 JSON via create-spdx — covering every package in the image with its version and licence. Increasingly this is a contractual and regulatory requirement (the EU Cyber Resilience Act among them), and it is far cheaper to generate at build time than to reconstruct.
  • The licence manifest (license.manifest, deploy/licenses/) and the sources for anything under a copyleft licence, or a working bitbake -c archiver configuration that regenerates them.

8. The build environment itself

“Works on my machine” is not a hand-over. Pin the host too:

  • A container (the CROPS base or a project Dockerfile) that fixes the host distro, the essential host packages, Python, and locale. Yocto is sensitive to host GCC and host Python; a 2024 build host and a 2027 build host are not equivalent.
  • The exact bitbake invocation, target names, and any BB_ENV_PASSTHROUGH additions.
  • Expected build resources: disk for TMPDIR and SSTATE_DIR, RAM, rough wall time on a stated machine — so the client can size CI.
  • A populated sstate and downloads mirror, or instructions to build one. Without it the first rebuild pulls hundreds of source archives from the internet, and some of those URLs will be dead. An offline DL_DIR archive is the single most valuable non-obvious artefact in the box.

9. Documentation that is task-shaped

Not a wiki dump. Five documents, each answering a question someone will actually ask:

  1. Build it — from empty machine to flashable image, copy-paste commands.
  2. Flash it — factory path and field path, including the recovery/USB-download procedure for a bricked board.
  3. Change the kernel config / add a package / bump a recipe — the routine maintenance loop.
  4. Cut a release — versioning, signing, what gets tagged, what gets archived.
  5. Known issues and deferred work — the honest list. Every BSP has one; a hand-over without it just means the client finds them the hard way.

What this costs

Producing this is real work — on the order of one to two engineer-weeks on top of the BSP itself, most of it in items 1, 7, and 8. It is worth being explicit about that in a statement of work rather than discovering it at the end. The payoff is entirely deferred: it shows up the first time the client rebuilds without the original team, and it is the difference between an afternoon and a re-engagement.

The trade-off we make deliberately: we do not hand over a build that tracks upstream branches, even though that would look more “up to date” on delivery day. A pinned build is frozen and will drift out of security currency until someone deliberately bumps it — that is the cost. It is the right cost, because a build that silently changes under the client is not a build they own.

Talk to an engineer

Ask about this directly — the person who wrote it answers, not a sales desk.