From: Clayton Craft Date: Tue, 26 Aug 2025 18:21:50 +0000 (-0700) Subject: Add devicetree-auto support for UKI X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7edca634783b591bf1ee8287ffbb80591a6a87c2;p=thirdparty%2Fmkosi.git Add devicetree-auto support for UKI This renames Devicetree --> Devicetrees, and adds support for listing globs and 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 --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index df81cd4e6..30f8854b3 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -90,7 +90,13 @@ from mkosi.documentation import show_docs from mkosi.installer import clean_package_manager_metadata from mkosi.installer.pacman import Pacman from mkosi.installer.zypper import Zypper -from mkosi.kmod import gen_required_kernel_modules, is_valid_kdir, loaded_modules, process_kernel_modules +from mkosi.kmod import ( + filter_devicetrees, + gen_required_kernel_modules, + is_valid_kdir, + loaded_modules, + process_kernel_modules, +) from mkosi.log import ARG_DEBUG, complete_step, die, log_notice, log_step from mkosi.manifest import Manifest from mkosi.mounts import ( @@ -1597,21 +1603,6 @@ def build_kernel_modules_initrd(context: Context, kver: str) -> Path: return kmods -def find_devicetree(context: Context, kver: str) -> Path: - assert context.config.devicetree - - for d in ( - context.root / f"usr/lib/firmware/{kver}/device-tree", - context.root / f"usr/lib/linux-image-{kver}", - context.root / f"usr/lib/modules/{kver}/dtb", - ): - dtb = d / context.config.devicetree - if dtb.exists(): - return dtb - - die(f"Requested devicetree {context.config.devicetree} not found") - - def want_signed_pcrs(config: Config) -> bool: return config.sign_expected_pcr == ConfigFeature.enabled or ( config.sign_expected_pcr == ConfigFeature.auto @@ -1769,10 +1760,13 @@ def build_uki( *flatten(["--ro-bind", os.fspath(profile), os.fspath(workdir(profile))] for profile in profiles), ] # fmt: skip - if context.config.devicetree: - dtb = find_devicetree(context, kver) - arguments += ["--devicetree", workdir(dtb)] - options += ["--ro-bind", dtb, workdir(dtb)] + if context.config.devicetrees: + dtbs = filter_devicetrees(context.root, kver, include=context.config.devicetrees) + switch = "--devicetree" if len(dtbs) == 1 else "--devicetree-auto" + for dtb_rel in dtbs: + dtb = context.root / dtb_rel + arguments += [switch, workdir(dtb)] + options += ["--ro-bind", dtb, workdir(dtb)] if context.config.splash: splash = context.root / os.fspath(context.config.splash).lstrip("/") @@ -2016,8 +2010,16 @@ def install_type1( entry.parent.mkdir(parents=True, exist_ok=True) dtb = None - if context.config.devicetree: - dtb = dst / context.config.devicetree + source_dtb = None + if context.config.devicetrees: + dtbs = filter_devicetrees(context.root, kver, include=context.config.devicetrees) + if len(dtbs) != 1: + die( + "Type 1 boot entries support only single devicetree, use UKI builds for multiple devicetrees" + ) + + source_dtb = context.root / dtbs[0] + dtb = dst / dtbs[0].relative_to(f"usr/lib/modules/{kver}/dtb") with umask(~0o700): dtb.parent.mkdir(parents=True, exist_ok=True) @@ -2040,8 +2042,8 @@ def install_type1( Path(shutil.copy2(initrd, dst.parent / initrd.name)) for initrd in microcode + initrds ] + [Path(shutil.copy2(kmods, dst / "kernel-modules.initrd"))] - if dtb: - shutil.copy2(find_devicetree(context, kver), dtb) + if dtb and source_dtb: + shutil.copy2(source_dtb, dtb) with entry.open("w") as f: f.write( diff --git a/mkosi/config.py b/mkosi/config.py index c006fd81c..d61cc6f51 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -2023,7 +2023,7 @@ class Config: initrd_packages: list[str] initrd_volatile_packages: list[str] microcode_host: bool - devicetree: Optional[str] + devicetrees: list[str] splash: Optional[Path] kernel_command_line: list[str] kernel_modules_include: list[str] @@ -3184,10 +3184,11 @@ SETTINGS: list[ConfigSetting[Any]] = [ help="Packages to install in the initrd that are not cached", ), ConfigSetting( - dest="devicetree", + dest="devicetrees", section="Content", - parse=config_parse_string, - help="Devicetree to be used by the booting kernel", + parse=config_make_list_parser(delimiter=","), + help="Devicetree(s) to be used by the booting kernel", + compat_names=("Devicetree",), ), ConfigSetting( dest="splash", @@ -5493,7 +5494,7 @@ def summary(config: Config) -> str: Initrd Profiles: {line_join_list(config.initrd_profiles)} Initrd Packages: {line_join_list(config.initrd_packages)} Initrd Volatile Packages: {line_join_list(config.initrd_volatile_packages)} - Devicetree: {none_to_none(config.devicetree)} + Devicetrees: {line_join_list(config.devicetrees)} Splash: {none_to_none(config.splash)} Kernel Command Line: {line_join_list(config.kernel_command_line)} Kernel Modules: {line_join_list(config.kernel_modules_include)} diff --git a/mkosi/kmod.py b/mkosi/kmod.py index 8f1106bb1..2937666ba 100644 --- a/mkosi/kmod.py +++ b/mkosi/kmod.py @@ -510,3 +510,43 @@ def is_valid_kdir(kdir: Path) -> bool: # check that kdir contains more than just updates return dircontent != [kdir / "updates"] + + +def filter_devicetrees( + root: Path, + kver: str, + *, + include: Iterable[str], +) -> list[Path]: + if not include: + return [] + + logging.debug(f"Devicetrees include: {' '.join(include)}") + + # Search standard DTB locations + dtb_dirs = [ + Path("usr/lib/firmware") / kver / "device-tree", + Path(f"usr/lib/linux-image-{kver}"), + Path("usr/lib/modules") / kver / "dtb", + ] + + matched_dtbs = [] + globs = list(include) + + with chdir(root): + for dtb_dir in dtb_dirs: + all_dtbs = [p for p in dtb_dir.rglob("*.dtb") if p.is_file() or p.is_symlink()] + logging.debug(f"Found {len(all_dtbs)} DTB files in {dtb_dir}") + + for dtb in all_dtbs: + rel_path = os.fspath(dtb.relative_to(dtb_dir)) + if globs_match_filename(rel_path, globs): + logging.debug(f"Matched DTB: {rel_path} in {dtb_dir}") + matched_dtbs.append(dtb) + + if not matched_dtbs: + logging.warning(f"Devicetrees patterns '{globs}' matched 0 files") + else: + logging.debug(f"Including {len(matched_dtbs)} devicetree files") + + return sorted(matched_dtbs) diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index e41663852..84cfae16d 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -1110,11 +1110,17 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, : Similar to `VolatilePackages=`, except it applies to the default initrd. -`Devicetree=`, `--devicetree=` -: When set, specifies a Devicetree blob to be used by the booting system, - instead of the one provided by firmware. **mkosi** will search for the - specified file relative to common paths where Linux distributions install - Devicetree files. It should typically have the format `/.dtb`. +`Devicetrees=`, `--devicetrees=` +: Comma-separated list of devicetree patterns for automatic hardware-based selection. + Patterns are glob expressions. **mkosi** searches for devicetree files in standard + locations relative to `/usr/lib/modules//dtb/`, `/usr/lib/firmware//device-tree/`, + and `/usr/lib/linux-image-/`. + + For UKI builds, multiple matches enable automatic hardware-based selection using + the `.dtbauto` sections. Type 1 boot entries require exactly one match. + + Example: `Devicetrees=rockchip/*,imx.*` would include all Rockchip devicetrees + and any IMX devicetrees. `Splash=`, `--splash=` : When set, the boot splash for any unified kernel image built by **mkosi** will diff --git a/tests/test_json.py b/tests/test_json.py index 929ec2aeb..36c5e46ef 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -150,7 +150,9 @@ def test_config() -> None: "Dependencies": [ "dep1" ], - "Devicetree": "freescale/imx8mm-verdin-nonwifi-dev.dtb", + "Devicetrees": [ + "freescale/imx8mm-verdin-nonwifi-dev.dtb" + ], "Distribution": "fedora", "Drives": [ { @@ -511,7 +513,7 @@ def test_config() -> None: make_initrd=False, manifest_format=[ManifestFormat.json, ManifestFormat.changelog], microcode_host=True, - devicetree="freescale/imx8mm-verdin-nonwifi-dev.dtb", + devicetrees=["freescale/imx8mm-verdin-nonwifi-dev.dtb"], minimum_version="123", mirror=None, nspawn_settings=None,