Merge remote-tracking branch 'raito/nwa50ax'
This commit is contained in:
commit
f9ab0590a6
19 changed files with 1172 additions and 23 deletions
|
@ -3,6 +3,7 @@
|
||||||
, liminix-config ? <liminix-config>
|
, liminix-config ? <liminix-config>
|
||||||
, nixpkgs ? <nixpkgs>
|
, nixpkgs ? <nixpkgs>
|
||||||
, borderVmConf ? ./bordervm.conf.nix
|
, borderVmConf ? ./bordervm.conf.nix
|
||||||
|
, imageType ? "primary"
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -30,6 +31,9 @@ let
|
||||||
./modules/s6
|
./modules/s6
|
||||||
./modules/users.nix
|
./modules/users.nix
|
||||||
./modules/outputs.nix
|
./modules/outputs.nix
|
||||||
|
{
|
||||||
|
boot.imageType = imageType;
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
config = eval.config;
|
config = eval.config;
|
||||||
|
|
155
devices/zyxel-nwa50ax/a_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal file
155
devices/zyxel-nwa50ax/a_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#include "mt7621.dtsi"
|
||||||
|
|
||||||
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
#include <dt-bindings/input/input.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
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@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";
|
||||||
|
reg = <0x800000 0x2000000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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_b";
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
155
devices/zyxel-nwa50ax/b_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal file
155
devices/zyxel-nwa50ax/b_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#include "mt7621.dtsi"
|
||||||
|
|
||||||
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
#include <dt-bindings/input/input.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
367
devices/zyxel-nwa50ax/default.nix
Normal file
367
devices/zyxel-nwa50ax/default.nix
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
{
|
||||||
|
system = {
|
||||||
|
crossSystem = {
|
||||||
|
config = "mipsel-unknown-linux-musl";
|
||||||
|
gcc = {
|
||||||
|
abi = "32";
|
||||||
|
arch = "mips32"; # mips32r2?
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
description = ''
|
||||||
|
Zyxel NWA50AX
|
||||||
|
********************
|
||||||
|
|
||||||
|
Zyxel NWA50AX is quite close to the GL-MT300N-v2 "Mango" device, but it is based on the MT7621
|
||||||
|
chipset instead of the MT7628.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
This device is pretty, but, due to its A/B capabilities, can be a bit hard
|
||||||
|
to use completely.
|
||||||
|
|
||||||
|
The stock vendor firmware is a downstream fork of U-Boot: <https://github.com/RaitoBezarius/uboot-nwa50ax>
|
||||||
|
with restricted boot commands. Fortunately, OpenWrt folks figured out trivial command injections,
|
||||||
|
so you can use most of the OpenWrt commands without trouble by just command injecting
|
||||||
|
atns, atna or atnf, e.g. atns "; $real_command".
|
||||||
|
|
||||||
|
From factory web UI, you can upload the result of the zyxel-nwa-fit output.
|
||||||
|
From another operating system, you need to `dumpimage -T flat_dt -p 0 $zyxel-nwa-fit -o firmware.bin`,
|
||||||
|
`flash_erase $(mtd partition of the target partition firmware or zy_firmware) 0 0`, then you complete by
|
||||||
|
`nandwrite -p $(mtd partition of the target partition firmware or zy_firmware) firmware.bin`.
|
||||||
|
|
||||||
|
How to put the firmware.bin on the machine is left to you as an exercise, e.g. SSH, TFTP, whatever.
|
||||||
|
|
||||||
|
From serial, you have two choices:
|
||||||
|
|
||||||
|
- Flash this system via U-Boot:
|
||||||
|
same reasoning as from an existing Linux system, two choices:
|
||||||
|
- ymodem the binary, perform the write manually, you can inspire yourself
|
||||||
|
from the `script` contained in the vendor firmware, those are just a FIT containing a script.
|
||||||
|
- prepare a FIT containing a script executing your commands, tftpboot this.
|
||||||
|
|
||||||
|
- boot from an existing Liminix system, e.g. TFTPBOOT image.
|
||||||
|
- boot from an OpenWrt system, i.e. follow OpenWrt steps.
|
||||||
|
|
||||||
|
Once you are in a Linux system, understand that this device has A/B boot.
|
||||||
|
|
||||||
|
OpenWrt provides you with `zyxel-bootconfig` to set/unset the image status and choice.
|
||||||
|
|
||||||
|
The kernel is booted with `bootImage=<number>` which tells you which slot are you on.
|
||||||
|
|
||||||
|
You should find yourself with 10ish MTD partitions, the most interesting ones are two:
|
||||||
|
|
||||||
|
- firmware: 40MB
|
||||||
|
- firmware_1: 40MB
|
||||||
|
|
||||||
|
In the current setup, they are split further into kernel (8MB) and ubi (32MB).
|
||||||
|
|
||||||
|
Once you are done with first installation, note that if you want to use the A/B feature,
|
||||||
|
you need to write a _secondary_ image on the slot B. There is no proper flashing code
|
||||||
|
that will set the being-updated slot to `new` and boot on it to verify if it's working.
|
||||||
|
This is a WIP.
|
||||||
|
|
||||||
|
Upgrading your system can be achieved via:
|
||||||
|
|
||||||
|
- `liminix-rebuild` for the userspace.
|
||||||
|
- `flash_erase` + `nandwrite` for the kernelspace to the other slot than the one you are booted on,
|
||||||
|
note that you can just nandwrite the mtd partition corresponding to the *kernel* and not the whole firmware.
|
||||||
|
|
||||||
|
If you soft-bricked your AP, i.e. you cannot boot anything in U-Boot, no worries, just plug the serial console,
|
||||||
|
prepare a TFTP server (via `tufted` for example), download vendor firmware, set up `atns`, `atnf`, etc. and run `atnz`.
|
||||||
|
|
||||||
|
This will reflash everything back to normal via TFTP.
|
||||||
|
|
||||||
|
If you hard-bricked your AP, i.e. U-Boot is telling you to transfer a valid bootloader via ymodem, just extract
|
||||||
|
a U-Boot from the vendor OS, send it via ymodem and use the previous operations to perform a full flash this time
|
||||||
|
of all partitions.
|
||||||
|
|
||||||
|
Note that if you erased your MRD partition, you lost your serial and MAC address. There's no way to recover the original one
|
||||||
|
except by reading the physical label on your… device!
|
||||||
|
|
||||||
|
If you super-hard-bricked your AP, i.e. no output on serial console, congratulations, you reached one of the rare state
|
||||||
|
of this device. You need an external NAND flasher to repair it and write the first stage from Mediatek to continue the previous
|
||||||
|
recovery operations.
|
||||||
|
|
||||||
|
Development TODO list:
|
||||||
|
|
||||||
|
- Better support for upgrade automation w.r.t. to A/B, e.g. automagic scripts.
|
||||||
|
- 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
|
||||||
|
|
||||||
|
OpenWrt web page: https://openwrt.org/inbox/toh/zyxel/nwa50ax
|
||||||
|
OpenWrt tech data: https://openwrt.org/toh/hwdata/zyxel/zyxel_nwa50ax
|
||||||
|
|
||||||
|
'';
|
||||||
|
|
||||||
|
module = { pkgs, config, lib, lim, ...}:
|
||||||
|
let
|
||||||
|
inherit (pkgs.liminix.networking) interface;
|
||||||
|
inherit (pkgs.liminix.services) oneshot;
|
||||||
|
inherit (pkgs.pseudofile) dir symlink;
|
||||||
|
inherit (pkgs) openwrt;
|
||||||
|
|
||||||
|
mac80211 = pkgs.mac80211.override {
|
||||||
|
drivers = [ "mt7915e" ];
|
||||||
|
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||||
|
};
|
||||||
|
# v204520220929
|
||||||
|
wlan_firmware = pkgs.fetchurl {
|
||||||
|
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wa.bin";
|
||||||
|
hash = "sha256-wooyefzb0i8640+lwq3vNhcBXRFCtGuo+jiL7afZaKA=";
|
||||||
|
};
|
||||||
|
wlan_firmware' = pkgs.fetchurl {
|
||||||
|
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wm.bin";
|
||||||
|
hash = "sha256-k62nQewRuKjBLd5R3RxU4F74YKnQx5zr6gqMMImqVQw=";
|
||||||
|
};
|
||||||
|
wlan_firmware'' = pkgs.fetchurl {
|
||||||
|
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_rom_patch.bin";
|
||||||
|
hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg=";
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
imports = [
|
||||||
|
# We include it to ensure the bridge functionality
|
||||||
|
# is available on the target kernel.
|
||||||
|
../../modules/bridge
|
||||||
|
../../modules/arch/mipsel.nix
|
||||||
|
../../modules/outputs/tftpboot.nix
|
||||||
|
../../modules/outputs/zyxel-nwa-fit.nix
|
||||||
|
../../modules/zyxel-dual-image
|
||||||
|
];
|
||||||
|
|
||||||
|
filesystem = dir {
|
||||||
|
lib = dir {
|
||||||
|
firmware = dir {
|
||||||
|
mediatek = dir {
|
||||||
|
"mt7915_wa.bin" = symlink wlan_firmware;
|
||||||
|
"mt7915_wm.bin" = symlink wlan_firmware';
|
||||||
|
"mt7915_rom_patch.bin" = symlink wlan_firmware'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
rootfsType = "ubifs";
|
||||||
|
hardware = {
|
||||||
|
# Taken from OpenWRT
|
||||||
|
# root@OpenWrt:/# ubinfo /dev/ubi0
|
||||||
|
# ubi0
|
||||||
|
# Volumes count: 2
|
||||||
|
# Logical eraseblock size: 126976 bytes, 124.0 KiB
|
||||||
|
# Total amount of logical eraseblocks: 256 (32505856 bytes, 31.0 MiB)
|
||||||
|
# Amount of available logical eraseblocks: 0 (0 bytes)
|
||||||
|
# Maximum count of volumes 128
|
||||||
|
# Count of bad physical eraseblocks: 0
|
||||||
|
# Count of reserved physical eraseblocks: 19
|
||||||
|
# Current maximum erase counter value: 2
|
||||||
|
# Minimum input/output unit size: 2048 bytes
|
||||||
|
# Character device major/minor: 250:0
|
||||||
|
# Present volumes: 0, 1
|
||||||
|
ubi = {
|
||||||
|
minIOSize = "2048";
|
||||||
|
logicalEraseBlockSize = "126976";
|
||||||
|
physicalEraseBlockSize = "128KiB";
|
||||||
|
maxLEBcount = "256";
|
||||||
|
};
|
||||||
|
|
||||||
|
# This is a FIT containing a kernel padded and
|
||||||
|
# a UBI volume rootfs.
|
||||||
|
defaultOutput = "zyxel-nwa-fit";
|
||||||
|
|
||||||
|
loadAddress = lim.parseInt "0x80001000";
|
||||||
|
entryPoint = lim.parseInt "0x80001000";
|
||||||
|
# Aligned on 2kb.
|
||||||
|
alignment = 2048;
|
||||||
|
|
||||||
|
rootDevice = "ubi:rootfs";
|
||||||
|
|
||||||
|
dts = {
|
||||||
|
# Actually, this is not what we want.
|
||||||
|
# This DTS is insufficient.
|
||||||
|
src = ./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}")
|
||||||
|
"${openwrt.src}/target/linux/ramips/dts"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
networkInterfaces =
|
||||||
|
let
|
||||||
|
inherit (config.system.service.network) link;
|
||||||
|
in {
|
||||||
|
eth = link.build { ifname = "eth0"; };
|
||||||
|
lan = link.build { ifname = "lan"; };
|
||||||
|
wlan0 = link.build {
|
||||||
|
ifname = "wlan0";
|
||||||
|
dependencies = [ mac80211 ];
|
||||||
|
};
|
||||||
|
wlan1 = link.build {
|
||||||
|
ifname = "wlan1";
|
||||||
|
dependencies = [ mac80211 ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
boot = {
|
||||||
|
# Critical because NWA50AX will extend your cmdline with the image number booted.
|
||||||
|
# and some bootloader version.
|
||||||
|
# You don't want to find yourself being overridden.
|
||||||
|
commandLineDtbNode = "bootargs-override";
|
||||||
|
|
||||||
|
imageFormat = "fit";
|
||||||
|
tftp = {
|
||||||
|
# 5MB is nice.
|
||||||
|
freeSpaceBytes = 5 * 1024 * 1024;
|
||||||
|
loadAddress = lim.parseInt "0x2000000";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Dual image management service in userspace.
|
||||||
|
services.zyxel-dual-image = config.boot.zyxel-dual-image.build {
|
||||||
|
ensureActiveImage = "primary";
|
||||||
|
# TODO: use mtd names rather…
|
||||||
|
# primary and secondary are always /dev/mtd3 by virtue of the
|
||||||
|
# dtb being not too wrong…
|
||||||
|
# TODO: remove this hack.
|
||||||
|
primaryMtdPartition = "/dev/mtd3";
|
||||||
|
secondaryMtdPartition = "/dev/mtd3";
|
||||||
|
bootConfigurationMtdPartition = "/dev/mtd12";
|
||||||
|
};
|
||||||
|
|
||||||
|
# DEVICE_VENDOR := ZyXEL
|
||||||
|
# KERNEL_SIZE := 8192k
|
||||||
|
# DEVICE_PACKAGES := kmod-mt7915-firmware zyxel-bootconfig
|
||||||
|
# KERNEL := kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb
|
||||||
|
# IMAGES += factory.bin ramboot-factory.bin
|
||||||
|
# IMAGE/factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi | zyxel-nwa-fit
|
||||||
|
# IMAGE/ramboot-factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi
|
||||||
|
|
||||||
|
kernel = {
|
||||||
|
src = pkgs.fetchurl {
|
||||||
|
name = "linux.tar.gz";
|
||||||
|
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||||
|
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||||
|
};
|
||||||
|
extraPatchPhase = ''
|
||||||
|
${openwrt.applyPatches.ramips}
|
||||||
|
|
||||||
|
'';
|
||||||
|
config = {
|
||||||
|
|
||||||
|
RALINK = "y";
|
||||||
|
PCI = "y";
|
||||||
|
PHY_MT7621_PCI = "y";
|
||||||
|
PCIE_MT7621 = "y";
|
||||||
|
SOC_MT7621 = "y";
|
||||||
|
CLK_MT7621 = "y";
|
||||||
|
CLOCKSOURCE_WATCHDOG = "y";
|
||||||
|
|
||||||
|
SERIAL_8250_CONSOLE = "y";
|
||||||
|
SERIAL_8250 = "y";
|
||||||
|
SERIAL_CORE_CONSOLE = "y";
|
||||||
|
SERIAL_OF_PLATFORM = "y";
|
||||||
|
SERIAL_8250_NR_UARTS = "3";
|
||||||
|
SERIAL_8250_RUNTIME_UARTS = "3";
|
||||||
|
SERIAL_MCTRL_GPIO = "y";
|
||||||
|
|
||||||
|
CONSOLE_LOGLEVEL_DEFAULT = "8";
|
||||||
|
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||||
|
|
||||||
|
# MTD_UBI_BEB_LIMIT = "20";
|
||||||
|
# MTD_UBI_WL_THRESHOLD = "4096";
|
||||||
|
|
||||||
|
MTD = "y";
|
||||||
|
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
|
||||||
|
MTD_RAW_NAND = "y";
|
||||||
|
MTD_NAND_MT7621 = "y";
|
||||||
|
MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table
|
||||||
|
MTD_NAND_ECC_SW_HAMMING= "y";
|
||||||
|
MTD_SPI_NAND= "y";
|
||||||
|
MTD_OF_PARTS = "y";
|
||||||
|
MTD_NAND_CORE= "y";
|
||||||
|
MTD_SPLIT_FIRMWARE= "y";
|
||||||
|
MTD_SPLIT_FIT_FW= "y";
|
||||||
|
|
||||||
|
PINCTRL = "y";
|
||||||
|
PINCTRL_MT7621 = "y";
|
||||||
|
|
||||||
|
I2C = "y";
|
||||||
|
I2C_MT7621 = "y";
|
||||||
|
|
||||||
|
SPI = "y";
|
||||||
|
MTD_SPI_NOR = "y";
|
||||||
|
SPI_MT7621 = "y";
|
||||||
|
SPI_MASTER = "y";
|
||||||
|
SPI_MEM = "y";
|
||||||
|
|
||||||
|
REGULATOR = "y";
|
||||||
|
REGULATOR_FIXED_VOLTAGE = "y";
|
||||||
|
RESET_CONTROLLER = "y";
|
||||||
|
POWER_RESET = "y";
|
||||||
|
POWER_RESET_GPIO = "y";
|
||||||
|
POWER_SUPPLY = "y";
|
||||||
|
LED_TRIGGER_PHY = "y";
|
||||||
|
|
||||||
|
PCI_DISABLE_COMMON_QUIRKS = "y";
|
||||||
|
PCI_DOMAINS = "y";
|
||||||
|
PCI_DOMAINS_GENERIC = "y";
|
||||||
|
PCI_DRIVERS_GENERIC = "y";
|
||||||
|
PCS_MTK_LYNXI = "y";
|
||||||
|
|
||||||
|
SOC_BUS = "y";
|
||||||
|
|
||||||
|
NET = "y";
|
||||||
|
ETHERNET = "y";
|
||||||
|
WLAN = "y";
|
||||||
|
|
||||||
|
PHYLIB = "y";
|
||||||
|
AT803X_PHY = "y";
|
||||||
|
FIXED_PHY = "y";
|
||||||
|
GENERIC_PHY = "y";
|
||||||
|
NET_DSA = "y";
|
||||||
|
NET_DSA_MT7530 = "y";
|
||||||
|
NET_DSA_MT7530_MDIO = "y";
|
||||||
|
NET_DSA_TAG_MTK = "y";
|
||||||
|
NET_MEDIATEK_SOC = "y";
|
||||||
|
NET_SWITCHDEV = "y";
|
||||||
|
NET_VENDOR_MEDIATEK = "y";
|
||||||
|
|
||||||
|
SWPHY = "y";
|
||||||
|
|
||||||
|
GPIOLIB = "y";
|
||||||
|
GPIO_MT7621 = "y";
|
||||||
|
OF_GPIO = "y";
|
||||||
|
|
||||||
|
EARLY_PRINTK = "y";
|
||||||
|
|
||||||
|
NEW_LEDS = "y";
|
||||||
|
LEDS_TRIGGERS = "y";
|
||||||
|
LEDS_CLASS = "y"; # required by rt2x00lib
|
||||||
|
LEDS_CLASS_MULTICOLOR = "y";
|
||||||
|
LEDS_BRIGHTNESS_HW_CHANGED = "y";
|
||||||
|
|
||||||
|
PRINTK_TIME = "y";
|
||||||
|
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||||
|
SWCONFIG = "y";
|
||||||
|
} // lib.optionalAttrs (config.system.service ? watchdog) {
|
||||||
|
RALINK_WDT = "y"; # watchdog
|
||||||
|
MT7621_WDT = "y"; # or it might be this one
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
56
devices/zyxel-nwa50ax/mt7621_zyxel_nwa50ax.dtsi
Normal file
56
devices/zyxel-nwa50ax/mt7621_zyxel_nwa50ax.dtsi
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "mt7621_zyxel_nwa-ax-for-ab.dtsi"
|
||||||
|
|
||||||
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
#include <dt-bindings/input/input.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
compatible = "zyxel,nwa50ax", "mediatek,mt7621-soc";
|
||||||
|
model = "ZyXEL NWA50AX";
|
||||||
|
|
||||||
|
aliases {
|
||||||
|
led-boot = &led_system_green;
|
||||||
|
led-failsafe = &led_system_red;
|
||||||
|
led-running = &led_system_green;
|
||||||
|
led-upgrade = &led_system_red;
|
||||||
|
};
|
||||||
|
|
||||||
|
leds {
|
||||||
|
compatible = "gpio-leds";
|
||||||
|
|
||||||
|
led_system_red: system_red {
|
||||||
|
label = "red:system";
|
||||||
|
gpios = <&gpio 6 GPIO_ACTIVE_HIGH>;
|
||||||
|
};
|
||||||
|
|
||||||
|
led_system_green: system_green {
|
||||||
|
label = "green:system";
|
||||||
|
gpios = <&gpio 7 GPIO_ACTIVE_HIGH>;
|
||||||
|
};
|
||||||
|
|
||||||
|
system_blue {
|
||||||
|
label = "blue:system";
|
||||||
|
gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
keys {
|
||||||
|
compatible = "gpio-keys";
|
||||||
|
|
||||||
|
reset {
|
||||||
|
label = "reset";
|
||||||
|
gpios = <&gpio 30 GPIO_ACTIVE_LOW>;
|
||||||
|
linux,code = <KEY_RESTART>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ðernet {
|
||||||
|
pinctrl-0 = <&mdio_pins>, <&rgmii1_pins>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&state_default {
|
||||||
|
gpio {
|
||||||
|
groups = "uart3", "rgmii2";
|
||||||
|
function = "gpio";
|
||||||
|
};
|
||||||
|
};
|
120
examples/nwa50ax-ap.nix
Normal file
120
examples/nwa50ax-ap.nix
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
{ config, pkgs, ... } :
|
||||||
|
let
|
||||||
|
inherit (pkgs.liminix.services) oneshot longrun bundle target;
|
||||||
|
inherit (pkgs) writeText;
|
||||||
|
svc = config.system.service;
|
||||||
|
secrets-1 = {
|
||||||
|
ssid = "Zyxel 2G (N)";
|
||||||
|
wpa_passphrase = "diamond dogs";
|
||||||
|
};
|
||||||
|
secrets-2 = {
|
||||||
|
ssid = "Zyxel 5G (AX)";
|
||||||
|
wpa_passphrase = "diamond dogs";
|
||||||
|
};
|
||||||
|
baseParams = {
|
||||||
|
country_code = "FR";
|
||||||
|
hw_mode = "g";
|
||||||
|
channel = 6;
|
||||||
|
wmm_enabled = 1;
|
||||||
|
ieee80211n = 1;
|
||||||
|
ht_capab = "[LDPC][GF][HT40-][HT40+][SHORT-GI-40][MAX-AMSDU-7935][TX-STBC]";
|
||||||
|
auth_algs = 1;
|
||||||
|
wpa = 2;
|
||||||
|
wpa_key_mgmt = "WPA-PSK";
|
||||||
|
wpa_pairwise = "TKIP CCMP";
|
||||||
|
rsn_pairwise = "CCMP";
|
||||||
|
};
|
||||||
|
|
||||||
|
modernParams = {
|
||||||
|
hw_mode = "a";
|
||||||
|
he_su_beamformer = 1;
|
||||||
|
he_su_beamformee = 1;
|
||||||
|
he_mu_beamformer = 1;
|
||||||
|
preamble = 1;
|
||||||
|
# Allow radar detection.
|
||||||
|
ieee80211d = 1;
|
||||||
|
ieee80211h = 1;
|
||||||
|
ieee80211ac = 1;
|
||||||
|
ieee80211ax = 1;
|
||||||
|
vht_capab = "[MAX-MPDU-7991][SU-BEAMFORMEE][SU-BEAMFORMER][RXLDPC][SHORT-GI-80][MAX-A-MPDU-LEN-EXP3][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN][TX-STBC-2BY1][RX-STBC-1][MU-BEAMFORMER]";
|
||||||
|
vht_oper_chwidth = 1;
|
||||||
|
he_oper_chwidth = 1;
|
||||||
|
channel = 36;
|
||||||
|
vht_oper_centr_freq_seg0_idx = 42;
|
||||||
|
he_oper_centr_freq_seg0_idx = 42;
|
||||||
|
require_vht = 1;
|
||||||
|
};
|
||||||
|
mkWifiSta = params: interface: secrets: svc.hostapd.build {
|
||||||
|
inherit interface;
|
||||||
|
params = params // {
|
||||||
|
inherit (secrets) ssid wpa_passphrase;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in rec {
|
||||||
|
imports = [
|
||||||
|
../modules/wlan.nix
|
||||||
|
../modules/network
|
||||||
|
../modules/hostapd
|
||||||
|
../modules/ssh
|
||||||
|
../modules/ntp
|
||||||
|
../modules/vlan
|
||||||
|
../modules/bridge
|
||||||
|
];
|
||||||
|
|
||||||
|
hostname = "zyxel";
|
||||||
|
|
||||||
|
users.root = {
|
||||||
|
# EDIT: choose a root password and then use
|
||||||
|
# "mkpasswd -m sha512crypt" to determine the hash.
|
||||||
|
# It should start wirh $6$.
|
||||||
|
passwd = "$y$j9T$f8GhLiqYmr3lc58eKhgyD0$z7P/7S9u.kq/cANZExxhS98bze/6i7aBxU6tbl7RMi.";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
# EDIT: you can add your ssh pubkey here
|
||||||
|
# "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.int = svc.bridge.primary.build {
|
||||||
|
ifname = "int";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.bridge = svc.bridge.members.build {
|
||||||
|
primary = services.int;
|
||||||
|
members = with config.hardware.networkInterfaces; [
|
||||||
|
lan
|
||||||
|
wlan0
|
||||||
|
wlan1
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.dhcpv4 =
|
||||||
|
let iface = services.int;
|
||||||
|
in svc.network.dhcp.client.build { interface = iface; };
|
||||||
|
|
||||||
|
services.defaultroute4 = svc.network.route.build {
|
||||||
|
via = "$(output ${services.dhcpv4} address)";
|
||||||
|
target = "default";
|
||||||
|
dependencies = [ services.dhcpv4 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.packet_forwarding = svc.network.forward.build { };
|
||||||
|
services.sshd = svc.ssh.build {
|
||||||
|
allowRoot = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.ntp = config.system.service.ntp.build {
|
||||||
|
pools = { "pool.ntp.org" = ["iburst"] ; };
|
||||||
|
};
|
||||||
|
|
||||||
|
boot.tftp = {
|
||||||
|
serverip = "192.0.2.10";
|
||||||
|
ipaddr = "192.0.2.12";
|
||||||
|
};
|
||||||
|
|
||||||
|
# wlan0 is the 2.4GHz interface.
|
||||||
|
services.hostap-1 = mkWifiSta baseParams config.hardware.networkInterfaces.wlan0 secrets-1;
|
||||||
|
# wlan1 is the 5GHz interface, e.g. AX capable.
|
||||||
|
services.hostap-2 = mkWifiSta (baseParams // modernParams) config.hardware.networkInterfaces.wlan1 secrets-2;
|
||||||
|
|
||||||
|
defaultProfile.packages = with pkgs; [ zyxel-bootconfig iw min-collect-garbage mtdutils ];
|
||||||
|
}
|
|
@ -31,6 +31,8 @@
|
||||||
./ssh
|
./ssh
|
||||||
./outputs/tftpboot.nix
|
./outputs/tftpboot.nix
|
||||||
./outputs/ubifs.nix
|
./outputs/ubifs.nix
|
||||||
|
./ubifs.nix
|
||||||
|
./ubinize.nix
|
||||||
./users.nix
|
./users.nix
|
||||||
./vlan
|
./vlan
|
||||||
./watchdog
|
./watchdog
|
||||||
|
|
|
@ -58,6 +58,15 @@ in {
|
||||||
default = [];
|
default = [];
|
||||||
description = "Kernel command line";
|
description = "Kernel command line";
|
||||||
};
|
};
|
||||||
|
commandLineDtbNode = mkOption {
|
||||||
|
type = types.enum [ "bootargs" "bootargs-override" ];
|
||||||
|
default = "bootargs";
|
||||||
|
description = "Kernel command line's devicetree node";
|
||||||
|
};
|
||||||
|
imageType = mkOption {
|
||||||
|
type = types.enum [ "primary" "secondary" ];
|
||||||
|
default = "primary";
|
||||||
|
};
|
||||||
imageFormat = mkOption {
|
imageFormat = mkOption {
|
||||||
type = types.enum ["fit" "uimage"];
|
type = types.enum ["fit" "uimage"];
|
||||||
default = "uimage";
|
default = "uimage";
|
||||||
|
|
|
@ -32,23 +32,21 @@ let
|
||||||
(a: symlink "${busybox}/bin/busybox");
|
(a: symlink "${busybox}/bin/busybox");
|
||||||
minimalApplets = [
|
minimalApplets = [
|
||||||
# this is probably less minimal than it could be
|
# this is probably less minimal than it could be
|
||||||
"arch" "ash" "base64" "basename" "bc" "brctl" "bunzip2" "bzcat"
|
"arch" "ash" "base64" "basename" "bc" "brctl" "bunzip2" "bzcat" "bzip2"
|
||||||
"bzip2" "cal" "cat" "chattr" "chgrp" "chmod" "chown" "chpst"
|
"cal" "cat" "chattr" "chgrp" "chmod" "chown" "chpst" "chroot" "clear" "cmp"
|
||||||
"chroot" "clear" "cmp" "comm" "cp" "cpio" "cut" "date" "dd" "df"
|
"comm" "cp" "cpio" "cut" "date" "dhcprelay" "dd" "df" "dirname" "dmesg"
|
||||||
"dirname" "dmesg" "du" "echo" "egrep" "env" "expand" "expr"
|
"du" "echo" "egrep" "env" "expand" "expr" "false" "fdisk" "fgrep" "find"
|
||||||
"false" "fdisk" "fgrep" "find" "free" "fuser" "grep" "gunzip"
|
"free" "fuser" "grep" "gunzip" "gzip" "head" "hexdump" "hostname" "hwclock"
|
||||||
"gzip" "head" "hexdump" "hostname" "hwclock" "ifconfig" "ip"
|
"ifconfig" "ip" "ipaddr" "iplink" "ipneigh" "iproute" "iprule" "kill"
|
||||||
"ipaddr" "iplink" "ipneigh" "iproute" "iprule" "kill" "killall"
|
"killall" "killall5" "less" "ln" "ls" "lsattr" "lsof" "md5sum" "mkdir"
|
||||||
"killall5" "less" "ln" "ls" "lsattr" "lsof" "md5sum" "mkdir"
|
"mknod" "mktemp" "mount" "mv" "nc" "netstat" "nohup" "od" "pgrep" "pidof"
|
||||||
"mknod" "mktemp" "mount" "mv" "nc" "netstat" "nohup" "od" "pgrep"
|
"ping" "ping6" "pkill" "pmap" "printenv" "printf" "ps" "pwd" "readlink"
|
||||||
"pidof" "ping" "ping6" "pkill" "pmap" "printenv" "printf" "ps"
|
"realpath" "reset" "rm" "rmdir" "route" "sed" "seq" "setsid" "sha1sum"
|
||||||
"pwd" "readlink" "realpath" "reset" "rm" "rmdir" "route" "sed"
|
"sha256sum" "sha512sum" "sleep" "sort" "stat" "strings" "stty" "su" "sum"
|
||||||
"seq" "setsid" "sha1sum" "sha256sum" "sha512sum" "sleep" "sort"
|
"swapoff" "swapon" "sync" "tail" "tee" "test" "time" "touch" "tr"
|
||||||
"stat" "strings" "stty" "su" "sum" "swapoff" "swapon" "sync"
|
"traceroute" "traceroute6" "true" "truncate" "tty" "udhcpc" "umount"
|
||||||
"tail" "tee" "test" "time" "touch" "tr" "traceroute" "traceroute6"
|
"uname" "unexpand" "uniq" "unlink" "unlzma" "unxz" "unzip" "uptime" "watch"
|
||||||
"true" "truncate" "tty" "udhcpc" "umount" "uname"
|
"wc" "whoami" "xargs" "xxd" "xz" "xzcat" "yes" "zcat"
|
||||||
"unexpand" "uniq" "unlink" "unlzma" "unxz" "unzip" "uptime"
|
|
||||||
"watch" "wc" "whoami" "xargs" "xxd" "xz" "xzcat" "yes" "zcat"
|
|
||||||
];
|
];
|
||||||
in {
|
in {
|
||||||
options = {
|
options = {
|
||||||
|
|
|
@ -67,6 +67,7 @@ in {
|
||||||
};
|
};
|
||||||
loadAddress = mkOption { type = types.ints.unsigned; default = null; };
|
loadAddress = mkOption { type = types.ints.unsigned; default = null; };
|
||||||
entryPoint = mkOption { type = types.ints.unsigned; };
|
entryPoint = mkOption { type = types.ints.unsigned; };
|
||||||
|
alignment = mkOption { type = types.ints.unsigned; default = null; description = "Alignment passed to `mkimage` for FIT"; };
|
||||||
radios = mkOption {
|
radios = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Kernel modules (from mac80211 package) required for the
|
Kernel modules (from mac80211 package) required for the
|
||||||
|
|
|
@ -111,7 +111,8 @@ in
|
||||||
};
|
};
|
||||||
uimage = liminix.builders.uimage {
|
uimage = liminix.builders.uimage {
|
||||||
commandLine = concatStringsSep " " config.boot.commandLine;
|
commandLine = concatStringsSep " " config.boot.commandLine;
|
||||||
inherit (config.hardware) loadAddress entryPoint;
|
inherit (config.boot) commandLineDtbNode;
|
||||||
|
inherit (config.hardware) loadAddress entryPoint alignment;
|
||||||
inherit (config.boot) imageFormat;
|
inherit (config.boot) imageFormat;
|
||||||
inherit (o) kernel dtb;
|
inherit (o) kernel dtb;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,9 +12,16 @@ in
|
||||||
imports = [
|
imports = [
|
||||||
./initramfs.nix
|
./initramfs.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
options.system.outputs.rootubifs = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
internal = true;
|
||||||
|
};
|
||||||
|
|
||||||
options.hardware.ubi = {
|
options.hardware.ubi = {
|
||||||
minIOSize = mkOption { type = types.str; };
|
minIOSize = mkOption { type = types.str; };
|
||||||
eraseBlockSize = mkOption { type = types.str; }; # LEB
|
logicalEraseBlockSize = mkOption { type = types.str; }; # LEB
|
||||||
|
physicalEraseBlockSize = mkOption { type = types.str; }; # PEB
|
||||||
maxLEBcount = mkOption { type = types.str; }; # LEB
|
maxLEBcount = mkOption { type = types.str; }; # LEB
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,7 +33,7 @@ in
|
||||||
};
|
};
|
||||||
boot.initramfs.enable = true;
|
boot.initramfs.enable = true;
|
||||||
system.outputs = {
|
system.outputs = {
|
||||||
rootfs =
|
rootubifs =
|
||||||
let
|
let
|
||||||
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
|
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
|
||||||
cfg = config.hardware.ubi;
|
cfg = config.hardware.ubi;
|
||||||
|
@ -35,7 +42,7 @@ in
|
||||||
} ''
|
} ''
|
||||||
mkdir tmp
|
mkdir tmp
|
||||||
tree=${o.bootablerootdir}
|
tree=${o.bootablerootdir}
|
||||||
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.eraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
|
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.logicalEraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
91
modules/outputs/ubivolume.nix
Normal file
91
modules/outputs/ubivolume.nix
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
{
|
||||||
|
config
|
||||||
|
, pkgs
|
||||||
|
, lib
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (pkgs) liminix;
|
||||||
|
inherit (lib) mkIf mkOption types concatStringsSep optionalString;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./initramfs.nix
|
||||||
|
./ubifs.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
options.hardware.ubi = {
|
||||||
|
minIOSize = mkOption { type = types.str; };
|
||||||
|
eraseBlockSize = mkOption { type = types.str; }; # LEB
|
||||||
|
maxLEBcount = mkOption { type = types.str; }; # LEB
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf (config.rootfsType == "ubifs") {
|
||||||
|
kernel.config = {
|
||||||
|
MTD_UBI="y";
|
||||||
|
|
||||||
|
UBIFS_FS = "y";
|
||||||
|
UBIFS_FS_SECURITY = "n";
|
||||||
|
};
|
||||||
|
boot.initramfs.enable = true;
|
||||||
|
|
||||||
|
system.outputs.rootfs =
|
||||||
|
let
|
||||||
|
inherit (pkgs.pkgsBuildBuild) runCommand;
|
||||||
|
ubiVolume = ({ name, volumeId, image, flags ? [] }:
|
||||||
|
''
|
||||||
|
[${name}]
|
||||||
|
mode=ubi
|
||||||
|
vol_id=${toString volumeId}
|
||||||
|
vol_type=dynamic
|
||||||
|
vol_name=${name}
|
||||||
|
vol_alignment=1
|
||||||
|
${optionalString (image != null) ''
|
||||||
|
image=${image}
|
||||||
|
''}
|
||||||
|
${optionalString (image == null) ''
|
||||||
|
vol_size=1MiB
|
||||||
|
''}
|
||||||
|
${optionalString (flags != []) ''
|
||||||
|
vol_flags=${concatStringsSep "," flags}
|
||||||
|
''}
|
||||||
|
'');
|
||||||
|
|
||||||
|
ubiImage = (volumes:
|
||||||
|
let
|
||||||
|
ubinizeConfig = pkgs.writeText "ubinize.conf" (concatStringsSep "\n" volumes);
|
||||||
|
inherit (pkgs.pkgsBuildBuild) mtdutils;
|
||||||
|
in
|
||||||
|
runCommand "ubinize" {
|
||||||
|
depsBuildBuild = [ mtdutils ];
|
||||||
|
# block size := 128kb
|
||||||
|
# page size := 2048
|
||||||
|
# ubninize opts := -E 5
|
||||||
|
} ''
|
||||||
|
ubinize -Q "$SOURCE_DATE_EPOCH" -o $out \
|
||||||
|
-p ${config.hardware.ubi.physicalEraseBlockSize} -m ${config.hardware.ubi.minIOSize} \
|
||||||
|
-e ${config.hardware.ubi.logicalEraseBlockSize} \
|
||||||
|
${ubinizeConfig}
|
||||||
|
'');
|
||||||
|
|
||||||
|
ubiDisk = ({ initramfs }:
|
||||||
|
let
|
||||||
|
initramfsUbi = ubiVolume {
|
||||||
|
name = "rootfs";
|
||||||
|
volumeId = 0;
|
||||||
|
image = initramfs;
|
||||||
|
flags = [ "autoresize" ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
ubiImage [
|
||||||
|
initramfsUbi
|
||||||
|
]);
|
||||||
|
|
||||||
|
disk = ubiDisk {
|
||||||
|
initramfs = config.system.outputs.rootubifs; # liminix.builders.squashfs config.filesystem.contents; # # assert this is a proper FIT.
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
disk;
|
||||||
|
};
|
||||||
|
}
|
71
modules/outputs/zyxel-nwa-fit.nix
Normal file
71
modules/outputs/zyxel-nwa-fit.nix
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
{
|
||||||
|
config
|
||||||
|
, pkgs
|
||||||
|
, lib
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
|
||||||
|
models = "6b e1 6f e1 ff ff ff ff ff ff";
|
||||||
|
in {
|
||||||
|
options.system.outputs = {
|
||||||
|
zyxel-nwa-fit = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
description = ''
|
||||||
|
zyxel-nwa-fit
|
||||||
|
*************
|
||||||
|
|
||||||
|
This output provides a FIT image for Zyxel NWA series
|
||||||
|
containing a kernel image and an UBIFS rootfs.
|
||||||
|
|
||||||
|
It can usually be used as a factory image to install Liminix
|
||||||
|
on a system with pre-existing firmware and OS.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
./ubivolume.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
config = mkIf (config.rootfsType == "ubifs") {
|
||||||
|
|
||||||
|
system.outputs.zyxel-nwa-fit =
|
||||||
|
let
|
||||||
|
o = config.system.outputs;
|
||||||
|
# 8129kb padding.
|
||||||
|
paddedKernel = pkgs.runCommand "padded-kernel" {} ''
|
||||||
|
cp --no-preserve=mode ${o.uimage} $out
|
||||||
|
dd if=/dev/zero of=$out bs=1 count=1 seek=8388607
|
||||||
|
'';
|
||||||
|
firmwareImage = pkgs.runCommand "firmware-image" {} ''
|
||||||
|
cat ${paddedKernel} ${o.rootfs} > $out
|
||||||
|
'';
|
||||||
|
dts = pkgs.writeText "image.its" ''
|
||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
description = "Zyxel FIT (Flattened Image Tree)";
|
||||||
|
compat-models = [${models}];
|
||||||
|
#address-cells = <1>;
|
||||||
|
|
||||||
|
images {
|
||||||
|
firmware {
|
||||||
|
data = /incbin/("${firmwareImage}");
|
||||||
|
type = "firmware";
|
||||||
|
compression = "none";
|
||||||
|
hash@1 {
|
||||||
|
algo = "sha1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
pkgs.runCommand "zyxel-nwa-fit-${config.boot.imageType}" {
|
||||||
|
nativeBuildInputs = [ pkgs.pkgsBuildBuild.ubootTools pkgs.pkgsBuildBuild.dtc ];
|
||||||
|
} ''
|
||||||
|
mkimage -f ${dts} $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
60
modules/zyxel-dual-image/default.nix
Normal file
60
modules/zyxel-dual-image/default.nix
Normal file
|
@ -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.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
33
modules/zyxel-dual-image/service.nix
Normal file
33
modules/zyxel-dual-image/service.nix
Normal file
|
@ -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
|
||||||
|
'';
|
||||||
|
}
|
|
@ -79,6 +79,7 @@ in {
|
||||||
lzma = callPackage ./lzma {};
|
lzma = callPackage ./lzma {};
|
||||||
|
|
||||||
mac80211 = callPackage ./mac80211 {};
|
mac80211 = callPackage ./mac80211 {};
|
||||||
|
zyxel-bootconfig = callPackage ./zyxel-bootconfig {};
|
||||||
min-collect-garbage = callPackage ./min-collect-garbage {};
|
min-collect-garbage = callPackage ./min-collect-garbage {};
|
||||||
min-copy-closure = callPackage ./min-copy-closure {};
|
min-copy-closure = callPackage ./min-copy-closure {};
|
||||||
netlink-lua = callPackage ./netlink-lua {};
|
netlink-lua = callPackage ./netlink-lua {};
|
||||||
|
|
|
@ -15,10 +15,12 @@ let
|
||||||
in {
|
in {
|
||||||
kernel
|
kernel
|
||||||
, commandLine
|
, commandLine
|
||||||
|
, commandLineDtbNode ? "bootargs"
|
||||||
, entryPoint
|
, entryPoint
|
||||||
, extraName ? "" # e.g. socFamily
|
, extraName ? "" # e.g. socFamily
|
||||||
, loadAddress
|
, loadAddress
|
||||||
, imageFormat
|
, imageFormat
|
||||||
|
, alignment ? null
|
||||||
, dtb ? null
|
, dtb ? null
|
||||||
} : stdenv.mkDerivation {
|
} : stdenv.mkDerivation {
|
||||||
name = "kernel.image";
|
name = "kernel.image";
|
||||||
|
@ -39,7 +41,7 @@ in {
|
||||||
'';
|
'';
|
||||||
mungeDtbPhase = ''
|
mungeDtbPhase = ''
|
||||||
dtc -I dtb -O dts -o tmp.dts ${dtb}
|
dtc -I dtb -O dts -o tmp.dts ${dtb}
|
||||||
echo '/{ chosen { bootargs = ${builtins.toJSON commandLine}; }; };' >> tmp.dts
|
echo '/{ chosen { ${commandLineDtbNode} = ${builtins.toJSON commandLine}; }; };' >> tmp.dts
|
||||||
dtc -I dts -O dtb -o tmp.dtb tmp.dts
|
dtc -I dts -O dtb -o tmp.dtb tmp.dts
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -69,7 +71,7 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
_VARS
|
_VARS
|
||||||
mkimage -f mkimage.its kernel.uimage
|
mkimage -f mkimage.its ${lib.optionalString (alignment != null) "-B 0x${lib.toHexString alignment}"} kernel.uimage
|
||||||
mkimage -l kernel.uimage
|
mkimage -l kernel.uimage
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
16
pkgs/zyxel-bootconfig/default.nix
Normal file
16
pkgs/zyxel-bootconfig/default.nix
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
stdenv
|
||||||
|
, openwrt
|
||||||
|
}:
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
name = "zyxel-bootconfig";
|
||||||
|
inherit (openwrt) src;
|
||||||
|
sourceRoot = "openwrt-source/package/utils/zyxel-bootconfig/src";
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
install -Dm544 zyxel-bootconfig $out/bin/zyxel-bootconfig
|
||||||
|
'';
|
||||||
|
meta = {
|
||||||
|
mainProgram = "zyxel-bootconfig";
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue