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 的某个区域读写不稳定。

调试步骤

  1. 写模式测试:用 mw 向 DDR 地址 0x80000000 写入 0xAA55AA55
  2. 读回验证:用 md 读同一地址,确认数据一致
  3. 边界扫描:依次测试 0x80000000、0x80001000、0x80002000……直到发现异常
  4. 压力测试:用 mw.l 0x80000000 0x00000000 0x100000 写满 4MB,再用 md 逐段检查

结果:我发现 0x80080000 附近读回的数据偶尔会跳变。后来定位到是 DDR 控制器的时序参数配置偏紧,调整后问题解决。

3.5 知识结构图:内存操作命令体系

下面这张图帮你理清三个命令的关系和适用场景:

U-Boot 内存操作命令体系 U-Boot 内存操作命令体系 md 内存查看 mw 内存写入 mm 内存修改 md.b 字节 md.w 字 md.l 长字 单次写入 连续写入 模式填充 交互修改 逐地址修改 q 退出 实战建议 调试新板:md 扫内存 → mw 写测试数据 → md 读回验证 寄存器配置:mm 交互修改,逐地址确认

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 确认。三步走,基本不会漏掉问题。