跳转至

记一次路由器刷出问题修复过程

前言

起因是想在 openwrt 上装上openclash插件,但路由器空间不够了(路由器为 mi4a 千兆版,有一个 16M 的 NOR flash。通过在编译时设置块大小为 512KB,成功安装了zerotier,wireguard,python等软件,编译的镜像大小为 13,894,754 B),因此参考官方教程:[OpenWrt Wiki] Saving firmware space and RAM,在编译时关闭了一些内核相关的选项,并将块大小设置为了 1MB。最后成功生成镜像,大小为 14,943,330 B。

可没想到在更新系统后路由器无法正确启动,于是需要先使用小米官方工具恢复,然后再重刷 openwrt 系统。但考虑到之后如果要多次尝试的话,每次失败都需要先恢复官方系统然后重新刷比较麻烦。于是这一次重装,我选择了刷breed引导程序。这样,下次刷失败后便可以通过 breed 直接重刷固件。

然而,不知是否是因为操作过程中的一个失误,我使用 breed 刷 openwrt 会失败,路由器会无限重启。并且在刷回官方固件后,甚至无法上网。最后,我从原厂固件直接刷回了 openwrt,但是却发现 openwrt 中的5GHz 的无线最大发射功率 (Maximum transmit power) 居然变为了 1mW(3dBm),这导致路由器的 5G 信号非常弱,即使是 1m 的距离,信号也很弱。于是我各种尝试去修复这个问题,以下是记录的过程。

说明

可能需要了解 mi4a 的刷机过程,官方救砖工具

过程

第一次尝试

在某次运行OpenWRTInvasion脚本时,切换了下网络,导致该次运行失败,可能导致从本地下载 busybox-mipsel 出错,从而导致了后来的错误。在后面的每一次通过官方工具重刷后,初始设置时的自动检测上网环境无法检测成功。并且,路由器网络出现问题,测速结果也很不正常。如图所示。

image-20211016225643521

image-20211016225745960

此时我在网上也搜索到 EEPROM 错误可能导致无线网络的问题。

注意:点击 EEPROM 后,浏览器会自动下载 eeprom 备份,默认文件名 eeprom.bin,文件大小一般 64K,这个文件中一般包含路由器 MAC 地址、无线校准参数等。丢失该文件,无线路由器可能 WAN 因为没有 MAC 无法从上级路由器获取 IP;无线路由器没有无线信号或信号极弱。编程器固件就是闪存全部备份,相当于电脑硬盘的全盘镜像。无线路由器刷机中,Breed 详细使用教程,一些注意事项_备份 (sohu.com)

幸好我第一次刷 breed 时,在 breed 中选择保存了 EEPROM 以及编程器固件。于是我进行了以下操作:

  1. 使用 breed 恢复 EEPROM 后,同时刷入 openwrt 官方 21.02 版本 initramfs-kernel.bin,结果没有无线网络。
  2. 在 openwrt 中更新为官方 21.02 版本 sysupgrade.bin 后,变砖
  3. 使用 breed 恢复官方 bootloader,恢复官方固件,仍然有上面问题
  4. 再次刷 breed,恢复编程器固件 full.bin
  5. 刷 openwrt sysupgrade.bin,反复重启(黄灯不亮)
  6. 刷自己编译的 squashfs-sysupgrade.bin,反复重启
  7. 发现 MAC 地址发生了改变,恢复 full.bin
  8. 再次刷自己编译的 squashfs-sysupgrade.bin,同时勾选 EEPROM,反复重启
  9. 刷了网上教程提供的标明为 mir3g 的固件(这里可能导致后面的 factory 分区前移的问题),可以启动,但是貌似没有网速。分区如下:(记住这里的 factory offset 为 0x40000) image-20211014150520013

  10. 最后还是不通过 breed 刷回了我的系统。需要注意的是:官方固件 OS1 分区为 13M(但加上官方固件里多出的 disk 分区,大小为 14.5M),而我自己编译的镜像超过了 13M,因此直接刷会报错。于是我先刷官方镜像,再升级为我的镜像,成功。然而还是没有解决问题。

第二次尝试

我还是认为是 EEPROM 丢失导致的问题,想到我之前保存了完整的 flash 内容,因此我考虑直接在 openwrt 中将 flash 内容 dump 下来,和之前保存的进行对比。

理论

这次我仔细阅读了 openwrt 官方关于 Flash Layout 的文档([OpenWrt Wiki] The OpenWrt Flash Layout),其中的内容非常丰富,介绍了:

  • flash 可能会因为electromigration, whisker growth而"磨损"。
  • raw flash 和 FTL (Flash Translation Layer) flash 的区别
    • 一个直接接入 SoC(spi 控制器也算?),需要通过软件来处理坏块和磨损均衡
    • 而另一个则在 flash 上包含一个硬件模块,来处理坏块和磨损均衡
    • 嵌入式中基本都是 raw flash
  • NOR 和 NAND 的区别
    • NOR flash 一般比较小(如 4MB-16MB),可以保证是error-free
    • NAND flash 通常更大些(32MB-256MB),不是error-free的,需要处理坏块
      1. the manufacturer of the “raw NAND flash” has to guarantee that certain erase blocks are error-free:
        • namely the one(s) which the bootloader is to be written to
        • but also the ones which the Linux kernel and the SquashFS are to be written to, because the firmware image file is generated on some desktop computer, that cannot know which erase blocks of the “raw NAND flash” of the device are bad.
      2. the Image Generator has be constrained to build only file sizes that are equal or smaller than the size of the area of the “raw NAND flash”, that consists of guaranteed error-free erase blocks.
      3. OpenWrt would replace JFFS2 with UBIFS, and the entire area of the “raw NAND flash”, that consists of potentially bad erase blocks, would be written to exclusively from an installed OpenWrt system through UBIFS.
  • flash 的分区

    The available storage is not partitioned in the traditional way, where you store the data about the partitions in the MBR and PBRs, but it is done in the Linux Kernel (and sometimes independently in the bootloader as well!). It's simply defined, that “partition kernel starts at offset x and ends at offset y”. Using names allows convenient addressing of partitions by name instead of giving the start offset over and over again.

  • 什么是 MTD:Memory Technology Device - Wikipedia
  • openwrt 文件系统例子 image-20211016233520062

    Explanations

    The Linux kernel treats “raw flash memory” (no matter whether NOR or NAND) chips as an MTD (Memory Technology Device) and employs filesystems developed for this purpose on top of the MTD layer.

    Since the partitions are nested we look at this whole thing in layers:

    1. Layer0: So we have the Flashchip, 8 MiB in size, which is soldered to the PCB and connected to the soc over SPI (Serial Peripheral Interface Bus).
    2. Layer1: We “partition” the space into mtd0 for the bootloader, mtd5 for OpenWrt and, in this case, mtd4 for the ART (Atheros Radio Test) - it contains calibration data for the wifi (EEPROM). If it is missing or corrupt, ath9k (wireless driver) won't come up anymore. The bootloader (128 KiB) contains of the u-boot 64KiB block AND a data section which contains the MAC, WPS-PIN and type description. If no MAC is configured ath9k will not work correctly due to a faulty MAC.
    3. Layer2: we subdivide mtd5 (firmware) into mtd1 (kernel) and mtd2 (rootfs); In the generation process of the firmware (see imagebuilder) the Kernel binary file is first packed with LZMA, then the obtained file is packed with gzip and then this file will be written onto the raw flash (mtd1) without being part of any filesystem! During boot, u-boot copies this entire section into RAM and executes it. From there on, the Linux kernel bootstraps itself…
    4. Layer3: we subdivide rootfs even further into mtd3 for rootfs_data and the rest for an unnamed partition which will accommodate the SquashFS-partition.

    Mount Points

    • / this is your entire root filesystem, it comprises /rom and /overlay. Please ignore /rom and /overlay and use exclusively / for your daily routines!
    • /rom contains all the basic files, like busybox, dropbear or iptables. It also includes default configuration files used when booting into OpenWrt Failsafe mode. It does not contain the Linux kernel. All files in this directory are located on the SqashFS partition, and thus cannot be altered or deleted. But, because we use overlay_fs filesystem, overlay-whiteout-symlinks can be created on the JFFS2 partition.
    • /overlay is the writable part of the file system that gets merged with /rom to create a uniform /-tree. It contains anything that was written to the router after installation, e.g. changed configuration files, additional packages installed with opkg, etc. It is formated with JFFS2.

    Whenever the system is asked to look for an existing file in /, it first looks in /overlay, and if not there, then in /rom. In this way /overlay overrides /rom and creates the effect of a writable / while much of the content is safely and efficiently stored in the read-only /rom.

实践

Windows 下我使用了Free Hex Editor: Fastest Binary File Editing Software. Freeware. Windows (hhdsoftware.com)来查看二进制文件信息

  1. 首先查看 flash 分区信息 可以通过cat /proc/mtd来查看 mtd 分区,不过其中有些分区是重叠的。因此可以通过dmsg来查看系统的启动信息,找到其中的 mtd 信息,如下:

    [    0.615097] Creating 8 MTD partitions on "spi0.0":
    [    0.619893] 0x000000000000-0x000000030000 : "u-boot"
    [    0.626143] 0x000000030000-0x000000040000 : "u-boot-env"
    [    0.632722] 0x000000040000-0x000000050000 : "Bdata"
    [    0.638791] 0x000000050000-0x000000060000 : "factory"
    [    0.645206] 0x000000060000-0x000000070000 : "crash"
    [    0.651386] 0x000000070000-0x000000080000 : "cfg_bak"
    [    0.657804] 0x000000080000-0x000000180000 : "overlay"
    [    0.664113] 0x000000180000-0x000001000000 : "firmware"
    [    0.670710] 2 uimage-fw partitions found on MTD device firmware
    [    0.676656] Creating 2 MTD partitions on "firmware":
    [    0.681625] 0x000000000000-0x00000024d580 : "kernel"
    [    0.687745] 0x00000024d580-0x000000e80000 : "rootfs"
    [    0.693746] mtd: device 9 (rootfs) set to be root filesystem
    [    0.699502] 1 squashfs-split partitions found on MTD device rootfs
    [    0.705684] 0x000000d30000-0x000000e80000 : "rootfs_data"
    [    0.713298] libphy: Fixed MDIO Bus: probed
    [    0.743012] libphy: mdio: probed
    [    0.746455] mt7530 mdio-bus:1f: MT7530 adapts as multi-chip module
    [    0.755279] mtk_soc_eth 1e100000.ethernet: generated random MAC address aa:f9:37:65:6e:50
    [    0.764330] mtk_soc_eth 1e100000.ethernet eth0: mediatek frame engine at 0xbe100000, irq 21
    [    0.774735] mt7621-pci 1e140000.pcie: Parsing DT failed
    

    可和官方固件作对比(启动信息从 openwrt 对应设备页面获得):

    [    2.280000] flash manufacture id: c8, device id 40 18
    [    2.280000] GD25Q128C(c8 40180000) (16384 Kbytes)
    [    2.280000] mtd .name = raspi, .size = 0x01000000 (16M) .erasesize = 0x00010000 (64K) .numeraseregions = 0
    [    2.280000] Creating 10 MTD partitions on "raspi":
    [    2.280000] 0x000000000000-0x000001000000 : "ALL"
    [    2.280000] 0x000000000000-0x000000030000 : "Bootloader"
    [    2.280000] 0x000000030000-0x000000040000 : "Config"
    [    2.280000] 0x000000040000-0x000000050000 : "Bdata"
    [    2.280000] 0x000000050000-0x000000060000 : "Factory"
    [    2.290000] 0x000000060000-0x000000070000 : "crash"
    [    2.290000] 0x000000070000-0x000000080000 : "cfg_bak"
    [    2.290000] 0x000000080000-0x000000180000 : "overlay"
    [    2.290000] 0x000000180000-0x000000e80000 : "OS1"
    [    2.290000] mtd: try split OS1 partition
    [    2.290000] mtd: split_firmware
    [    2.290000] mtd: firmware_partition->size   0xd00000
    [    2.290000] mtd: firmware_partition->offset 0x180000
    [    2.290000] mtd: uimage_len 1855601
    [    2.290000] mtd: uimage_len 1900544
    [    2.290000] mtd: rootfs_partition->size   0xb30000
    [    2.290000] mtd: rootfs_partition->offset 0x350000
    [    2.290000] mtd: partition "rootfs" created automatically, ofs=350000, len=B30000
    [    2.290000] 0x000000350000-0x000000e80000 : "rootfs"
    [    2.290000] 0x000000e80000-0x000001000000 : "disk"
    
  2. 然后 dump 完整的 flash(之前通过 breed 保存的 EEPROM 为 eeprom.bin,完整 flash 为 full.bin)

    cat /dev/mtd0 > dump.bin
    cat /dev/mtd1 >> dump.bin
    cat /dev/mtd2 >> dump.bin
    cat /dev/mtd3 >> dump.bin
    cat /dev/mtd4 >> dump.bin
    cat /dev/mtd5 >> dump.bin
    cat /dev/mtd6 >> dump.bin
    cat /dev/mtd7 >> dump.bin
    
  3. 发现 eeprom.bin 的大小为 64KB,刚好对应 0x10000 的大小。通过在 full.bin 中查找 eeprom.bin 内容,容易定位到 Factory(0x50000) 这个分区。发现 Factory 分区中即存储了 EEPROM 内容。 image-20211016194415806

  4. 然而 openwrt 直接 dump 下来的文件中,相同的内容,位置却定位到了 Bdata(0x40000) 这个分区 image-20211016235247436

  5. 对比其他分区,可以发现:
    • (0x00000-0x30000)Bootloader 对应 u-boot image-20211016235954775
    • (0x30000-0x40000)Config 对应 u-boot-env image-20211016235726924
    • (0x80000-0x180000)overlay 和 overlay 相对应 image-20211016235835586
    • 中间的内容两边都不同,比较明显的是 dump.bin 中 Bdata 存储了 Factory 内容,而 Factory 的内容未知。
  6. 因此考虑将 eeprom.bin 内容在 openwrt 中重新写入到 Factory 中 然而直接 mtd write 会报以下错误 image-20211017000149003 搜索后发现需要安装内核模块kmod-mtd-rw,由于自己编译的内核 hash 值和官方版本不一致,于是我重新编译了 openwrt 镜像 然后运行命令 I WANT A BRICK!!!

    insmod mtd-rw i_want_a_brick=1
    

    于是我们便可以愉快地解锁分区了,写入 eeprom 即可

mtd -r write eeprom.bin factory
  1. 结束,写完后,我的 5GHz 无线终于恢复正常了!

后记

虽然解决了问题,但其实还有一些疑问:

  1. 使用 breed 无法成功刷 breed。使用的 breed 有两个版本,看别人的教程说 r3g 和 mi4a 百兆版使用下面的版本,mi4a 千兆版本使用上面的版本。 image-20211017001124130 然而使用 breed 时,可以看到,上面标的闪存布局 0x50000 并不对应 OS1 的地址,所以感觉不应该使用这个版本的 breed!(所以上面发生的问题会不会是用错版本 breed 导致的?)breed 上也没有说明是否支持 mi4aIndex of / (hackpascal.net) image-20211014133720106
  2. 印象中原本台式插上路由器测网速是能跑上 1000M 的,但是现在台式直插寝室网口下载速率可以跑到 960M,而插上路由器却只有 600M 左右,感觉损失挺大的。不知是不是路由器还有潜在问题。(难道是 block 设置为 512KB 拖慢了速度?)
  3. 可以看到路由分区中除了 u-boot, u-boot-env, Factory, firmware,其他部分感觉都是没用的,这部分是否能利用起来?比如那个紧挨 firmware 的 overlay 分区感觉是没起作用的,但是占了 1M 的大小。crash 和 cfg_back 也可以获得 128KB 的空间。

后记 2

之前折腾 vlan 时,一不注意就导致无法访问到设备了。需要反复刷 op,因此想使用 breed 来简化刷 op 过程。但其实 breed 官方根本没有对 mi4a 千兆版做支持,只是因为其 flash 分区和 WR1200JS 一样,所以网上都使用那个版本的 breed,当时我觉得 mi4a 的硬件和 r3g 一摸一样,因此觉得刷 r3g 的应该也可以。结果刷完后就变砖了,官方恢复工具也无法恢复。后面才意识到上面的问题,breed 的 flash 分区和 openwrt 的不一样。

过了半学期,我买了 ch341a 编程器,可以直接刷 flash,因此可以把 mi4a 救回来了。但是发现之前备份的 eeprom 被我删了,然后使用编程器的时候,在意识到需要先备份时已经点了擦除。所以无奈只能在网上找了别人提供的 eeprom。想到 mi4a 是我第一个 openwrt 的设备,我对它 SLAAC 的 ipv6 地址印象深刻(123d 结尾),而现在没办法恢复了,不免有点感伤。

如果要用 breed 刷 openwrt 的话,需要修改 op 的 flash 分区:

但是想想还是算了,为什么要想办法去用一个官方不支持的东西呢?(造成这一切悲剧的源头)