Skip to content

Instantly share code, notes, and snippets.

@erincandescent
Created January 6, 2020 01:06
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save erincandescent/c3266fc3cbb7fe21be0ab1def7adbc48 to your computer and use it in GitHub Desktop.
Save erincandescent/c3266fc3cbb7fe21be0ab1def7adbc48 to your computer and use it in GitHub Desktop.
netbooting a raspberry pi running alpine with nix
# dhcp server settings:
# dhcp-mac=set:rpi,b8:27:eb:*:*:*
# dhcp-boot=tag:rpi,boot,10.69.69.179
{ lib, stdenv, config, pkgs, ... }:
let
alpineRelease = "3.11";
alpineRevision = "2";
alpineVersion = "${alpineRelease}.${alpineRevision}";
fetchAlpinePi = {arch, sha256}: builtins.fetchTarball {
url = "http://dl-cdn.alpinelinux.org/alpine/v${alpineRelease}/releases/${arch}/alpine-rpi-${alpineVersion}-${arch}.tar.gz";
sha256 = sha256;
};
alpinePiArmhf = fetchAlpinePi { arch = "armhf"; sha256 = "0rkv6d0hpqfxmzn2fv6i4scn9hf5azg32kdbcxafyk2s92yrvlgr"; };
alpinePiArmv7 = fetchAlpinePi { arch = "armv7"; sha256 = "1k83wyfb186hv14ajvqw9qi5j8jz2g033idpdvxz6ycm2gla3gk5"; };
alpinePiAarch64 = fetchAlpinePi { arch = "aarch64"; sha256 = "1qj0ipg80k5sv4bj1h0sd6z3nj7l4frwznbs31yw3m04gc300vmp"; };
configTxt = pkgs.writeTextFile {
name = "config.txt";
text = ''#
[pi0]
kernel=vmlinuz-rpi
initramfs initramfs-rpi
[pi0w]
kernel=vmlinuz-rpi
initramfs initramfs-rpi
enable_uart=1
[pi1]
kernel=vmlinuz-rpi
initramfs initramfs-rpi
[pi2]
kernel=vmlinuz-rpi2
initramfs initramfs-rpi2
[pi3]
kernel=vmlinuz-rpi2
initramfs initramfs-rpi2
enable_uart=1
[pi3+]
kernel=vmlinuz-rpi2
initramfs initramfs-rpi2
enable_uart=1
[pi4]
enable_gic=1
kernel=vmlinuz-rpi4
initramfs initramfs-rpi4
arm_64bit=1'';
};
cmdlineTxt = pkgs.writeTextFile {
name = "cmdline.txt";
text = "modules=loop,squashfs,sd-mod,usb-storage console=ttyS0,115200 ip=dhcp alpine_repo=http://dl-cdn.alpinelinux.org/alpine/v${alpineRelease}/main/ apkovl=http://netboot.service.consul/apkovl/{MAC}.tar.gz";
};
tftpRoot = pkgs.stdenv.mkDerivation {
name = "tftproot";
preferLocalBuild = true;
allowSubstitutes = false;
buildInputs = [ pkgs.squashfsTools pkgs.cpio ];
buildCommand = ''
mkdir -p $out
# Hack: add af_packet.ko to the initramfs
mkdir modloop-rpi modloop-rpi2 modloop-rpi4
unsquashfs -d modloop-rpi/lib ${alpinePiArmhf}/boot/modloop-rpi 'modules/*/modules.*' 'modules/*/kernel/net/packet/af_packet.ko'
unsquashfs -d modloop-rpi2/lib ${alpinePiArmv7}/boot/modloop-rpi2 'modules/*/modules.*' 'modules/*/kernel/net/packet/af_packet.ko'
unsquashfs -d modloop-rpi4/lib ${alpinePiAarch64}/boot/modloop-rpi4 'modules/*/modules.*' 'modules/*/kernel/net/packet/af_packet.ko'
(cd modloop-rpi && find . | cpio -H newc -ov | gzip) > initramfs-ext-rpi
(cd modloop-rpi2 && find . | cpio -H newc -ov | gzip) > initramfs-ext-rpi2
(cd modloop-rpi4 && find . | cpio -H newc -ov | gzip) > initramfs-ext-rpi4
cat ${alpinePiArmhf}/boot/initramfs-rpi initramfs-ext-rpi > $out/initramfs-rpi
cat ${alpinePiArmv7}/boot/initramfs-rpi2 initramfs-ext-rpi2 > $out/initramfs-rpi2
cat ${alpinePiAarch64}/boot/initramfs-rpi4 initramfs-ext-rpi4 > $out/initramfs-rpi4
cd $out
# Pi Classic initial bootloader (VC4)
sed -e "s/BOOT_UART=0/BOOT_UART=1/" bootcode.bin ${alpinePiArmhf}/bootcode.bin >bootcode.bin
# Primary bootloader (ARM)
ln -s ${alpinePiArmhf}/start{,4}.elf .
# SDRAM setup
ln -s ${alpinePiArmhf}/fixup{,4}.dat .
# Configuration
ln -s ${configTxt} ./config.txt
# Kernels
ln -s ${alpinePiArmhf}/boot/vmlinuz-rpi ./
ln -s ${alpinePiArmv7}/boot/vmlinuz-rpi2 ./
ln -s ${alpinePiAarch64}/boot/vmlinuz-rpi4 ./
# Device Tree Blobs
ln -s ${alpinePiArmhf}/*.dtb .
# Commandlines
ln -s ${cmdlineTxt} ./cmdline.txt
'';
};
in
{
services.atftpd = {
enable = true;
root = tftpRoot;
extraOptions = [ "--verbose=7" ];
};
consulServices = [
{
name = "tftp";
port = 69;
tags = [ "udp" ];
}
];
networking.firewall = {
allowedUDPPorts = [ 69 ];
connectionTrackingModules = [ "tftp" ];
};
}
@erincandescent
Copy link
Author

I have no idea with regards to NFS, I'm afraid. My setup uses APKOVL downloads from a HTTP server in order to net-boot individual devices

@biemster
Copy link

This is nothing short of magic! I hope you don't mind I've adapted the rpi4 part of this gist to a bash script (which keeps all the packages and tarballs on the local network), since I don't intend to use nix (looks cool though). Script lives here: https://github.com/biemster/rpi4_alpine_netboot. Thanks a lot for sharing this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment