3. 内存操作命令(md/mw/mm):内存查看、写入与修改实战
内存操作,是 U-Boot 调试里最基础也最常用的三板斧。说白了,就是直接读写板子上的物理内存地址。我刚开始做嵌入式时,总觉得这跟应用层的指针操作差不多,后来踩过几次坑才明白——在 U-Boot 里写内存,写错了是真的会死机的。
这一节,我把 md、mw、mm 三个命令的用法和实战场景拆开讲。你跟着敲一遍,很快就能上手。
3.1 md —— 内存查看命令
md 的全称是 memory display,用来查看指定地址的内存数据。我个人习惯在调试驱动时先用它确认寄存器或内存缓冲区的初始值。
基本用法
md [.b, .w, .l] 地址 [长度]
后缀 .b 表示字节(8 位),.w 表示字(16 位),.l 表示长字(32 位)。不写后缀时,默认按 32 位显示。
| 后缀 | 数据宽度 | 每行显示 | 典型场景 |
|---|---|---|---|
| .b | 8 位 | 16 字节 | 查看串口 FIFO、MAC 地址 |
| .w | 16 位 | 16 字节 | 查看 GPIO 寄存器、定时器 |
| .l | 32 位 | 16 字节 | 查看 DDR 配置、DMA 描述符 |
实战示例
# 查看 0x80000000 开始的 64 字节(32 位模式)
md 0x80000000 0x10
# 按字节查看同一地址
md.b 0x80000000 0x40
输出格式一般是:左侧是地址,中间是十六进制数据,右侧是 ASCII 字符。嗯,这里要注意——如果地址对应的内存还没初始化,读出来可能是全 0xDEADBEEF 或者随机值,别慌,先确认 DDR 初始化是否完成。
md 扫一遍 DDR 起始地址,看看是不是全 0 或全 F。如果读到稳定的 0x00000000,说明 DDR 初始化大概率没问题。
3.2 mw —— 内存写入命令
mw 是 memory write,用来向指定地址写入数据。我曾在一次项目里用它手动修复过一段被误擦除的 BootROM 备份——当然,前提是你知道自己在写什么。
基本用法
mw [.b, .w, .l] 地址 数据 [长度]
同样支持 .b、.w、.l 后缀。如果不指定长度,默认只写一次。
实战示例
# 向 0x80001000 写入 0xA5A5A5A5(32 位,写 1 次)
mw.l 0x80001000 0xA5A5A5A5
# 向 0x80002000 开始连续写入 0x55(字节模式,写 256 次)
mw.b 0x80002000 0x55 0x100
mw。我曾经在调试一块 FPGA 加速卡时,不小心写了一个保留地址,结果整个 PCIe 总线挂死,只能断电重启。
3.3 mm —— 内存修改命令
mm 是 memory modify,它跟 mw 最大的区别是:mm 是交互式的。你执行一次 mm,它会显示当前地址的值,然后等你输入新值。按回车跳过,输入 q 退出。
基本用法
mm [.b, .w, .l] 地址
实战示例
# 交互式修改 0x80003000 开始的 32 位数据
mm.l 0x80003000
# 终端显示效果
80003000: 00000000 ? 0x12345678
80003004: 00000000 ? q
# 输入 q 退出
我个人觉得 mm 在调试寄存器时特别好用。比如你要逐个配置某个外设的多个寄存器,用 mm 可以一边看当前值一边改,不容易漏掉。
3.4 实战场景:用内存操作命令调试 DDR 初始化
这是我在一个 ARMv8 平台项目里真实遇到的情况。板子启动后,U-Boot 能打印信息,但一加载内核就崩溃。我怀疑是 DDR 的某个区域读写不稳定。
调试步骤
- 写模式测试:用
mw向 DDR 地址 0x80000000 写入 0xAA55AA55 - 读回验证:用
md读同一地址,确认数据一致 - 边界扫描:依次测试 0x80000000、0x80001000、0x80002000……直到发现异常
- 压力测试:用
mw.l 0x80000000 0x00000000 0x100000写满 4MB,再用md逐段检查
结果:我发现 0x80080000 附近读回的数据偶尔会跳变。后来定位到是 DDR 控制器的时序参数配置偏紧,调整后问题解决。
3.5 知识结构图:内存操作命令体系
下面这张图帮你理清三个命令的关系和适用场景:
3.6 避坑指南
- 地址对齐:32 位访问时,地址必须是 4 字节对齐。我曾经用
md.l 0x80000001读数据,U-Boot 直接报错。解决办法:用md.b或手动对齐地址。 - 长度单位:
md和mw的「长度」参数,单位是「次数」而不是字节。比如md.l 0x80000000 0x10显示 16 个 32 位数据,共 64 字节。 - 写敏感区域:DDR 控制器的配置寄存器、中断控制器寄存器,写之前一定要确认数据手册。写错了系统可能直接挂掉。
- 缓存一致性问题:在某些 ARM 平台上,U-Boot 默认开启数据缓存。你用
md读到的可能是缓存里的旧数据。遇到这种情况,先执行dcache off关掉数据缓存再读。
md 看一眼原始值,再用 mw 写一个新值,最后 md 确认。三步走,基本不会漏掉问题。