1. CRC校验基础
CRC(Cyclic Redundancy Check,循环冗余校验)是嵌入式系统中最常用的数据完整性校验算法之一。在u-boot启动流程中,CRC校验贯穿于镜像加载、环境变量验证、设备树检查等多个关键环节。理解CRC的原理与u-boot中的应用方式,是定位启动故障的第一步。
1.1 CRC校验原理
CRC校验的核心思想是将二进制数据视为一个巨大的多项式,通过模2除法(无进位加法)计算余数,并将该余数作为校验值附加在数据之后。接收方使用相同的多项式对数据(含校验值)进行除法运算,若余数为0,则认为数据未被篡改。
关键运算规则:
- 模2加法 = 异或(XOR),不进位
- 模2减法 = 异或(XOR),不借位
- 除法过程:每次比较被除数最高位与除数最高位,若对齐则做异或,否则直接下移
下图展示了CRC-32计算的基本流程,其中输入数据按字节流入移位寄存器,最终寄存器中的值即为CRC校验码。
1.2 多项式选择
CRC校验的核心参数是生成多项式(Generator Polynomial),它决定了校验的检错能力和计算效率。不同应用场景选择不同的多项式,u-boot中主要使用以下两种:
| CRC类型 | 生成多项式(十六进制) | 多项式(二进制) | u-boot应用场景 |
|---|---|---|---|
| CRC-32 | 0x04C11DB7 | 100000100110000010001110110110111 | 镜像完整性校验、环境变量校验 |
| CRC-16-CCITT | 0x1021 | 1000000100000100001 | 部分存储设备、通信协议 |
多项式选择原则:
- CRC-32(IEEE 802.3标准)是u-boot默认使用的校验算法,检错能力覆盖所有奇数位错误、所有双位错误、所有长度不超过32位的突发错误。
- CRC-16-CCITT常用于对计算速度要求更高、数据量较小的场景,如SPI Flash的页内校验。
- 多项式选择需与烧录工具、上位机软件保持一致,否则必然导致校验失败。
1.3 u-boot中的CRC应用场景
在u-boot启动流程中,CRC校验出现在以下关键位置,每个位置的校验失败都会导致不同的启动故障现象:
1.4 u-boot CRC校验相关代码结构
u-boot中CRC校验的实现集中在 lib/crc32.c 和 include/u-boot/crc.h 中。核心函数如下:
/* lib/crc32.c - CRC-32计算核心 */
uint32_t crc32(uint32_t crc, const unsigned char *buf, uint32_t len)
{
while (len--) {
crc ^= *buf++;
for (int i = 0; i < 8; i++) {
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320; /* 反转多项式 */
else
crc >>= 1;
}
}
return crc;
}
/* include/u-boot/crc.h - 对外接口声明 */
uint32_t crc32(uint32_t crc, const unsigned char *buf, uint32_t len);
uint32_t crc32_wd(uint32_t crc, const unsigned char *buf, uint32_t len, uint32_t chunk);
int crc32_verify(uint32_t expected, const unsigned char *buf, uint32_t len);
常见误区:
- u-boot使用的CRC-32多项式是
0xEDB88320(反转形式),而非标准形式0x04C11DB7。这是因为u-boot采用小端序逐位计算,多项式也做了位反转。 - CRC初始值固定为
0xFFFFFFFF,最终结果需取反(XOR 0xFFFFFFFF),这是IEEE 802.3标准的要求。 - 使用
crc32命令手动计算时,注意内存地址对齐和长度参数的单位是字节。
1.5 CRC校验失败的根本原因分类
从CRC校验原理出发,校验失败的原因可以归纳为以下三类,后续章节将逐一深入分析:
| 故障类别 | 根本原因 | 典型表现 |
|---|---|---|
| 数据损坏 | 存储介质位翻转、传输错误、Flash磨损 | CRC校验值不匹配,重烧录后恢复 |
| 算法不匹配 | 多项式、初始值、输出异或值不一致 | 上位机与u-boot计算结果不同 |
| 长度/偏移错误 | 校验范围与实际数据长度不一致 | 部分数据被遗漏或重复计算 |