u-boot 移植 --->7、u-bootl流程粗线条梳理
阅读原文时间:2023年07月08日阅读:3

通过前面的调试了解到s5pv210这个芯片的启动流程是需要将u-boot分为两部分的分别为SPL和u-boot。这里我使用网上的方式不直接使用u-boot的SPL连接脚本单独生成SPL的image而是用前面介绍的方法 [https://www.cnblogs.com/w-smile/p/13124631.html](u-boot 移植 --->3、S5PV210启动序列)将u-boot的前16k直接截取出来作为SPL。

IROM中

这一部分可以参考其他博文,或者三星的文档。

在IRAM中

通过启动连接脚本arch\arm\cpu\u-boot.lds 我们找到了整个代码的入口_start。全局寻找找到文件arch\arm\lib\vectors.S符合本次架构所以整个代码入口就在这里内容如下:

_start:

#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
    .word   CONFIG_SYS_DV_NOR_BOOT_CFG
#endif

    b   reset
    ldr pc, _undefined_instruction
    ldr pc, _software_interrupt
    ldr pc, _prefetch_abort
    ldr pc, _data_abort
    ldr pc, _not_used
    ldr pc, _irq
    ldr pc, _fiq

所以可以看出来这里也是符合ARM架构要求,在这里定义了中断向量表,然后第一句指令是一个跳转指令,将执行流跳转到reset符号了,经过查找这个符号定义在arch\arm\cpu\armv7\start.S 这是架构通用的部分所以可以参考u-boot源码这里仅罗列这段程序操作的内容:

1. 检查是否使能了大物理页面扩展,如果使能了则进行必要的配置。(CP15)

2. 禁止IRQ和FIQ,配置处理器模式为SVC。(CPSR)

3. 设置_start 符号地址为异常向量表基址,并禁止cache,TLB,BP,MMU(CP15)

4. 调用lowlevel_init(如果定义了CONFIG_SKIP_LOWLEVEL_INIT_ONLY则不执行),这一部分就是芯片移植人员需要早期初始化的内容可以放在这个标号下实现,我这里就是配置时钟,配置芯片DRAM接口,配置UART0。

5. 设置堆栈为IRAM空间,并拷贝SD卡中特定地址的完整u-boot镜像到DRAM指定地址,SD的驱动实在芯片的IROM中完成的。(这一部分是本来的u-boot内容没有的因为我们未使用u-boot的SPL方式单独构建SPL程序所以修改了这部分内容)。

DRAM中

完成上面的操作后进行绝对跳转就能到DRAM地址空间继续运行代码了,因为代码本身就是连接在这个地址的。跳转后运行_main 符号处的指令他实现在rch\arm\lib\crt0.S 中操作序列大致如下:

1. 重新修改栈基址到DRAM地址空间中

2. 调整栈指针 (实际则是在栈中分配内存)这里是为malloc和struct global_data 的GD变量预留内存

3. 初始化 GD变量的部分成员,这是整个u-boot的一个重要的全局数据用来记录各种数据状态等。

执行了以上的处理步骤后将又可以使用C代码调用了。然后调用board_init_f 这是在common\board_f.c 中实现的属于u-boot的内容的代码,主要就是围绕GD变量进行操作详细的操作这里不深入分析,就是进行一些配置初始化相关的主要就是调用initcall_run_list 这个函数依次执行一个初始化list进行初始化,这个初始化函数数组init_sequence_f也是在common\board_f.c 中定义并根据u-boot的使用kbuild配置产生的配置文件编译生成。其中大部分初始化函数都是平台无关的少量与平台相关需要注意对应修改下,除此之外需要注意的是在这里执行board_init_f 相关调用时有打开cache。这里执行完成后GD变量的成员也越来越完善,再次根据GD中记录的信息修改SP栈指针。然后还需要再次移动并重定位代码,这一次移动是为了为了将u-boot放到DRAM高地址为拷贝Linux内核image准备空间,拷贝完成后无效指令和数据cache(因为原来地址空间的缓存已经无效了)。这里还使用了一个技巧使拷贝完成后直接返回到新地址的下一条指令执行。使用的方式就是在LR寄存器上做文章,具体参考代码实现。然后在次修改异常向量表地址到新的地址空间。注意的是这里代码重定向还是比较复杂一点的不是简单的拷贝,因为前面的GD变量也是需要维护的,主要就是然后对新地址的数据段的拷贝。然后就是调用common\board_r.c 文件中的board_init_r 函数,这个函数的实现和board_init_f 如出一辙他们末尾的字母就是标记他们调用的配置是在重定向前(f)和之后(r)执行的初始化。最后执行的就是 run_main_loop这里就是u-boot程序的主循环,在这里进行u-boot命令等相关功能的操作。

总结

u-boot的整个执行过程的粗流程其实是不复杂的,调试过程最复杂的感觉就是DRAM的配置那些部分。整个移植实际上出去kbuild配置工具外就是,一部分关于底层的初始化配置相关内容,其余就是u-boot的大致执行流程。流程我在做一下梳理总结如下:

_start(arch\arm\lib\vectors.S)向量表,然后跳转(reset)--->arch\arm\cpu\armv7\start.SARM 架构相关的操作,调用(lowlevel_init)---> 平台相关的底层或前提初始化部分的必要配置,调用(_main)--->arch\arm\lib\crt0.S 进行u-boot相关功能的变量,栈等部分的维护初始化等,调用(board_init_f)--->common\board_f.c 根据配置调用以系列的初始化调用,返回crt0.S,重定位u-boot代码在DRAM中的位置,调用(board_init_f)--->common\board_r.c 继续进行必要的初始化--->u-boot loop(如果引导Linux则u-boot就执行完了)。如果像知道更详细的内容可以参考这个博主的博客:https://to-run-away.blog.csdn.net/article/details/81711413?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.control