Rescuing a Bricked 360T7 Router via UART After a DDR3 RAM Upgrade

8 minute read Published: 2026-05-23

A memory-modded 360T7 ran fine for years until I flashed OpenWrt's bl2 and fip, which bricked it with dram size: 0MB. Every rescue package found online failed. Eventually I traced the issue to a DDR3 frequency mismatch in the ATF source, compiled a custom bl2, and recovered the router via UART.

Background

A few years back I picked up a modded 360T7 on Xianyu (a Chinese secondhand marketplace) where the seller had replaced the original 256MB DDR3 chip with a 512MB one. Flashed hanwckf's U-Boot and Lean's QWRT, ran rock solid for years without a single issue.

Recently I had time off and wanted to switch the entire stack to upstream OpenWrt for the latest features. Following the official docs, I SSH'd into the router and flashed OpenWrt's bl2 and fip, erased the firmware partition, and the router never came back. Couldn't enter recovery to pull TFTP firmware. Connected TTL and saw serial output stuck at EMI detected dram size: 0MB - memory initialization failed, boot chain broken at the bl2 stage.

Investigation

Searched everywhere online, found that MediaTek platforms have a UART rescue mode (mtk_uartboot) that can push bl2 to the SoC's SRAM via serial for temporary boot. Tried every 360T7 rescue bl2 and fip shared by the community, all failed with the same dram size: 0MB.

First thought was the 512MB memory chip had died. But I spotted someone on Xianyu selling remote repair services for 360T7 with identical symptoms. If others can fix it with software, it's not a hardware problem. Besides, this thing ran fine for years.

Had time on my hands, decided to sort it out from source. Left U-Boot alone, kept using the bl-mt798x repo that had always worked, minimized variables and focused on DDR configuration.

Opened up the router to confirm the chip: Zentel A3T4GF40ABF (DDR3-1600). Checked 360T7's default defconfig - no DDR3 frequency specified, and MT7981's Kconfig defaults to DDR3-2133:

# plat/mediatek/apsoc_common/Config.in
config _PLAT_MT7981
    select _DEFAULT_DDR3_2133

Found the problem: the DDR3-1600 chip was being driven at 2133MT/s, DRAM calibration failed outright. The previous hanwckf firmware worked because the seller had already set the correct frequency when flashing. OpenWrt's official bl2 and community rescue packages were all compiled for DDR3-2133, naturally incompatible.

Worth noting that this was later fixed upstream in OpenWrt - PR #22797 merged on 2026-05-09, changing 360T7's BL2 from DDR3-2133 to DDR3-1866 and adding UBI layout support. But I had flashed the latest OpenWrt stable at the time, which didn't include this fix.

MT7981 ATF only supports two DDR3 frequency options: _DDR3_FREQ_1866 (1866MT/s) and _DDR3_FREQ_2133 (2133MT/s, default). No 1600 option, lowest available is 1866, which is about 17% overclock for this chip. DDR3 stepping up one bin is usually fine.

After identifying the frequency issue, I compiled a DDR3-1866 bl2 and tried pushing it via mtk_uartboot. Timeout, handshake failed. Another trap: the default build uses the SPI-NAND boot path and doesn't include UART download handshake code. Confirmed in source that bl2 needs both _BOOT_DEVICE_RAM=y and _RAM_BOOT_RAM_BOOT_UART_DL=y to work with mtk_uartboot - the former selects the RAM boot path compiling bl2_boot_ram.c, the latter enables UART handshake compiling uart_dl.c.

Solution

Two things to compile: a UART rescue bl2 (RAM boot + UART_DL + DDR3-1866, temporary boot only), and a production bl2 + FIP for flashing to NAND (SPI-NAND boot + DDR3-1866).

UART Boot bl2

Create configs/mt7981_360t7_uart_defconfig:

_PLAT_MT7981=y
_MT7981_BOARD_BGA=y
_DRAM_DDR3=y
_DDR3_FREQ_1866=y
_LOG_LEVEL_INFO=y
_BOOT_DEVICE_RAM=y
_RAM_BOOT_RAM_BOOT_UART_DL=y

Build (can't use the repo's build.sh - it hardcodes defconfig names and builds FIP):

rm -rf atf-20240117-bacca82a8/build
make -C atf-20240117-bacca82a8 mt7981_360t7_uart_defconfig \
    CONFIG_CROSS_COMPILER=aarch64-linux-gnu- CROSS_COMPILER=aarch64-linux-gnu-
make -C atf-20240117-bacca82a8 bl2 \
    CONFIG_CROSS_COMPILER=aarch64-linux-gnu- CROSS_COMPILER=aarch64-linux-gnu- -j$(nproc)

Output: build/mt7981/release/bl2.bin, renamed to uart_bl2.bin.

Production bl2 + FIP

Modify configs/mt7981_360t7_defconfig:

_PLAT_MT7981=y
_MT7981_BOARD_BGA=y
_DRAM_DDR3=y
_DDR3_FREQ_1866=y
_LOG_LEVEL_INFO=y

Build:

SOC=mt7981 BOARD=360t7 ./build.sh

Output: output/mt7981_360t7-bl2.bin (flash_bl2.bin) and output/mt7981_360t7-fip-fixed-parts.bin (flash_fip.bin).

Rescue Procedure

Boot via UART into U-Boot:

mtk_uartboot -s /dev/ttyUSB0 -p uart_bl2.bin --aarch64 -f flash_fip.bin

Serial output shows EMI: detected dram size: 512MB, successfully enters MT7981> U-Boot command line.

Flash via TFTP (PC connected to router's LAN port, U-Boot IP is 192.168.1.1, PC set to 192.168.1.2/24, TFTP server running):

tftpboot 0x46000000 flash_bl2.bin
mtd erase bl2
mtd write bl2 0x46000000 0x0 $filesize

tftpboot 0x46000000 flash_fip.bin
mtd erase fip
mtd write fip 0x46000000 0x0 $filesize

reset

Rebooted, flashed QWRT back, booted successfully, 512MB memory fully recognized, all good.

Rescue Files Download

I've uploaded the compiled files. Since the DDR3 frequency is lowered to 1866MT/s, these should have a higher success rate for memory-modded 360T7 units:

https://drive.qzydustin.com/360t7

Contains uart_bl2.bin, flash_bl2.bin, flash_fip.bin. Follow the steps above.

Reference

MTD Partition Layout

Stock firmware (dual firmware partitions):

mtdparts=nmbm_spim_nand:1024k(bl2),512k(u-boot-env),2048k(Factory),2048k(fip),36M(ubi),36M(firmware-1),36M(plugin),1M(config),512k(factory),7M(log)
PartitionSizeDescription
bl21024KBL2 bootloader
u-boot-env512KU-Boot environment variables
Factory2048KFactory calibration data
fip2048KFIP (bl31 + U-Boot)
ubi36MPrimary firmware
firmware-136MBackup firmware
plugin36MPlugin partition
config1MConfiguration
factory512KFactory info
log7MLog

hanwckf bl-mt798x partition layout:

mtdparts=nmbm_spim_nand:1024k(bl2),512k(u-boot-env),2048k(Factory),2048k(fip),108M(ubi),1M(config),512k(factory),7M(log)
PartitionSizeDescription
bl21024KBL2 bootloader
u-boot-env512KU-Boot environment variables
Factory2048KFactory calibration data
fip2048KFIP (bl31 + U-Boot)
ubi108MSystem firmware (merged stock ubi + firmware-1 + plugin)
config1MConfiguration
factory512KFactory info
log7MLog

Pitfall log:

#ActionResultCause
1Flashed official OpenWrt bl2/fip per docsBricked, dram size: 0MBOfficial firmware compiled for DDR3-2133, incompatible with 1600 chip
2Community rescue bl2Same 0MBAlso 2133 frequency
3Self-compiled DDR3-1866 bl2 + mtk_uartbootTimeoutSPI-NAND boot mode, no UART_DL code
4UART bl2 + DDR3-1866 + RAM bootSuccessFrequency and boot mode both correct

Appendix: NMBM Compatibility on 360T7

While this rescue was about DDR frequency, the investigation also revealed another major pitfall in the 360T7 ecosystem: NMBM (NAND Mapping Block Management) compatibility.

NMBM is MediaTek's proprietary NAND bad block management layer. When enabled, it reserves space at the end of flash for block mapping and must be enabled/disabled consistently in both ATF (BL2) and Linux kernel - mismatch causes problems. Critical data on NAND (Factory calibration, fip) without bad block protection can be lost if preceding blocks go bad, shifting fixed offsets. Different firmware takes different approaches:

  • NMBM (MediaTek proprietary, used by hanwckf): builds a bad block mapping table for the entire NAND at the BL2 stage. Upper layers see a "bad-block-free" virtual device, all partitions including Factory calibration are protected
  • OpenWrt default layout (old approach): uses a fixed-partitions framework overall. Only the ubi partition holding rootfs/kernel has UBI bad block management. Other partitions (Factory, fip) sit at raw fixed offsets on NAND with no protection
  • OpenWrt UBI layout (new approach, PR #22797): no NMBM, but puts Factory and fip inside UBI volumes so all critical data is under bad block management
Firmware/U-BootNMBMFactory ProtectionPartition Scheme
360 stockEnabledNMBM transparent mappingDual firmware (ubi 36M + firmware-1 36M)
hanwckf bl-mt798xEnabledNMBM transparent mappingLarge ubi 108M + trailing partitions
OpenWrt defaultDisabledNone (fixed-partitions, bad block risk)Large ubi 108M + trailing partitions
OpenWrt UBI (PR #22797, snapshot only)DisabledUBI volume managementOnly bl2 1M fixed, rest in UBI volumes (127M)

The key issue: OpenWrt firmware is incompatible with hanwckf U-Boot. hanwckf's ATF enables NMBM while OpenWrt's ATF does not. NMBM reserves space at the end of NAND for mapping, so the two see different usable partition sizes. Mixing them causes UBI volume table checksum failures and potentially another brick. Bottom line: flash the full stack together - bl2 + fip + firmware must all agree on NMBM and partition scheme, no mixing.

Practical Advice

If your 360T7 runs hanwckf U-Boot, don't flash OpenWrt UBI layout firmware alone. Either stick with immortalwrt-mt798x / QWRT and compatible firmware, or switch the entire stack to OpenWrt's official BL2 + FIP + firmware (but watch out for the DDR frequency issue discussed in this article).

Good news: PR #22797 (merged 2026-05-09) fixes both DDR3 frequency and adds UBI layout. Once a future OpenWrt stable release ships with this, memory-modded 360T7 units should no longer hit the frequency problem described here.

Appendix: 360T7 Firmware Comparison

Since we've touched on firmware compatibility, here's an overview of available firmware options for the 360T7.

OpenWrt OfficialImmortalWrt OfficialhanwckfQWRT (Lean)
BaseOpenWrt mainlineImmortalWrt (OpenWrt fork)ImmortalWrt 21.02 forkLean's LEDE
LicenseOpen sourceOpen sourceOpen source (WiFi blob closed)Closed source, paid
DistributionOfficial siteOfficial siteGitHub source / community buildsQQ group, paid binary
Paired U-BootOpenWrt official U-BootImmortalWrt official U-Boothanwckf bl-mt798xhanwckf bl-mt798x
Kernel6.6 (24.10)6.6 (24.10)5.4 (locked to 21.02)Undisclosed
WiFi DriverOpen source mt76Open source mt76MTK closed sourceMTK closed source
Hardware OffloadHardware flow offloadingHardware flow offloadingMTK SDK native HNATMTK SDK native HNAT

Key Differences

Open source mt76 vs MTK closed source driver: the closed source driver has advantages in signal strength, multi-client concurrency, and roaming. Open source mt76 is still catching up.

OpenWrt vs ImmortalWrt: ImmortalWrt carries patches not accepted upstream and China-region packages, with more aggressive kernel configuration.

hanwckf stuck on 21.02: the MTK closed source driver is tied to the old kernel SDK and cannot upgrade to newer kernels.

Hardware offloading: mainline firmware uses Hardware flow offloading which supports MT7981, though some users report suboptimal latency under heavy load. hanwckf/QWRT use MTK SDK native HNAT, closer to the stock implementation.