nixos/xen: Add v2 bootspec extension with multiboot support
This commit is contained in:
@@ -3,14 +3,15 @@
|
|||||||
|
|
||||||
export LC_ALL=C
|
export LC_ALL=C
|
||||||
|
|
||||||
# Handle input argument and exit if the flag is invalid. See virtualisation.xen.efi.bootBuilderVerbosity below.
|
# Handle input argument and exit if the flag is invalid. See virtualisation.xen.boot.builderVerbosity below.
|
||||||
[[ $# -ne 1 ]] && echo -e "\e[1;31merror:\e[0m xenBootBuilder must be called with exactly one verbosity argument. See the \e[1;34mvirtualisation.xen.efi.bootBuilderVerbosity\e[0m option." && exit 1
|
[[ $# -ne 1 ]] && echo -e "\e[1;31merror:\e[0m xenBootBuilder must be called with exactly one verbosity argument. See the \e[1;34mvirtualisation.xen.boot.builderVerbosity\e[0m option." && exit 1
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
"quiet") true ;;
|
"quiet") true ;;
|
||||||
"default" | "info") echo -n "Installing Xen Project Hypervisor boot entries..." ;;
|
"default" | "info") echo -n "Installing Xen Project Hypervisor boot entries..." ;;
|
||||||
"debug") echo -e "\e[1;34mxenBootBuilder:\e[0m called with the '$1' flag" ;;
|
"debug") echo -e "\e[1;34mxenBootBuilder:\e[0m called with the '$1' flag" ;;
|
||||||
*)
|
*)
|
||||||
echo -e "\e[1;31merror:\e[0m xenBootBuilder was called with an invalid argument. See the \e[1;34mvirtualisation.xen.efi.bootBuilderVerbosity\e[0m option."
|
echo -e "\e[1;31merror:\e[0m xenBootBuilder was called with an invalid argument. See the \e[1;34mvirtualisation.xen.boot.builderVerbosity\e[0m option."
|
||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -46,8 +47,26 @@ for gen in "${gens[@]}"; do
|
|||||||
|
|
||||||
# We do nothing if the Bootspec for the current $gen does not contain the Xen
|
# We do nothing if the Bootspec for the current $gen does not contain the Xen
|
||||||
# extension, which is added as a configuration attribute below.
|
# extension, which is added as a configuration attribute below.
|
||||||
|
# We determine this by checking for the v1 or v2 bootspec extension,
|
||||||
|
# and setting the appropriate attributes based on version
|
||||||
|
xenSpecVer=""
|
||||||
|
xenParamVar=""
|
||||||
|
xenEfiPath=""
|
||||||
if grep -sq '"org.xenproject.bootspec.v1"' "$bootspecFile"; then
|
if grep -sq '"org.xenproject.bootspec.v1"' "$bootspecFile"; then
|
||||||
[ "$1" = "debug" ] && echo -e " \e[1;32msuccess:\e[0m found Xen entries in $gen."
|
xenSpecVer="v1"
|
||||||
|
xenParamVar="xenParams"
|
||||||
|
xenEfiPath="xen"
|
||||||
|
fi
|
||||||
|
# We prefer the v2 extension, so if both are present somehow,
|
||||||
|
# we will use the v2 attributes
|
||||||
|
if grep -sq '"org.xenproject.bootspec.v2"' "$bootspecFile"; then
|
||||||
|
xenSpecVer="v2"
|
||||||
|
xenParamVar="params"
|
||||||
|
xenEfiPath="efiPath"
|
||||||
|
fi
|
||||||
|
# Check for a valid Xen spec being detected
|
||||||
|
if [ -n "$xenSpecVer" ]; then
|
||||||
|
[ "$1" = "debug" ] && echo -e " \e[1;32msuccess:\e[0m found $xenSpecVer Xen entries in $gen."
|
||||||
|
|
||||||
# TODO: Support DeviceTree booting. Xen has some special handling for DeviceTree
|
# TODO: Support DeviceTree booting. Xen has some special handling for DeviceTree
|
||||||
# attributes, which will need to be translated in a boot script similar to this
|
# attributes, which will need to be translated in a boot script similar to this
|
||||||
@@ -63,7 +82,7 @@ for gen in "${gens[@]}"; do
|
|||||||
# the corresponding nixos generation, substituting `nixos` with `xen`:
|
# the corresponding nixos generation, substituting `nixos` with `xen`:
|
||||||
# `xen-$profile-generation-$number-specialisation-$specialisation.{cfg,conf}`
|
# `xen-$profile-generation-$number-specialisation-$specialisation.{cfg,conf}`
|
||||||
xenGen=$(echo "$gen" | sed 's_/loader/entries/nixos_/loader/entries/xen_g;s_^.*/xen_xen_g;s_.conf$__g')
|
xenGen=$(echo "$gen" | sed 's_/loader/entries/nixos_/loader/entries/xen_g;s_^.*/xen_xen_g;s_.conf$__g')
|
||||||
bootParams=$(jq -re '."org.xenproject.bootspec.v1".xenParams | join(" ")' "$bootspecFile")
|
bootParams=$(jq -re ".\"org.xenproject.bootspec.$xenSpecVer\".$xenParamVar | join(\" \")" "$bootspecFile")
|
||||||
kernel=$(jq -re '."org.nixos.bootspec.v1".kernel | sub("^/nix/store/"; "") | sub("/bzImage"; "-bzImage.efi")' "$bootspecFile")
|
kernel=$(jq -re '."org.nixos.bootspec.v1".kernel | sub("^/nix/store/"; "") | sub("/bzImage"; "-bzImage.efi")' "$bootspecFile")
|
||||||
kernelParams=$(jq -re '."org.nixos.bootspec.v1".kernelParams | join(" ")' "$bootspecFile")
|
kernelParams=$(jq -re '."org.nixos.bootspec.v1".kernelParams | join(" ")' "$bootspecFile")
|
||||||
initrd=$(jq -re '."org.nixos.bootspec.v1".initrd | sub("^/nix/store/"; "") | sub("/initrd"; "-initrd.efi")' "$bootspecFile")
|
initrd=$(jq -re '."org.nixos.bootspec.v1".initrd | sub("^/nix/store/"; "") | sub("/initrd"; "-initrd.efi")' "$bootspecFile")
|
||||||
@@ -90,7 +109,7 @@ EOF
|
|||||||
# Create Xen UKI for $generation. Most of this is lifted from
|
# Create Xen UKI for $generation. Most of this is lifted from
|
||||||
# https://xenbits.xenproject.org/docs/unstable/misc/efi.html.
|
# https://xenbits.xenproject.org/docs/unstable/misc/efi.html.
|
||||||
[ "$1" = "debug" ] && echo -e "\e[1;34mxenBootBuilder:\e[0m making Xen UKI..."
|
[ "$1" = "debug" ] && echo -e "\e[1;34mxenBootBuilder:\e[0m making Xen UKI..."
|
||||||
xenEfi=$(jq -re '."org.xenproject.bootspec.v1".xen' "$bootspecFile")
|
xenEfi=$(jq -re ".\"org.xenproject.bootspec.$xenSpecVer\".$xenEfiPath" "$bootspecFile")
|
||||||
finalSection=$(objdump --header --wide "$xenEfi" | tail -n +6 | sort --key="4,4" | tail -n 1 | grep -Eo '\.[a-z]*')
|
finalSection=$(objdump --header --wide "$xenEfi" | tail -n +6 | sort --key="4,4" | tail -n 1 | grep -Eo '\.[a-z]*')
|
||||||
padding=$(objdump --header --section="$finalSection" "$xenEfi" | awk -v section="$finalSection" '$0 ~ section { printf("0x%016x\n", and(strtonum("0x"$3) + strtonum("0x"$4) + 0xfff, compl(0xfff)))};')
|
padding=$(objdump --header --section="$finalSection" "$xenEfi" | awk -v section="$finalSection" '$0 ~ section { printf("0x%016x\n", and(strtonum("0x"$3) + strtonum("0x"$4) + 0xfff, compl(0xfff)))};')
|
||||||
[ "$1" = "debug" ] && echo " - padding: $padding"
|
[ "$1" = "debug" ] && echo " - padding: $padding"
|
||||||
@@ -133,8 +152,8 @@ mapfile -t postGenerations < <(find "$efiMountPoint"/loader/entries -type f -nam
|
|||||||
# any hypervisor boot entries.
|
# any hypervisor boot entries.
|
||||||
if ((${#postGenerations[@]} == 0)); then
|
if ((${#postGenerations[@]} == 0)); then
|
||||||
case "$1" in
|
case "$1" in
|
||||||
"default" | "info") echo "none found." && echo -e "If you believe this is an error, set the \e[1;34mvirtualisation.xen.efi.bootBuilderVerbosity\e[0m option to \e[1;34m\"debug\"\e[0m and rebuild to print debug logs." ;;
|
"default" | "info") echo "none found." && echo -e "If you believe this is an error, set the \e[1;34mvirtualisation.xen.boot.builderVerbosity\e[0m option to \e[1;34m\"debug\"\e[0m and rebuild to print debug logs." ;;
|
||||||
"debug") echo -e "\e[1;34mxenBootBuilder:\e[0m wrote \e[1;31mno generations\e[0m. Most likely, there were no generations with a valid \e[1;34morg.xenproject.bootspec.v1\e[0m entry." ;;
|
"debug") echo -e "\e[1;34mxenBootBuilder:\e[0m wrote \e[1;31mno generations\e[0m. Most likely, there were no generations with a valid \e[1;34morg.xenproject.bootspec.v1\e[0m or \e[1;34morg.xenproject.bootspec.v2\e[0m entry." ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# If the script is successful, change the default boot, say "done.", write a
|
# If the script is successful, change the default boot, say "done.", write a
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ let
|
|||||||
gnused
|
gnused
|
||||||
jq
|
jq
|
||||||
])
|
])
|
||||||
++ optionals (cfg.efi.bootBuilderVerbosity == "info") (
|
++ optionals (cfg.boot.builderVerbosity == "info") (
|
||||||
with pkgs;
|
with pkgs;
|
||||||
[
|
[
|
||||||
bat
|
bat
|
||||||
@@ -146,6 +146,48 @@ in
|
|||||||
"path"
|
"path"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
(mkRenamedOptionModule
|
||||||
|
[
|
||||||
|
"virtualisation"
|
||||||
|
"xen"
|
||||||
|
"efi"
|
||||||
|
"bootBuilderVerbosity"
|
||||||
|
]
|
||||||
|
[
|
||||||
|
"virtualisation"
|
||||||
|
"xen"
|
||||||
|
"boot"
|
||||||
|
"builderVerbosity"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
(mkRenamedOptionModule
|
||||||
|
[
|
||||||
|
"virtualisation"
|
||||||
|
"xen"
|
||||||
|
"bootParams"
|
||||||
|
]
|
||||||
|
[
|
||||||
|
"virtualisation"
|
||||||
|
"xen"
|
||||||
|
"boot"
|
||||||
|
"params"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
(mkRenamedOptionModule
|
||||||
|
[
|
||||||
|
"virtualisation"
|
||||||
|
"xen"
|
||||||
|
"efi"
|
||||||
|
"path"
|
||||||
|
]
|
||||||
|
[
|
||||||
|
"virtualisation"
|
||||||
|
"xen"
|
||||||
|
"boot"
|
||||||
|
"efi"
|
||||||
|
"path"
|
||||||
|
]
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
## Interface ##
|
## Interface ##
|
||||||
@@ -178,25 +220,24 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
bootParams = mkOption {
|
boot = {
|
||||||
default = [ ];
|
params = mkOption {
|
||||||
example = ''
|
default = [ ];
|
||||||
[
|
example = ''
|
||||||
"iommu=force:true,qinval:true,debug:true"
|
[
|
||||||
"noreboot=true"
|
"iommu=force:true,qinval:true,debug:true"
|
||||||
"vga=ask"
|
"noreboot=true"
|
||||||
]
|
"vga=ask"
|
||||||
'';
|
]
|
||||||
type = listOf str;
|
'';
|
||||||
description = ''
|
type = listOf str;
|
||||||
Xen Command Line parameters passed to Domain 0 at boot time.
|
description = ''
|
||||||
Note: these are different from `boot.kernelParams`. See
|
Xen Command Line parameters passed to Domain 0 at boot time.
|
||||||
the [Xen documentation](https://xenbits.xenproject.org/docs/unstable/misc/xen-command-line.html) for more information.
|
Note: these are different from `boot.kernelParams`. See
|
||||||
'';
|
the [Xen documentation](https://xenbits.xenproject.org/docs/unstable/misc/xen-command-line.html) for more information.
|
||||||
};
|
'';
|
||||||
|
};
|
||||||
efi = {
|
builderVerbosity = mkOption {
|
||||||
bootBuilderVerbosity = mkOption {
|
|
||||||
type = enum [
|
type = enum [
|
||||||
"default"
|
"default"
|
||||||
"info"
|
"info"
|
||||||
@@ -206,7 +247,7 @@ in
|
|||||||
default = "default";
|
default = "default";
|
||||||
example = "info";
|
example = "info";
|
||||||
description = ''
|
description = ''
|
||||||
The EFI boot entry builder script should be called with exactly one of the following arguments in order to specify its verbosity:
|
The boot entry builder script should be called with exactly one of the following arguments in order to specify its verbosity:
|
||||||
|
|
||||||
- `quiet` supresses all messages.
|
- `quiet` supresses all messages.
|
||||||
|
|
||||||
@@ -220,19 +261,33 @@ in
|
|||||||
This option does not alter the actual functionality of the script, just the number of messages printed when rebuilding the system.
|
This option does not alter the actual functionality of the script, just the number of messages printed when rebuilding the system.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
bios = {
|
||||||
path = mkOption {
|
path = mkOption {
|
||||||
type = path;
|
type = path;
|
||||||
default = "${cfg.package.boot}/${cfg.package.efi}";
|
default = "${cfg.package.boot}/${cfg.package.multiboot}";
|
||||||
defaultText = literalExpression "\${config.virtualisation.xen.package.boot}/\${config.virtualisation.xen.package.efi}";
|
defaultText = literalExpression "\${config.virtualisation.xen.package.boot}/\${config.virtualisation.xen.package.multiboot}";
|
||||||
example = literalExpression "\${config.virtualisation.xen.package}/boot/efi/efi/nixos/xen-\${config.virtualisation.xen.package.version}.efi";
|
example = literalExpression "\${config.virtualisation.xen.package}/boot/xen-\${config.virtualisation.xen.package.version}";
|
||||||
description = ''
|
description = ''
|
||||||
Path to xen.efi. `pkgs.xen` is patched to install the xen.efi file
|
Path to the Xen `multiboot` binary used for BIOS booting.
|
||||||
on `$boot/boot/xen.efi`, but an unpatched Xen build may install it
|
Unless you're building your own Xen derivation, you should leave this
|
||||||
somewhere else, such as `$out/boot/efi/efi/nixos/xen.efi`. Unless
|
option as the default value.
|
||||||
you're building your own Xen derivation, you should leave this
|
'';
|
||||||
option as the default value.
|
};
|
||||||
'';
|
};
|
||||||
|
efi = {
|
||||||
|
path = mkOption {
|
||||||
|
type = path;
|
||||||
|
default = "${cfg.package.boot}/${cfg.package.efi}";
|
||||||
|
defaultText = literalExpression "\${config.virtualisation.xen.package.boot}/\${config.virtualisation.xen.package.efi}";
|
||||||
|
example = literalExpression "\${config.virtualisation.xen.package}/boot/efi/efi/nixos/xen-\${config.virtualisation.xen.package.version}.efi";
|
||||||
|
description = ''
|
||||||
|
Path to xen.efi. `pkgs.xen` is patched to install the xen.efi file
|
||||||
|
on `$boot/boot/xen.efi`, but an unpatched Xen build may install it
|
||||||
|
somewhere else, such as `$out/boot/efi/efi/nixos/xen.efi`. Unless
|
||||||
|
you're building your own Xen derivation, you should leave this
|
||||||
|
option as the default value.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -612,8 +667,9 @@ in
|
|||||||
{
|
{
|
||||||
assertion =
|
assertion =
|
||||||
config.boot.loader.systemd-boot.enable
|
config.boot.loader.systemd-boot.enable
|
||||||
|| (config.boot ? lanzaboote) && config.boot.lanzaboote.enable;
|
|| (config.boot ? lanzaboote) && config.boot.lanzaboote.enable
|
||||||
message = "Xen only supports booting on systemd-boot or Lanzaboote.";
|
|| config.boot.loader.limine.enable;
|
||||||
|
message = "Xen only supports booting on systemd-boot, Lanzaboote or Limine.";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = config.boot.initrd.systemd.enable;
|
assertion = config.boot.initrd.systemd.enable;
|
||||||
@@ -639,7 +695,7 @@ in
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
virtualisation.xen.bootParams =
|
virtualisation.xen.boot.params =
|
||||||
optionals cfg.trace [
|
optionals cfg.trace [
|
||||||
"loglvl=all"
|
"loglvl=all"
|
||||||
"guest_loglvl=all"
|
"guest_loglvl=all"
|
||||||
@@ -692,17 +748,28 @@ in
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
# Xen Bootspec extension. This extension allows NixOS bootloaders to
|
# Xen Bootspec extension. This extension allows NixOS bootloaders to
|
||||||
# fetch the `xen.efi` path and access the `cfg.bootParams` option.
|
# fetch the dom0 kernel paths and access the `cfg.boot.params` option.
|
||||||
bootspec.extensions = {
|
bootspec.extensions = {
|
||||||
|
# Bootspec extension v1 is deprecated, and will be removed in 26.05
|
||||||
|
# It is present for backwards compatibility
|
||||||
"org.xenproject.bootspec.v1" = {
|
"org.xenproject.bootspec.v1" = {
|
||||||
xen = cfg.efi.path;
|
xen = cfg.boot.efi.path;
|
||||||
xenParams = cfg.bootParams;
|
xenParams = cfg.boot.params;
|
||||||
|
};
|
||||||
|
# Bootspec extension v2 includes more detail,
|
||||||
|
# including supporting multiboot, and is the current supported
|
||||||
|
# bootspec extension
|
||||||
|
"org.xenproject.bootspec.v2" = {
|
||||||
|
efiPath = cfg.boot.efi.path;
|
||||||
|
multibootPath = cfg.boot.bios.path;
|
||||||
|
version = cfg.package.version;
|
||||||
|
params = cfg.boot.params;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# See the `xenBootBuilder` script in the main `let...in` statement of this file.
|
# See the `xenBootBuilder` script in the main `let...in` statement of this file.
|
||||||
loader.systemd-boot.extraInstallCommands = ''
|
loader.systemd-boot.extraInstallCommands = ''
|
||||||
${getExe xenBootBuilder} ${cfg.efi.bootBuilderVerbosity}
|
${getExe xenBootBuilder} ${cfg.boot.builderVerbosity}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user