diff options
author | Jörg Thalheim <Mic92@users.noreply.github.com> | 2022-12-17 08:35:53 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-17 08:35:53 +0000 |
commit | 668a2b2f3322c062d7a040cf084d763c9f2e271f (patch) | |
tree | efde23edbce251b2a730a84220501a2945ad9e63 /nixos/modules/system | |
parent | 8392158289e7d1a801905bc4195983f1b7434770 (diff) | |
parent | aac4134f430fb15386a174c6556ca0116616d2ea (diff) |
Merge pull request #172237 from DeterminateSystems/bootspec-rfc
Support external bootloader backends (RFC-0125)
Diffstat (limited to 'nixos/modules/system')
-rw-r--r-- | nixos/modules/system/activation/bootspec.cue | 17 | ||||
-rw-r--r-- | nixos/modules/system/activation/bootspec.nix | 124 | ||||
-rw-r--r-- | nixos/modules/system/activation/top-level.nix | 5 | ||||
-rw-r--r-- | nixos/modules/system/boot/loader/external/external.md | 26 | ||||
-rw-r--r-- | nixos/modules/system/boot/loader/external/external.nix | 38 | ||||
-rw-r--r-- | nixos/modules/system/boot/loader/external/external.xml | 41 |
6 files changed, 251 insertions, 0 deletions
diff --git a/nixos/modules/system/activation/bootspec.cue b/nixos/modules/system/activation/bootspec.cue new file mode 100644 index 0000000000000..3fc9ca381df77 --- /dev/null +++ b/nixos/modules/system/activation/bootspec.cue @@ -0,0 +1,17 @@ +#V1: { + init: string + initrd?: string + initrdSecrets?: string + kernel: string + kernelParams: [...string] + label: string + toplevel: string + specialisation?: { + [=~"^"]: #V1 + } + extensions?: {...} +} + +Document: { + v1: #V1 +} diff --git a/nixos/modules/system/activation/bootspec.nix b/nixos/modules/system/activation/bootspec.nix new file mode 100644 index 0000000000000..da76bf9084af8 --- /dev/null +++ b/nixos/modules/system/activation/bootspec.nix @@ -0,0 +1,124 @@ +# Note that these schemas are defined by RFC-0125. +# This document is considered a stable API, and is depended upon by external tooling. +# Changes to the structure of the document, or the semantics of the values should go through an RFC. +# +# See: https://github.com/NixOS/rfcs/pull/125 +{ config +, pkgs +, lib +, ... +}: +let + cfg = config.boot.bootspec; + children = lib.mapAttrs (childName: childConfig: childConfig.configuration.system.build.toplevel) config.specialisation; + schemas = { + v1 = rec { + filename = "boot.json"; + json = + pkgs.writeText filename + (builtins.toJSON + { + v1 = { + kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}"; + kernelParams = config.boot.kernelParams; + initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; + initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets"; + label = "NixOS ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"; + + inherit (cfg) extensions; + }; + }); + + generator = + let + # NOTE: Be careful to not introduce excess newlines at the end of the + # injectors, as that may affect the pipes and redirects. + + # Inject toplevel and init into the bootspec. + # This can only be done here because we *cannot* depend on $out + # referring to the toplevel, except by living in the toplevel itself. + toplevelInjector = lib.escapeShellArgs [ + "${pkgs.jq}/bin/jq" + '' + .v1.toplevel = $toplevel | + .v1.init = $init + '' + "--sort-keys" + "--arg" "toplevel" "${placeholder "out"}" + "--arg" "init" "${placeholder "out"}/init" + ] + " < ${json}"; + + # We slurp all specialisations and inject them as values, such that + # `.specialisations.${name}` embeds the specialisation's bootspec + # document. + specialisationInjector = + let + specialisationLoader = (lib.mapAttrsToList + (childName: childToplevel: lib.escapeShellArgs [ "--slurpfile" childName "${childToplevel}/bootspec/${filename}" ]) + children); + in + lib.escapeShellArgs [ + "${pkgs.jq}/bin/jq" + "--sort-keys" + ".v1.specialisation = ($ARGS.named | map_values(. | first | .v1))" + ] + " ${lib.concatStringsSep " " specialisationLoader}"; + in + '' + mkdir -p $out/bootspec + + ${toplevelInjector} | ${specialisationInjector} > $out/bootspec/${filename} + ''; + + validator = pkgs.writeCueValidator ./bootspec.cue { + document = "Document"; # Universal validator for any version as long the schema is correctly set. + }; + }; + }; +in +{ + options.boot.bootspec = { + enable = lib.mkEnableOption (lib.mdDoc "Enable generation of RFC-0125 bootspec in $system/bootspec, e.g. /run/current-system/bootspec"); + + extensions = lib.mkOption { + type = lib.types.attrs; + default = { }; + description = lib.mdDoc '' + User-defined data that extends the bootspec document. + + To reduce incompatibility and prevent names from clashing + between applications, it is **highly recommended** to use a + unique namespace for your extensions. + ''; + }; + + # This will be run as a part of the `systemBuilder` in ./top-level.nix. This + # means `$out` points to the output of `config.system.build.toplevel` and can + # be used for a variety of things (though, for now, it's only used to report + # the path of the `toplevel` itself and the `init` executable). + writer = lib.mkOption { + internal = true; + default = schemas.v1.generator; + }; + + validator = lib.mkOption { + internal = true; + default = schemas.v1.validator; + }; + + filename = lib.mkOption { + internal = true; + default = schemas.v1.filename; + }; + }; + + config = lib.mkIf (cfg.enable) { + warnings = [ + ''RFC-0125 is not merged yet, this is a feature preview of bootspec. + The schema is not definitive and features are not guaranteed to be stable until RFC-0125 is merged. + See: + - https://github.com/NixOS/nixpkgs/pull/172237 to track merge status in nixpkgs. + - https://github.com/NixOS/rfcs/pull/125 to track RFC status. + '' + ]; + }; +} diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 64e97b510b067..0bb3628ceed9a 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -79,6 +79,11 @@ let echo -n "$extraDependencies" > $out/extra-dependencies + ${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) '' + ${config.boot.bootspec.writer} + ${config.boot.bootspec.validator} "$out/bootspec/${config.boot.bootspec.filename}" + ''} + ${config.system.extraSystemBuilderCmds} ''; diff --git a/nixos/modules/system/boot/loader/external/external.md b/nixos/modules/system/boot/loader/external/external.md new file mode 100644 index 0000000000000..ba1dfd4d9b9af --- /dev/null +++ b/nixos/modules/system/boot/loader/external/external.md @@ -0,0 +1,26 @@ +# External Bootloader Backends {#sec-bootloader-external} + +NixOS has support for several bootloader backends by default: systemd-boot, grub, uboot, etc. +The built-in bootloader backend support is generic and supports most use cases. +Some users may prefer to create advanced workflows around managing the bootloader and bootable entries. + +You can replace the built-in bootloader support with your own tooling using the "external" bootloader option. + +Imagine you have created a new package called FooBoot. +FooBoot provides a program at `${pkgs.fooboot}/bin/fooboot-install` which takes the system closure's path as its only argument and configures the system's bootloader. + +You can enable FooBoot like this: + +```nix +{ pkgs, ... }: { + boot.loader.external = { + enable = true; + installHook = "${pkgs.fooboot}/bin/fooboot-install"; + }; +} +``` + +## Developing Custom Bootloader Backends + +Bootloaders should use [RFC-0125](https://github.com/NixOS/rfcs/pull/125)'s Bootspec format and synthesis tools to identify the key properties for bootable system generations. + diff --git a/nixos/modules/system/boot/loader/external/external.nix b/nixos/modules/system/boot/loader/external/external.nix new file mode 100644 index 0000000000000..5cf478e6c83cd --- /dev/null +++ b/nixos/modules/system/boot/loader/external/external.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.boot.loader.external; +in +{ + meta = { + maintainers = with maintainers; [ cole-h grahamc raitobezarius ]; + # Don't edit the docbook xml directly, edit the md and generate it: + # `pandoc external.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > external.xml` + doc = ./external.xml; + }; + + options.boot.loader.external = { + enable = mkEnableOption (lib.mdDoc "use an external tool to install your bootloader"); + + installHook = mkOption { + type = with types; path; + description = lib.mdDoc '' + The full path to a program of your choosing which performs the bootloader installation process. + + The program will be called with an argument pointing to the output of the system's toplevel. + ''; + }; + }; + + config = mkIf cfg.enable { + boot.loader = { + grub.enable = mkDefault false; + systemd-boot.enable = mkDefault false; + supportsInitrdSecrets = mkDefault false; + }; + + system.build.installBootLoader = cfg.installHook; + }; +} diff --git a/nixos/modules/system/boot/loader/external/external.xml b/nixos/modules/system/boot/loader/external/external.xml new file mode 100644 index 0000000000000..39ab2156bc8c6 --- /dev/null +++ b/nixos/modules/system/boot/loader/external/external.xml @@ -0,0 +1,41 @@ +<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-bootloader-external"> + <title>External Bootloader Backends</title> + <para> + NixOS has support for several bootloader backends by default: + systemd-boot, grub, uboot, etc. The built-in bootloader backend + support is generic and supports most use cases. Some users may + prefer to create advanced workflows around managing the bootloader + and bootable entries. + </para> + <para> + You can replace the built-in bootloader support with your own + tooling using the <quote>external</quote> bootloader option. + </para> + <para> + Imagine you have created a new package called FooBoot. FooBoot + provides a program at + <literal>${pkgs.fooboot}/bin/fooboot-install</literal> which takes + the system closure’s path as its only argument and configures the + system’s bootloader. + </para> + <para> + You can enable FooBoot like this: + </para> + <programlisting language="nix"> +{ pkgs, ... }: { + boot.loader.external = { + enable = true; + installHook = "${pkgs.fooboot}/bin/fooboot-install"; + }; +} +</programlisting> + <section xml:id="developing-custom-bootloader-backends"> + <title>Developing Custom Bootloader Backends</title> + <para> + Bootloaders should use + <link xlink:href="https://github.com/NixOS/rfcs/pull/125">RFC-0125</link>’s + Bootspec format and synthesis tools to identify the key properties + for bootable system generations. + </para> + </section> +</chapter> |