mBrush V4.2刷机

Lichium 于 2026-03-23 发布

记一次救砖:MBrush v4.2 (君正 X1021) 内存注入

踩坑背景

在折腾MBrush v4.2时,因为疏忽,把针对新版单片机架构(MBrush2)的固件刷了进去。

重启后,打印机彻底变成了“砖头”:USB 连不上、网页打不开。

我尝试UART调试,由于触点间距1mm,我的选择是拿三个别针接触它们,然后连接usb工具。显示日志:

U-Boot 2013.07-g0fa8b95 (Sep 29 2019 - 22:45:03)
Board: Sepal (Ingenic XBurst X1021 SoC)
DRAM:  64 MiB
...
NAND:  128 MiB (ATO25D1GA)

然而


绝望一:手速再快也按不住的 bootdelay=0

系统确实在启动,内核也确实报错了(Wrong Image Format for bootm command)。但我发现,U-Boot 的日志中赫然写着:

Hit any key to stop autoboot: 0

0 秒延迟!这意味着 U-Boot 在初始化串口后,连一毫秒都不等,直接去读坏掉的内核。无论我怎么疯狂按回车,都无法进入 U-Boot 命令行。

方案:Python 加特林物理外挂

既然人的手速不行,那就让机器来。我写了一个 Python 脚本,以 3Mbps 的满速在串口疯狂循环发送回车符。

import serial, time
# 提前疯狂发送回车,通电瞬间拦截 U-Boot
ser = serial.Serial('COM3', 3000000, timeout=0.01)
while True:
    ser.write(b' \r\n')
    time.sleep(0.005)
    if b'sepal' in ser.read_all():
        print("拦截成功!")
        break

然而行不通。 我另辟蹊径,找到其NAND Flash,在 NAND read: device 0 offset 0x100000... 瞬间用镊子短接VCC和CS,成功进入U-Boot。


绝望二:被阉割到极致的 U-Boot

拿到 Shell 后,敲下 loady 0x80600000(准备用 Ymodem 协议把正确的 uImage 内核传进内存)。

结果系统冰冷地回复:

Unknown command 'loady' - try 'help'

我敲下 help,心凉了半截。这个 U-Boot 为了节省空间,loadyloadxloadb 等所有的串口文件传输协议全部阉割了! 同时,日志显示 GMAC failed to reset!,意味着底层根本没有初始化网卡,tftpboot 也成了废设。

USB 线刷又卡在 Win10 的驱动签名上…… 难道真的没救了?


绝望三变绝杀:3Mbps 串口下的“内存硬写”大法!

看着手里的 uImage 文件(大约 2.9MB)和这个只有基础读写命令的 U-Boot,我突然意识到: U-Boot 支持 mw.l (Memory Write Long) 命令,可以直接向指定内存地址写入 32 位的十六进制数据。

既然串口波特率高达 3Mbps,我为什么不直接写一个 Python 脚本,把这 2.9MB 的内核文件切碎,翻译成几十万条 mw.l 文本命令,一行一行硬“灌”进打印机的内存里呢?!

说干就干!我编写了以下这个“硬核注入器”脚本:

核心注入脚本 (inject_uImage.py)

import serial
import struct
import time
import sys

PORT = 'COM3'         # 你的串口号
BAUDRATE = 3000000
FILE_PATH = 'uImage'  # 正确的内核文件

def main():
    ser = serial.Serial(PORT, BAUDRATE, timeout=1)
    
    with open(FILE_PATH, 'rb') as f:
        data = f.read()

    # 补齐 4 字节对齐
    while len(data) % 4 != 0:
        data += b'\x00'

    addr = 0x80600000
    print(f"🚀 开始将 {len(data)} 字节硬核注入到内存 0x{addr:X} ...")
    
    # 唤醒 U-Boot 提示符
    ser.write(b'\r\n')
    ser.read_until(b'#')

    start_time = time.time()
    # 将二进制文件转换为 32位 小端序整数
    words =[struct.unpack('<I', data[i:i+4])[0] for i in range(0, len(data), 4)]

    for i, word in enumerate(words):
        # 拼接 U-Boot 的 mw.l 命令
        cmd = f"mw.l {addr + i*4:x} {word:08x}\r\n".encode('ascii')
        ser.write(cmd)
        
        # 极速等待 U-Boot 响应,确保数据不丢失
        if b'#' not in ser.read_until(b'#'):
            # 超时重试机制
            ser.write(b'\r\n')
            ser.read_until(b'#')
            ser.write(cmd)
            ser.read_until(b'#')
        
        if i % 10000 == 0 or i == len(words) - 1:
            progress = (i / len(words)) * 100
            print(f"📦 进度: {progress:.1f}% | 已耗时 {time.time() - start_time:.1f} 秒")

    print("\n🎉 内存注入完成!")
    ser.close()

if __name__ == '__main__':
    main()

运行结果: 脚本启动后,电脑就像一个发疯的黑客,以每秒几百条命令的速度向 X1021 芯片狂轰滥炸。得益于 3Mbps 的超高波特率,这几百万字节的文本命令传输居然没有任何卡顿。 大约 10 多分钟后,进度条走到 100%,2.9MB 的 uImage 完美地在机器的 0x80600000 内存处拼装完成!


终局:擦除与烧录

关闭 Python 脚本,我重新打开了 XCOM 串口助手,敲下回车,熟悉的 sepal_x1021-sfcnand# 依然在等我。

既然内核已经躺在内存里了,剩下的就是把它永久烙印进 SPI NAND 闪存里:

# 1. 顺手把这该死的 0 秒延迟改掉,拯救未来的自己
sepal_x1021-sfcnand# setenv bootdelay 3
sepal_x1021-sfcnand# saveenv

# 2. 擦除原本损坏的内核分区 (从 0x100000 开始的 4MB 空间)
sepal_x1021-sfcnand# nand erase 0x100000 0x400000

# 3. 将刚刚辛苦灌入 0x80600000 内存的数据写入闪存
sepal_x1021-sfcnand# nand write 0x80600000 0x100000 0x400000

# 4. 重启,见证奇迹!
sepal_x1021-sfcnand# reset

随着 reset 命令的敲下,屏幕上终于出现了令人热泪盈眶的: Starting kernel ...

紧接着,Linux 的开机日志倾泻而出,MBrush 满血复活!

总结

这次救砖之旅简直叠满了嵌入式开发的 debuff:按键锁死 (bootdelay=0) + 传输协议被阉割 + 网络模块失效

但这也恰恰证明了底层的魅力:只要 UART 串口还在,只要 CPU 还能解析基础的读写指令,哪怕是用最原始的 mw.l 文本命令敲进去几百万行代码,也一样能把系统救回来。

本文由AI生成,仅做简单记录和适当参考