diff --git a/nixos/modules/virtualisation/xen-boot-builder.sh b/nixos/modules/virtualisation/xen-boot-builder.sh index f8363b337662..3fe689297864 100755 --- a/nixos/modules/virtualisation/xen-boot-builder.sh +++ b/nixos/modules/virtualisation/xen-boot-builder.sh @@ -3,14 +3,15 @@ export LC_ALL=C -# Handle input argument and exit if the flag is invalid. See virtualisation.xen.efi.bootBuilderVerbosity 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 +# 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.boot.builderVerbosity\e[0m option." && exit 1 + case "$1" in "quiet") true ;; "default" | "info") echo -n "Installing Xen Project Hypervisor boot entries..." ;; "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 ;; 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 # 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 - [ "$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 # 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`: # `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') - 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") 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") @@ -90,7 +109,7 @@ EOF # Create Xen UKI for $generation. Most of this is lifted from # https://xenbits.xenproject.org/docs/unstable/misc/efi.html. [ "$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]*') 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" @@ -133,8 +152,8 @@ mapfile -t postGenerations < <(find "$efiMountPoint"/loader/entries -type f -nam # any hypervisor boot entries. if ((${#postGenerations[@]} == 0)); then 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." ;; - "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." ;; + "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 or \e[1;34morg.xenproject.bootspec.v2\e[0m entry." ;; esac # If the script is successful, change the default boot, say "done.", write a diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix index 2911c0c1d97c..a67706a2eb0b 100644 --- a/nixos/modules/virtualisation/xen-dom0.nix +++ b/nixos/modules/virtualisation/xen-dom0.nix @@ -51,7 +51,7 @@ let gnused jq ]) - ++ optionals (cfg.efi.bootBuilderVerbosity == "info") ( + ++ optionals (cfg.boot.builderVerbosity == "info") ( with pkgs; [ bat @@ -146,6 +146,48 @@ in "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 ## @@ -178,25 +220,24 @@ in }; }; - bootParams = mkOption { - default = [ ]; - example = '' - [ - "iommu=force:true,qinval:true,debug:true" - "noreboot=true" - "vga=ask" - ] - ''; - type = listOf str; - description = '' - Xen Command Line parameters passed to Domain 0 at boot time. - 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 = { - bootBuilderVerbosity = mkOption { + boot = { + params = mkOption { + default = [ ]; + example = '' + [ + "iommu=force:true,qinval:true,debug:true" + "noreboot=true" + "vga=ask" + ] + ''; + type = listOf str; + description = '' + Xen Command Line parameters passed to Domain 0 at boot time. + 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. + ''; + }; + builderVerbosity = mkOption { type = enum [ "default" "info" @@ -206,7 +247,7 @@ in default = "default"; example = "info"; 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. @@ -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. ''; }; - - 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. - ''; + bios = { + path = mkOption { + type = path; + default = "${cfg.package.boot}/${cfg.package.multiboot}"; + defaultText = literalExpression "\${config.virtualisation.xen.package.boot}/\${config.virtualisation.xen.package.multiboot}"; + example = literalExpression "\${config.virtualisation.xen.package}/boot/xen-\${config.virtualisation.xen.package.version}"; + description = '' + Path to the Xen `multiboot` binary used for BIOS booting. + Unless 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 = config.boot.loader.systemd-boot.enable - || (config.boot ? lanzaboote) && config.boot.lanzaboote.enable; - message = "Xen only supports booting on systemd-boot or Lanzaboote."; + || (config.boot ? lanzaboote) && config.boot.lanzaboote.enable + || config.boot.loader.limine.enable; + message = "Xen only supports booting on systemd-boot, Lanzaboote or Limine."; } { assertion = config.boot.initrd.systemd.enable; @@ -639,7 +695,7 @@ in } ]; - virtualisation.xen.bootParams = + virtualisation.xen.boot.params = optionals cfg.trace [ "loglvl=all" "guest_loglvl=all" @@ -692,17 +748,28 @@ in ''; # 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 extension v1 is deprecated, and will be removed in 26.05 + # It is present for backwards compatibility "org.xenproject.bootspec.v1" = { - xen = cfg.efi.path; - xenParams = cfg.bootParams; + xen = cfg.boot.efi.path; + 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. loader.systemd-boot.extraInstallCommands = '' - ${getExe xenBootBuilder} ${cfg.efi.bootBuilderVerbosity} + ${getExe xenBootBuilder} ${cfg.boot.builderVerbosity} ''; };