liminix

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

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 }