1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 blCfg = config.boot.loader;
12 dtCfg = config.hardware.deviceTree;
13 cfg = blCfg.generic-extlinux-compatible;
14
15 timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout;
16
17 # The builder used to write during system activation
18 builder = import ./extlinux-conf-builder.nix { inherit lib pkgs; };
19 # The builder exposed in populateCmd, which runs on the build architecture
20 populateBuilder = import ./extlinux-conf-builder.nix {
21 inherit lib;
22 pkgs = pkgs.buildPackages;
23 };
24in
25{
26 options = {
27 boot.loader.generic-extlinux-compatible = {
28 enable = mkOption {
29 default = false;
30 type = types.bool;
31 description = ''
32 Whether to generate an extlinux-compatible configuration file
33 under `/boot/extlinux.conf`. For instance,
34 U-Boot's generic distro boot support uses this file format.
35
36 See [U-boot's documentation](https://u-boot.readthedocs.io/en/latest/develop/distro.html)
37 for more information.
38 '';
39 };
40
41 useGenerationDeviceTree = mkOption {
42 default = true;
43 type = types.bool;
44 description = ''
45 Whether to generate Device Tree-related directives in the
46 extlinux configuration.
47
48 When enabled, the bootloader will attempt to load the device
49 tree binaries from the generation's kernel.
50
51 Note that this affects all generations, regardless of the
52 setting value used in their configurations.
53 '';
54 };
55
56 configurationLimit = mkOption {
57 default = 20;
58 example = 10;
59 type = types.int;
60 description = ''
61 Maximum number of configurations in the boot menu.
62 '';
63 };
64
65 mirroredBoots = mkOption {
66 default = [ { path = "/boot"; } ];
67 example = [
68 { path = "/boot1"; }
69 { path = "/boot2"; }
70 ];
71 description = ''
72 Mirror the boot configuration to multiple paths.
73 '';
74
75 type =
76 with types;
77 listOf (submodule {
78 options = {
79 path = mkOption {
80 example = "/boot1";
81 type = types.str;
82 description = ''
83 The path to the boot directory where the extlinux-compatible
84 configuration files will be written.
85 '';
86 };
87 };
88 });
89 };
90
91 populateCmd = mkOption {
92 type = types.str;
93 readOnly = true;
94 description = ''
95 Contains the builder command used to populate an image,
96 honoring all options except the `-c <path-to-default-configuration>`
97 argument.
98 Useful to have for sdImage.populateRootCommands
99 '';
100 };
101
102 };
103 };
104
105 config =
106 let
107 builderArgs =
108 "-g ${toString cfg.configurationLimit} -t ${timeoutStr}"
109 + lib.optionalString (dtCfg.name != null) " -n ${dtCfg.name}"
110 + lib.optionalString (!cfg.useGenerationDeviceTree) " -r";
111 installBootLoader = pkgs.writeScript "install-extlinux-conf.sh" (
112 ''
113 #!${pkgs.runtimeShell}
114 set -e
115 ''
116 + flip concatMapStrings cfg.mirroredBoots (args: ''
117 ${builder} ${builderArgs} -d '${args.path}' -c "$@"
118 '')
119 );
120 in
121 mkIf cfg.enable {
122 system.build.installBootLoader = installBootLoader;
123 system.boot.loader.id = "generic-extlinux-compatible";
124
125 boot.loader.generic-extlinux-compatible.populateCmd = "${populateBuilder} ${builderArgs}";
126
127 assertions = [
128 {
129 assertion = cfg.mirroredBoots != [ ];
130 message = ''
131 You must not remove all elements from option 'boot.loader.generic-extlinux-compatible.mirroredBoots',
132 otherwise the system will not be bootable.
133 '';
134 }
135 ];
136 };
137}