Linux
Kernel Source, Versions & Patching
"Just update the kernel" hides a surprising amount of real work in embedded Linux — which source tree, which version scheme, and how a fix that landed in mainline actually reaches a product's kernel that's years and hundreds of vendor patches away from it.
- What it is
- Where kernel source comes from, how its version numbers and maintenance branches work, and how fixes move between them via patches.
- Mental model
- Mainline is the trunk; stable and longterm (LTS) are bugfix-only branches off specific releases; a vendor BSP is a long-running fork that occasionally sends changes back upstream.
- Key numbers
- Mainline releases roughly every 9–10 weeks; a longterm (LTS) branch is maintained for 2–6 years of security/bugfix backports.
- Reach for it when
- Backporting a security fix onto a product kernel, evaluating a vendor BSP, or figuring out whether a bug is already fixed upstream.
Where kernel source actually comes from
Mainline is Linus Torvalds' own tree at
git.kernel.org — the trunk everything else eventually
traces back to. Almost no embedded product ships mainline directly,
though: silicon vendors (Broadcom, NXP, TI, Xilinx, and others)
maintain their own long-running forks — vendor BSPs
(board support packages) — carrying the driver and platform code
their chips need, much of which hasn't been merged into mainline
(and in some cases never will be). A real embedded project is almost
always working from one of these vendor forks, not from
torvalds/linux directly.
Versioning: mainline, stable, and longterm
A kernel version like 6.6.30 breaks down as
major.minor.patch: 6.6 is a mainline
release (major/minor numbers today are largely just sequential, not
meaningful on their own), and .30 is the stable patch
level — the 30th round of bugfixes backported onto 6.6
since its release.
- Mainline
- Linus's tree. New features land here first, in a ~9–10 week cycle: a 2-week merge window, then 6–8 weekly release candidates (rc1…rcN) stabilizing before the final release.
- Stable
- Bugfix-only backports onto the most recent mainline release, maintained only until the next mainline release supersedes it.
- Longterm (LTS)
- Select releases (a few per year) chosen for extended maintenance — typically 2 years, sometimes up to 6 — receiving security and critical bugfixes long after their mainline peers have gone unmaintained. This is what almost every real embedded product is actually built on.
- linux-next
- An integration tree combining what's queued for the next merge window — used by kernel developers to catch conflicts early, not something a product ships.
Getting the source
# mainline, directly from kernel.org
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
git checkout v6.6
# a vendor BSP fork instead — e.g. Raspberry Pi's kernel, carrying
# hundreds of out-of-tree patches for the Broadcom SoC it targets
git clone https://github.com/raspberrypi/linux.git
Which one is right depends entirely on the board: mainline only supports hardware that's actually been upstreamed, so a board whose vendor hasn't finished that work simply won't boot on a plain mainline checkout — the vendor fork exists precisely to fill that gap.
How patches are actually applied
A patch is a text diff describing exactly which
lines changed in which files — the standard way a fix moves from one
tree to another without needing a full merge. Two common forms show
up constantly in embedded work: a plain diff (applied with
patch) and a git-formatted patch that preserves the
original commit's author and message (applied with
git am).
# plain diff, old-school
patch -p1 < fix-i2c-timeout.patch
# a git-formatted patch — keeps the original commit's author/message
git am 0001-i2c-fix-timeout-on-slow-bus.patch
# backporting one specific upstream fix by commit hash, directly
git cherry-pick a1b2c3d4e5f6
Configuration and cross-compiling
A kernel build is driven by a .config
file selecting which subsystems, drivers, and features to compile —
generated from a board-specific baseline and then adjusted.
# cross-compiling for ARM64 from an x86_64 build host
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make defconfig # start from a board's known-good baseline .config
make menuconfig # interactively enable/disable subsystems
make -j$(nproc) Image modules dtbs
ARCH and CROSS_COMPILE are how the same
kernel source tree builds for a completely different target
architecture than the machine doing the compiling — the toolchain
prefix in CROSS_COMPILE selects the actual
cross-compiler binaries (aarch64-linux-gnu-gcc, etc.)
used for every compilation step.
Real-world applications
- Security patch backporting — a CVE surfaced by dependency analysis against a component the kernel bundles often means manually locating and cherry-picking the fix onto a product's specific LTS-based tree.
- Vendor BSP evaluation — choosing a board partly means choosing how far behind mainline its BSP is, and how actively the vendor maintains it — a BSP frozen on an end-of-life LTS is a long-term liability.
- Bug triage — before writing a custom fix, checking whether a bug is already fixed in a newer mainline/stable release (and just needs backporting) saves reinventing a fix that already exists.
- Yocto kernel recipes — a Yocto
linux-yocto(or vendor) recipe'sSRC_URIlists a base kernel source plus an ordered list of.patchfiles applied automatically during the build — the samegit ammechanics, just declared in a recipe instead of run by hand.
Gotchas
- A vendor kernel can be years behind mainline with hundreds of out-of-tree patches — an upstream security fix doesn't "just arrive" on a product; someone has to find it and backport it manually.
- Longterm (LTS) is a promise about backported bugfixes and security patches only, not new features — jumping between LTS versions is still a real migration, not a routine update.
- A patch generated against one tree state can fail to apply cleanly (or worse, apply with silent offset) against a tree that has diverged even slightly — always check what base commit/tag a patch was generated against.
- Editing a
.configby hand and forgetting to actually rerun the build for the affected objects is a classic "I changed it but nothing happened" — the same failure mode as forgetting to rebuild a stale device tree blob.