Lazy loaded image
Uboot 启动第一阶段
Words 3391Read Time 9 min
2025-9-10
u-boot加载启动内核过程可以大致分为两个阶段上,接下来我们将详细分析u-boot源代码(版本号为2019.04)。
 
对于imx6ull而言,其第一阶段对应的文件时arch/arm/cpu/armv7/start.S和arch/arm/cpu/armv7/lowlevel_init.S
u-boot启动第一阶段流程图如下所示:
notion image
 

1. 从链接脚本说起

上面是总的链接脚本u-boot.lds,将_start作为入口点,_start arch/arm/lib/vectors.S 中定义异常向量表, 当 cpu 产生异常时,便会将对应的异常入口地址加载到pc中,进而处理相应的异常处理程序。其中复位异常向量指令“b resets”决定了 u-boot 启动或者复位后将自动跳转到 resets 标志处执行。

2. 跳转到复位处理程序

接着分析一下 resets 做了哪些工作,全局搜索我们发现 resets 其实就定义在arch/arm/cpu/armv7/start.S文件中

2.1 分析 reset

分析下面代码 reset 中只有一条跳转指令 “b save_boot_params”

2.2 分析save_boot_params

save_boot_params 中只有一条跳转指令 b save_boot_params_ret

2.3 分析 save_boot_params_ret

第一部分:通过操作CPSR寄存器,执行下列操作:

  1. 将cpu的工作模式设置为SVC32模式(即管理模式);
  1. 屏蔽IRQ和FIQ的中断。

第二部分:通过操作SCTLR寄存器,执行如下操作:

  1. 设置异常向量表的及地址为0x00000000,且支持重映射;
  1. 向量表重定位到 _start 地址(uboot的运行介质(norflash nandflash sram等)映射地址可能不在0x0起始的地址)。

第三部分:配置cp15协处理器, 执行如下操作:

 
cpu_init_cp15 执行如下操作:
  1. 使整个数据和指令TLB、指令缓冲、分支预测无效,清空写缓冲区和预取缓冲区。
  1. 禁止内存管理单元mmu、地址对齐检查、数据缓冲。打开ARM系统的跳转预测(分支预测)功能,不打断流水线,提高指令执行效率。(数据cache一定要关闭,避免缓存不一致,指令cache可保留,以提高操作效率。)
 
cpu_init_crit 执行如下操作:
lowlevel_init:与特定开发板相关的初始化函数,会做 pll 初始化, 如果不是从内存启动,则会做内存初始化,方便后续拷贝到内存中运行。 设置栈指针指向CONFIG_SYS_INIT_SP_ADDR;
 
_main 执行如下操作:
  1. board_init_f_alloc_reserve:为早期 malloc 和 GD数据分配空间,CONFIG_SYS_INIT_SP_ADDR = 0x0091ff00,r0=0x0091ff00 - (0x400(early malloc arena) + 0x100(GD_SIZE)) = 0x0091fa00
  1. board_init_f_init_reserve:该函数主要作用是将GD区域清零,返回最初malloc区域的地址,即 0x0091fb00 = 0x0091fa00 + 0x100(GD_SIZE)。
  1. board_init_f - 初始化boot_flags 和 have_console 标志位
    1. initcall_run_list - 遍历执行 init_sequence[] 中函数,初始化uboot前半段
下面init_sequence_f包括哪些函数:
  1. setup_mon_len - 根据.lds文件中__bss_end与__bss_end计算出u-boot本身的大,赋给gd->mon_len变量;
  1. fdtdec_setup - 检查gd->fdt_blob处是否存在dtb;
  1. env_init - 使用default_environment[]数组初始化env_addr和env_valid状态;
  1. init_baud_rate - 设置默认串口波特率;
  1. serial_init - 设置flags串口准备好标志,返回当前指向的串口设备指针;
  1. console_init_f - 设置have_console 标志,初始化console;
  1. display_options - 打印横幅信息;
  1. display_text_info - 打印代码段基地址、BSS段起始地址以及BSS段末尾地址
  1. show_board_info - 从fdt中获取model信息并打印;
  1. announce_dram_init - 打印 "DRAM: ";
  1. dram_init - 获取dram大小,并设置dram_size;
  1. setup_dest_addr - 初始化ram_size,ram_base,ram_top,relocaddr,DRAM的基地址为0x80000000,大小为0x20000000(512M),RAM顶端地址为0xa0000000 = 0x80000000 + 0x20000000(512M)。 重定位后地址为0x9ff02000;
  1. reserve_round_4k - 内存指针指向下一个4kB处,也就是4kB对齐;
  1. reserve_mmu - 为TLB腾4kB 空间,gd->relocaddr = 0x9fffc000 = 0xA0000000 - 4kB,做64kB对齐,赋值给gd->relocaddr;
  1. reserve_video - 空;
  1. reserve_trace - 空;
  1. reserve_uboot - 为uboot分配空间,同时做4kB对齐;
  1. reserve_malloc - 为malloc腾出一段空间, malloc 空间16MB, Env空间 64KB;
  1. reserve_board - 给bd预留空间,存放板子信息,如DRAM起始地址、DRAM大小、SRAM起始地址、SRAM大小、boot参数等,共预留了80字节,gd->start_addr_sp = 0x9eef1fb0;
  1. setup_machine - 空;
  1. reserve_global_data - 预留256字节给 new_gd (新的全局数据);
  1. reserve_fdt - 预留 40032 字节存放设备树信息;
  1. 以上内存分配完成
  1. reloc_fdt - 负责将设备树数据搬运到新分配的 new_fdt 地址中去;
  1. setup_reloc - 将gd重定位到new_gd中;
以上board_init_f中的内容就已经分析完了。
以下uboot自身的重定位和bss段的初始化。
  1. relocate_code -
 
总结:初始化c语言环境,以便调用board_init_f函数。这个环境只提供了一个堆栈和一个存储GD(全局数据)结构的地方,两者都位于一些可用的RAM中。在调用board_init_f()之前,GD应该被归零。
 
上一篇
宏的用法
下一篇
Guide to Linux System

Comments
Loading...