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}