Skip to content

Commit 156eba9

Browse files
committed
Add devicetree-auto support for UKI
This renames Devicetree --> Devicetrees, and adds support for listing regex, globs, files as currently supported for KernelModule* options. When multiple devicetrees are found, they are added to dtbauto sections when building a UKI. Multiple dtbs are not supported for type 1 booting. Fixes #3827
1 parent 786b8fe commit 156eba9

File tree

5 files changed

+92
-36
lines changed

5 files changed

+92
-36
lines changed

mkosi/__init__.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import hashlib
88
import itertools
99
import json
10+
import glob
1011
import logging
1112
import os
1213
import re
@@ -90,7 +91,13 @@
9091
from mkosi.installer import clean_package_manager_metadata
9192
from mkosi.installer.pacman import Pacman
9293
from mkosi.installer.zypper import Zypper
93-
from mkosi.kmod import gen_required_kernel_modules, is_valid_kdir, loaded_modules, process_kernel_modules
94+
from mkosi.kmod import (
95+
filter_devicetrees,
96+
gen_required_kernel_modules,
97+
is_valid_kdir,
98+
loaded_modules,
99+
process_kernel_modules,
100+
)
94101
from mkosi.log import ARG_DEBUG, complete_step, die, log_notice, log_step
95102
from mkosi.manifest import Manifest
96103
from mkosi.mounts import (
@@ -1596,21 +1603,6 @@ def build_kernel_modules_initrd(context: Context, kver: str) -> Path:
15961603
return kmods
15971604

15981605

1599-
def find_devicetree(context: Context, kver: str) -> Path:
1600-
assert context.config.devicetree
1601-
1602-
for d in (
1603-
context.root / f"usr/lib/firmware/{kver}/device-tree",
1604-
context.root / f"usr/lib/linux-image-{kver}",
1605-
context.root / f"usr/lib/modules/{kver}/dtb",
1606-
):
1607-
dtb = d / context.config.devicetree
1608-
if dtb.exists():
1609-
return dtb
1610-
1611-
die(f"Requested devicetree {context.config.devicetree} not found")
1612-
1613-
16141606
def want_signed_pcrs(config: Config) -> bool:
16151607
return config.sign_expected_pcr == ConfigFeature.enabled or (
16161608
config.sign_expected_pcr == ConfigFeature.auto
@@ -1768,10 +1760,17 @@ def build_uki(
17681760
*flatten(["--ro-bind", os.fspath(profile), os.fspath(workdir(profile))] for profile in profiles),
17691761
] # fmt: skip
17701762

1771-
if context.config.devicetree:
1772-
dtb = find_devicetree(context, kver)
1773-
arguments += ["--devicetree", workdir(dtb)]
1774-
options += ["--ro-bind", dtb, workdir(dtb)]
1763+
if context.config.devicetrees:
1764+
dtbs = filter_devicetrees(context.root, kver, include=context.config.devicetrees)
1765+
if len(dtbs) == 1:
1766+
dtb = context.root / dtbs[0]
1767+
arguments += ["--devicetree", workdir(dtb)]
1768+
options += ["--ro-bind", dtb, workdir(dtb)]
1769+
else:
1770+
for dtb_rel in dtbs:
1771+
dtb = context.root / dtb_rel
1772+
arguments += ["--devicetree-auto", workdir(dtb)]
1773+
options += ["--ro-bind", dtb, workdir(dtb)]
17751774

17761775
if context.config.splash:
17771776
splash = context.root / os.fspath(context.config.splash).lstrip("/")
@@ -2015,8 +2014,16 @@ def install_type1(
20152014
entry.parent.mkdir(parents=True, exist_ok=True)
20162015

20172016
dtb = None
2018-
if context.config.devicetree:
2019-
dtb = dst / context.config.devicetree
2017+
source_dtb = None
2018+
if context.config.devicetrees:
2019+
dtbs = filter_devicetrees(context.root, kver, include=context.config.devicetrees)
2020+
if len(dtbs) != 1:
2021+
die(
2022+
"Type 1 boot entries support only single devicetree, use UKI builds for multiple devicetrees"
2023+
)
2024+
2025+
source_dtb = context.root / dtbs[0]
2026+
dtb = dst / dtbs[0].relative_to(f"usr/lib/modules/{kver}/dtb")
20202027
with umask(~0o700):
20212028
dtb.parent.mkdir(parents=True, exist_ok=True)
20222029

@@ -2039,8 +2046,8 @@ def install_type1(
20392046
Path(shutil.copy2(initrd, dst.parent / initrd.name)) for initrd in microcode + initrds
20402047
] + [Path(shutil.copy2(kmods, dst / "kernel-modules.initrd"))]
20412048

2042-
if dtb:
2043-
shutil.copy2(find_devicetree(context, kver), dtb)
2049+
if dtb and source_dtb:
2050+
shutil.copy2(source_dtb, dtb)
20442051

20452052
with entry.open("w") as f:
20462053
f.write(

mkosi/config.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,7 +2014,7 @@ class Config:
20142014
initrd_packages: list[str]
20152015
initrd_volatile_packages: list[str]
20162016
microcode_host: bool
2017-
devicetree: Optional[str]
2017+
devicetrees: list[str]
20182018
splash: Optional[Path]
20192019
kernel_command_line: list[str]
20202020
kernel_modules_include: list[str]
@@ -3165,10 +3165,11 @@ def parse_kernel_module_filter_regexp(p: str) -> str:
31653165
help="Packages to install in the initrd that are not cached",
31663166
),
31673167
ConfigSetting(
3168-
dest="devicetree",
3168+
dest="devicetrees",
31693169
section="Content",
3170-
parse=config_parse_string,
3171-
help="Devicetree to be used by the booting kernel",
3170+
parse=config_make_list_parser(delimiter=","),
3171+
help="Devicetree(s) to be used by the booting kernel",
3172+
compat_names=("Devicetree",),
31723173
),
31733174
ConfigSetting(
31743175
dest="splash",
@@ -5473,7 +5474,7 @@ def summary(config: Config) -> str:
54735474
Initrd Profiles: {line_join_list(config.initrd_profiles)}
54745475
Initrd Packages: {line_join_list(config.initrd_packages)}
54755476
Initrd Volatile Packages: {line_join_list(config.initrd_volatile_packages)}
5476-
Devicetree: {none_to_none(config.devicetree)}
5477+
Devicetrees: {line_join_list(config.devicetrees)}
54775478
Splash: {none_to_none(config.splash)}
54785479
Kernel Command Line: {line_join_list(config.kernel_command_line)}
54795480
Kernel Modules: {line_join_list(config.kernel_modules_include)}

mkosi/kmod.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,45 @@ def is_valid_kdir(kdir: Path) -> bool:
510510

511511
# check that kdir contains more than just updates
512512
return dircontent != [kdir / "updates"]
513+
514+
515+
def filter_devicetrees(
516+
root: Path,
517+
kver: str,
518+
*,
519+
include: Iterable[str],
520+
) -> list[Path]:
521+
if not include:
522+
return []
523+
524+
logging.debug(f"Devicetrees include: {' '.join(include)}")
525+
526+
# Search standard DTB locations
527+
dtb_dirs = [
528+
Path("usr/lib/firmware") / kver / "device-tree",
529+
Path(f"usr/lib/linux-image-{kver}"),
530+
Path("usr/lib/modules") / kver / "dtb",
531+
]
532+
533+
matched_dtbs = []
534+
patterns = [p[3:] for p in include if p.startswith("re:")]
535+
regex = re.compile("|".join(patterns)) if patterns else None
536+
globs = [p for p in include if not p.startswith("re:")]
537+
538+
with chdir(root):
539+
for dtb_dir in dtb_dirs:
540+
all_dtbs = [p for p in dtb_dir.rglob("*.dtb") if p.is_file() or p.is_symlink()]
541+
logging.debug(f"Found {len(all_dtbs)} DTB files in {dtb_dir}")
542+
543+
for dtb in all_dtbs:
544+
rel_path = os.fspath(dtb.relative_to(dtb_dir))
545+
if (regex and regex.search(rel_path)) or globs_match_filename(rel_path, globs):
546+
logging.debug(f"Matched DTB: {rel_path} in {dtb_dir}")
547+
matched_dtbs.append(dtb)
548+
549+
if not matched_dtbs:
550+
logging.warning(f"Devicetrees patterns {list(include)} matched 0 files")
551+
else:
552+
logging.debug(f"Including {len(matched_dtbs)} devicetree files")
553+
554+
return sorted(matched_dtbs)

mkosi/resources/man/mkosi.1.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,11 +1080,17 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
10801080
: Similar to `VolatilePackages=`, except it applies to the default
10811081
initrd.
10821082

1083-
`Devicetree=`, `--devicetree=`
1084-
: When set, specifies a Devicetree blob to be used by the booting system,
1085-
instead of the one provided by firmware. **mkosi** will search for the
1086-
specified file relative to common paths where Linux distributions install
1087-
Devicetree files. It should typically have the format `<vendor>/<board>.dtb`.
1083+
`Devicetrees=`, `--devicetrees=`
1084+
: Comma-separated list of devicetree patterns for automatic hardware-based selection.
1085+
Patterns can be glob expressions or regex prefixed with `re:`. **mkosi** searches
1086+
for devicetree files in standard locations relative to `/usr/lib/modules/<kver>/dtb/`,
1087+
`/usr/lib/firmware/<kver>/device-tree/`, and `/usr/lib/linux-image-<kver>/`.
1088+
1089+
For UKI builds, multiple matches enable automatic hardware-based selection using
1090+
the `.dtbauto` sections. Type 1 boot entries require exactly one match.
1091+
1092+
Examples: `Devicetrees=rockchip/*,re:imx.*` would include all Rockchip devicetrees
1093+
and any devicetrees matching the regex pattern `imx.*`.
10881094

10891095
`Splash=`, `--splash=`
10901096
: When set, the boot splash for any unified kernel image built by **mkosi** will

tests/test_json.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def test_config() -> None:
150150
"Dependencies": [
151151
"dep1"
152152
],
153-
"Devicetree": "freescale/imx8mm-verdin-nonwifi-dev.dtb",
153+
"Devicetrees": ["freescale/imx8mm-verdin-nonwifi-dev.dtb"],
154154
"Distribution": "fedora",
155155
"Drives": [
156156
{
@@ -510,7 +510,7 @@ def test_config() -> None:
510510
make_initrd=False,
511511
manifest_format=[ManifestFormat.json, ManifestFormat.changelog],
512512
microcode_host=True,
513-
devicetree="freescale/imx8mm-verdin-nonwifi-dev.dtb",
513+
devicetrees=["freescale/imx8mm-verdin-nonwifi-dev.dtb"],
514514
minimum_version="123",
515515
mirror=None,
516516
nspawn_settings=None,

0 commit comments

Comments
 (0)