205б. Open-source licenses and SBOM: what you may put into firmware
Section 205а put together the ESKD documents for the device: the schematic, the parts list, the specification. The parts list contains every resistor. But the firmware of the same device usually contains more third-party code than your own: CMSIS, HAL, FreeRTOS, a file system, a TCP/IP stack, libraries from sections 42, 42а, 106в and 115г. Each piece has its own owner and its own license, and the license says on what terms this code may be passed on — including inside a device. This section covers the licenses of the code already met in the handbook, what they say and how to draw up a list of the software components of the firmware (SBOM, software bill of materials) as strictly as a parts list.
This is not legal advice. The section restates only what is written in the license texts themselves, as they lie on disk, with clause numbers. How to apply them to a specific device and contract is a question for a lawyer.
Sources: the STM32CubeF1 1.8.7 archive (License.md, Package_license.md,
sbom_cdx.json, the components' LICENSE* files, the SLA0044 text in
Projects/…/Applications/LICENSE.txt); modules of the Zephyr tree (littlefs,
nanopb, zcbor, Mbed TLS 3.6) and the Zephyr SPDX generator
(scripts/west_commands/zspdx); the texts of GPL-2.0, LGPL-3.0 and the GCC
runtime library exception — from zephyr-sdk-1.0.1. All the
matching is done by our own generator sbom205.py; the check is
_verify/lic205c_check.py.
Terms used in the documentation
Term Meaning permissive license conditions: keep the notice, include the text copyleft derived work is passed on under the same terms, with sources notice / attribution the copyright notice and the license text object code / binary form compiled code — including code programmed into a device combined work (LGPL) a program linked with the library runtime library exception the GCC exception for its runtime library SBOM the list of software components SPDX license identifier the short license name from the SPDX list: MIT,BSD-3-Clauselicense expression an expression such as Apache-2.0 OR GPL-2.0-or-laterLicenseRef-…your own name for a license that is not in the SPDX list
205б.1 What has already been taken, and under which licenses
The experiment: the device's source tree is assembled from archives on disk — 12
components, 39 files, 2490629 bytes. The generator determined the license of each file
from its header, and the license of a component from its LICENSE file:
| Component | Version | License | Where the version comes from |
|---|---|---|---|
| CMSIS-Core | 5.4.0 | Apache-2.0 | ARM.CMSIS.pdsc |
| CMSIS-Device STM32F1 | 4.3.5 | Apache-2.0 | stm32f1xx.h |
| HAL STM32F1 | 1.1.10 | BSD-3-Clause | stm32f1xx_hal.c |
| FreeRTOS | 10.3.1 | MIT | task.h |
| USB Device Library ST | 2.5.3 | SLA0044 | Release_Notes.html |
| FatFs | R0.11 | its own, one condition (ff.c); ST, five conditions (ff_gen_drv.c) |
ff.h |
| lwIP | 2.0.3 | BSD-3-Clause | init.h |
| littlefs | 2.11 | BSD-3-Clause | lfs.h |
| nanopb | 0.4.9 | Zlib | library.json |
| zcbor | 0.9.1 | Apache-2.0 | VERSION |
| Mbed TLS | 3.6.6 | Apache-2.0 OR GPL-2.0-or-later | build_info.h |
| the device's own code | 1.4.2 | proprietary (LicenseRef-Proprietary) |
— |
HAL and CMSIS-Device have no license in the file header — only a sentence saying that
the terms are in the component's LICENSE file. And that file says: inside the package
the terms of Package_license apply, outside the package BSD-3-Clause
(for HAL) and Apache-2.0 (for CMSIS-Device). The Package_license.md of the
F1 package is the ST agreement SLA0048, and its table lists these components
with BSD-3-Clause and Apache-2.0.
The FatFs component shows that a license must be read file by file, not
by the folder name: ff.c from ChaN carries a license with one condition, while
ff_gen_drv.c from ST lying next to it carries a license with five conditions, and
the fourth one requires the code to run only on ST chips.
205б.2 What the permissive licenses say
The main question for firmware: what does the license require when the code is passed on in binary form — programmed into a device. A summary from the texts on disk:
| License | What it says about binary form | Where in the text |
|---|---|---|
| Apache-2.0 (CMSIS, zcbor, Mbed TLS) | may be distributed "in Source or Object form" on these conditions: the recipient gets a copy of the license, modified files are marked, notices are kept, the NOTICE file, if any, is passed on |
sec. 4 (a)–(d) |
| MIT (FreeRTOS) | the notice and the permission text are included in all copies or substantial portions | the only paragraph of conditions |
| BSD-3-Clause (lwIP, littlefs, HAL outside the package) | in binary form — reproduce the notice, the conditions and the disclaimer in the documentation or other materials | condition 2 |
| Zlib (nanopb) | an acknowledgment in the product documentation is appreciated but not required; mark modified sources | conditions 1 and 2 |
| FatFs R0.11 (ChaN) | one condition — for source code: keep the notice | condition 1 |
There is no NOTICE file in the CMSIS, zcbor or Mbed TLS directories on disk. For
permissive licenses the texts lead to one and the same conclusion: the code may be
passed on inside the device, but the device documentation must contain the
notices and license texts of those components that require them.
The generator collects these texts into one file LICENSES-THIRD-PARTY.txt
(58774 bytes for the nine components that have a LICENSE file) — it
is attached to the manual.
ST licenses. SLA0044 (the text is in the examples' LICENSE.txt, Rev6,
October 2025) allows redistribution in binary form without a notice if
the code is embedded in an ST chip (condition 2), requires the code to run only on
ST chips or together with them (condition 4) and prohibits any
use that would bring the code under open-source
license terms — GPL, EPL, Apache, BSD, MIT are named explicitly (condition 5).
The package's SLA0048 contains conditions 2, 4 and 5 with the same meaning.
205б.3 GPL and LGPL in firmware
Copyleft licenses have a different condition: the sources go together with the binary.
- GPL-2.0, sec. 3 (the
COPYING.GPL2text from picolibc): object code of a program based on GPL code may be distributed if you include the complete machine-readable source code or a written offer to provide it. Firmware into which such code is compiled is exactly that object code. - LGPL-3.0, sec. 4 (
COPYING3.LIBfrom gcc): a combined work may be passed on under your own terms if, among other things, you do one of two things: provide the library sources and the application code in a form that allows relinking it with a modified library — or use a shared library already installed on the user's system. Microcontroller firmware has no shared libraries — so only the first way remains: hand over the application's object files for relinking. - The GCC runtime library exception
(
COPYING.RUNTIME): code generated by the compiler, together with the runtime library files that carry this exception, may be passed on under your own terms, even if this would otherwise violate GPLv3 — provided that all the code was produced by an "Eligible Compilation Process". This part of the texts applies to any firmware that the gcc linker built together with its library. - The recipient's choice. Mbed TLS is licensed under the expression
Apache-2.0 OR GPL-2.0-or-later: theLICENSEfile says explicitly that the user chooses one of the two. In the SBOM this expression is written as is, and in the release documents — which of the two was chosen.
A separate issue is the clash with the ST licenses. Condition 5 of SLA0044 prohibits using ST code in a way that brings it under open-source terms; GPL is named in it explicitly. GPL code and the ST USB library in one firmware are two texts with opposite requirements for one and the same image.
205б.4 SBOM: two formats and two ready examples on disk
An SBOM is a table of a program's components: name, version, supplier, license, checksum. There are two formats, both JSON:
- SPDX 2.3 — a description of packages and of every file with
SHA-1 and SHA-256 checksums, the file's license (
licenseInfoInFiles, what is written in the file itself) kept separate from the conclusion (licenseConcluded), and the package verification code — SHA-1 over the sorted SHA-1s of its files; - CycloneDX 1.5 — components with versions and licenses, nested file components with SHA-256, dependencies.
Ready examples are already on disk. ST puts a file
sbom_cdx.json into the STM32CubeF1 archive — CycloneDX 1.5, 46 components, made by a third-party
analysis tool. Zephyr ships its own SPDX generator
(west spdx, the zspdx directory): it writes SPDX 2.3 in tag-value form, uses the SPDX
license list version 3.12 (499 identifiers), and looks for a file's license
only by the SPDX-License-Identifier: tag in the first 20 lines.
That is not enough for ST code and old libraries: of the 39 files in the experiment, only 13 have an SPDX tag. The rest need their header text parsed.
205б.5 Our own generator: over the source tree on a PC
sbom205.py assembles the device tree from the archives on disk and describes it.
It looks for a file's license in the first 80 lines — first the SPDX tag, then
known texts:
def detect(text):
"""Лицензия по тексту заголовка или файла LICENSE. None — ничего не сказано; 'SEE-LICENSE' — отсылка к файлу."""
m = re.search(r"SPDX-License-Identifier:\s*(.+)", text)
if m:
return m.group(1).strip().rstrip("*/").strip()
t = re.sub(r"\s+", " ", re.sub(r"^[ \t]*[*/#;]+ ?", "", text, flags=re.M))
if "Ultimate Liberty license SLA0044" in t or "terms of the SLA0044 license shall apply" in t:
return "LicenseRef-ST-SLA0044"
The order matters. The ST five-clause license starts with the same words as BSD — "Redistribution and use in source and binary forms" — so the "ST chips only" marker is searched for first, and only then are the three BSD conditions counted:
if "must execute solely and exclusively on microcontroller" in t:
return "LicenseRef-ST-5-Clause" # похоже на BSD, но условий пять, и четвёртое — только кристаллы ST
Result for the 39 files: SPDX tag — 13, recognized text — 13, reference to the component's
LICENSE — 9, nothing in the header — 4 (three nanopb files and
pbuf.c from lwIP, whose license text starts at line 81).
If a file is silent, the component's license from its LICENSE is used; if there is none
either, the file gets NOASSERTION and the release stops.
That is exactly what happened on the first run: your own code contained app/ringbuf.c,
taken "from somewhere", without a license line. The generator replied
нет лицензии: ./app/ringbuf.c ("no license"), and the component's result was NOASSERTION.
After the SPDX-License-Identifier: line was added, there were 0 errors.
The output is three files: sbom.spdx.json, sbom.cdx.json and
LICENSES-THIRD-PARTY.txt. The date inside is fixed, the SPDX namespace
and the CycloneDX serial number are derived from the file checksums —
two runs gave reports that matched byte for byte. The SBOM is reproducible just
like the firmware in section 201а, and it can be stored next to
merged.hex in the release log.
205б.6 Cross-check: all files found, licenses match
The section's check compares the report with the tree and with two independent implementations:
| What was compared | Result |
|---|---|
| files in the tree / in SPDX | 39 / 39, SHA-1 and SHA-256 recomputed for each |
| verification code of 12 packages | matches the calculateVerificationCode function from zspdx |
| license identifiers | 5 — in the SPDX 3.12 list from Zephyr; 4 — LicenseRef-… with an explanation in hasExtractedLicensingInfos |
| SPDX tag in the first 20 lines (Zephyr scanner) | 13 files, all with the same answer as our own generator |
| license in the header ↔ component license | 0 mismatches |
SBOM from ST (sbom_cdx.json) |
7 common components: versions match for all, licenses for 6 |
There is one mismatch — FatFs: ST's SBOM says BSD-3-Clause, while the header of
ff.c of the same version R0.11 has one condition, and ff_gen_drv.c has five
ST conditions. Whose SBOM is "right" is not a question for the generator; the generator showed
what is written in the files and pointed to where they disagree.
Under Windows the Zephyr scanner first found 11 files, not 13: it
opens files in the system encoding (here cp1251), and two files of our own
code with Russian comments in UTF-8 could not be read. With PYTHONUTF8=1
it found 13.
205б.7 Pitfalls
"Same license as the folder". One FatFs folder holds a license with one
condition and an ST license with five. The SBOM is built per file, and the
component's result is the expression LicenseRef-FatFs-ChaN AND LicenseRef-ST-5-Clause.
"Looks like BSD". The ST five-clause license starts with the words of BSD.
A search by the first sentence will record it as BSD-3-Clause and lose the
"ST chips only" condition. Markers of restrictions are checked before general ones.
A license below line twenty. In pbuf.c the text starts at line 81,
after the module description. A scanner of the first lines does not see it;
the component's LICENSE saves you — if there is one.
A file "from somewhere". A piece of code without a license line is the most common
hole. The generator must stop the release on NOASSERTION rather than
silently assign the project's license.
Two ST agreement numbers in one package. License.md names
SLA0044 for the USB library, Package_license.md of the same package names
SLA0048, the library files name SLA0044. The SBOM records what is in the file, and
the mismatch is noted.
A version "from the header". nanopb's pb.h says
nanopb-1.0.0-dev, while library.json says 0.4.9. For CMSIS,
cmsis_version.h gives 5.1 (the version of the CMSIS-Core core), while the package description says
5.4.0. The SBOM states where the version was taken from, and it is taken from the same
place for every release.
Scanner encoding. A third-party scanner that reads files in the system encoding skips files with Russian comments in UTF-8 — without an error, simply "license not found".
GPL "through a dependency". Mbed TLS lets you choose GPL-2.0, but
the choice is made by the recipient of the code. If the choice is not recorded in the release documents,
an SBOM with an OR expression remains a question, not an answer.
205б.8 Summary
- The device tree is 12 components, 39 files: licenses Apache-2.0, MIT,
BSD-3-Clause, Zlib, the one-condition FatFs, SLA0044, the ST five-clause license and
Apache-2.0 OR GPL-2.0-or-laterfor Mbed TLS. - Permissive licenses do not forbid programming the code into a device; BSD-3, MIT and
Apache-2.0 require notices and the license text in the documentation —
LICENSES-THIRD-PARTY.txtof 58774 bytes. - The ST licenses allow redistribution in binary form inside an ST chip, but require the code to run only on ST chips and prohibit mixing with open-source licenses.
- GPL-2.0 requires sources when object code is passed on; LGPL-3.0 without shared libraries requires the ability to relink the application; the GCC exception removes the GPLv3 conditions from the compiler's runtime library under an "Eligible Compilation Process".
- Our own generator: SPDX tag in 13 files out of 39, recognized text in 13,
reference to
LICENSEin 9, nothing in 4; a file without a license stops the release. - Cross-check: 39 of 39 files, verification codes of 12 packages = the Zephyr function, versions of 7 common components = the ST SBOM, licenses match for 6 of 7 (all but FatFs).
Next: section 206 "Certification: conformity assessment and radio modules".