liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

default.nix (13872B)


      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 = { pkgs, config, lib, lim, ...}:
    105     let
    106       inherit (pkgs.liminix.networking) interface;
    107       inherit (pkgs.liminix.services) oneshot;
    108       inherit (pkgs.pseudofile) dir symlink;
    109       inherit (pkgs) openwrt;
    110 
    111       mac80211 = pkgs.mac80211.override {
    112         drivers = [ "mt7915e" ];
    113         klibBuild = config.system.outputs.kernel.modulesupport;
    114       };
    115       # v204520220929
    116       wlan_firmware = pkgs.fetchurl {
    117         url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wa.bin";
    118         hash = "sha256-wooyefzb0i8640+lwq3vNhcBXRFCtGuo+jiL7afZaKA=";
    119       };
    120       wlan_firmware' = pkgs.fetchurl {
    121         url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wm.bin";
    122         hash = "sha256-k62nQewRuKjBLd5R3RxU4F74YKnQx5zr6gqMMImqVQw=";
    123       };
    124       wlan_firmware'' = pkgs.fetchurl {
    125         url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_rom_patch.bin";
    126         hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg=";
    127       };
    128     in {
    129       imports = [ 
    130         # We include it to ensure the bridge functionality
    131         # is available on the target kernel.
    132         ../../modules/bridge
    133         ../../modules/arch/mipsel.nix
    134         ../../modules/outputs/tftpboot.nix
    135         ../../modules/outputs/zyxel-nwa-fit.nix
    136         ../../modules/zyxel-dual-image
    137       ];
    138 
    139       filesystem = dir {
    140         lib = dir {
    141           firmware = dir {
    142             mediatek = dir {
    143               "mt7915_wa.bin" = symlink wlan_firmware;
    144               "mt7915_wm.bin" = symlink wlan_firmware';
    145               "mt7915_rom_patch.bin" = symlink wlan_firmware'';
    146             };
    147           };
    148         };
    149       };
    150 
    151       rootfsType = "ubifs";
    152       hardware = {
    153         # Taken from OpenWRT
    154         # root@OpenWrt:/# ubinfo /dev/ubi0
    155         # ubi0
    156         # Volumes count:                           2
    157         # Logical eraseblock size:                 126976 bytes, 124.0 KiB
    158         # Total amount of logical eraseblocks:     256 (32505856 bytes, 31.0 MiB)
    159         # Amount of available logical eraseblocks: 0 (0 bytes)
    160         # Maximum count of volumes                 128
    161         # Count of bad physical eraseblocks:       0
    162         # Count of reserved physical eraseblocks:  19
    163         # Current maximum erase counter value:     2
    164         # Minimum input/output unit size:          2048 bytes
    165         # Character device major/minor:            250:0
    166         # Present volumes:                         0, 1
    167         ubi = {
    168           minIOSize = "2048";
    169           logicalEraseBlockSize = "126976";
    170           physicalEraseBlockSize = "128KiB";
    171           maxLEBcount = "256";
    172         };
    173 
    174         # This is a FIT containing a kernel padded and
    175         # a UBI volume rootfs.
    176         defaultOutput = "zyxel-nwa-fit";
    177 
    178         loadAddress = lim.parseInt "0x80001000";
    179         entryPoint = lim.parseInt "0x80001000";
    180         # Aligned on 2kb.
    181         alignment = 2048;
    182 
    183         rootDevice = "ubi:rootfs";
    184 
    185         dts = {
    186           # Actually, this is not what we want.
    187           # This DTS is insufficient.
    188           src = ./mt7621_zyxel_nwa50ax.dtsi;
    189           includes = [
    190             # Here's one weird trick to make `ubi` detection
    191             # out of the box.
    192             # We will write ubi on /dev/firmware_a:rootfs location
    193             # and same for /dev/firmware_b:rootfs.
    194             # How do we distinguish both?
    195             # We can just use the DTS to point ubi at A or B.
    196             # This, unfortunately, means that we have "two images".
    197             # But they are really just 1 image with 2 different DTS.
    198             # TODO: improve this hack in preinit?
    199             (if config.boot.imageType == "primary" then "${./a_image}" else "${./b_image}")
    200             "${openwrt.src}/target/linux/ramips/dts"
    201           ];
    202         };
    203         networkInterfaces =
    204           let
    205             inherit (config.system.service.network) link;
    206           in {
    207             eth = link.build { ifname = "eth0"; };
    208             lan = link.build { ifname = "lan"; };
    209             wlan0 = link.build {
    210               ifname = "wlan0";
    211               dependencies = [ mac80211 ];
    212             };
    213             wlan1 = link.build {
    214               ifname = "wlan1";
    215               dependencies = [ mac80211 ];
    216             };
    217           };
    218       };
    219 
    220       boot = {
    221         # Critical because NWA50AX will extend your cmdline with the image number booted.
    222         # and some bootloader version.
    223         # You don't want to find yourself being overridden.
    224         commandLineDtbNode = "bootargs-override";
    225 
    226         imageFormat = "fit";
    227         tftp = {
    228           # 5MB is nice.
    229           freeSpaceBytes = 5 * 1024 * 1024;
    230           loadAddress = lim.parseInt "0x2000000";
    231         };
    232       };
    233 
    234       # Dual image management service in userspace.
    235       services.zyxel-dual-image = config.boot.zyxel-dual-image.build {
    236         ensureActiveImage = "primary";
    237         # TODO: use mtd names rather…
    238         # primary and secondary are always /dev/mtd3 by virtue of the 
    239         # dtb being not too wrong…
    240         # TODO: remove this hack.
    241         primaryMtdPartition = "/dev/mtd3";
    242         secondaryMtdPartition = "/dev/mtd3";
    243         bootConfigurationMtdPartition = "/dev/mtd12";
    244       };
    245 
    246       #  DEVICE_VENDOR := ZyXEL
    247       #  KERNEL_SIZE := 8192k
    248       #  DEVICE_PACKAGES := kmod-mt7915-firmware zyxel-bootconfig
    249       #  KERNEL := kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb
    250       #  IMAGES += factory.bin ramboot-factory.bin
    251       #  IMAGE/factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi | zyxel-nwa-fit
    252       #  IMAGE/ramboot-factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi
    253 
    254       kernel = {
    255         src = pkgs.fetchurl {
    256           name = "linux.tar.gz";
    257           url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
    258           hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
    259         };
    260         extraPatchPhase = ''
    261           ${openwrt.applyPatches.ramips}
    262 
    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           WLAN = "y";
    331 
    332           PHYLIB = "y";
    333           AT803X_PHY = "y";
    334           FIXED_PHY = "y";
    335           GENERIC_PHY = "y";
    336           NET_DSA = "y";
    337           NET_DSA_MT7530 = "y";
    338           NET_DSA_MT7530_MDIO = "y";
    339           NET_DSA_TAG_MTK = "y";
    340           NET_MEDIATEK_SOC = "y";
    341           NET_SWITCHDEV = "y";
    342           NET_VENDOR_MEDIATEK = "y";
    343 
    344           SWPHY = "y";
    345 
    346           GPIOLIB = "y";
    347           GPIO_MT7621 = "y";
    348           OF_GPIO = "y";
    349 
    350           EARLY_PRINTK = "y";
    351 
    352           NEW_LEDS = "y";
    353           LEDS_TRIGGERS = "y";
    354           LEDS_CLASS = "y";         # required by rt2x00lib
    355           LEDS_CLASS_MULTICOLOR = "y";
    356           LEDS_BRIGHTNESS_HW_CHANGED = "y";
    357 
    358           PRINTK_TIME = "y";
    359         } // lib.optionalAttrs (config.system.service ? vlan) {
    360           SWCONFIG = "y";
    361         } // lib.optionalAttrs (config.system.service ? watchdog) {
    362           RALINK_WDT = "y";  # watchdog
    363           MT7621_WDT = "y";  # or it might be this one
    364         };
    365       };
    366     };
    367 }