-
-
Notifications
You must be signed in to change notification settings - Fork 14k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support external bootloader backends (RFC-0125) #172237
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
942dcd2
nixos/activation/bootspec: init bootspec support (RFC-0125)
cole-h 83d06ce
nixos/boot/external: init
cole-h 6c0e4e8
nixos/activation/bootspec: embed the entire contents of specialisatio…
grahamc e9c85d6
nixos/activation/bootspec: embed the document into a bootspec subdir
grahamc e69c37e
nixos/activation: don't generate bootspec for containers
K900 ee27291
nixos/activation/bootspec: fix slurping specialisation bootspecs
RaitoBezarius 9a431a5
nixos/activation/bootspec: add @raitobezarius as a code-owner
RaitoBezarius 348ba1b
nixos/activation/bootspec: module-ify
RaitoBezarius 092e6d3
nixos/tests/bootspec: init
RaitoBezarius 980f501
nixos/boot/external: add @raitobezarius as maintainer and allow initr…
RaitoBezarius 9832e3e
nixos/activation/bootspec: remove SB extension example in Cue schema
RaitoBezarius 11dfbee
nixos/activation/bootspec: add bootspec chapter in NixOS manual
RaitoBezarius 680369e
nixos/activation/bootspec: add some comments to explain the delicate …
RaitoBezarius ad6ea54
nixos/boot/external: DocBook -> Markdown
cole-h cc63293
nixos/boot/external: fixup typo in generated docs, regenerate docs
cole-h 97f657c
nixos/activation/bootspec: DocBook -> Markdown, add description for e…
cole-h 38e5089
nixos/activation/bootspec: drop problematic comment, only generate bo…
cole-h dce9add
nixos/activation/bootspec: refactor the generator script
cole-h fc88e4c
nixos/boot/external: drop duplicated external bootloader documentation
cole-h b37cee3
bootspec: init at unstable-2022-12-05
cole-h 5af481f
nixos/activation/bootspec: fixup improper $out substitution
cole-h 6eb04c5
nixos/activation/bootspec: bootspec owners also own the cue file
cole-h aac4134
nixos/tests/bootspec: add EFI support for GRUB test
RaitoBezarius File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Experimental feature: Bootspec {#sec-experimental-bootspec} | ||
|
||
Bootspec is a experimental feature, introduced in the [RFC-0125 proposal](https://github.com/NixOS/rfcs/pull/125), the reference implementation can be found [there](https://github.com/NixOS/nixpkgs/pull/172237) in order to standardize bootloader support | ||
and advanced boot workflows such as SecureBoot and potentially more. | ||
|
||
You can enable the creation of bootspec documents through [`boot.bootspec.enable = true`](options.html#opt-boot.bootspec.enable), which will prompt a warning until [RFC-0125](https://github.com/NixOS/rfcs/pull/125) is officially merged. | ||
|
||
## Schema {#sec-experimental-bootspec-schema} | ||
|
||
The bootspec schema is versioned and validated against [a CUE schema file](https://cuelang.org/) which should considered as the source of truth for your applications. | ||
|
||
You will find the current version [here](../../../modules/system/activation/bootspec.cue). | ||
|
||
## Extensions mechanism {#sec-experimental-bootspec-extensions} | ||
|
||
Bootspec cannot account for all usecases. | ||
|
||
For this purpose, Bootspec offers a generic extension facility [`boot.bootspec.extensions`](options.html#opt-boot.bootspec.extensions) which can be used to inject any data needed for your usecases. | ||
|
||
An example for SecureBoot is to get the Nix store path to `/etc/os-release` in order to bake it into a unified kernel image: | ||
|
||
```nix | ||
{ config, lib, ... }: { | ||
boot.bootspec.extensions = { | ||
"org.secureboot.osRelease" = config.environment.etc."os-release".source; | ||
}; | ||
} | ||
``` | ||
|
||
To reduce incompatibility and prevent names from clashing between applications, it is **highly recommended** to use a unique namespace for your extensions. | ||
|
||
## External bootloaders {#sec-experimental-bootspec-external-bootloaders} | ||
|
||
It is possible to enable your own bootloader through [`boot.loader.external.installHook`](options.html#opt-boot.loader.external.installHook) which can wrap an existing bootloader. | ||
|
||
Currently, there is no good story to compose existing bootloaders to enrich their features, e.g. SecureBoot, etc. It will be necessary to reimplement or reuse existing parts. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-experimental-bootspec"> | ||
<title>Experimental feature: Bootspec</title> | ||
<para> | ||
Bootspec is a experimental feature, introduced in the | ||
<link xlink:href="https://github.com/NixOS/rfcs/pull/125">RFC-0125 | ||
proposal</link>, the reference implementation can be found | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/pull/172237">there</link> | ||
in order to standardize bootloader support and advanced boot | ||
workflows such as SecureBoot and potentially more. | ||
</para> | ||
<para> | ||
You can enable the creation of bootspec documents through | ||
<link xlink:href="options.html#opt-boot.bootspec.enable"><literal>boot.bootspec.enable = true</literal></link>, | ||
which will prompt a warning until | ||
<link xlink:href="https://github.com/NixOS/rfcs/pull/125">RFC-0125</link> | ||
is officially merged. | ||
</para> | ||
<section xml:id="sec-experimental-bootspec-schema"> | ||
<title>Schema</title> | ||
<para> | ||
The bootspec schema is versioned and validated against | ||
<link xlink:href="https://cuelang.org/">a CUE schema file</link> | ||
which should considered as the source of truth for your | ||
applications. | ||
</para> | ||
<para> | ||
You will find the current version | ||
<link xlink:href="../../../modules/system/activation/bootspec.cue">here</link>. | ||
</para> | ||
</section> | ||
<section xml:id="sec-experimental-bootspec-extensions"> | ||
<title>Extensions mechanism</title> | ||
<para> | ||
Bootspec cannot account for all usecases. | ||
</para> | ||
<para> | ||
For this purpose, Bootspec offers a generic extension facility | ||
<link xlink:href="options.html#opt-boot.bootspec.extensions"><literal>boot.bootspec.extensions</literal></link> | ||
which can be used to inject any data needed for your usecases. | ||
</para> | ||
<para> | ||
An example for SecureBoot is to get the Nix store path to | ||
<literal>/etc/os-release</literal> in order to bake it into a | ||
unified kernel image: | ||
</para> | ||
<programlisting language="bash"> | ||
{ config, lib, ... }: { | ||
boot.bootspec.extensions = { | ||
"org.secureboot.osRelease" = config.environment.etc."os-release".source; | ||
}; | ||
} | ||
</programlisting> | ||
<para> | ||
To reduce incompatibility and prevent names from clashing between | ||
applications, it is <emphasis role="strong">highly | ||
recommended</emphasis> to use a unique namespace for your | ||
extensions. | ||
</para> | ||
</section> | ||
<section xml:id="sec-experimental-bootspec-external-bootloaders"> | ||
<title>External bootloaders</title> | ||
<para> | ||
It is possible to enable your own bootloader through | ||
<link xlink:href="options.html#opt-boot.loader.external.installHook"><literal>boot.loader.external.installHook</literal></link> | ||
which can wrap an existing bootloader. | ||
</para> | ||
<para> | ||
Currently, there is no good story to compose existing bootloaders | ||
to enrich their features, e.g. SecureBoot, etc. It will be | ||
necessary to reimplement or reuse existing parts. | ||
</para> | ||
</section> | ||
</chapter> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
'' | ||
]; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = { | ||
RaitoBezarius marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
}; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no native way to turn this rather annoying warning off other than introducing another option?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An other way to turn it off is to merge RFC-0125 :)