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校验码。

CRC-32 计算流程 输入数据流 32位移位寄存器(初始值 0xFFFFFFFF) b31 b30 b29 ... b1 b0 XOR CRC校验值 说明: 每输入1位数据,寄存器左移1位;若移出的最高位为1,则与生成多项式进行异或反馈。 所有数据位处理完毕后,寄存器内容取反即得CRC-32校验值。

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校验出现在以下关键位置,每个位置的校验失败都会导致不同的启动故障现象:

u-boot中CRC校验的应用场景 场景1:SPL加载 校验u-boot.img头部CRC 场景2:环境变量 env CRC校验(冗余存储) 场景3:设备树 FDT头部CRC校验 场景4:Kernel镜像 bootm命令加载时校验 场景5:Firmware更新 烧录前CRC校验 场景6:校验和命令 crc32命令手动校验 故障关联分析: • SPL加载CRC失败 → 启动卡死,无任何输出 • 环境变量CRC失败 → 回退默认环境变量,启动参数异常 • 设备树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计算结果不同
长度/偏移错误 校验范围与实际数据长度不一致 部分数据被遗漏或重复计算