commit 422f3edab1136871f79b8579643fe0fb922fac3a
parent c14b2f6356b7718742cc66f0e5b494288627ef54
Author: Raito Bezarius <masterancpp@gmail.com>
Date: Sun, 18 Feb 2024 22:35:27 +0100
modules/zyxel-dual-image: init
This adds a simple boot blessing module, to be used, with the Zyxel NWA50AX.
There's a lot of elephant in the rooms: how do you upgrade kernel, etc.
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
Diffstat:
4 files changed, 107 insertions(+), 0 deletions(-)
diff --git 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
@@ -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 <image index>` 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
@@ -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
@@ -10,4 +10,7 @@ stdenv.mkDerivation {
mkdir -p $out/bin
install -Dm644 zyxel-bootconfig $out/bin/zyxel-bootconfig
'';
+ meta = {
+ mainProgram = "zyxel-bootconfig";
+ };
}