From: Antonio Alvarez Feijoo Date: Fri, 7 Feb 2025 10:55:44 +0000 (+0100) Subject: Add FirmwareInclude= and FirmwareExclude= options X-Git-Tag: v26~406 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b264e833ba0e8819c3d38bf69bf790f0a449fe4f;p=thirdparty%2Fmkosi.git Add FirmwareInclude= and FirmwareExclude= options Rigth now, firmware added to the image is determined as a dependency of any added kernel modules. However, a common use case is that a user may need to force certain firmware to be included or excluded regardless of that dependency. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 997a4fa96..d0d77b88c 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -1487,12 +1487,14 @@ def build_kernel_modules_initrd(context: Context, kver: str) -> Path: files=gen_required_kernel_modules( context, kver, - include=finalize_kernel_modules_include( + modules_include=finalize_kernel_modules_include( context, include=context.config.kernel_modules_initrd_include, host=context.config.kernel_modules_initrd_include_host, ), - exclude=context.config.kernel_modules_initrd_exclude, + modules_exclude=context.config.kernel_modules_initrd_exclude, + firmware_include=context.config.firmware_include, + firmware_exclude=context.config.firmware_exclude, ), sandbox=context.sandbox, ) @@ -2858,12 +2860,14 @@ def run_depmod(context: Context, *, cache: bool = False) -> None: process_kernel_modules( context, kver, - include=finalize_kernel_modules_include( + modules_include=finalize_kernel_modules_include( context, include=context.config.kernel_modules_include, host=context.config.kernel_modules_include_host, ), - exclude=context.config.kernel_modules_exclude, + modules_exclude=context.config.kernel_modules_exclude, + firmware_include=context.config.firmware_include, + firmware_exclude=context.config.firmware_exclude, ) if context.config.output_format.is_extension_or_portable_image(): diff --git a/mkosi/config.py b/mkosi/config.py index 8f6750fd2..7d46d0339 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -1858,6 +1858,8 @@ class Config: kernel_modules_include: list[str] kernel_modules_exclude: list[str] kernel_modules_include_host: bool + firmware_include: list[str] + firmware_exclude: list[str] kernel_modules_initrd: bool kernel_modules_initrd_include: list[str] @@ -2895,6 +2897,20 @@ SETTINGS: list[ConfigSetting[Any]] = [ parse=config_make_list_parser(delimiter=","), help="When building a kernel modules initrd, exclude the specified kernel modules", ), + ConfigSetting( + dest="firmware_include", + metavar="REGEX", + section="Content", + parse=config_make_list_parser(delimiter=","), + help="Include the specified firmware in the image", + ), + ConfigSetting( + dest="firmware_exclude", + metavar="REGEX", + section="Content", + parse=config_make_list_parser(delimiter=","), + help="Exclude the specified firmware from the image", + ), ConfigSetting( dest="locale", section="Content", @@ -4907,6 +4923,8 @@ def summary(config: Config) -> str: Kernel Modules Include: {line_join_list(config.kernel_modules_include)} Kernel Modules Exclude: {line_join_list(config.kernel_modules_exclude)} Kernel Modules Include Host: {yes_no(config.kernel_modules_include_host)} + Firmware Include: {line_join_list(config.firmware_include)} + Firmware Exclude: {line_join_list(config.firmware_exclude)} Kernel Modules Initrd: {yes_no(config.kernel_modules_initrd)} Kernel Modules Initrd Include: {line_join_list(config.kernel_modules_initrd_include)} diff --git a/mkosi/kmod.py b/mkosi/kmod.py index a25bb5267..e591b953c 100644 --- a/mkosi/kmod.py +++ b/mkosi/kmod.py @@ -55,6 +55,39 @@ def filter_kernel_modules( return sorted(modules) +def filter_firmware( + root: Path, + firmware: set[Path], + *, + include: Iterable[str], + exclude: Iterable[str], +) -> set[Path]: + if not include and not exclude: + return firmware + + if exclude: + remove = set() + regex = re.compile("|".join(exclude)) + for f in firmware: + rel = os.fspath(Path(*f.parts[3:])) + if regex.search(rel): + remove.add(f) + + firmware -= remove + + if include: + firmwared = Path("usr/lib/firmware") + with chdir(root): + all_firmware = set(firmwared.rglob("*")) + regex = re.compile("|".join(include)) + for f in all_firmware: + rel = os.fspath(Path(*f.parts[3:])) + if regex.search(rel): + firmware.add(f) + + return firmware + + def normalize_module_name(name: str) -> str: return name.replace("_", "-") @@ -175,16 +208,19 @@ def gen_required_kernel_modules( context: Context, kver: str, *, - include: Iterable[str], - exclude: Iterable[str], + modules_include: Iterable[str], + modules_exclude: Iterable[str], + firmware_include: Iterable[str], + firmware_exclude: Iterable[str], ) -> Iterator[Path]: modulesd = Path("usr/lib/modules") / kver + firmwared = Path("usr/lib/firmware") # There is firmware in /usr/lib/firmware that is not depended on by any modules so if any firmware was # installed we have to take the slow path to make sure we don't copy firmware into the initrd that is not # depended on by any kernel modules. - if exclude or (context.root / "usr/lib/firmware").glob("*"): - modules = filter_kernel_modules(context.root, kver, include=include, exclude=exclude) + if modules_exclude or (context.root / firmwared).glob("*"): + modules = filter_kernel_modules(context.root, kver, include=modules_include, exclude=modules_exclude) names = [module_path_to_name(m) for m in modules] mods, firmware = resolve_module_dependencies(context, kver, names) else: @@ -195,6 +231,9 @@ def gen_required_kernel_modules( mods = set(modulesd.rglob("*.ko*")) firmware = set() + # Include or exclude firmware explicitly configured + firmware = filter_firmware(context.root, firmware, include=firmware_include, exclude=firmware_exclude) + # Some firmware dependencies are symbolic links, so the targets for those must be included in the list # of required firmware files too. Intermediate symlinks are not included, and so links pointing to links # results in dangling symlinks in the final image. @@ -232,17 +271,28 @@ def process_kernel_modules( context: Context, kver: str, *, - include: Iterable[str], - exclude: Iterable[str], + modules_include: Iterable[str], + modules_exclude: Iterable[str], + firmware_include: Iterable[str], + firmware_exclude: Iterable[str], ) -> None: - if not exclude: + if not modules_exclude and not firmware_exclude: return modulesd = Path("usr/lib/modules") / kver firmwared = Path("usr/lib/firmware") with complete_step("Applying kernel module filters"): - required = set(gen_required_kernel_modules(context, kver, include=include, exclude=exclude)) + required = set( + gen_required_kernel_modules( + context, + kver, + modules_include=modules_include, + modules_exclude=modules_exclude, + firmware_include=firmware_include, + firmware_exclude=firmware_exclude, + ) + ) with chdir(context.root): modules = sorted(modulesd.rglob("*.ko*"), reverse=True) diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index 775b5096b..d6a4e8fe3 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -1052,6 +1052,17 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, `KernelModulesInitrdExclude=`, `--kernel-modules-initrd-exclude=` : Like `KernelModulesExclude=`, but applies to the kernel modules included in the kernel modules initrd. +`FirmwareInclude=`, `--firmware-include=` +: Takes a list of regex patterns that specify firmware files to include in the image. Patterns should be + relative to `/usr/lib/firmware/` paths. **mkosi** checks for a match anywhere in the firmware path + (e.g. `bcm8483` will match against `cxgb4/bcm8483.bin`). All firmware files that match any of the specified + patterns are included in the image. + +`FirmwareExclude=`, `--firmware-exclude=` +: Takes a list of regex patterns that specify firmware files to exclude from the image. Behaves the same as + `FirmwareInclude=` except that all firmware that match any of the specified patterns is excluded from the + image. Firmware specified with this option is excluded even if an included kernel module depends on it. + `Locale=`, `--locale=`, `LocaleMessages=`, `--locale-messages=`, `Keymap=`, `--keymap=`, `Timezone=`, `--timezone=`, `Hostname=`, `--hostname=`, `RootShell=`, `--root-shell=` : The settings `Locale=`, `--locale=`, `LocaleMessages=`, `--locale-messages=`, `Keymap=`, `--keymap=`, `Timezone=`, `--timezone=`, `Hostname=`, diff --git a/tests/test_json.py b/tests/test_json.py index 96d5f050c..ad0967944 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -171,6 +171,12 @@ def test_config() -> None: "Files": [], "FinalizeScripts": [], "Firmware": "linux", + "FirmwareExclude": [ + "brcm/" + ], + "FirmwareInclude": [ + "ath3k-1" + ], "FirmwareVariables": "/foo/bar", "Format": "uki", "ForwardJournal": "/mkosi.journal", @@ -451,6 +457,8 @@ def test_config() -> None: extra_trees=[], files=[], finalize_scripts=[], + firmware_exclude=["brcm/"], + firmware_include=["ath3k-1"], firmware_variables=Path("/foo/bar"), firmware=Firmware.linux, forward_journal=Path("/mkosi.journal"),