Build Your Own BSP Layer¶
This tutorial builds an integration project that consumes meta-tolomeo as a source layer. You scaffold it from the DAVE Yocto project template, wire the kas configuration that binds meta-tolomeo and its upstream dependencies into one build, derive your own distribution and image, and verify the result with a build.
meta-tolomeo supplies the distribution and the ToloMEO application stack, including the services that give the device
access to ToloMEO Cloud features. The machine configuration is yours alone. This tutorial therefore takes you as far as
a verified build against meta-tolomeo's QEMU reference machine, then hands off to your own machine with
meta-tolomeo-qemu as the worked example.
What each side provides¶
This boundary determines which files you write and which you only reference.
| meta-tolomeo provides | You provide |
|---|---|
tolomeo.inc — the distribution base |
acme.inc and acme-devel.conf / acme-prod.conf deriving from it |
| CVE check, CycloneDX/VEX, SWUpdate, secure boot and hardening classes and includes | The DISTRO_FEATURES selection your product needs |
tolomeo-image-common.inc, packagegroup-tlm-*, ToloMEO Cloud service recipes |
acme-image-common.inc and acme-image-devel.bb deriving from them |
| A kas fragment per layer, declaring repositories, config blocks and passthrough variables | kas/base.yml, kas/common.yml, the target files and kas-ci/ |
| Storage and secure-boot machine includes, plus a reference machine to build against | Your machine configurations, board recipes and WIC (.wks) partition layouts |
Note
meta-tolomeo-distro ships only conf/distro/include/. The concrete tolomeo-devel and tolomeo-prod
distributions live in meta-tolomeo-qemu as reference distributions. Do not inherit them — derive your own from
tolomeo.inc, exactly as they do.
Prerequisites¶
- Build the ToloMEO Reference Image completed. It teaches the devcontainer and kas workflow this tutorial assumes, and its populated sstate cache speeds up Step 8.
- Docker 20.10+ (Get Docker) with at least 16 GB RAM allocated
- VS Code 1.60+ with the Dev Containers extension
- Copier 9.0 or newer
- An SSH key configured for GitLab (GitLab SSH guide), with read access to the DAVE Yocto project template
- 50 GB free disk space
- An empty GitLab (or GitHub) project, created without a README or any other initial file
Install Copier in an isolated environment if you prefer:
Choose your names¶
Every name in the project derives from a single project slug. This tutorial uses acme; substitute your own throughout.
| Item | Value | Where it appears |
|---|---|---|
| Project slug | acme |
everywhere below |
| Repository | meta-acme |
Copier repo_name |
| Layer name prefix | meta-tolomeo-acme |
Copier meta_layer |
| Layers | meta-tolomeo-acme-app, -bsp, -distro |
repository root |
| Distributions | acme-devel, acme-prod |
meta-tolomeo-acme-distro/conf/distro/ |
| Images | acme-image-devel, acme-image-prod |
meta-tolomeo-acme-app/recipes-core/images/ |
| Machines (yours, later) | acme-<soc>-devel, acme-<soc>-prod |
meta-tolomeo-acme-bsp/conf/machine/ |
The meta-tolomeo- prefix on meta_layer marks the layers as ToloMEO integration layers in bblayers.conf and
LAYERDEPENDS; the repository keeps the shorter product name.
Step 1 — Generate the project¶
Run Copier against the template. Supplying the answers on the command line keeps the run reproducible:
copier copy git@gitlab.com:DAVEEmbeddedSystems/public/project-templates/yocto-template-project.git \
meta-acme --trust \
-d project_name="Acme Edge Gateway" \
-d repo_name="meta-acme" \
-d meta_layer="meta-tolomeo-acme" \
-d description="ToloMEO integration layer for the Acme Edge Gateway" \
-d yocto_version="scarthgap" \
-d kas_version="4.8.2" \
-d secure_boot_type="none" \
-d firmware_name="Acme-edge-gateway-firmware" \
-d build_version="1.0.0+26.07" \
-d author_name="Your Name" \
-d author_email="you@example.com" \
-d gitlab_org="YourCompany"
secure_boot_type selects which NXP development PKI to ship: none, hab4 (i.MX 6/⅞, RSA-2048) or ahab (i.MX 9,
ECC secp384r1). firmware_name and build_version are recorded in local.conf as FIRMWARE_NAME and BUILD_VERSION.
Any omitted answer is prompted for. Copier then initialises the Git repository, creates the default branch and adds the
origin remote.
The result:
meta-acme/
├── meta-tolomeo-acme-app/ # image and application recipes
├── meta-tolomeo-acme-bsp/ # machine configurations and board recipes — yours
├── meta-tolomeo-acme-distro/ # distribution configuration
├── kas/
│ ├── base.yml # layer stack, with poky and meta-openembedded already pinned
│ └── common.yml # build paths and mirrors
├── kas-ci/
│ └── common.yml # CI variant of common.yml
├── .devcontainer/ # development environment
├── devel-keys/ # development signing material, plus secure-boot PKI if selected
├── docs/ # documentation scaffold
├── .gitlab-ci.yml
├── .pre-commit-config.yaml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── renovate.json # keeps the pins in base.yml current
└── zensical.toml
Linter configuration (.oelint.cfg, .markdownlint.yml, .yamllint, .gitleaks.toml and friends) is generated
alongside these.
Each layer already has a conf/layer.conf with the correct collection name, BBFILE_PRIORITY and LAYERSERIES_COMPAT.
The generated BBFILE_PRIORITY is 10, above meta-tolomeo's -app, -bsp, -distro and -nxp layers, which are all
9. meta-tolomeo-qemu is also 10, so raise yours if you ever need to override a recipe from that layer
specifically.
Step 2 — Link the signing keys¶
meta-tolomeo's kas fragments default the U-Boot FIT and SWUpdate signing directories to ${BSPDIR}/keys, which resolves
to your repository root. The template ships development keys in devel-keys/; point keys at them:
If you chose hab4 or ahab, devel-keys/secure-boot/ also holds the NXP development PKI, which the symlink puts at
the ${BSPDIR}/keys/secure-boot path that SIG_DATA_PATH expects. The generated .gitignore already excludes keys,
so the symlink will not be committed.
Warning
The keys in devel-keys/ are public and exist only so that a build completes. Never ship a device signed with them.
See Generate a Signed Update Package for producing your own key
material.
Step 3 — Start the development environment¶
The template's devcontainer is built on the official kas image, so no host-side Yocto tooling is needed.
- Open the project in VS Code: File → Open Folder, select
meta-acme. - VS Code detects
.devcontainer/devcontainer.jsonand offers Reopen in Container. Accept it. Alternatively, use the Command Palette →Dev Containers: Reopen in Container. - The first container build takes several minutes. Its
postCreateCommandinstalls the pre-commit hooks, so the linters CI runs are active locally too.
Verify the environment from a terminal inside the container:
For cache configuration and resource tuning, see Configure the Dev Environment.
Step 4 — Declare the layer stack in kas/base.yml¶
kas/base.yml declares which repositories are part of the build, which of their layers are enabled, and which settings
apply to every target. The template generates most of it: poky and meta-openembedded are pinned, your three layers are
declared, and BSPDIR and the build-metadata blocks are set. What you add is meta-tolomeo.
4.1 Include the meta-tolomeo kas fragments¶
Each meta-tolomeo layer ships a kas fragment declaring what it needs. Include those fragments rather than copying their contents:
---
header:
version: 14
includes:
- repo: meta-tolomeo
file: meta-tolomeo-app/kas/common.yml
- repo: meta-tolomeo
file: meta-tolomeo-bsp/kas/common.yml
- repo: meta-tolomeo
file: meta-tolomeo-distro/kas/common.yml
What each one brings in:
| Fragment | Contributes |
|---|---|
meta-tolomeo-app/kas/common.yml |
Passthrough of DEFAULT_ROOT_PASSWORD and DEFAULT_ADMIN_PASSWORD from the environment |
meta-tolomeo-bsp/kas/common.yml |
U-Boot FIT signing defaults (UBOOT_SIGN_KEYDIR, UBOOT_SIGN_KEYNAME) and their passthrough |
meta-tolomeo-distro/kas/common.yml |
The meta-security, meta-swupdate, meta-vulnscout and meta-virtualization repositories, the SWUpdate signing defaults, the CVE database settings, and the build-version passthrough |
Add meta-tolomeo-nxp/kas/common.yml as a fourth include only if your SoC is an NXP i.MX part.
Tip
The fragments own meta-tolomeo's dependencies, so a new upstream layer reaches your build the moment you bump the
meta-tolomeo commit. Never restate their contents in your own files.
4.2 Review the upstream pins¶
poky and meta-openembedded arrive already pinned, on the branch matching your yocto_version answer and with the
sub-layers meta-tolomeo needs enabled. Check the two commits against the meta-tolomeo release you are about to consume;
they must be the ones it is tested with.
Each pin carries a # renovate: annotation, so the generated renovate.json raises a merge request when a newer commit
is available and the pins stay current without hand-editing.
Three conventions are visible in those entries, and they apply to every repository you add later — meta-tolomeo next, and your SoC vendor's BSP layers in Step 10:
branchandcommittogether. The commit makes the build reproducible; the branch records the upstream line and gives a bump its target. A branch alone lets your builds drift.path: sources/<name>. Fetched repositories all live undersources/, keeping the root readable.- Explicit
layers:. Only the listed sub-layers are enabled. Omit the key to enable the repository root as a single layer, as a flat layer likemeta-swupdateneeds.
4.3 Pin meta-tolomeo¶
meta-tolomeo is just another pinned repository:
meta-tolomeo:
url: https://gitlab.com/DAVEEmbeddedSystems/public/tolomeo-public-projects/edge/tolomeo-edge/meta-tolomeo
branch: scarthgap
commit: <sha of the latest stable release>
path: sources/meta-tolomeo
layers:
meta-tolomeo-app:
meta-tolomeo-bsp:
meta-tolomeo-distro:
meta-tolomeo-qemu:
meta-tolomeo-bsp contributes no machines of its own. It provides the storage and secure-boot machine includes that
your machine configurations will require, documented in Machine Configuration.
meta-tolomeo-qemu is enabled here because it provides tolomeo-qemux86-64, the reference machine you will build
against in Step 8 to verify the wiring, and because its machine configurations are the worked example you will follow in
Step 10. Keep it enabled afterwards — a QEMU target costs nothing and keeps a hardware-independent build available.
4.4 Your own layer entry¶
Your layers are already on disk, so their entry carries no url and no commit: kas treats a url-less repository as
the working repository and resolves layer paths from its root. The template generates this entry and BSPDIR with it:
meta-tolomeo-acme:
layers:
meta-tolomeo-acme-app:
meta-tolomeo-acme-bsp:
meta-tolomeo-acme-distro:
bblayers_conf_header:
env: |
BSPDIR := "${@os.path.abspath(os.path.dirname(d.getVar('FILE', True)) + '/../..')}"
The entry is keyed on your meta_layer answer, not the repository name, so it reads meta-tolomeo-acme even though the
repository is meta-acme.
BSPDIR resolves to your repository root. It lets meta-tolomeo default its key directories to ${BSPDIR}/keys without
knowing your layout, and your own recipes can use it the same way.
4.5 Build metadata and project defaults¶
The template generates the common-minimal block — disk-space safety nets, parallelism, rm_work — an empty block for
your own settings, and a buildinfo block filled from your firmware_name and build_version answers:
local_conf_header:
common-meta-tolomeo-acme: |-
# --- Project-specific defaults ---
buildinfo: |-
FIRMWARE_NAME = "Acme-edge-gateway-firmware"
BUILD_VERSION = "1.0.0+26.07"
The keys under local_conf_header are block names, not variables, and they are the override mechanism: kas merges
blocks by name and the last definition wins. meta-tolomeo-qemu's fragment also defines buildinfo, and the generated
one replaces it because base.yml is evaluated after its includes. Reuse a block name to override; pick a fresh one,
prefixed with your slug, to add.
A vendor licence acknowledgement goes in its own block, for example common-nxp-eula: |- containing ACCEPT_FSL_EULA =
"1".
Complete kas/base.yml
---
header:
version: 14
includes:
- repo: meta-tolomeo
file: meta-tolomeo-app/kas/common.yml
- repo: meta-tolomeo
file: meta-tolomeo-bsp/kas/common.yml
- repo: meta-tolomeo
file: meta-tolomeo-distro/kas/common.yml
repos:
poky:
url: https://git.yoctoproject.org/poky
branch: scarthgap
# renovate: depName=poky packageName=... currentValue=scarthgap+26.07
commit: <pinned by the template>
path: sources/poky
layers:
meta:
meta-poky:
meta-openembedded:
url: https://git.openembedded.org/meta-openembedded
branch: scarthgap
# renovate: depName=meta-openembedded packageName=... currentValue=scarthgap+26.07
commit: <pinned by the template>
path: sources/meta-openembedded
layers:
meta-filesystems:
meta-networking:
meta-oe:
meta-perl:
meta-python:
meta-webserver:
meta-tolomeo:
url: https://gitlab.com/DAVEEmbeddedSystems/public/tolomeo-public-projects/edge/tolomeo-edge/meta-tolomeo
branch: scarthgap
commit: <sha of the latest stable release>
path: sources/meta-tolomeo
layers:
meta-tolomeo-app:
meta-tolomeo-bsp:
meta-tolomeo-distro:
meta-tolomeo-qemu:
meta-tolomeo-acme:
layers:
meta-tolomeo-acme-app:
meta-tolomeo-acme-bsp:
meta-tolomeo-acme-distro:
bblayers_conf_header:
env: |
BSPDIR := "${@os.path.abspath(os.path.dirname(d.getVar('FILE', True)) + '/../..')}"
local_conf_header:
common-minimal: |-
# --- Disk space safety nets ---
# Stop scheduling new tasks if disk low, halt if critically low
BB_DISKMON_DIRS ??= "\
STOPTASKS,${TMPDIR},1G,100K \
STOPTASKS,${DL_DIR},1G,100K \
STOPTASKS,${SSTATE_DIR},1G,100K \
STOPTASKS,/tmp,100M,100K \
HALT,${TMPDIR},100M,1K \
HALT,${DL_DIR},100M,1K \
HALT,${SSTATE_DIR},100M,1K \
HALT,/tmp,10M,1K"
# Number of CPU cores
BB_NUMBER_THREADS ?= "${@oe.utils.cpu_count()}"
# Parsing benefits less from parallelism
BB_NUMBER_PARSE_THREADS ?= "${@max(1, oe.utils.cpu_count() // 2)}"
# Allow mild over-subscription inside recipes
PARALLEL_MAKE ?= "-j ${@int(oe.utils.cpu_count() * 1.25)}"
PARALLEL_MAKEINST ?= "${PARALLEL_MAKE}"
# --- Save disk space by cleaning workdirs after successful tasks ---
INHERIT += "rm_work"
# Collect build stats (optional, helps analyze build bottlenecks)
USER_CLASSES ?= "buildstats"
common-meta-tolomeo-acme: |-
# --- Project-specific defaults ---
buildinfo: |-
FIRMWARE_NAME = "Acme-edge-gateway-firmware"
BUILD_VERSION = "1.0.0+26.07"
Step 5 — Review the build paths and mirrors¶
kas/common.yml holds where the build happens rather than what it builds. The template provides a working version:
---
header:
version: 14
includes:
- kas/base.yml
local_conf_header:
common-path: |-
TMPDIR = "${TOPDIR}/tmp"
# Central downloads directory (shared across builds)
# DL_DIR ?= "/workspaces/yocto/downloads"
# Central sstate cache directory (use only for production)
# SSTATE_DIR ?= "/workspaces/yocto/sstate-cache"
SSTATE_MIRRORS ?= "file://.* https://reed-cache01.tolomeo.io/sstate-cache/PATH;downloads=PATH \
file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH\
"
BB_HASHSERVE = ""
BB_SIGNATURE_HANDLER = "OEBasicHash"
Two points are worth understanding rather than accepting:
BB_HASHSERVE = ""andOEBasicHashbelong with the mirrors. Yocto's default handler derives task signatures from the local hash-equivalence server's history, so they never match what a shared mirror was populated with.OEBasicHashmakes signatures a pure function of the metadata, which is what produces cache hits. Change either and you lose the mirror.TMPDIRis inside the build directory, and the shared download and sstate directories are commented out. Uncomment them once you have chosen where those caches live.
Optional: feature fragments¶
When a group of settings is meaningful on its own — provisioning parameters, secure-boot fuse values, a manufacturing
profile — give it its own file under kas/ instead of growing base.yml:
# kas/secure-boot.yml
---
header:
version: 14
local_conf_header:
common-secure-boot: |-
# ... your signing and fuse configuration ...
kas-ci/common.yml then reuses the same fragment verbatim, so local and CI builds cannot drift apart.
Step 6 — Derive your distribution¶
A ToloMEO-based distribution takes two files: a shared include holding everything common to your product, and one thin configuration per build flavour.
Create meta-tolomeo-acme-distro/conf/distro/include/acme.inc:
# Copyright (C) 2026 Acme Corp
# Released under the MIT license (see COPYING.MIT for the terms)
require conf/distro/include/tolomeo.inc
# Additional DISTRO_FEATURES common to every Acme flavour
DISTRO_FEATURES += "ipv4 \
ipv6 \
"
# Update support features
DISTRO_FEATURES += "update-offline \
update-ota \
"
# Remove backends that conflict with a headless device
DISTRO_FEATURES:remove = "directfb \
x11 \
"
That single require is the whole inheritance mechanism. tolomeo.inc sets DISTRO_VERSION, adds tolomeo to
DISTROOVERRIDES, selects systemd and RPM packaging, pulls in the upstream security-flag includes and registers the
CycloneDX supplier map. Everything after it is yours.
Then meta-tolomeo-acme-distro/conf/distro/acme-devel.conf:
require conf/distro/include/acme.inc
DISTRO = "acme-devel"
DISTRO_NAME = "Acme Edge Gateway Development Distro"
# Development features
DISTRO_FEATURES += "ptest"
And acme-prod.conf, which adds the security and reporting stack:
require conf/distro/include/acme.inc
require conf/distro/include/cve-extra-exclusions.inc
require conf/distro/include/tolomeo-cve-check.inc
require conf/distro/include/tolomeo-cyclonedx.inc
DISTRO = "acme-prod"
DISTRO_NAME = "Acme Edge Gateway Production Distro"
# Security hardening features
DISTRO_FEATURES += "acl \
audit \
harden \
integrity \
pam \
security \
xattr \
"
# Strict QA checks for production
WARN_TO_ERROR_QA = "already-stripped \
compile-host-path \
install-host-path \
installed-vs-shipped \
ldflags \
pn-overrides \
rpaths \
staticdev \
unknown-configure-option \
useless-rpaths \
"
ERROR_QA:append = " ${WARN_TO_ERROR_QA}"
tolomeo-cve-check.inc and tolomeo-cyclonedx.inc come from meta-tolomeo-distro, cve-extra-exclusions.inc from
poky. Keeping them out of the development flavour keeps those builds fast. See
Export SBOM for what the pipeline produces.
Note
secure-boot is absent above because it needs machine-level signing configuration that does not exist yet. Add it
with your machine — see Enable FIT Image Signing.
Step 7 — Derive your image¶
Images follow the same two-file pattern. Create
meta-tolomeo-acme-app/recipes-core/images/include/acme-image-common.inc:
require recipes-core/images/include/tolomeo-image-common.inc
# Extra space for on-device development, in KB
IMAGE_ROOTFS_EXTRA_SPACE = "0"
tolomeo-image-common.inc installs the packagegroup-tlm-core, packagegroup-tlm-system-monitoring and
packagegroup-tlm-system-update package groups, enables the SSH server and overlayfs-etc, and configures artifact
collection — including QEMU artifact patterns, which is why the Step 8 build produces a bootable result.
Then meta-tolomeo-acme-app/recipes-core/images/acme-image-devel.bb:
# Copyright (C) 2026 Acme Corp
# Released under the MIT license (see COPYING.MIT for the terms)
SUMMARY = "Acme Edge Gateway development image"
DESCRIPTION = "Development image for the Acme Edge Gateway, including debugging and test tooling."
AUTHOR = "Acme Corp <devel@example.com>"
HOMEPAGE = "https://www.example.com"
SECTION = "base"
LICENSE = "MIT"
inherit core-image
require include/acme-image-common.inc
# nooelint: oelint.var.badimagefeature
IMAGE_FEATURES += "allow-empty-password \
allow-root-login \
empty-root-password \
post-install-logging \
serial-autologin-root \
tools-debug \
tools-sdk \
"
IMAGE_INSTALL += "packagegroup-core-tools-debug \
packagegroup-core-tools-profile \
parted \
python3-pip \
systemd-analyze \
"
export IMAGE_BASENAME = "acme-image-devel"
export IMAGE_BASENAME must match the recipe name: artifact collection, update-package generation and the deployment
file names all key off it.
Step 8 — Verify the wiring with a build¶
Your machine comes in Step 10. Prove everything else first, so that when your own machine later fails to build you know the fault is in the machine and not the layer stack.
Build against tolomeo-qemux86-64, the QEMU reference machine in meta-tolomeo-qemu. It exercises the whole chain —
repository pins, fragment includes, layer resolution, your distribution and image derivations — with no hardware
involved. kas dump validates structure, but only a build catches a poky pin that disagrees with meta-tolomeo's or a
missing layer dependency.
A target file composes kas/common.yml and states the three coordinates of a build. Create
kas/tolomeo-qemux86-64_acme-devel_acme-image-devel.yml:
---
header:
version: 14
includes:
- kas/common.yml
machine: tolomeo-qemux86-64
distro: acme-devel
target: acme-image-devel
The name <machine>_<distro>_<image>.yml makes the whole build matrix visible from ls kas/. Non-image targets —
update-artifact generation, a bootloader, a provisioning image — use <machine>_<purpose>.yml, since the purpose
implies the distribution.
Check that the configuration resolves before starting a long build. kas dump merges the includes and prints the
effective configuration without fetching or building:
Check three things: every repository has a concrete commit, every expected layer appears under layers:, and your
local_conf_header blocks are present with no unintended duplicates.
Then build:
kas fetches the repositories into sources/, generates build/conf/bblayers.conf and build/conf/local.conf, and runs
BitBake. A cold build takes 2–4 hours; with the sstate mirrors reachable, most tasks are cache hits.
The build succeeded when BitBake reports:
Your image is in build/tmp-glibc/deploy/images/tolomeo-qemux86-64/. To boot it, follow
Run Standalone, using acme-image-devel-tolomeo-qemux86-64.rootfs.ext4.gz.
Inspect what kas generated — the clearest confirmation that the wiring is right:
Your three layers appear as ${TOPDIR}/../meta-tolomeo-acme-*, meta-tolomeo's under
${TOPDIR}/../sources/meta-tolomeo/*, and the fragments' contributions — meta-security, meta-swupdate,
meta-vulnscout, meta-virtualization — appear without your having named them.
With this build green, the only untested part of your project is the machine.
Step 9 — Wire the CI configuration¶
Note
If you host the project on GitLab, the generated .gitlab-ci.yml builds the Yocto project for you, provided the
build infrastructure and a runner are available.
kas-ci/ mirrors kas/ but decomposes by axis rather than by build: one fragment per machine, one per distribution, so
the pipeline can compose a matrix.
kas-ci/common.yml is the CI counterpart of kas/common.yml, and the template generates it. It includes the same
kas/base.yml, so CI cannot drift from local builds, drops the local cache directories, and adds a commented block for
injecting the CI job token into recipes that fetch from private repositories:
---
header:
version: 14
includes:
- kas/base.yml
local_conf_header:
common-path: |-
TMPDIR = "${TOPDIR}/tmp"
SSTATE_MIRRORS ?= "file://.* https://reed-cache01.tolomeo.io/sstate-cache/PATH;downloads=PATH \
file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH\
"
BB_HASHSERVE = ""
BB_SIGNATURE_HANDLER = "OEBasicHash"
common-meta-tolomeo-acme-ci: |-
# --- Private GitLab repository access ---
# VAR:pn-<package> = "git://gitlab-ci-token:${CI_JOB_TOKEN}@<git-clone-url>;protocol=https;"
Uncomment that last line once per private dependency, replacing VAR with the variable the recipe reads its SRC_URI
from and <git-clone-url> with the repository path. If you add feature fragments as in Step 5, include them here too so
the two entry points stay identical.
One addition is worth making yourself — PREMIRRORS, in the same common-path block:
# Fetch source downloads from the shared mirror before going upstream,
# for faster and more reliable do_fetch.
PREMIRRORS ?= "git://.*/.* https://reed-cache01.tolomeo.io/downloads/ \
gitsm://.*/.* https://reed-cache01.tolomeo.io/downloads/ \
http://.*/.* https://reed-cache01.tolomeo.io/downloads/ \
https://.*/.* https://reed-cache01.tolomeo.io/downloads/ \
ftp://.*/.* https://reed-cache01.tolomeo.io/downloads/ \
"
Runners start with an empty download directory on every job, so trying the shared mirror before upstream makes
do_fetch faster and far less flaky.
Then one fragment per machine:
# kas-ci/tolomeo-qemux86-64.yml
---
header:
version: 14
includes:
- kas-ci/common.yml
machine: tolomeo-qemux86-64
And one per distribution, with no includes at all:
The pipeline combines them with kas's colon operator, merging left to right:
The distribution fragment omits includes precisely so it can be appended to any machine fragment. With m machines and
n distributions you maintain m + n files instead of m × n.
The generated .gitlab-ci.yml already wires the DAVE Yocto CI component. Before the first pipeline run, add the CI/CD
variables listed in the template's README.md. If your recipes fetch from private repositories, also grant your
project's job token inbound access on each dependency project under Settings → CI/CD → Job token permissions.
Step 10 — Add your machine¶
The layer stack resolves, your distribution derives from tolomeo.inc, your image builds and CI is wired. What remains
describes your hardware, so only you can write it.
Machine configurations go in meta-tolomeo-acme-bsp/conf/machine/, in the same two-level pattern as the distribution: a
shared include/acme-<soc>.inc for the SoC and board, and one thin .conf per flavour. ToloMEO expects four parts:
# meta-tolomeo-acme-bsp/conf/machine/acme-<soc>-prod.conf
# 1. Your SoC vendor's base machine
require conf/machine/<vendor-base-machine>.conf
# 2. A storage profile — eMMC/block or NAND/UBI
require conf/machine/include/tolomeo-mmc-storage.inc
# 3. Secure boot support (inert unless the distro enables secure-boot)
require conf/machine/include/u-boot-secure-boot.inc
# 4. Your board's specifics
UBOOT_CONFIG = "emmc"
OVERLAYFS_ETC_DEVICE = "/dev/mmcblk2p5"
TLM_CERTS_DEVICE = "/dev/disk/by-partlabel/certs"
meta-tolomeo-qemu/conf/machine/tolomeo-qemux86-64.conf — the machine you just built against — implements exactly this
structure. Read it as a worked example: what looks QEMU-specific in it (QB_MEM, QB_OPT_APPEND) fills the same slot
your UBOOT_CONFIG and WIC layout will.
Machine Configuration documents the full contract: both storage profiles, the variables they set, which ones a machine must override, and what the secure-boot include does. Hardware bring-up — kernel, device tree, bootloader port, partition layout — sits outside that contract; for signing and fusing see Enable FIT Image Signing and Enable NXP Authenticated Boot.
Add the vendor BSP layers your SoC needs to repos: in kas/base.yml. For an NXP i.MX part that means
meta-freescale, meta-freescale-distro, meta-imx and meta-arm, plus the meta-tolomeo-nxp/kas/common.yml include
and an ACCEPT_FSL_EULA block.
Then add a target file and CI fragment per flavour:
kas/acme-<soc>-devel_acme-devel_acme-image-devel.yml
kas/acme-<soc>-prod_acme-prod_acme-image-prod.yml
kas-ci/acme-<soc>-devel.yml
kas-ci/acme-<soc>-prod.yml
Keep meta-tolomeo-qemu enabled once your own machines build. A QEMU target is a cheap way to test distribution and
application changes without hardware, and the fastest way to tell a layer-stack regression from a board problem.
To pick up later template improvements, from the repository root:
Copier reapplies only what changed, preserving your customisations. Review the diff before committing.
Next steps¶
- Machine Configuration — the machine contract: storage profiles, secure-boot include, and the variables your board must set
- kas Configuration — the complete file-by-file contract, fragment reference and naming conventions
- Architecture — how the meta-tolomeo layers are structured and why
- Configure Update Modes — enable OTA, local or encrypted updates in your distribution
- Export SBOM — the CycloneDX and VEX output your production distribution produces
- Configure Passwords — replace the development credentials