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