diff --git a/devices/zyxel-nwa50ax/default.nix b/devices/zyxel-nwa50ax/default.nix index 7c5e09b..a6c77c6 100644 --- a/devices/zyxel-nwa50ax/default.nix +++ b/devices/zyxel-nwa50ax/default.nix @@ -94,6 +94,7 @@ ../../modules/arch/mipsel.nix ../../modules/outputs/tftpboot.nix ../../modules/outputs/zyxel-nwa-fit.nix + ../../modules/zyxel-dual-image ]; filesystem = dir { @@ -168,6 +169,7 @@ }; }; }; + boot = { # Critical because NWA50AX will extend your cmdline with the image number booted. # and some bootloader version. @@ -182,6 +184,15 @@ }; }; + # Dual image management service in userspace. + services.zyxel-dual-image = config.boot.zyxel-dual-image.build { + ensureActiveImage = "primary"; + # TODO: use mtd names rather… + primaryMtdPartition = "/dev/mtd3"; + secondaryMtdPartition = "/dev/mtd6"; + bootConfigurationMtdPartition = "/dev/mtd10"; + }; + # DEVICE_VENDOR := ZyXEL # KERNEL_SIZE := 8192k # DEVICE_PACKAGES := kmod-mt7915-firmware zyxel-bootconfig diff --git a/modules/zyxel-dual-image/default.nix b/modules/zyxel-dual-image/default.nix new file mode 100644 index 0000000..91ed48e --- /dev/null +++ b/modules/zyxel-dual-image/default.nix @@ -0,0 +1,60 @@ +## Boot blessing via Zyxel +## ======================= +## Boot blessing is the process to bless a particular boot configuration +## It is commonly encountered in devices with redundant partitions +## for automatic recovery of broken upgrades. +## This is also known as A/B schemas, where A represents the primary partition +## and B the secondary partition used for recovery. +## To use boot blessing on Liminix, you need to have the support of +## your bootloader to help you boot on the secondary partition in case of +## failure on the primary partition. The exact details are specifics to your device. +## See the Zyxel NWA50AX for an example. +## TODO: generalize this module. +{ config, lib, pkgs, ... }: +let + inherit (lib) mkOption types; + inherit (pkgs) liminix; +in +{ + options.boot.zyxel-dual-image = mkOption { + type = liminix.lib.types.serviceDefn; + }; + + config.boot.zyxel-dual-image = liminix.callService ./service.nix { + ensureActiveImage = mkOption { + type = types.enum [ "primary" "secondary" ]; + default = "primary"; + description = ''At boot, ensure that the active image is the one specified. + + If you are already on a broken image, you need to manually boot + into the right image via `atgo ` in U-Boot. + ''; + }; + + kernelCommandLineSource = mkOption { + type = types.enum [ "/proc/cmdline" "/proc/device-tree/chosen/bootargs" ]; + default = "/proc/device-tree/chosen/bootargs"; + description = ''Kernel command line arguments source file. + On MIPS, Liminix embeds the kernel command line in /proc/device-tree/chosen/bootargs-override. + + In this instance, it does not get concatenated with `/proc/cmdline`. + Therefore you may prefer to source it from another place, like `/proc/device-tree/chosen/bootargs`. + ''; + }; + + primaryMtdPartition = mkOption { + type = types.str; + description = "Primary MTD partition device node, i.e. for image 0."; + }; + + secondaryMtdPartition = mkOption { + type = types.str; + description = "Secondary MTD partition device node, i.e. for image 1."; + }; + + bootConfigurationMtdPartition = mkOption { + type = types.str; + description = "Boot configuration MTD partition device node."; + }; + }; +} diff --git a/modules/zyxel-dual-image/service.nix b/modules/zyxel-dual-image/service.nix new file mode 100644 index 0000000..3edd642 --- /dev/null +++ b/modules/zyxel-dual-image/service.nix @@ -0,0 +1,33 @@ +{ + liminix +, lib +, zyxel-bootconfig +}: +{ ensureActiveImage, primaryMtdPartition, secondaryMtdPartition, bootConfigurationMtdPartition, kernelCommandLineSource }: +let + inherit (liminix.services) oneshot; + activeImageIndex = if ensureActiveImage == "primary" then 0 else 1; +in oneshot { + name = "zyxel-boot-configure"; + up = '' + set -- $(cat /proc/device-tree/chosen/bootargs) + for x in "$@"; do + case "$x" in + bootImage=*) + BOOT_IMAGE="''${x#bootImage=}" + echo "Current boot image is $BOOT_IMAGE." + ;; + esac + done + + if test -z "$BOOT_IMAGE"; then + echo "No valid image was provided in the kernel command line." + exit 1 + else + ${lib.getExe zyxel-bootconfig} ${bootConfigurationMtdPartition} set-image-status "$BOOT_IMAGE" valid + ${lib.getExe zyxel-bootconfig} ${bootConfigurationMtdPartition} set-active-image ${toString activeImageIndex} + + echo "Active image is now ${ensureActiveImage}" + fi + ''; +} diff --git a/pkgs/zyxel-bootconfig/default.nix b/pkgs/zyxel-bootconfig/default.nix index ead47e6..ce148ae 100644 --- a/pkgs/zyxel-bootconfig/default.nix +++ b/pkgs/zyxel-bootconfig/default.nix @@ -10,4 +10,7 @@ stdenv.mkDerivation { mkdir -p $out/bin install -Dm644 zyxel-bootconfig $out/bin/zyxel-bootconfig ''; + meta = { + mainProgram = "zyxel-bootconfig"; + }; }