U-Boot 2026年8月31日 156 分钟

04-U-Boot完整启动流程详解

概述 U-Boot 的启动过程是一个复杂而精密的过程,从硬件上电复位到最终进入命令行,经历了多个阶段。本文档将详细剖析整...
本文目录 展开目录

概述

U-Boot 的启动过程是一个复杂而精密的过程,从硬件上电复位到最终进入命令行,经历了多个阶段。本文档将详细剖析整个启动流程,包括汇编启动代码、重定位、C代码初始化等关键环节。

1. 启动流程总览

1.1 启动阶段划分

flowchart TD
    A[上电复位<br/>PC = 0x0] --> B[Stage 1<br/>start.S 汇编启动]
    B --> C[Stage 2<br/>lowlevel_init 底层初始化]
    C --> D[Stage 3<br/>_main 进入 C 运行时]
    D --> E[Stage 4<br/>board_init_f 重定位前初始化]
    E --> F[Stage 5<br/>relocate_code 代码重定位]
    F --> G[Stage 6<br/>board_init_r 重定位后初始化]
    G --> H[Stage 7<br/>main_loop 命令行循环]
    
    style A fill:#f99,stroke:#333,stroke-width:3px
    style D fill:#9f9,stroke:#333,stroke-width:2px
    style F fill:#99f,stroke:#333,stroke-width:2px
    style H fill:#fcf,stroke:#333,stroke-width:3px

1.2 内存使用变化

graph LR
    A[启动时<br/>Flash/ROM] --> B[重定位前<br/>Flash执行]
    B --> C[重定位<br/>复制到RAM]
    C --> D[重定位后<br/>RAM执行]
    
    style A fill:#fcc,stroke:#333,stroke-width:2px
    style B fill:#ffc,stroke:#333,stroke-width:2px
    style D fill:#cfc,stroke:#333,stroke-width:2px

2. Stage 1: 汇编启动代码 (start.S)

2.1 入口点:reset

文件位置: arch/arm/cpu/armv7/start.S

.globl  reset
.globl  save_boot_params_ret

reset:
    /* 允许开发板保存重要寄存器 */
    b   save_boot_params

save_boot_params_ret:
    /*
     * 禁用中断 (FIQ 和 IRQ),设置 CPU 为 SVC32 模式
     */
    mrs r0, cpsr
    and r1, r0, #0x1f           @ 提取模式位
    teq r1, #0x1a               @ 测试是否为 HYP 模式
    bicne r0, r0, #0x1f         @ 清除所有模式位
    orrne r0, r0, #0x13         @ 设置为 SVC 模式
    orr r0, r0, #0xc0           @ 禁用 FIQ 和 IRQ
    msr cpsr, r0

2.2 关键步骤详解

flowchart TD
    A[reset 复位入口] --> B[save_boot_params<br/>保存启动参数]
    B --> C[设置 CPU 模式<br/>SVC32 + 禁用中断]
    C --> D[设置异常向量表<br/>VBAR 寄存器]
    D --> E[cpu_init_cp15<br/>CP15 协处理器初始化]
    E --> F[cpu_init_crit<br/>关键硬件初始化]
    F --> G[_main<br/>进入 C 环境]
    
    style A fill:#f99,stroke:#333,stroke-width:3px
    style G fill:#9f9,stroke:#333,stroke-width:3px

2.3 CPU 模式和寄存器设置

ARM CPU 模式:

模式编码说明
User0x10用户模式(非特权)
FIQ0x11快速中断模式
IRQ0x12普通中断模式
SVC0x13管理模式(U-Boot 使用)
Abort0x17数据访问异常模式
Undef0x1B未定义指令模式
HYP0x1A虚拟化模式(ARMv7)
graph TD
    A[CPSR 寄存器<br/>当前程序状态] --> B[位 0-4<br/>模式位]
    A --> C[位 5<br/>Thumb状态]
    A --> D[位 6<br/>FIQ禁用]
    A --> E[位 7<br/>IRQ禁用]
    A --> F[位 24-31<br/>条件标志]
    
    B --> G[0x13 = SVC模式]
    D --> H[1 = 禁用FIQ]
    E --> I[1 = 禁用IRQ]
    
    style A fill:#fcf,stroke:#333,stroke-width:2px
    style G fill:#9f9,stroke:#333,stroke-width:2px

2.4 CP15 协处理器初始化

ENTRY(cpu_init_cp15)
    /*
     * 使无效 L1 I/D cache
     */
    mov r0, #0
    mcr p15, 0, r0, c8, c7, 0   @ 使无效 TLB
    mcr p15, 0, r0, c7, c5, 0   @ 使无效 I-cache
    mcr p15, 0, r0, c7, c5, 6   @ 使无效分支预测
    mcr p15, 0, r0, c7, c10, 4  @ 数据同步屏障
    mcr p15, 0, r0, c7, c5, 4   @ 指令同步屏障

    /*
     * 禁用 MMU 和 Cache
     */
    mrc p15, 0, r0, c1, c0, 0   @ 读取系统控制寄存器
    bic r0, r0, #0x00002000     @ 清除 V 位(向量表位置)
    bic r0, r0, #0x00000007     @ 清除 C、A、M 位
    orr r0, r0, #0x00000002     @ 设置 A 位(对齐检查)
    orr r0, r0, #0x00000800     @ 设置 Z 位(分支预测)
#ifndef CONFIG_SYS_ICACHE_OFF
    orr r0, r0, #0x00001000     @ 设置 I 位(I-cache)
#endif
    mcr p15, 0, r0, c1, c0, 0   @ 写回系统控制寄存器

CP15 寄存器功能:

graph TD
    A[CP15 协处理器] --> B[c1: 系统控制]
    A --> C[c7: Cache/TLB操作]
    A --> D[c8: TLB管理]
    A --> E[c12: 向量基地址]
    A --> F[c15: 实现定义]
    
    B --> G[MMU使能<br/>Cache使能<br/>对齐检查]
    C --> H[Cache清除<br/>Cache使无效]
    D --> I[TLB使无效]
    
    style A fill:#f99,stroke:#333,stroke-width:2px

3. Stage 2: 底层初始化 (lowlevel_init)

3.1 函数调用链

sequenceDiagram
    participant start as start.S
    participant crit as cpu_init_crit
    participant low as lowlevel_init
    participant board as s_init
    
    start->>crit: bl cpu_init_crit
    crit->>low: bl lowlevel_init
    low->>low: 设置栈指针
    low->>board: bl s_init
    board->>board: 时钟初始化
    board->>board: DDR 初始化
    board->>low: 返回
    low->>crit: 返回
    crit->>start: 返回
    
    Note over board: 板级特定初始化

3.2 栈的设置

在进入 C 代码之前,必须先设置好栈:

lowlevel_init:
    /*
     * 设置栈指针到片上 SRAM
     */
    ldr sp, =CONFIG_SYS_INIT_SP_ADDR
    bic sp, sp, #7  /* 8字节对齐 */
    
    /* 调用板级初始化 */
    bl  s_init
    
    /* 返回到调用者 */
    mov pc, lr

栈空间布局:

graph TD
    A[片上 SRAM<br/>CONFIG_SYS_INIT_SP_ADDR] --> B[栈顶<br/>sp 指向这里]
    B --> C[栈向下增长]
    C --> D[GD 全局数据]
    D --> E[BD 板级数据]
    
    style A fill:#fcf,stroke:#333,stroke-width:2px
    style B fill:#f99,stroke:#333,stroke-width:2px

3.3 ⚠️ DDR 初始化的时机澄清

重要说明: 很多人误以为 lowlevel_init 或 s_init() 中会初始化 DDR,实际情况取决于平台。

i.MX6 平台的 DDR 初始化方式

在 i.MX6 平台上,DDR 初始化在 U-Boot 代码执行之前 就已经完成:

sequenceDiagram
    participant BootROM
    participant DCD as DCD 表
    participant DDR as DDR 硬件
    participant UBoot as U-Boot 代码
    
    Note over BootROM: 上电后 BootROM 执行
    BootROM->>BootROM: 1. 读取 u-boot.imx
    BootROM->>DCD: 2. 找到 DCD 表
    BootROM->>DCD: 3. 执行 DCD 命令
    DCD->>DDR: 4. 配置 MMDC 寄存器
    DDR->>DDR: 5. DDR 初始化完成
    Note over DDR: ✅ DDR 硬件已可用
    BootROM->>DDR: 6. 复制 U-Boot 到 DDR
    BootROM->>UBoot: 7. 跳转到 U-Boot
    Note over UBoot: lowlevel_init 开始执行<br/>此时 DDR 已经可用

DCD 表示例:

/* DCD 表:在 u-boot.imx 中,BootROM 执行 */
DCD Write Commands:
  // 1. 使能 DDR 时钟
  WRITE 0x020C4018, 0x00260324
  
  // 2. 配置 IOMUX(DDR 引脚)
  WRITE 0x020E0798, 0x000C0000  // DDR_A0
  WRITE 0x020E079C, 0x000C0000  // DDR_A1
  ... (50+ 条配置)
  
  // 3. 配置 MMDC(DDR 控制器)
  WRITE 0x021B0800, 0xA1390003  // 控制寄存器
  WRITE 0x021B080C, 0x001F001F  // 时序参数
  
  // 4. 启动 DDR
  WRITE 0x021B0000, 0xC3190000  // DDR3, 512MB

因此在 i.MX6 平台:

  • ✅ BootROM 阶段:通过 DCD 表初始化 DDR 硬件
  • ✅ lowlevel_init:只设置栈(在 OCRAM),不初始化 DDR
  • ✅ **s_init()**:初始化时钟、GPIO 等,不初始化 DDR
  • ✅ **dram_init()**:探测 DDR 大小并告诉 U-Boot

时间线对比:

阶段DDR 硬件状态U-Boot 对 DDR 的认知
BootROM + DCD✅ 已初始化❌ U-Boot 还没运行
lowlevel_init✅ 可用❌ 不知道大小
board_init_f::dram_init()✅ 可用✅ 知道大小了
relocate_code✅ 可用✅ 知道布局

❓ 为什么 lowlevel_init 不用 DDR 做栈?

虽然此时 DDR 硬件已可用,但:

  • U-Boot 还不知道 DDR 的大小和布局
  • 代码还在 Flash/低地址执行,未重定位
  • OCRAM 是 CPU 内部 SRAM,更快更可靠
  • 后续会通过 dram_init() 探测 DDR,再进行重定位

其他平台可能不同:

在没有 DCD 机制的平台(如某些 ARM Cortex-A 芯片),DDR 初始化确实在 s_init() 或 SPL 中完成:

/* 示例:其他平台的 s_init() 可能包含 DDR 初始化 */
void s_init(void)
{
    /* 时钟初始化 */
    setup_clocks();
    
    /* DDR 初始化(非 i.MX6) */
    ddr_init();  // 配置 DDR 控制器寄存器
    
    /* GPIO 初始化 */
    setup_gpios();
}

4. Stage 3: 进入 C 环境 (_main)

4.1 _main 函数流程

文件位置: arch/arm/lib/crt0.S

flowchart TD
    A[_main 入口] --> B[设置栈指针<br/>sp = CONFIG_SYS_INIT_SP_ADDR]
    B --> C[board_init_f_alloc_reserve<br/>为 GD/BD 预留空间]
    C --> D[调整栈指针<br/>sp = sp - GD_SIZE - BD_SIZE]
    D --> E[board_init_f_init_reserve<br/>初始化 GD/BD 结构]
    E --> F[清零 GD 结构<br/>memset gd, 0]
    F --> G[board_init_f<br/>重定位前初始化]
    
    G --> H[遍历 init_sequence_f 数组]
    H --> I[dram_init<br/>探测 DDR 大小]
    I --> J[reserve_xxx<br/>预留内存空间]
    J --> K[setup_reloc<br/>计算重定位地址]
    
    K --> L[relocate_code<br/>代码重定位]
    L --> M[重定位到 DDR 高地址]
    M --> N[c_runtime_cpu_setup<br/>设置新的 GD 地址]
    N --> O[board_init_r<br/>重定位后初始化]
    
    O --> P[run_main_loop<br/>进入主循环]
    
    style A fill:#f99,stroke:#333,stroke-width:3px
    style G fill:#9f9,stroke:#333,stroke-width:2px
    style L fill:#99f,stroke:#333,stroke-width:2px
    style P fill:#fcf,stroke:#333,stroke-width:3px

_main 函数代码:

ENTRY(_main)
    /*
     * 设置初始 C 运行时环境并调用 board_init_f()
     */
    ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
    bic sp, sp, #7  /* 8字节对齐 */
    
    mov r0, sp
    bl  board_init_f_alloc_reserve
    mov sp, r0
    
    /* 清零 GD (global data) */
    mov r0, sp
    bl  board_init_f_init_reserve
    
    /* 调用重定位前初始化函数 */
    mov r0, #0
    bl  board_init_f
    
    /* 此处不应该返回,除非出错 */
    
    /*
     * board_init_f() 不会返回
     * 而是跳转到 relocate_code,然后调用 board_init_r
     */
ENDPROC(_main)

❓ 为什么 _main 是汇编而不是 C?

因为此时 C 运行时环境还未完全建立:

  • 栈指针未设置
  • BSS 段未清零
  • 全局变量未初始化
  • 需要精确控制栈和 GD 的位置

4.1.1 _main 详细执行流程

sequenceDiagram
    participant Start as start.S
    participant Main as _main (crt0.S)
    participant Alloc as board_init_f_alloc_reserve
    participant InitRes as board_init_f_init_reserve
    participant InitF as board_init_f
    participant Reloc as relocate_code
    participant InitR as board_init_r
    
    Start->>Main: bl _main
    Note over Main: 1. 设置初始栈
    Main->>Main: ldr sp, CONFIG_SYS_INIT_SP_ADDR
    
    Note over Main: 2. 预留 GD/BD 空间
    Main->>Alloc: bl board_init_f_alloc_reserve
    Alloc-->>Main: 返回新栈地址
    Main->>Main: mov sp, r0
    
    Note over Main: 3. 初始化 GD/BD 结构
    Main->>InitRes: bl board_init_f_init_reserve
    InitRes->>InitRes: 清零 GD 结构
    InitRes->>InitRes: 设置 gd->bd 指针
    InitRes-->>Main: 返回
    
    Note over Main: 4. 重定位前初始化
    Main->>InitF: bl board_init_f(0)
    InitF->>InitF: 执行 init_sequence_f[]
    InitF->>InitF: dram_init() - 探测 DDR
    InitF->>InitF: reserve_xxx() - 预留内存
    InitF->>InitF: setup_reloc() - 计算重定位地址
    InitF-->>Main: 不返回,跳转到重定位
    
    Note over Main: 5. 代码重定位(ARM 架构)
    InitF->>Reloc: 跳转到 relocate_code
    Reloc->>Reloc: 复制代码到 DDR 高地址
    Reloc->>Reloc: 修正地址引用
    
    Note over Main: 6. 重定位后初始化
    Reloc->>InitR: 跳转到 board_init_r
    InitR->>InitR: 执行 init_sequence_r[]
    InitR->>InitR: 初始化外设驱动
    InitR->>InitR: 加载环境变量
    
    Note over Main: 7. 进入主循环
    InitR->>InitR: run_main_loop()
    Note over InitR: 永不返回

4.1.2 栈空间分配

SRAM (片上 SRAM / OCRAM)

0x00900000  ┌─────────────────────┐ IRAM_BASE_ADDR
            │                     │
            │   未使用空间         │
            │                     │
0x0091E000  ├─────────────────────┤
            │   栈空间 (8KB)       │
            │   ↓↓↓ 向下增长      │  ← sp 从这里开始
0x0091FE00  ├─────────────────────┤
            │   Board Data (bd_t) │  (约 256 字节)
0x0091FF00  ├─────────────────────┤
            │   Global Data (gd_t)│  (约 256 字节)
0x00920000  └─────────────────────┘ IRAM_BASE_ADDR + IRAM_SIZE

/* 栈指针计算 */
sp = CONFIG_SYS_INIT_SP_ADDR           // 0x00920000
sp = sp - sizeof(gd_t)                 // 预留 GD
sp = sp - sizeof(bd_t)                 // 预留 BD
sp = sp & ~7                           // 8 字节对齐

关键点:

  • 栈向下增长(从高地址向低地址)
  • GD 和 BD 在栈空间的顶部
  • r9 寄存器指向 GD 结构

4.2 内存布局(重定位前)

graph TD
    A[Flash 0x00000000] --> B[U-Boot 代码段<br/>.text .rodata]
    B --> C[U-Boot 数据段<br/>.data]
    
    D[SRAM CONFIG_SYS_INIT_SP_ADDR] --> E[栈空间<br/>向下增长]
    E --> F[GD 全局数据结构<br/>sizeof gd_t]
    F --> G[BD 板级数据]
    
    H[DDR 内存<br/>尚未初始化] --> I[将来重定位的目标]
    
    style A fill:#fcc,stroke:#333,stroke-width:2px
    style D fill:#cfc,stroke:#333,stroke-width:2px
    style H fill:#ccc,stroke:#333,stroke-width:2px

4.3 全局数据结构 (gd_t)

💡 重要概念: gd_t 是 U-Boot 中最核心的全局数据结构,贯穿整个启动流程。详细说明请参考:10-U-Boot核心全局数据结构详解.md

结构体定义: include/asm-generic/global_data.h

typedef struct global_data {
    bd_t *bd;               /* 板级数据指针 */
    unsigned long flags;     /* 标志位(GD_FLG_RELOC 等)*/
    unsigned int baudrate;   /* 串口波特率 */
    unsigned long cpu_clk;   /* CPU 时钟频率 */
    unsigned long bus_clk;   /* 总线时钟频率 */
    unsigned long ram_size;  /* DDR 大小 */
    unsigned long ram_top;   /* DDR 顶部地址 */
    unsigned long relocaddr; /* U-Boot 重定位目标地址 */
    unsigned long start_addr_sp; /* 新栈地址 */
    unsigned long reloc_off; /* 重定位偏移量 */
    const void *fdt_blob;    /* 设备树地址 */
    struct udevice *cur_serial_dev; /* 当前串口设备 */
    void **jt;              /* 跳转表指针 */
    char env_buf[32];       /* 环境变量缓冲区 */
    // ... 更多字段,详见完整结构体文档
} gd_t;

GD 在寄存器中的存放:

  • ARM 架构: r9 寄存器固定指向 gd
  • 访问方式: 通过 DECLARE_GLOBAL_DATA_PTR 宏
/* arch/arm/include/asm/global_data.h */
#define DECLARE_GLOBAL_DATA_PTR  register volatile gd_t *gd asm ("r9")

/* 使用示例 */
void some_function(void)
{
    DECLARE_GLOBAL_DATA_PTR;  /* 声明 gd 指针 */
    
    printf("RAM size: 0x%lx\n", gd->ram_size);
    printf("Relocated to: 0x%lx\n", gd->relocaddr);
}

gd 的作用:

  1. 启动参数传递 – 在各初始化阶段传递关键信息
  2. 内存布局管理 – 记录 DDR 大小、重定位地址等
  3. 硬件状态记录 – CPU 频率、时钟、外设状态
  4. 跨阶段通信 – 重定位前后的数据传递桥梁

gd 在启动流程中的演变:

阶段gd 的状态关键字段
_main 开始分配空间,清零gd 地址固定在 SRAM 顶部
board_init_f填充基本信息baudrate, ram_size, ram_top
setup_reloc()计算重定位参数relocaddr, reloc_off, start_addr_sp
relocate_code复制到新地址r9 更新为新 gd 地址
board_init_r完整可用`flags

常用标志位:

#define GD_FLG_RELOC         0x00001  /* 代码已重定位 */
#define GD_FLG_ENV_READY     0x00080  /* 环境变量已加载 */
#define GD_FLG_SERIAL_READY  0x00100  /* 串口已就绪 */

/* 检查标志位 */
if (gd->flags & GD_FLG_RELOC) {
    printf("Code has been relocated\n");
}

5. Stage 4: 重定位前初始化 (board_init_f)

5.1 初始化序列

文件位置: common/board_f.c

static init_fnc_t init_sequence_f[] = {
    setup_mon_len,              /* 设置 monitor 长度 */
    fdtdec_setup,               /* 设备树解析 */
    initf_malloc,               /* 早期 malloc 初始化 */
    arch_cpu_init,              /* 架构相关 CPU 初始化 */
    mark_bootstage,             /* 启动阶段标记 */
    board_early_init_f,         /* 板级早期初始化 */
    timer_init,                 /* 定时器初始化 */
    env_init,                   /* 环境变量初始化 */
    init_baud_rate,             /* 波特率初始化 */
    serial_init,                /* 串口初始化 */
    console_init_f,             /* 控制台初始化(阶段1) */
    display_options,            /* 显示版本信息 */
    print_cpuinfo,              /* 打印CPU信息 */
    dram_init,                  /* DRAM 初始化 */
    setup_dest_addr,            /* 设置重定位目标地址 */
    reserve_mmu,                /* 预留 MMU 页表空间 */
    reserve_uboot,              /* 预留 U-Boot 代码空间 */
    reserve_malloc,             /* 预留 malloc 空间 */
    reserve_global_data,        /* 预留全局数据空间 */
    reserve_fdt,                /* 预留设备树空间 */
    reserve_stacks,             /* 预留栈空间 */
    setup_dram_config,          /* 设置 DRAM 配置 */
    display_new_sp,             /* 显示新的栈指针 */
    reloc_fdt,                  /* 重定位设备树 */
    setup_reloc,                /* 设置重定位参数 */
    NULL,
};

5.2 初始化流程图

flowchart TD
    A[board_init_f 开始] --> B[setup_mon_len<br/>计算U-Boot大小]
    B --> C[arch_cpu_init<br/>CPU架构初始化]
    C --> D[timer_init<br/>定时器初始化]
    D --> E[serial_init<br/>串口初始化]
    E --> F[console_init_f<br/>控制台初始化]
    F --> G[display_options<br/>显示版本信息]
    G --> H[print_cpuinfo<br/>打印CPU信息]
    H --> I[dram_init<br/>DRAM初始化]
    I --> J[计算内存布局]
    J --> K[reserve_xxx<br/>预留各种空间]
    K --> L[setup_reloc<br/>设置重定位参数]
    
    style A fill:#f99,stroke:#333,stroke-width:2px
    style I fill:#9f9,stroke:#333,stroke-width:2px
    style L fill:#99f,stroke:#333,stroke-width:2px

5.3 内存预留计算

graph TD
    A[RAM 顶部<br/>gd->ram_top] --> B[预留 TLB<br/>MMU页表 16KB]
    B --> C[预留 U-Boot<br/>代码+数据]
    C --> D[预留 Malloc<br/>堆空间]
    D --> E[预留 Board Info<br/>bd_t结构]
    E --> F[预留 GD<br/>gd_t结构]
    F --> G[预留 FDT<br/>设备树]
    G --> H[预留栈空间<br/>16KB]
    H --> I[最终可用RAM底部]
    
    style A fill:#f99,stroke:#333,stroke-width:2px
    style C fill:#9f9,stroke:#333,stroke-width:2px
    style I fill:#99f,stroke:#333,stroke-width:2px

计算示例:

假设 DDR 内存为 512MB (0x80000000 – 0xA0000000)

RAM 顶部: 0xA0000000
- TLB (16KB):           0x9FFFC000
- U-Boot (512KB):       0x9FF7C000
- Malloc (32MB):        0x9DF7C000
- Board Info (256B):    0x9DF7BF00
- Global Data (256B):   0x9DF7BE00
- FDT (64KB):           0x9DF6BE00
- Stack (16KB):         0x9DF67E00
= 新的栈顶:             0x9DF67E00

5.4 关键函数详解

dram_init() – DRAM 初始化:

int dram_init(void)
{
    /* 从设备树或板级配置获取 DRAM 大小 */
    gd->ram_size = get_ram_size((long *)PHYS_SDRAM, PHYS_SDRAM_SIZE);
    return 0;
}

setup_dest_addr() – 计算重定位地址:

static int setup_dest_addr(void)
{
    gd->ram_top = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
    gd->relocaddr = gd->ram_top;
    return 0;
}

6. Stage 5: 代码重定位 (relocate_code)

6.1 重定位流程

文件位置: arch/arm/lib/relocate.S

ENTRY(relocate_code)
    /*
     * r0 = 新的 GD 地址
     * r1 = 保留(旧参数)
     * r2 = 重定位目标地址
     */
    ldr r1, =__image_copy_start  /* 源地址起始 */
    subs r4, r2, r1              /* r4 = 重定位偏移 */
    beq relocate_done            /* 如果不需要重定位则跳过 */
    
    ldr r1, =__image_copy_start  /* 源起始 */
    ldr r2, =__image_copy_end    /* 源结束 */

copy_loop:
    ldmia r1!, {r10-r11}         /* 从源加载 */
    stmia r6!, {r10-r11}         /* 写入目标 */
    cmp r1, r2                   /* 是否完成? */
    blo copy_loop
    
    /* 修正重定位表 */
    ldr r2, =__rel_dyn_start
    ldr r3, =__rel_dyn_end

fixloop:
    ldmia r2!, {r0-r1}           /* (r0,r1) = (地址,信息) */
    and r1, r1, #0xff
    cmp r1, #23                  /* R_ARM_RELATIVE? */
    bne fixnext
    
    ldr r1, [r0]                 /* 读取需要修正的地址 */
    add r1, r1, r4               /* 加上偏移量 */
    str r1, [r0]                 /* 写回 */
    
fixnext:
    cmp r2, r3
    blo fixloop

relocate_done:
    bx lr

6.2 重定位过程图解

sequenceDiagram
    participant Flash as Flash空间
    participant Old as 旧位置(SRAM)
    participant New as 新位置(DDR高地址)
    participant Rel as 重定位表
    
    Flash->>Old: 1. 初始加载
    Note over Old: 在Flash/SRAM中执行
    
    Old->>New: 2. 复制代码段
    Note over New: 复制.text .rodata .data
    
    Old->>New: 3. 复制数据段
    
    Old->>Rel: 4. 读取重定位表
    Rel->>New: 5. 修正地址引用
    Note over New: 修正全局变量、函数指针
    
    New->>New: 6. 跳转到新位置继续执行

6.3 内存布局变化

重定位前:

+-------------------+  0x00000000
|   Flash/ROM       |
|   U-Boot 代码     |  ← 当前执行位置
+-------------------+  0x00100000
|                   |
|   ...             |
|                   |
+-------------------+  CONFIG_SYS_INIT_SP_ADDR
|   栈 (SRAM)       |
|   GD              |
+-------------------+

重定位后:

+-------------------+  0x80000000 (DDR起始)
|   Linux 内核区    |
|                   |
+-------------------+
|   ...             |
|                   |
+-------------------+  0x9DF67E00
|   U-Boot 栈       |  ← 新的栈位置
+-------------------+
|   GD              |
|   BD              |
+-------------------+
|   设备树 FDT      |
+-------------------+
|   Malloc 堆       |
+-------------------+
|   U-Boot 代码     |  ← 重定位后的执行位置
|   .text           |
|   .rodata         |
|   .data           |
+-------------------+  gd->relocaddr
|   MMU 页表        |
+-------------------+  0xA0000000 (DDR顶部)

6.4 重定位表 (.rel.dyn)

重定位表记录了所有需要修正的地址:

struct elf32_rel {
    uint32_t r_offset;   /* 需要修正的地址 */
    uint32_t r_info;     /* 重定位类型 */
};

重定位类型:

  • R_ARM_RELATIVE (23): 相对地址,需要加上重定位偏移
  • R_ARM_ABS32 (2): 绝对地址

示例:

原始代码中的全局变量引用:
    ldr r0, =global_var

重定位前: global_var 地址 = 0x00012345
重定位后: global_var 地址 = 0x9FF8E345
偏移量 = 0x9FF7C000

7. Stage 6: 重定位后初始化 (board_init_r)

7.1 初始化序列

文件位置: common/board_r.c

init_fnc_t init_sequence_r[] = {
    initr_trace,                /* 跟踪初始化 */
    initr_reloc,                /* 重定位完成标记 */
    initr_caches,               /* 使能 Cache */
    initr_reloc_global_data,    /* 重定位全局数据 */
    initr_malloc,               /* malloc 初始化 */
    initr_dm,                   /* 驱动模型初始化 */
    board_init,                 /* 板级初始化 */
    stdio_init_tables,          /* stdio 设备表初始化 */
    initr_serial,               /* 串口完全初始化 */
    initr_announce,             /* 打印重定位信息 */
    power_init_board,           /* 电源管理初始化 */
    initr_flash,                /* Flash 初始化 */
    initr_nand,                 /* NAND 初始化 */
    initr_mmc,                  /* MMC/SD 初始化 */
    initr_env,                  /* 环境变量完全初始化 */
    stdio_add_devices,          /* 添加 stdio 设备 */
    console_init_r,             /* 控制台完全初始化 */
    interrupt_init,             /* 中断初始化 */
    initr_enable_interrupts,    /* 使能中断 */
    initr_net,                  /* 网络初始化 */
    run_main_loop,              /* 进入主循环 */
    NULL,
};

7.2 关键初始化详解

flowchart TD
    A[board_init_r 开始] --> B[initr_caches<br/>使能I/D Cache]
    B --> C[initr_malloc<br/>完整malloc初始化]
    C --> D[board_init<br/>板级硬件初始化]
    D --> E[initr_serial<br/>串口完全配置]
    E --> F[initr_flash<br/>NOR Flash初始化]
    F --> G[initr_mmc<br/>SD/eMMC初始化]
    G --> H[initr_env<br/>从存储加载环境变量]
    H --> I[console_init_r<br/>控制台完整功能]
    I --> J[initr_net<br/>网络协议栈]
    J --> K[run_main_loop<br/>进入命令循环]
    
    style A fill:#f99,stroke:#333,stroke-width:2px
    style H fill:#9f9,stroke:#333,stroke-width:2px
    style K fill:#99f,stroke:#333,stroke-width:3px

7.3 使能 Cache

static int initr_caches(void)
{
    /* 使能 D-Cache */
    dcache_enable();
    
    /* 使能 I-Cache (如果未在 start.S 中使能)*/
    icache_enable();
    
    return 0;
}

Cache 的作用:

graph LR
    A[CPU] --> B{Cache?}
    B -->|命中| C[从Cache读取<br/>1个周期]
    B -->|未命中| D[从DDR读取<br/>~100个周期]
    D --> E[更新Cache]
    E --> C
    
    style C fill:#9f9,stroke:#333,stroke-width:2px
    style D fill:#f99,stroke:#333,stroke-width:2px

7.4 环境变量初始化

static int initr_env(void)
{
    /* 从存储设备加载环境变量 */
    env_relocate();
    
    /* 设置 bootdelay 等关键变量 */
    if (getenv("bootdelay") != NULL)
        gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);
    
    return 0;
}

环境变量存储位置:

存储介质位置备份
NOR Flash专用分区支持冗余
NAND Flash专用块支持冗余
eMMC/SD专用分区支持冗余
SPI Flash固定偏移可选冗余

8. Stage 7: 主循环 (main_loop)

8.1 主循环流程

文件位置: common/main.c

void main_loop(void)
{
    /* 自动启动处理 */
    autoboot_command(stored_bootdelay);
    
    /* 进入命令循环 */
    cli_loop();
}

8.2 自动启动流程

flowchart TD
    A[main_loop 开始] --> B{检查 bootdelay}
    B -->|>0| C[倒计时]
    B -->|=-1| D[跳过自动启动]
    B -->|=0| E[立即启动]
    
    C --> F{用户按键?}
    F -->|是| D
    F -->|超时| G[执行 bootcmd]
    
    G --> H{启动成功?}
    H -->|是| I[启动 Linux]
    H -->|否| D
    
    D --> J[命令行循环]
    
    style A fill:#f99,stroke:#333,stroke-width:2px
    style G fill:#9f9,stroke:#333,stroke-width:2px
    style J fill:#99f,stroke:#333,stroke-width:2px

8.3 自动启动代码

const char *s;
int bootdelay;

/* 获取 bootdelay 环境变量 */
bootdelay = getenv_ulong("bootdelay", 10, CONFIG_BOOTDELAY);

if (bootdelay >= 0) {
    s = getenv("bootcmd");
    
    /* 倒计时 */
    if (abortboot(bootdelay)) {
        /* 用户中断,进入命令行 */
        return;
    }
    
    /* 执行启动命令 */
    run_command_list(s, -1, 0);
}

8.4 命令行循环

void cli_loop(void)
{
    /* 解析器初始化 */
    parse_file_outer();
    
    /* 永不返回 */
}

命令行交互:

sequenceDiagram
    participant User as 用户
    participant CLI as 命令行
    participant Parser as 解析器
    participant Cmd as 命令处理
    
    loop 命令循环
        CLI->>User: 显示提示符 "=> "
        User->>CLI: 输入命令
        CLI->>Parser: 解析命令行
        Parser->>Cmd: 查找命令
        Cmd->>Cmd: 执行命令函数
        Cmd->>CLI: 返回结果
        CLI->>User: 显示输出
    end

9. 完整启动时序图

sequenceDiagram
    participant HW as 硬件
    participant Boot as BootROM
    participant Start as start.S
    participant Main as _main
    participant BoardF as board_init_f
    participant Reloc as relocate_code
    participant BoardR as board_init_r
    participant MainLoop as main_loop
    
    HW->>Boot: 1. 上电复位
    Boot->>Start: 2. 跳转到 U-Boot
    Start->>Start: 3. CPU模式设置
    Start->>Start: 4. CP15初始化
    Start->>Start: 5. 底层硬件初始化
    Start->>Main: 6. bl _main
    Main->>Main: 7. 设置栈
    Main->>BoardF: 8. bl board_init_f
    BoardF->>BoardF: 9. 串口初始化
    BoardF->>BoardF: 10. DRAM初始化
    BoardF->>BoardF: 11. 计算内存布局
    BoardF->>Main: 12. 返回
    Main->>Reloc: 13. bl relocate_code
    Reloc->>Reloc: 14. 复制代码到DDR
    Reloc->>Reloc: 15. 修正地址
    Reloc->>Main: 16. 返回(在新位置)
    Main->>BoardR: 17. bl board_init_r
    BoardR->>BoardR: 18. 使能Cache
    BoardR->>BoardR: 19. 初始化外设
    BoardR->>BoardR: 20. 加载环境变量
    BoardR->>MainLoop: 21. run_main_loop
    MainLoop->>MainLoop: 22. 自动启动/命令行
    
    Note over HW,MainLoop: 启动完成,进入交互模式

10. 内存布局演变全景

graph TD
    A[阶段1: 复位后<br/>PC=0x00000000] --> B[Flash中执行]
    
    C[阶段2: lowlevel_init<br/>设置栈] --> D[SRAM栈]
    
    E[阶段3: board_init_f<br/>DDR初始化] --> F[计算重定位地址]
    
    G[阶段4: relocate_code<br/>代码重定位] --> H[DDR高地址执行]
    
    I[阶段5: board_init_r<br/>完整初始化] --> J[所有资源可用]
    
    K[最终布局] --> L[0x80000000: Kernel加载区]
    K --> M[0x9DF00000: Malloc堆]
    K --> N[0x9FF00000: U-Boot代码]
    K --> O[0xA0000000: DDR顶部]
    
    style A fill:#f99,stroke:#333,stroke-width:2px
    style G fill:#9f9,stroke:#333,stroke-width:2px
    style K fill:#99f,stroke:#333,stroke-width:2px

11. 启动日志示例

U-Boot 2016.03 (Aug 20 2019 - 19:46:00 +0800)

CPU:   Freescale i.MX6DL rev1.3 at 792 MHz
Reset cause: POR
Board: MX6SABRESD
I2C:   ready
DRAM:  512 MiB
MMC:   FSL_SDHC: 0, FSL_SDHC: 1
*** Warning - bad CRC, using default environment

Display: HDMI (1024x768)
In:    serial
Out:   serial
Err:   serial
Net:   FEC [PRIME]
Normal Boot
Hit any key to stop autoboot:  0
=>

日志解析:

日志行对应阶段
U-Boot 2016.03版本信息(board_init_f)
CPU: Freescale i.MX6DLprint_cpuinfo()
DRAM: 512 MiBdram_init()
MMC: FSL_SDHCinitr_mmc()
In: serialconsole_init_r()
Net: FECinitr_net()
Hit any keyautoboot 倒计时
=>命令行提示符

12. 启动流程关键函数总结

mindmap
  root((U-Boot启动))
    汇编阶段
      reset
      cpu_init_cp15
      lowlevel_init
      _main
    重定位前
      board_init_f
      dram_init
      reserve_xxx
      setup_reloc
    重定位
      relocate_code
      修正地址引用
    重定位后
      board_init_r
      initr_caches
      initr_env
      board_init
    主循环
      main_loop
      autoboot
      cli_loop

下一步

接下来我们将进行 U-Boot 移植实战教学,手把手教你如何为一个虚拟的开发板移植 U-Boot。


13. U-Boot 启动 Linux 内核

💡 重要概念: 启动内核时使用 bootm_headers_t 结构体管理镜像信息。详细说明请参考:10-U-Boot核心全局数据结构详解.md

13.1 启动流程概览

flowchart TD
    A[U-Boot 命令行] --> B[加载内核镜像]
    B --> C[加载设备树]
    C --> D[准备启动参数]
    D --> E[设置寄存器]
    E --> F[跳转到内核]
    F --> G[Linux 内核接管]
    
    style A fill:#f99,stroke:#333,stroke-width:2px
    style D fill:#9f9,stroke:#333,stroke-width:2px
    style F fill:#99f,stroke:#333,stroke-width:3px
    style G fill:#fcf,stroke:#333,stroke-width:3px

13.2 bootm_headers_t 结构体在内核启动中的作用

文件位置: include/image.h

全局变量: bootm_headers_t images; (定义在 common/bootm.c)

核心字段:

typedef struct bootm_headers {
    /* 内核镜像信息 */
    image_info_t os;            /* OS 镜像信息 */
    ulong ep;                   /* 内核入口点地址 */
    
    /* Ramdisk 信息 */
    ulong rd_start, rd_end;     /* ramdisk 起始/结束地址 */
    
    /* 设备树信息 */
    char *ft_addr;              /* 设备树地址 */
    ulong ft_len;               /* 设备树长度 */
    
    /* 启动状态机 */
    int state;                  /* BOOTM_STATE_* 标志 */
    
    /* 内存管理 */
    struct lmb lmb;             /* 逻辑内存块管理 */
} bootm_headers_t;

启动状态机:

#define BOOTM_STATE_START       0x00000001  /* 开始启动 */
#define BOOTM_STATE_FINDOS      0x00000002  /* 查找 OS 镜像 */
#define BOOTM_STATE_FINDOTHER   0x00000004  /* 查找 ramdisk/fdt */
#define BOOTM_STATE_LOADOS      0x00000008  /* 加载 OS 镜像 */
#define BOOTM_STATE_RAMDISK     0x00000010  /* 处理 ramdisk */
#define BOOTM_STATE_FDT         0x00000020  /* 处理设备树 */
#define BOOTM_STATE_OS_GO       0x00000400  /* 跳转到 OS */

13.3 bootz 命令

命令格式:

bootz [kernel_addr] [initrd_addr] [fdt_addr]

参数说明:

  • kernel_addr: 内核镜像地址(zImage)
  • initrd_addr: initramfs 地址(可选,用 - 表示无)
  • fdt_addr: 设备树地址(.dtb)

示例:

# 基本启动
=> bootz 0x80800000 - 0x83000000

# 带 ramdisk 启动
=> bootz 0x80800000 0x84000000 0x83000000

13.3 启动参数传递

bootargs 环境变量

# 查看启动参数
=> printenv bootargs
bootargs=console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw

# 设置启动参数
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'

常用参数说明:

参数含义示例
console=控制台设备ttymxc0,115200
root=根文件系统/dev/mmcblk1p2
rootwait等待根设备无值
rw读写挂载无值
ip=网络配置dhcp

ARM 寄存器约定

/* Linux 内核启动时寄存器状态 */
r0 = 0                    // 必须为 0
r1 = 0xFFFFFFFF          // 使用设备树标记(或 machid)
r2 = 0x83000000          // 设备树物理地址

代码实现: arch/arm/lib/bootm.c

static void boot_jump_linux(bootm_headers_t *images, int flag)
{
    DECLARE_GLOBAL_DATA_PTR;
    unsigned long machid = gd->bd->bi_arch_number;  /* 从 gd 获取机器 ID */
    void (*kernel_entry)(int zero, int arch, uint params);
    unsigned long r2;
    
    /* 1. 获取内核入口点(从 images 结构体)*/
    kernel_entry = (void (*)(int, int, uint))images->ep;
    
    /* 2. 准备 r2 寄存器(设备树地址)*/
    if (images->ft_len)
        r2 = (unsigned long)images->ft_addr;  /* 从 images 获取 */
    else
        r2 = gd->bd->bi_boot_params;          /* 从 gd 获取 */
    
    printf("## Transferring control to Linux (at address %08lx)...\n",
           (ulong)kernel_entry);
    printf("   r0: 0x%08x\n", 0);
    printf("   r1: 0x%08lx (machine id)\n", machid);
    printf("   r2: 0x%08lx (device tree)\n", r2);
    
    /* 3. 清理 CPU 状态 */
    cleanup_before_linux();
    
    /* 4. 跳转到内核 */
    kernel_entry(0, machid, r2);
    /* 此处永不返回 */
}

gd 和 bootm_headers 的协作:

  • gd->bd->bi_arch_number → 提供机器类型 ID
  • gd->bd->bi_boot_params → 备用启动参数地址
  • images->ep → 内核入口点
  • images->ft_addr → 设备树地址
  • images->rd_start/rd_end → ramdisk 地址范围

13.4 完整启动脚本示例

SD 卡启动

# 配置变量
=> setenv kernel_addr 0x80800000
=> setenv fdt_addr 0x83000000
=> setenv kernel_file zImage
=> setenv fdt_file imx6ull-myboard.dtb

# 启动参数
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'

# 启动命令
=> setenv bootcmd 'mmc dev 1; fatload mmc 1:1 ${kernel_addr} ${kernel_file}; fatload mmc 1:1 ${fdt_addr} ${fdt_file}; bootz ${kernel_addr} - ${fdt_addr}'

=> saveenv
=> reset

网络启动(TFTP + NFS)

# 网络配置
=> setenv ipaddr 192.168.1.100
=> setenv serverip 192.168.1.10
=> setenv netmask 255.255.255.0

# NFS 根文件系统
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=${serverip}:/nfsroot,v3,tcp ip=${ipaddr}'

# 启动命令
=> setenv netboot 'tftp ${kernel_addr} zImage; tftp ${fdt_addr} imx6ull-myboard.dtb; bootz ${kernel_addr} - ${fdt_addr}'

=> run netboot

13.5 启动过程内存布局

DDR 物理地址(启动 Linux 时)

0x80000000  ┌─────────────────┐
            │ 预留区域         │
0x80800000  ├─────────────────┤ ← kernel_addr
            │ zImage 内核      │   (4-8 MB)
0x82000000  ├─────────────────┤
            │                 │
            │ 未使用空间       │
            │                 │
0x83000000  ├─────────────────┤ ← fdt_addr
            │ Device Tree     │   (64 KB)
0x83010000  ├─────────────────┤
            │                 │
0x84000000  ├─────────────────┤ ← ramdisk_addr (可选)
            │ Ramdisk         │   (8-32 MB)
0x86000000  ├─────────────────┤
            │                 │
            │ 空闲空间         │
            │                 │
0x9FF00000  ├─────────────────┤
            │ U-Boot 代码      │
0xA0000000  └─────────────────┘

13.6 设备树传递

U-Boot 修改设备树的 /chosen 节点:

chosen {
    bootargs = "console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw";
    linux,initrd-start = <0x84000000>;  /* 如果有 ramdisk */
    linux,initrd-end   = <0x84800000>;
    stdout-path = &uart1;
};

13.7 启动前清理

/*
 * U-Boot 跳转到内核前的清理工作
 */
void cleanup_before_linux(void)
{
    disable_interrupts();       // 禁用中断
    icache_disable();          // 禁用 I-Cache
    dcache_disable();          // 禁用 D-Cache
    mmu_disable();             // 禁用 MMU
    flush_cache_all();         // 刷新所有缓存
}

/*
 * 跳转到内核
 */
kernel_entry(0, 0xFFFFFFFF, 0x83000000);

13.8 调试启动问题

常见问题:

现象原因解决方法
卡在 “Starting kernel…”内核损坏重新加载内核
无输出console 参数错误检查 bootargs
Kernel panic根文件系统问题检查 root= 参数
设备不工作设备树不匹配使用正确的 .dtb

调试命令:

# 查看内核是否正确加载
=> md 0x80800000 10

# 查看设备树
=> fdt addr 0x83000000
=> fdt print /chosen

# 检查内存
=> mtest 0x80000000 0x90000000

# 单步执行
=> mmc dev 1
=> fatload mmc 1:1 0x80800000 zImage
=> fatload mmc 1:1 0x83000000 imx6ull-myboard.dtb
=> bootz 0x80800000 - 0x83000000

13.9 启动流程关键代码

bootz 命令实现:

/* cmd/bootm.c */
int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    /* 解析参数 */
    if (argc < 2)
        return CMD_RET_USAGE;
    
    /* 调用启动状态机 */
    return do_bootm_states(cmdtp, flag, argc, argv,
                          BOOTM_STATE_START |
                          BOOTM_STATE_FINDOS |
                          BOOTM_STATE_LOADOS |
                          BOOTM_STATE_OS_GO,
                          &images, 1);
}

/* arch/arm/lib/bootm.c */
static void boot_jump_linux(bootm_headers_t *images)
{
    void (*kernel_entry)(int zero, int arch, uint params);
    unsigned long r2;
    
    kernel_entry = (void (*)(int, int, uint))images->ep;
    
    /* 清理环境 */
    cleanup_before_linux();
    
    /* 设备树地址 */
    r2 = (unsigned long)images->ft_addr;
    
    printf("Starting kernel ...\n\n");
    
    /* 跳转!r0=0, r1=0xFFFFFFFF, r2=DTB地址 */
    kernel_entry(0, 0xFFFFFFFF, r2);
}

13.10 Linux 内核接管

内核启动入口(汇编):

/* arch/arm/kernel/head.S */
ENTRY(stext)
    /* 检查 CPU */
    mrc p15, 0, r9, c0, c0
    
    /* 创建页表 */
    bl  __create_page_tables
    
    /* 启用 MMU */
    bl  __enable_mmu
    
    /* 跳转到 C 代码 */
    b   start_kernel
ENDPROC(stext)

内核 C 入口:

/* init/main.c */
asmlinkage __visible void __init start_kernel(void)
{
    setup_arch(&command_line);     // 架构初始化
    setup_command_line(command_line); // 解析启动参数
    parse_early_param();           // 早期参数解析
    
    mm_init();                     // 内存管理初始化
    sched_init();                  // 调度器初始化
    console_init();                // 控制台初始化
    
    rest_init();                   // 启动 init 进程
}

14. 总结

完整启动链路

上电 → BootROM → DCD初始化DDR → 加载U-Boot → 
汇编初始化 → C环境准备 → DDR探测 → 代码重定位 → 
外设初始化 → 命令行 → 加载内核 → 设置参数 → 
跳转内核 → Linux启动 → 用户空间

关键阶段

  1. 硬件初始化 – BootROM + DCD (DDR 初始化)
  2. 汇编启动 – 设置 CPU 模式、栈、异常向量
  3. C 初始化 – board_init_f (重定位前)
  4. 代码重定位 – 从 Flash 到 DDR 高地址
  5. 完整初始化 – board_init_r (重定位后)
  6. 启动内核 – bootz + 参数传递 + 跳转

核心数据结构

  • gd_t: 全局数据(r9 寄存器)
  • bd_t: 板级信息
  • bootargs: 内核启动参数
  • 设备树: 硬件描述

🎉 U-Boot 完整启动流程详解完成!

从上电复位到 Linux 内核启动,整个嵌入式系统启动过程已全部呈现。


提示: 理解启动流程是 U-Boot 开发和调试的基础,建议结合实际硬件跟踪启动过程。

详细调用过程整理 u-boot:启动详细的代码调用流程 u-boot.lds:(arch/arm/cpu/u-boot.lds) |–>_start:(arch/arm/lib/vectors.S) |–>reset(arch/arm/cpu/armv7/start.S)
|–>save_boot_params(arch/arm/cpu/armv7/start.S)/将引导参数保存到内存中/ |–>save_boot_params_ret(arch/arm/cpu/armv7/start.S) |–>cpu_init_cp15(arch/arm/cpu/armv7/start.S)/初始化/ |–>cpu_init_crit(arch/arm/cpu/armv7/start.S) |–>lowlevel_init(arch/arm/cpu/armv7/lowlevel_init.S) |–>_main(arch/arm/lib/crt0.S) |–>board_init_f_alloc_reserve(common/init/board_init.c)/为u-boot的gd结构体分配空间/ |–>board_init_f_init_reserve(common/init/board_init.c) /将gd结构体清零/ |–>board_init_f(common/board_f.c) |–>initcall_run_list(include/initcall.h) /初始化序列函数/ |–>init_sequence_f /* 初始化序列函数数组 */ |–>board_early_init_f(board/freescale/mx6ull_toto/mx6ull_toto.c)/初始化串口的IO配置/ |–>timer_init(arch/arm/imx-common/timer.c) /初始化内核定时器,为uboot提供时钟节拍/ |–>init_baud_rate(common/board_f.c) /初始化波特率/ |–>serial_init(drivers/serial/serial.c) /初始化串口通信设置/ |–>console_init_f(common/console.c) /初始化控制台/ |–>… |–>relocate_code(arch/arm/lib/relocate.S) /主要完成镜像拷贝和重定位/ |–>relocate_vectors(arch/arm/lib/relocate.S)/重定位向量表/ |–>board_init_r(common/board_r.c)/板级初始化/ |–>initcall_run_list(include/initcall.h)/初始化序列函数/ |–>init_sequence_r/序列函数/ |–>initr_reloc(common/board_r.c) /设置 gd->flags,标记重定位完成/ |–>serial_initialize(drivers/serial/serial-uclass.c)/初始化串口/ |–>serial_init(drivers/serial/serial-uclass.c) /初始化串口/ |–>initr_mmc(common/board_r.c) /初始化emmc/ |–>mmc_initialize(drivers/mmc/mmc.c) |–>mmc_do_preinit(drivers/mmc/mmc.c) |–>mmc_start_init(drivers/mmc/mmc.c) |–>console_init_r(common/console.c) /初始化控制台/ |–>interrupt_init(arch/arm/lib/interrupts.c) /初始化中断/ |–>initr_net(common/board_r.c) /初始化网络设备/ |–>eth_initialize(net/eth-uclass.c) |–>eth_common_init(net/eth_common.c) |–>phy_init(drivers/net/phy/phy.c) |–>uclass_first_device_check(drivers/core/uclass.c) |–>uclass_find_first_device(drivers/core/uclass.c) |–>device_probe(drivers/core/device.c) |–>device_of_to_plat(drivers/core/device.c) |–>drv->of_to_plat |–>fecmxc_of_to_plat(drivers/net/fec_mxc.c)/解析设备树信息/ |–>device_get_uclass_id(drivers/core/device.c) |–>uclass_pre_probe_device(drivers/core/uclass.c) |–>drv->probe(dev) /drivers/net/fec_mxc.c/ U_BOOT_DRIVER(fecmxc_gem) = { .name = “fecmxc”, .id = UCLASS_ETH, .of_match = fecmxc_ids, .of_to_plat = fecmxc_of_to_plat, .probe = fecmxc_probe, .remove = fecmxc_remove, .ops = &fecmxc_ops, .priv_auto = sizeof(struct fec_priv), .plat_auto = sizeof(struct eth_pdata), }; |–>fecmxc_probe(drivers/net/fec_mxc.c)/探测和初始化/ |–>fec_get_miibus(drivers/net/fec_mxc.c) |–>mdio_alloc(drivers/net/fec_mxc.c) |–>bus->read = fec_phy_read; |–>bus->write = fec_phy_write; |–>mdio_register(common/miiphyutil.c) |–>fec_mii_setspeed(drivers/net/fec_mxc.c) |–>fec_phy_init(drivers/net/fec_mxc.c) |–>device_get_phy_addr(drivers/net/fec_mxc.c) |–>phy_connect(drivers/net/phy/phy.c) |–>phy_find_by_mask(drivers/net/phy/phy.c) |–>bus->reset(bus) |–>get_phy_device_by_mask(drivers/net/phy/phy.c) |–>create_phy_by_mask(drivers/net/phy/phy.c) |–>phy_device_create(drivers/net/phy/phy.c) |–>phy_probe(drivers/net/phy/phy.c) |–>phy_connect_dev(drivers/net/phy/phy.c) |–>phy_reset(drivers/net/phy/phy.c) |–>phy_config(drivers/net/phy/phy.c) |–>board_phy_config(drivers/net/phy/phy.c) |–>phydev->drv->config(phydev) /drivers/net/phy/smsc.c/ static struct phy_driver lan8710_driver = { .name = “SMSC LAN8710/LAN8720”, .uid = 0x0007c0f0, .mask = 0xffff0, .features = PHY_BASIC_FEATURES, .config = &genphy_config_aneg, .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; |–>genphy_config_aneg(drivers/net/phy/phy.c) |–>phy_reset(需要手动调用)(drivers/net/phy/phy.c) |–>genphy_setup_forced(drivers/net/phy/phy.c) |–>genphy_config_advert(drivers/net/phy/phy.c) |–>genphy_restart_aneg(drivers/net/phy/phy.c) |–>uclass_post_probe_device(drivers/core/uclass.c) |–>uc_drv->post_probe(drivers/core/uclass.c) /net/eth-uclass.c/ UCLASS_DRIVER(ethernet) = { .name = “ethernet”, .id = UCLASS_ETH, .post_bind = eth_post_bind, .pre_unbind = eth_pre_unbind, .post_probe = eth_post_probe, .pre_remove = eth_pre_remove, .priv_auto = sizeof(struct eth_uclass_priv), .per_device_auto = sizeof(struct eth_device_priv), .flags = DM_UC_FLAG_SEQ_ALIAS, }; |–>eth_post_probe(net/eth-uclass.c) |–>eth_write_hwaddr(drivers/core/uclass.c) |–>… |–>run_main_loop(common/board_r.c)/主循环,处理命令/ |–>main_loop(common/main.c) |–>bootdelay_process(common/autoboot.c) /读取环境变量bootdelay和bootcmd的内容/ |–>autoboot_command(common/autoboot.c) /倒计时按下执行,没有操作执行bootcmd的参数/ |–>abortboot(common/autoboot.c) |–>printf(“Hit any key to stop autoboot: %2d “, bootdelay); /到这里就是我们看到uboot延时3s启动内核的地方/ |–>cli_loop(common/cli.c) /倒计时按下space键,执行用户输入命令/

上一篇 uboot顶层makefile详解

编译之后的uboot存在如下的目录 我们需要关注的文件有如下: 顶层makefile主要涉及一下内容 需要重点关注的内容...

下一篇 正点原子U-Boot移植过程深度解析

前言 本文档目标: 核心问题: 目录 1. 移植步骤总览 1.1 四大步骤概览 1.2 步骤对照表 步骤 操作 涉及文件...