diff --git a/.gitignore b/.gitignore index c1aaccb..aa6080f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ _build *-secrets.nix examples/static-leases.nix /doc/hardware.rst +.ccls-cache diff --git a/default.nix b/default.nix index 13d4148..7faea98 100644 --- a/default.nix +++ b/default.nix @@ -4,7 +4,6 @@ , liminix-config ? , nixpkgs ? , borderVmConf ? ./bordervm.conf.nix -, imageType ? "primary" }: let @@ -14,8 +13,7 @@ let config = { allowUnsupportedSystem = true; # mipsel permittedInsecurePackages = [ - "python-2.7.18.6" # kernel backports needs python <3 - "python-2.7.18.7" + "python-2.7.18.8" # kernel backports needs python <3 ]; }; }); @@ -35,9 +33,6 @@ let ./modules/s6 ./modules/users.nix ./modules/outputs.nix - { - boot.imageType = imageType; - } ]; }; config = eval.config; @@ -65,6 +60,13 @@ in { # cross-compiling nix-shell for any package we're customizing inherit pkgs; + deployEnv = pkgs.mkShell { + packages = with pkgs.pkgsBuildBuild; [ + tufted + min-copy-closure + ]; + }; + buildEnv = pkgs.mkShell { packages = with pkgs.pkgsBuildBuild; [ tufted diff --git a/devices/zyxel-nwa50ax/b_image/mt7621_zyxel_nwa-ax-for-ab.dtsi b/devices/zyxel-nwa50ax/b_image/mt7621_zyxel_nwa-ax-for-ab.dtsi deleted file mode 100644 index c35d362..0000000 --- a/devices/zyxel-nwa50ax/b_image/mt7621_zyxel_nwa-ax-for-ab.dtsi +++ /dev/null @@ -1,155 +0,0 @@ -#include "mt7621.dtsi" - -#include -#include - -/ { - aliases { - label-mac-device = &gmac0; - }; -}; - -&nand { - status = "okay"; - - mediatek,nmbm; - mediatek,bmt-max-ratio = <15>; - mediatek,bmt-max-reserved-blocks = <64>; - mediatek,bmt-remap-range = - <0x0 0x980000>, - <0x2980000 0x7800000>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "u-boot"; - reg = <0x0 0x80000>; - read-only; - }; - - partition@80000 { - label = "u-boot-env"; - reg = <0x80000 0x80000>; - read-only; - }; - - factory: partition@100000 { - label = "factory"; - reg = <0x100000 0x80000>; - read-only; - }; - - partition@2980000 { - label = "firmware_b"; - reg = <0x2980000 0x2800000>; - - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "kernel_b"; - reg = <0x0 0x800000>; - }; - - partition@400000 { - label = "ubi"; - reg = <0x800000 0x2000000>; - }; - }; - - partition@180000 { - label = "firmware_a"; - reg = <0x180000 0x2800000>; - - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "kernel_a"; - reg = <0x0 0x800000>; - }; - - partition@400000 { - label = "ubi_a"; - reg = <0x800000 0x2000000>; - }; - }; - - partition@5180000 { - label = "rootfs_data"; - reg = <0x5180000 0x1400000>; - }; - - partition@6580000 { - label = "logs"; - reg = <0x6580000 0xd00000>; - }; - - partition@7280000 { - label = "vendor-myzyxel"; - reg = <0x7280000 0x480000>; - read-only; - }; - - partition@7700000 { - label = "bootconfig"; - reg = <0x7700000 0x80000>; - }; - - mrd: partition@7780000 { - label = "mrd"; - reg = <0x7780000 0x80000>; - read-only; - - nvmem-layout { - compatible = "fixed-layout"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_mrd_1fff8: macaddr@1fff8 { - reg = <0x1fff8 0x6>; - }; - }; - }; - }; -}; - -&pcie { - status = "okay"; -}; - -&pcie1 { - wlan_5g: wifi@0,0 { - reg = <0x0 0 0 0 0>; - compatible = "mediatek,mt76"; - - mediatek,mtd-eeprom = <&factory 0x0>; - /* MAC-Address set in userspace */ - }; -}; - -&gmac0 { - nvmem-cells = <&macaddr_mrd_1fff8>; - nvmem-cell-names = "mac-address"; -}; - -&switch0 { - ports { - port@4 { - status = "okay"; - label = "lan"; - }; - }; -}; - -&state_default { - gpio { - groups = "uart3"; - function = "gpio"; - }; -}; diff --git a/devices/zyxel-nwa50ax/default.nix b/devices/zyxel-nwa50ax/default.nix index 23a394a..e58430f 100644 --- a/devices/zyxel-nwa50ax/default.nix +++ b/devices/zyxel-nwa50ax/default.nix @@ -91,7 +91,6 @@ - Mount the logs partition, mount / as overlayfs of firmware ? rootfs and rootfs_data for extended data. - Jitter-based entropy injection? Device can be slow to initialize its CRNG and hostapd will reject few clients at the start because of that. - Defaults for hostapd based on MT7915 capabilities? See the example for one possible list. - - Remove primary/secondary hack and put it in preinit. - Offer ways to reflash the *bootloader* itself to support direct boot via UBI and kernel upgrades via filesystem rewrite. Vendor web page: https://www.zyxel.com/fr/fr/products/wireless/ax1800-wifi-6-dual-radio-nebulaflex-access-point-nwa50ax @@ -171,6 +170,8 @@ maxLEBcount = "256"; }; + flash.eraseBlockSize = 65536; + # This is a FIT containing a kernel padded and # a UBI volume rootfs. defaultOutput = "zyxel-nwa-fit"; @@ -181,22 +182,18 @@ alignment = 2048; rootDevice = "ubi:rootfs"; + alternativeRootDevice = "ubi:rootfs"; + + # Auto-attach MTD devices: ubi_a then ubi_b. + ubi.mtds = [ "ubi_a" "ubi_b" ]; dts = { - # Actually, this is not what we want. - # This DTS is insufficient. - src = ./mt7621_zyxel_nwa50ax.dtsi; + # Strong DTB assumptions: + # ubi_a and ubi_b are two MTD devices. + # If those changes, disaster will occur. + src = ./dtb/mt7621_zyxel_nwa50ax.dtsi; includes = [ - # Here's one weird trick to make `ubi` detection - # out of the box. - # We will write ubi on /dev/firmware_a:rootfs location - # and same for /dev/firmware_b:rootfs. - # How do we distinguish both? - # We can just use the DTS to point ubi at A or B. - # This, unfortunately, means that we have "two images". - # But they are really just 1 image with 2 different DTS. - # TODO: improve this hack in preinit? - (if config.boot.imageType == "primary" then "${./a_image}" else "${./b_image}") + "${./dtb}" "${openwrt.src}/target/linux/ramips/dts" ]; }; diff --git a/devices/zyxel-nwa50ax/a_image/mt7621_zyxel_nwa-ax-for-ab.dtsi b/devices/zyxel-nwa50ax/dtb/mt7621_zyxel_nwa-ax-for-ab.dtsi similarity index 99% rename from devices/zyxel-nwa50ax/a_image/mt7621_zyxel_nwa-ax-for-ab.dtsi rename to devices/zyxel-nwa50ax/dtb/mt7621_zyxel_nwa-ax-for-ab.dtsi index 6c75b0b..519452f 100644 --- a/devices/zyxel-nwa50ax/a_image/mt7621_zyxel_nwa-ax-for-ab.dtsi +++ b/devices/zyxel-nwa50ax/dtb/mt7621_zyxel_nwa-ax-for-ab.dtsi @@ -56,7 +56,7 @@ }; partition@400000 { - label = "ubi"; + label = "ubi_a"; reg = <0x800000 0x2000000>; }; }; diff --git a/devices/zyxel-nwa50ax/mt7621_zyxel_nwa50ax.dtsi b/devices/zyxel-nwa50ax/dtb/mt7621_zyxel_nwa50ax.dtsi similarity index 100% rename from devices/zyxel-nwa50ax/mt7621_zyxel_nwa50ax.dtsi rename to devices/zyxel-nwa50ax/dtb/mt7621_zyxel_nwa50ax.dtsi diff --git a/modules/base.nix b/modules/base.nix index 4d3d8e4..3a3cade 100644 --- a/modules/base.nix +++ b/modules/base.nix @@ -67,10 +67,6 @@ in { default = "bootargs"; description = "Kernel command line's devicetree node"; }; - imageType = mkOption { - type = types.enum [ "primary" "secondary" ]; - default = "primary"; - }; imageFormat = mkOption { type = types.enum ["fit" "uimage"]; default = "uimage"; @@ -113,7 +109,10 @@ in { "root=${config.hardware.rootDevice}" "rootfstype=${config.rootfsType}" "fw_devlink=off" - ] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}"; + ] + ++ (map (mtd: "ubi.mtd=${mtd}") config.hardware.ubi.mtds) + ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}" + ++ lib.optional (config.hardware.alternativeRootDevice != null) "altroot=${config.hardware.alternativeRootDevice}"; system.callService = path : parameters : let diff --git a/modules/hardware.nix b/modules/hardware.nix index 819bbb0..d31a198 100644 --- a/modules/hardware.nix +++ b/modules/hardware.nix @@ -82,6 +82,18 @@ in { type = types.str; example = "/dev/mtdblock3"; }; + alternativeRootDevice = mkOption { + description = "Full path to alternative preferred root device (the B partition of your rootfs)"; + type = types.nullOr types.str; + example = "ubi_b:rootfs"; + default = null; + }; + ubi.mtds = mkOption { + description = "List of MTD device to attach"; + type = types.listOf types.str; + example = [ "ubi_a" "ubi_b" "data" ]; + default = null; + }; networkInterfaces = mkOption { type = types.attrsOf types.anything; }; diff --git a/modules/outputs/zyxel-nwa-fit.nix b/modules/outputs/zyxel-nwa-fit.nix index a68dd39..d6f894e 100644 --- a/modules/outputs/zyxel-nwa-fit.nix +++ b/modules/outputs/zyxel-nwa-fit.nix @@ -62,7 +62,7 @@ on a system with pre-existing firmware and OS. }; ''; in - pkgs.runCommand "zyxel-nwa-fit-${config.boot.imageType}" { + pkgs.runCommand "zyxel-nwa-fit" { nativeBuildInputs = [ pkgs.pkgsBuildBuild.ubootTools pkgs.pkgsBuildBuild.dtc ]; } '' mkimage -f ${dts} $out diff --git a/pkgs/preinit/opts.h b/pkgs/preinit/opts.h index d2b9eac..65c4fba 100644 --- a/pkgs/preinit/opts.h +++ b/pkgs/preinit/opts.h @@ -1,5 +1,6 @@ struct root_opts { char *device; + char *altdevice; /* For A/B schemas */ char *fstype; char *mount_opts; }; diff --git a/pkgs/preinit/parseopts.c b/pkgs/preinit/parseopts.c index 4aff14b..1803341 100644 --- a/pkgs/preinit/parseopts.c +++ b/pkgs/preinit/parseopts.c @@ -70,6 +70,7 @@ void parseopts(char * cmdline, struct root_opts *opts) { p = eat_param(p, "root=", &(opts->device)); p = eat_param(p, "rootfstype=", &(opts->fstype)); p = eat_param(p, "rootflags=", &(opts->mount_opts)); + p = eat_param(p, "altroot=", &(opts->altdevice)); }; } @@ -102,6 +103,14 @@ int main() expect_equal(opts.fstype, "ubifs"); expect_equal(opts.mount_opts, "subvol=1"); + // finds altroot= options + buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs rootflags=subvol=1 fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0 altroot=/dev/mtdblock6 foo"); + memset(&opts, '\0', sizeof opts); parseopts(buf, &opts); + expect_equal(opts.device, "/dev/mtdblock0"); + expect_equal(opts.altdevice, "/dev/mtdblock6"); + expect_equal(opts.fstype, "ubifs"); + expect_equal(opts.mount_opts, "subvol=1"); + // in case of duplicates, chooses the latter // also: works if the option is end of string buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0"); @@ -134,13 +143,15 @@ int main() if(opts.fstype) die("expected null rootfstype, got \"%s\"", opts.fstype); if(opts.device) die("expected null root, got \"%s\"", opts.device); if(opts.mount_opts) die("expected null mount_opts, got \"%s\"", opts.mount_opts); + if(opts.altdevice) die("expected null altdevice, got \"%s\"", opts.altdevice); // provides empty strings for empty options - buf = strdup("liminix rootfstype= fw_devlink=off root= /dev/hda1"); + buf = strdup("liminix rootfstype= fw_devlink=off root= altroot= /dev/hda1"); memset(&opts, '\0', sizeof opts); parseopts(buf, &opts); if(strlen(opts.fstype)) die("expected empty rootfstype, got \"%s\"", opts.fstype); - if(strlen(opts.device)) die("expected null root, got \"%s\"", opts.device); + if(strlen(opts.device)) die("expected empty root, got \"%s\"", opts.device); + if(strlen(opts.altdevice)) die("expected empty altroot, got \"%s\"", opts.altdevice); expect_equal("01", pr_u32(1)); expect_equal("ab", pr_u32(0xab)); diff --git a/pkgs/preinit/preinit.c b/pkgs/preinit/preinit.c index 076ada5..7dd896b 100644 --- a/pkgs/preinit/preinit.c +++ b/pkgs/preinit/preinit.c @@ -13,7 +13,7 @@ #include "opts.h" #define ERR(x) write(2, x, strlen(x)) -#define AVER(c) do { if(c < 0) { ERR("failed: " #c ": error=0x" ); pr_u32(errno); ERR("\n"); } } while(0) +#define AVER(c) do { if(c < 0) { ERR("failed: " #c ": error=0x" ); pr_u32(errno); ERR ( " - "); ERR(strerror(errno)); ERR("\n"); } } while(0) char * pr_u32(int32_t input); @@ -88,8 +88,21 @@ int main(int argc, char *argv[], char *envp[]) write(1, ", opts=", 7); write(1, opts.mount_opts, strlen(opts.mount_opts)); } + if(opts.altdevice) { + write(1, ", altdevice=", 12); + write(1, opts.altdevice, strlen(opts.altdevice)); + } write(1, ")\n", 2); - AVER(mount(opts.device, "/target/persist", opts.fstype, 0, opts.mount_opts)); + + if(!opts.altdevice) { + AVER(mount(opts.device, "/target/persist", opts.fstype, 0, opts.mount_opts)); + } else { + if(mount(opts.device, "/target/persist", opts.fstype, 0, opts.mount_opts) < 0) { + AVER(mount(opts.altdevice, "/target/persist", opts.fstype, 0, opts.mount_opts)); + } + } + + // FUTUREWORK: any failure using `opts.device` should force us to consider rerunning this with the alternative rootfs. AVER(mount("/target/persist/nix", "/target/nix", "bind", MS_BIND, NULL)); @@ -102,6 +115,7 @@ int main(int argc, char *argv[], char *envp[]) argv[0] = "init"; argv[1] = NULL; + AVER(execve("/persist/init", argv, envp)); } die();