第四章:SPI Flash与固件存储——固件在SPI NOR Flash中的布局
大家好,欢迎来到第四章。这一章我们聊聊固件到底是怎么躺在SPI Flash里的。你可能会想,不就是把二进制文件烧进去吗?嗯,没那么简单。AMD平台的Flash布局,其实是个精心设计的“三明治”。
4.1 为什么是SPI NOR Flash?
先说说为什么选SPI NOR Flash。我刚开始接触嵌入式时也纳闷,NAND Flash便宜容量又大,为啥不用?后来踩了坑才明白——NOR Flash支持XIP(就地执行),CPU可以直接从Flash里取指令跑,不用先拷到RAM里。这对启动来说太关键了。
你想想看,系统刚上电时,DRAM还没初始化呢。这时候CPU要跑代码,只能从NOR Flash里直接读。NAND Flash虽然快,但坏块多、需要ECC,不适合做启动介质。
我个人习惯把SPI NOR Flash比作“固件的家”。这个家怎么装修,直接决定了系统能不能顺利起床。
4.2 Flash的整体布局
AMD平台的SPI Flash,通常被划分为几个大区。我画个简化的布局给你看:
+----------------------------+ <-- Flash顶部(高地址)
| UEFI/BIOS Region |
| (包含DXE、BDS等阶段) |
+----------------------------+
| PSP Firmware Region |
| (包含PSP OS、Bootloader) |
+----------------------------+
| Flash Descriptor |
| (4KB,位于Flash底部) |
+----------------------------+ <-- Flash底部(低地址,0x0)
注意,这个顺序不是固定的。不同项目、不同芯片组,布局可能不一样。但核心思想一致:Flash Descriptor在最底部,PSP固件在中间,BIOS/UEFI在最上面。
4.3 Flash Descriptor——固件世界的“房产证”
Flash Descriptor,简称FD。它只有4KB大小,但地位极高。我遇到过不少工程师,觉得FD就是个配置表,随便改改就行。结果呢?板子变砖了。
FD里到底存了什么?我列几个关键字段:
| 字段 | 大小 | 说明 |
|---|---|---|
| Signature | 4字节 | 固定为0x0FF0A55A,用于识别FD有效性 |
| Flash Region Base/Limit | 每个Region 12位 | 定义BIOS、PSP、ME等区域的起始和结束地址 |
| Master Access Permissions | 多个字段 | 谁可以读、写、擦除哪个区域 |
| PCH/CPU Straps | 若干字节 | 硬件配置引脚,比如SPI频率、模式选择 |
| Checksum | 1字节 | 整个FD的校验和,必须为0 |
说白了,FD就是一张“房产证”,告诉CPU:哪块地归谁管,谁有权进去。
4.4 PSP Firmware Region——AMD的“秘密花园”
PSP(Platform Security Processor)是AMD平台里一个独立的ARM Cortex-A5处理器。它有自己的固件,存放在Flash的PSP Region里。
PSP固件包含哪些东西?我拆解一下:
- PSP Bootloader:PSP上电后第一个跑的代码,负责验证后续固件签名
- PSP OS:一个轻量级操作系统,管理安全服务
- SMU Firmware:系统管理单元固件,控制电源、时钟
- Secure OS:可信执行环境(TEE)的底层
我记得有一次调试,板子死活起不来。用逻辑分析仪抓SPI总线,发现PSP Bootloader读了一半就停了。后来查出来是PSP Region的起始地址在FD里配错了,PSP读到了BIOS区域的数据,校验失败直接挂起。
4.5 BIOS/UEFI Region——我们最熟悉的部分
BIOS Region存放的是大家熟悉的UEFI固件。它通常包含:
- SEC Phase:安全验证阶段,极小的汇编代码
- PEI Phase:早期初始化,比如CPU、内存
- DXE Phase:驱动执行环境,加载各种驱动
- BDS Phase:启动设备选择,进入Shell或OS
- NVRAM:存储BIOS设置项
这里有个容易忽略的点:BIOS Region的大小不是随便定的。我见过有人把BIOS Region设成8MB,但实际固件只有6MB。看起来没问题对吧?但FD里Region的结束地址是按Flash块对齐的,如果结束地址没对齐,PSP在验证时会认为Region越界,直接拒绝启动。
嗯,这里要注意:BIOS Region的结束地址必须等于Flash的物理块边界。比如Flash的块大小是64KB,那结束地址必须是0x...0000或0x...FFFF这样的对齐地址。
4.6 实战:如何查看Flash布局
在开发板上,我们可以用以下方法查看当前Flash布局:
# 在UEFI Shell下
shell> mm 0xFED01000 4 # 读取FD基地址(不同平台可能不同)
shell> dmem 0xFED01000 0x1000 # dump出整个FD
# 在Linux下
# 先确保spi驱动加载
# 读取FD区域
dd if=/dev/mtd0 of=fd_dump.bin bs=1k count=4
# 用hexdump查看
hexdump -C fd_dump.bin | head -20
我个人习惯用hexdump配合grep快速定位关键字段。比如查找Signature:
hexdump -C fd_dump.bin | grep "0ff0 a55a"
如果找不到这个签名,说明FD损坏或者读取地址不对。
4.7 避坑指南:Flash布局常见问题
做AMD平台启动这么多年,我总结了几条血泪教训:
- FD校验和必须为0:修改FD后,一定要重新计算校验和。工具通常会自动算,但手动改二进制时容易忘。
- Region不能重叠:BIOS Region和PSP Region的地址范围不能有交集。我曾经为了省空间,把两个Region设成相邻但没留空隙,结果PSP和BIOS互相踩踏。
- Master权限要谨慎:FD里定义了谁可以访问哪个Region。如果把BIOS的写权限关了,后续固件更新会失败。我遇到过客户把权限设得太死,导致无法在线升级。
- SPI频率别设太高:FD里有SPI时钟频率配置。我建议先设成30MHz以下调试,稳定了再往上提。有一次我直接设成66MHz,结果Flash时序跟不上,数据读出来全是错的。
4.8 小结
这一章我们聊了SPI NOR Flash里固件是怎么摆放的。说白了就是三块:底部的Flash Descriptor管全局,中间的PSP固件管安全,顶部的BIOS/UEFI管启动。每块各司其职,缺一不可。
下一章我们会深入PSP的启动流程,看看那个神秘的ARM核到底在干什么。到时候我会分享一个我调试PSP Bootloader时遇到的“灵异事件”——Flash里的数据明明是对的,但PSP就是报校验失败。你猜问题出在哪?
好,今天就到这里。如果你在Flash布局上遇到过什么奇葩问题,欢迎交流。咱们下章见。