default.nix (13762B)
1 { 2 system = { 3 crossSystem = { 4 config = "mipsel-unknown-linux-musl"; 5 gcc = { 6 abi = "32"; 7 arch = "mips32"; # mips32r2? 8 }; 9 }; 10 }; 11 12 description = '' 13 Zyxel NWA50AX 14 ******************** 15 16 Zyxel NWA50AX is quite close to the GL-MT300N-v2 "Mango" device, but it is based on the MT7621 17 chipset instead of the MT7628. 18 19 Installation 20 ============ 21 22 This device is pretty, but, due to its A/B capabilities, can be a bit hard 23 to use completely. 24 25 The stock vendor firmware is a downstream fork of U-Boot: <https://github.com/RaitoBezarius/uboot-nwa50ax> 26 with restricted boot commands. Fortunately, OpenWrt folks figured out trivial command injections, 27 so you can use most of the OpenWrt commands without trouble by just command injecting 28 atns, atna or atnf, e.g. atns "; $real_command". 29 30 From factory web UI, you can upload the result of the zyxel-nwa-fit output. 31 From another operating system, you need to `dumpimage -T flat_dt -p 0 $zyxel-nwa-fit -o firmware.bin`, 32 `flash_erase $(mtd partition of the target partition firmware or zy_firmware) 0 0`, then you complete by 33 `nandwrite -p $(mtd partition of the target partition firmware or zy_firmware) firmware.bin`. 34 35 How to put the firmware.bin on the machine is left to you as an exercise, e.g. SSH, TFTP, whatever. 36 37 From serial, you have two choices: 38 39 - Flash this system via U-Boot: 40 same reasoning as from an existing Linux system, two choices: 41 - ymodem the binary, perform the write manually, you can inspire yourself 42 from the `script` contained in the vendor firmware, those are just a FIT containing a script. 43 - prepare a FIT containing a script executing your commands, tftpboot this. 44 45 - boot from an existing Liminix system, e.g. TFTPBOOT image. 46 - boot from an OpenWrt system, i.e. follow OpenWrt steps. 47 48 Once you are in a Linux system, understand that this device has A/B boot. 49 50 OpenWrt provides you with `zyxel-bootconfig` to set/unset the image status and choice. 51 52 The kernel is booted with `bootImage=<number>` which tells you which slot are you on. 53 54 You should find yourself with 10ish MTD partitions, the most interesting ones are two: 55 56 - firmware: 40MB 57 - firmware_1: 40MB 58 59 In the current setup, they are split further into kernel (8MB) and ubi (32MB). 60 61 Once you are done with first installation, note that if you want to use the A/B feature, 62 you need to write a _secondary_ image on the slot B. There is no proper flashing code 63 that will set the being-updated slot to `new` and boot on it to verify if it's working. 64 This is a WIP. 65 66 Upgrading your system can be achieved via: 67 68 - `liminix-rebuild` for the userspace. 69 - `flash_erase` + `nandwrite` for the kernelspace to the other slot than the one you are booted on, 70 note that you can just nandwrite the mtd partition corresponding to the *kernel* and not the whole firmware. 71 72 If you soft-bricked your AP, i.e. you cannot boot anything in U-Boot, no worries, just plug the serial console, 73 prepare a TFTP server (via `tufted` for example), download vendor firmware, set up `atns`, `atnf`, etc. and run `atnz`. 74 75 This will reflash everything back to normal via TFTP. 76 77 If you hard-bricked your AP, i.e. U-Boot is telling you to transfer a valid bootloader via ymodem, just extract 78 a U-Boot from the vendor OS, send it via ymodem and use the previous operations to perform a full flash this time 79 of all partitions. 80 81 Note that if you erased your MRD partition, you lost your serial and MAC address. There's no way to recover the original one 82 except by reading the physical label on your… device! 83 84 If you super-hard-bricked your AP, i.e. no output on serial console, congratulations, you reached one of the rare state 85 of this device. You need an external NAND flasher to repair it and write the first stage from Mediatek to continue the previous 86 recovery operations. 87 88 Development TODO list: 89 90 - Better support for upgrade automation w.r.t. to A/B, e.g. automagic scripts. 91 - Mount the logs partition, mount / as overlayfs of firmware ? rootfs and rootfs_data for extended data. 92 - Jitter-based entropy injection? Device can be slow to initialize its CRNG and hostapd will reject few clients at the start because of that. 93 - Defaults for hostapd based on MT7915 capabilities? See the example for one possible list. 94 - Remove primary/secondary hack and put it in preinit. 95 - Offer ways to reflash the *bootloader* itself to support direct boot via UBI and kernel upgrades via filesystem rewrite. 96 97 Vendor web page: https://www.zyxel.com/fr/fr/products/wireless/ax1800-wifi-6-dual-radio-nebulaflex-access-point-nwa50ax 98 99 OpenWrt web page: https://openwrt.org/inbox/toh/zyxel/nwa50ax 100 OpenWrt tech data: https://openwrt.org/toh/hwdata/zyxel/zyxel_nwa50ax 101 102 ''; 103 104 module = 105 { 106 pkgs, 107 config, 108 lib, 109 lim, 110 ... 111 }: 112 let 113 inherit (pkgs.pseudofile) dir symlink; 114 inherit (pkgs) openwrt; 115 116 mac80211 = pkgs.mac80211.override { 117 drivers = [ "mt7915e" ]; 118 klibBuild = config.system.outputs.kernel.modulesupport; 119 }; 120 # v204520220929 121 wlan_firmware = pkgs.fetchurl { 122 url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wa.bin"; 123 hash = "sha256-wooyefzb0i8640+lwq3vNhcBXRFCtGuo+jiL7afZaKA="; 124 }; 125 wlan_firmware' = pkgs.fetchurl { 126 url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wm.bin"; 127 hash = "sha256-k62nQewRuKjBLd5R3RxU4F74YKnQx5zr6gqMMImqVQw="; 128 }; 129 wlan_firmware'' = pkgs.fetchurl { 130 url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_rom_patch.bin"; 131 hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg="; 132 }; 133 in 134 { 135 imports = [ 136 # We include it to ensure the bridge functionality 137 # is available on the target kernel. 138 ../../modules/bridge 139 ../../modules/arch/mipsel.nix 140 ../../modules/outputs/tftpboot.nix 141 ../../modules/outputs/zyxel-nwa-fit.nix 142 ../../modules/zyxel-dual-image 143 ]; 144 145 filesystem = dir { 146 lib = dir { 147 firmware = dir { 148 mediatek = dir { 149 "mt7915_wa.bin" = symlink wlan_firmware; 150 "mt7915_wm.bin" = symlink wlan_firmware'; 151 "mt7915_rom_patch.bin" = symlink wlan_firmware''; 152 }; 153 }; 154 }; 155 }; 156 157 rootfsType = "ubifs"; 158 hardware = { 159 # Taken from OpenWRT 160 # root@OpenWrt:/# ubinfo /dev/ubi0 161 # ubi0 162 # Volumes count: 2 163 # Logical eraseblock size: 126976 bytes, 124.0 KiB 164 # Total amount of logical eraseblocks: 256 (32505856 bytes, 31.0 MiB) 165 # Amount of available logical eraseblocks: 0 (0 bytes) 166 # Maximum count of volumes 128 167 # Count of bad physical eraseblocks: 0 168 # Count of reserved physical eraseblocks: 19 169 # Current maximum erase counter value: 2 170 # Minimum input/output unit size: 2048 bytes 171 # Character device major/minor: 250:0 172 # Present volumes: 0, 1 173 ubi = { 174 minIOSize = "2048"; 175 logicalEraseBlockSize = "126976"; 176 physicalEraseBlockSize = "128KiB"; 177 maxLEBcount = "256"; 178 }; 179 180 # This is a FIT containing a kernel padded and 181 # a UBI volume rootfs. 182 defaultOutput = "zyxel-nwa-fit"; 183 184 loadAddress = lim.parseInt "0x80001000"; 185 entryPoint = lim.parseInt "0x80001000"; 186 # Aligned on 2kb. 187 alignment = 2048; 188 189 rootDevice = "ubi:rootfs"; 190 191 dts = { 192 # Actually, this is not what we want. 193 # This DTS is insufficient. 194 src = ./mt7621_zyxel_nwa50ax.dtsi; 195 includePaths = [ 196 # Here's one weird trick to make `ubi` detection 197 # out of the box. 198 # We will write ubi on /dev/firmware_a:rootfs location 199 # and same for /dev/firmware_b:rootfs. 200 # How do we distinguish both? 201 # We can just use the DTS to point ubi at A or B. 202 # This, unfortunately, means that we have "two images". 203 # But they are really just 1 image with 2 different DTS. 204 # TODO: improve this hack in preinit? 205 (if config.boot.imageType == "primary" then "${./a_image}" else "${./b_image}") 206 "${openwrt.src}/target/linux/ramips/dts" 207 ]; 208 }; 209 networkInterfaces = 210 let 211 inherit (config.system.service.network) link; 212 in 213 { 214 eth = link.build { ifname = "eth0"; }; 215 lan = link.build { ifname = "lan"; }; 216 wlan0 = link.build { 217 ifname = "wlan0"; 218 dependencies = [ mac80211 ]; 219 }; 220 wlan1 = link.build { 221 ifname = "wlan1"; 222 dependencies = [ mac80211 ]; 223 }; 224 }; 225 }; 226 227 boot = { 228 # Critical because NWA50AX will extend your cmdline with the image number booted. 229 # and some bootloader version. 230 # You don't want to find yourself being overridden. 231 commandLineDtbNode = "bootargs-override"; 232 233 imageFormat = "fit"; 234 tftp = { 235 # 5MB is nice. 236 freeSpaceBytes = 5 * 1024 * 1024; 237 loadAddress = lim.parseInt "0x2000000"; 238 }; 239 }; 240 241 # Dual image management service in userspace. 242 services.zyxel-dual-image = config.boot.zyxel-dual-image.build { 243 ensureActiveImage = "primary"; 244 # TODO: use mtd names rather… 245 # primary and secondary are always /dev/mtd3 by virtue of the 246 # dtb being not too wrong… 247 # TODO: remove this hack. 248 primaryMtdPartition = "/dev/mtd3"; 249 secondaryMtdPartition = "/dev/mtd3"; 250 bootConfigurationMtdPartition = "/dev/mtd12"; 251 }; 252 253 # DEVICE_VENDOR := ZyXEL 254 # KERNEL_SIZE := 8192k 255 # DEVICE_PACKAGES := kmod-mt7915-firmware zyxel-bootconfig 256 # KERNEL := kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb 257 # IMAGES += factory.bin ramboot-factory.bin 258 # IMAGE/factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi | zyxel-nwa-fit 259 # IMAGE/ramboot-factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi 260 261 kernel = { 262 extraPatchPhase = '' 263 ${openwrt.applyPatches.ramips} 264 ''; 265 config = 266 { 267 268 RALINK = "y"; 269 PCI = "y"; 270 PHY_MT7621_PCI = "y"; 271 PCIE_MT7621 = "y"; 272 SOC_MT7621 = "y"; 273 CLK_MT7621 = "y"; 274 CLOCKSOURCE_WATCHDOG = "y"; 275 276 SERIAL_8250_CONSOLE = "y"; 277 SERIAL_8250 = "y"; 278 SERIAL_CORE_CONSOLE = "y"; 279 SERIAL_OF_PLATFORM = "y"; 280 SERIAL_8250_NR_UARTS = "3"; 281 SERIAL_8250_RUNTIME_UARTS = "3"; 282 SERIAL_MCTRL_GPIO = "y"; 283 284 CONSOLE_LOGLEVEL_DEFAULT = "8"; 285 CONSOLE_LOGLEVEL_QUIET = "4"; 286 287 # MTD_UBI_BEB_LIMIT = "20"; 288 # MTD_UBI_WL_THRESHOLD = "4096"; 289 290 MTD = "y"; 291 MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev 292 MTD_RAW_NAND = "y"; 293 MTD_NAND_MT7621 = "y"; 294 MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table 295 MTD_NAND_ECC_SW_HAMMING = "y"; 296 MTD_SPI_NAND = "y"; 297 MTD_OF_PARTS = "y"; 298 MTD_NAND_CORE = "y"; 299 MTD_SPLIT_FIRMWARE = "y"; 300 MTD_SPLIT_FIT_FW = "y"; 301 302 PINCTRL = "y"; 303 PINCTRL_MT7621 = "y"; 304 305 I2C = "y"; 306 I2C_MT7621 = "y"; 307 308 SPI = "y"; 309 MTD_SPI_NOR = "y"; 310 SPI_MT7621 = "y"; 311 SPI_MASTER = "y"; 312 SPI_MEM = "y"; 313 314 REGULATOR = "y"; 315 REGULATOR_FIXED_VOLTAGE = "y"; 316 RESET_CONTROLLER = "y"; 317 POWER_RESET = "y"; 318 POWER_RESET_GPIO = "y"; 319 POWER_SUPPLY = "y"; 320 LED_TRIGGER_PHY = "y"; 321 322 PCI_DISABLE_COMMON_QUIRKS = "y"; 323 PCI_DOMAINS = "y"; 324 PCI_DOMAINS_GENERIC = "y"; 325 PCI_DRIVERS_GENERIC = "y"; 326 PCS_MTK_LYNXI = "y"; 327 328 SOC_BUS = "y"; 329 330 NET = "y"; 331 ETHERNET = "y"; 332 WLAN = "y"; 333 334 PHYLIB = "y"; 335 AT803X_PHY = "y"; 336 FIXED_PHY = "y"; 337 GENERIC_PHY = "y"; 338 NET_DSA = "y"; 339 NET_DSA_MT7530 = "y"; 340 NET_DSA_MT7530_MDIO = "y"; 341 NET_DSA_TAG_MTK = "y"; 342 NET_MEDIATEK_SOC = "y"; 343 NET_SWITCHDEV = "y"; 344 NET_VENDOR_MEDIATEK = "y"; 345 346 SWPHY = "y"; 347 348 GPIOLIB = "y"; 349 GPIO_MT7621 = "y"; 350 OF_GPIO = "y"; 351 352 EARLY_PRINTK = "y"; 353 354 NEW_LEDS = "y"; 355 LEDS_TRIGGERS = "y"; 356 LEDS_CLASS = "y"; # required by rt2x00lib 357 LEDS_CLASS_MULTICOLOR = "y"; 358 LEDS_BRIGHTNESS_HW_CHANGED = "y"; 359 360 PRINTK_TIME = "y"; 361 } 362 // lib.optionalAttrs (config.system.service ? vlan) { 363 SWCONFIG = "y"; 364 } 365 // lib.optionalAttrs (config.system.service ? watchdog) { 366 RALINK_WDT = "y"; # watchdog 367 MT7621_WDT = "y"; # or it might be this one 368 }; 369 }; 370 }; 371 }