diff options
Diffstat (limited to 'nixpkgs/nixos/modules/system/boot/loader/raspberrypi')
5 files changed, 322 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix new file mode 100644 index 000000000000..7eb52e3d021f --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.nix @@ -0,0 +1,10 @@ +{ pkgs, configTxt }: + +pkgs.substituteAll { + src = ./raspberrypi-builder.sh; + isExecutable = true; + inherit (pkgs) bash; + path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; + firmware = pkgs.raspberrypifw; + inherit configTxt; +} diff --git a/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh new file mode 100644 index 000000000000..0fb07de10c04 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi-builder.sh @@ -0,0 +1,132 @@ +#! @bash@/bin/sh -e + +shopt -s nullglob + +export PATH=/empty +for i in @path@; do PATH=$PATH:$i/bin; done + +usage() { + echo "usage: $0 -c <path-to-default-configuration> [-d <boot-dir>]" >&2 + exit 1 +} + +default= # Default configuration +target=/boot # Target directory + +while getopts "c:d:" opt; do + case "$opt" in + c) default="$OPTARG" ;; + d) target="$OPTARG" ;; + \?) usage ;; + esac +done + +echo "updating the boot generations directory..." + +mkdir -p $target/old + +# Convert a path to a file in the Nix store such as +# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>. +cleanName() { + local path="$1" + echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' +} + +# Copy a file from the Nix store to $target/kernels. +declare -A filesCopied + +copyToKernelsDir() { + local src="$1" + local dst="$target/old/$(cleanName $src)" + # Don't copy the file if $dst already exists. This means that we + # have to create $dst atomically to prevent partially copied + # kernels or initrd if this script is ever interrupted. + if ! test -e $dst; then + local dstTmp=$dst.tmp.$$ + cp $src $dstTmp + mv $dstTmp $dst + fi + filesCopied[$dst]=1 + result=$dst +} + +copyForced() { + local src="$1" + local dst="$2" + cp $src $dst.tmp + mv $dst.tmp $dst +} + +outdir=$target/old +mkdir -p $outdir || true + +# Copy its kernel and initrd to $target/old. +addEntry() { + local path="$1" + local generation="$2" + + if ! test -e $path/kernel -a -e $path/initrd; then + return + fi + + local kernel=$(readlink -f $path/kernel) + local initrd=$(readlink -f $path/initrd) + local dtb_path=$(readlink -f $path/kernel-modules/dtbs) + + if test -n "@copyKernels@"; then + copyToKernelsDir $kernel; kernel=$result + copyToKernelsDir $initrd; initrd=$result + fi + + echo $(readlink -f $path) > $outdir/$generation-system + echo $(readlink -f $path/init) > $outdir/$generation-init + cp $path/kernel-params $outdir/$generation-cmdline.txt + echo $initrd > $outdir/$generation-initrd + echo $kernel > $outdir/$generation-kernel + + if test "$generation" = "default"; then + copyForced $kernel $target/kernel.img + copyForced $initrd $target/initrd + for dtb in $dtb_path/{broadcom,}/bcm*.dtb; do + dst="$target/$(basename $dtb)" + copyForced $dtb "$dst" + filesCopied[$dst]=1 + done + cp "$(readlink -f "$path/init")" $target/nixos-init + echo "`cat $path/kernel-params` init=$path/init" >$target/cmdline.txt + fi +} + +addEntry $default default + +# Add all generations of the system profile to the menu, in reverse +# (most recent to least recent) order. +for generation in $( + (cd /nix/var/nix/profiles && ls -d system-*-link) \ + | sed 's/system-\([0-9]\+\)-link/\1/' \ + | sort -n -r); do + link=/nix/var/nix/profiles/system-$generation-link + addEntry $link $generation +done + +# Add the firmware files +fwdir=@firmware@/share/raspberrypi/boot/ +copyForced $fwdir/bootcode.bin $target/bootcode.bin +copyForced $fwdir/fixup.dat $target/fixup.dat +copyForced $fwdir/fixup_cd.dat $target/fixup_cd.dat +copyForced $fwdir/fixup_db.dat $target/fixup_db.dat +copyForced $fwdir/fixup_x.dat $target/fixup_x.dat +copyForced $fwdir/start.elf $target/start.elf +copyForced $fwdir/start_cd.elf $target/start_cd.elf +copyForced $fwdir/start_db.elf $target/start_db.elf +copyForced $fwdir/start_x.elf $target/start_x.elf + +# Add the config.txt +copyForced @configTxt@ $target/config.txt + +# Remove obsolete files from $target and $target/old. +for fn in $target/old/*linux* $target/old/*initrd-initrd* $target/bcm*.dtb; do + if ! test "${filesCopied[$fn]}" = 1; then + rm -vf -- "$fn" + fi +done diff --git a/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix new file mode 100644 index 000000000000..7db60daa60b8 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix @@ -0,0 +1,108 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.boot.loader.raspberryPi; + + inherit (pkgs.stdenv.hostPlatform) platform; + + builderUboot = import ./uboot-builder.nix { inherit pkgs configTxt; inherit (cfg) version; }; + builderGeneric = import ./raspberrypi-builder.nix { inherit pkgs configTxt; }; + + builder = + if cfg.uboot.enable then + "${builderUboot} -g ${toString cfg.uboot.configurationLimit} -t ${timeoutStr} -c" + else + "${builderGeneric} -c"; + + blCfg = config.boot.loader; + timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout; + + isAarch64 = pkgs.stdenv.hostPlatform.isAarch64; + optional = pkgs.stdenv.lib.optionalString; + + configTxt = + pkgs.writeText "config.txt" ('' + # U-Boot used to need this to work, regardless of whether UART is actually used or not. + # TODO: check when/if this can be removed. + enable_uart=1 + + # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel + # when attempting to show low-voltage or overtemperature warnings. + avoid_warnings=1 + '' + optional isAarch64 '' + # Boot in 64-bit mode. + arm_64bit=1 + '' + (if cfg.uboot.enable then '' + kernel=u-boot-rpi.bin + '' else '' + kernel=kernel.img + initramfs initrd followkernel + '') + optional (cfg.firmwareConfig != null) cfg.firmwareConfig); + +in + +{ + options = { + + boot.loader.raspberryPi = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to create files with the system generations in + <literal>/boot</literal>. + <literal>/boot/old</literal> will hold files from old generations. + ''; + }; + + version = mkOption { + default = 2; + type = types.enum [ 0 1 2 3 ]; + description = '' + ''; + }; + + uboot = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Enable using uboot as bootmanager for the raspberry pi. + ''; + }; + + configurationLimit = mkOption { + default = 20; + example = 10; + type = types.int; + description = '' + Maximum number of configurations in the boot menu. + ''; + }; + + }; + + firmwareConfig = mkOption { + default = null; + type = types.nullOr types.string; + description = '' + Extra options that will be appended to <literal>/boot/config.txt</literal> file. + For possible values, see: https://www.raspberrypi.org/documentation/configuration/config-txt/ + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = singleton { + assertion = !pkgs.stdenv.hostPlatform.isAarch64 || cfg.version == 3; + message = "Only Raspberry Pi 3 supports aarch64."; + }; + + system.build.installBootLoader = builder; + system.boot.loader.id = "raspberrypi"; + system.boot.loader.kernelFile = platform.kernelTarget; + }; +} diff --git a/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix new file mode 100644 index 000000000000..94599a0081c6 --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.nix @@ -0,0 +1,34 @@ +{ pkgs, version, configTxt }: + +let + isAarch64 = pkgs.stdenv.hostPlatform.isAarch64; + + uboot = + if version == 0 then + pkgs.ubootRaspberryPiZero + else if version == 1 then + pkgs.ubootRaspberryPi + else if version == 2 then + pkgs.ubootRaspberryPi2 + else + if isAarch64 then + pkgs.ubootRaspberryPi3_64bit + else + pkgs.ubootRaspberryPi3_32bit; + + extlinuxConfBuilder = + import ../generic-extlinux-compatible/extlinux-conf-builder.nix { + pkgs = pkgs.buildPackages; + }; +in +pkgs.substituteAll { + src = ./uboot-builder.sh; + isExecutable = true; + inherit (pkgs.buildPackages) bash; + path = with pkgs.buildPackages; [coreutils gnused gnugrep]; + firmware = pkgs.raspberrypifw; + inherit uboot; + inherit configTxt; + inherit extlinuxConfBuilder; + inherit version; +} diff --git a/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh new file mode 100644 index 000000000000..ea591427179f --- /dev/null +++ b/nixpkgs/nixos/modules/system/boot/loader/raspberrypi/uboot-builder.sh @@ -0,0 +1,38 @@ +#! @bash@/bin/sh -e + +target=/boot # Target directory + +while getopts "t:c:d:g:" opt; do + case "$opt" in + d) target="$OPTARG" ;; + *) ;; + esac +done + +copyForced() { + local src="$1" + local dst="$2" + cp $src $dst.tmp + mv $dst.tmp $dst +} + +# Call the extlinux builder +"@extlinuxConfBuilder@" "$@" + +# Add the firmware files +fwdir=@firmware@/share/raspberrypi/boot/ +copyForced $fwdir/bootcode.bin $target/bootcode.bin +copyForced $fwdir/fixup.dat $target/fixup.dat +copyForced $fwdir/fixup_cd.dat $target/fixup_cd.dat +copyForced $fwdir/fixup_db.dat $target/fixup_db.dat +copyForced $fwdir/fixup_x.dat $target/fixup_x.dat +copyForced $fwdir/start.elf $target/start.elf +copyForced $fwdir/start_cd.elf $target/start_cd.elf +copyForced $fwdir/start_db.elf $target/start_db.elf +copyForced $fwdir/start_x.elf $target/start_x.elf + +# Add the uboot file +copyForced @uboot@/u-boot.bin $target/u-boot-rpi.bin + +# Add the config.txt +copyForced @configTxt@ $target/config.txt |