U-Boot载入到DDR过程的代码分析

首先需要明确如下几个概念

XIP的概念

XIP即片上执行,它的含义是CPU上电后无需任何别的操作,就可以直接读取该XIP设备上的第一条指令开始执行

号称支持Nand启动、支持SD卡启动、支持USB启动、支持UART启动的芯片,里面必定有BootROM

  • BootROM:硬件初始化、把程序从非XIP设备复制进RAM,从RAM里执行
    这些其他的启动方式,读取其存储介质里面的指令都必须先初始化对应的控制器,所以需要使用到BootROM来把程序复制到RAM,然后再跳转到RAM开始执行

BootROM的作用

BootROM:硬件初始化,把程序从非XIP设备复制进RAM,从RAM里执行

如何支持多种启动方式(SD卡、EMMC、USB、UART启动)

  • 方法1:芯片有boot pin,决定使用哪个外设。bootrom根据引脚决定读取哪个设备的程序
  • 方法2:芯片有boot pin,决定多种外设的尝试顺序
    • 示例顺序1:SD、EMMC、USB
    • 示例顺序2:EMMC、SD、USB
    • 示例顺序3:USB

如何将完整的u-boot复制进内存

BootROM被用来启动用户程序,用户程序可能有几百KB、几MB,但是片内的RAM只有几KB:

所以出现了两种方法

  • 方法1:
    • BootROM从启动设备读取用户程序的前几KB到SRAM
    • 这前几KB的代码必须保证初始化好容量大些的DDR,把完整的u-boot复制到DDR,并且跳转执行
  • 方法2:
    • BootROM从启动设备读取SPL到RAM------SPL(second program loader二级程序加载器)
    • SPL:负责初始化DDR,把完整的u-boot复制到DDR,并且跳转执行
      我用的韦东山的imx6ull板子使用的是方法1

重定位的2种方法

绝对重定位

从ROM复制程序到DDR的时候,直接重新指定重定位地址(比如DDR的头部地址),复制过来后,DDR的高地址就整块空出来

程序内部的链接地址则在复制过程中或者运行过程中,按照指定的重定位地址,进行修改,改为DDR里面的地址

下面讲解的U-Boot代码就采用了这种绝对重定位的方式

相对重定位

从ROM复制程序到DDR的时候,不强行指定DDR重定位地址,(不指定的话好像是中间地址)

而是复制过去后,按照从头部的偏移量,对其进行访问,即访问的是头部,但是物理地址是中间

ps:这个目前没遇到过,应该是这样hh~~

我用的板子是韦东山的imx6ull

U-Boot的大致加载流程

imx6ull对应的start.S文件是./arch/arm/cpu/armv7/start.S

该文件还调用了别的外部汇编文件

首先bootRom根据启动引脚,初始化EMMC外设,并去EMMC读取imx文件前面的几KB代码到SOC的SRAM上运行,imx6ull并没有把SPL单独拿出来编译,而是把所有这些初始化的函数放在了imx映像文件的头部几KB,仅仅是在board_init_r这里借用了SPL的代码框架回调厂商自己的函数

1.完成对cp15这个协处理器的初始化

U-Boot 需要干净、可预测的环境,所以需要实现以下功能:禁用 MMU(用物理地址跑代码)、禁用 cache(避免 stale data)、把向量表搬到 RAM(relocatable)

2.cpu_init_crit清理干净CPU

3.然后crt0.S文件中定义了__main函数,跳转到该函数执行

3.1.__main头部设置了栈顶指针sp,引导过程中的全局变量GD

3.2.该__main函数的开头位置,调用了下面SOC厂商提供的board_init_f函数

3.3.board_init_f函数执行结束后,DDR就能用了,所以设置载入u-boot时候的在DDR中的重定位地址(通常是DDR顶部)

后续复制u-boot到DDR的时候会将u-boot内部的链接地址也进行修改,改为符合DDR的地址

3.4.board_init_r 则借用了SPL的框架,内部塞了imx6ull对应的回调函数,负责在初始化一些子系统后真正加载u-boot到DDR中开始执行

U-Boot初始化的两个阶段

u-boot的源码初始化阶段大致可以分为2个阶段:

  • board_init_f:f的意思是"running from read-only flash"
    • 说是flash,其实还是加载到RAM中执行的
    • 作用:初始化硬件(比如DDR、UART),为各个功能预留内存(比如U-boot、Framebuffer、设备树)
  • board_init_r:r的意思是"relocated",意思是重定位过了
    • 它这个重定位指的是后续复制U-Boot到DDR的时候,重定位会起作用,把U-Boot放到DDR头部
    • 作用:初始化各个子系统(各个存储设备、环境变量、网络),进入main_loop

board_init_f

找到我当前板子,imx6ull的对应board_init_f函数,在文件Uboot-2017.03/board/freescale/mx6ul_14x14_evk/mx6ul_14x14_evk.c里面

我当前这块板子的默认配置文件是mx6ull_14x14_ddr3_arm2_emmc_defconfig,他没有使用SPL放在U-boot程序头的位置

所以和上面的代码就对上了,这里是在SOC厂商的代码里面使用的spl_dram_init函数来初始化DDR作为二级加载的位置,并没有使用SPL

现在开始分析board_init_f做了什么事情

ccgr_init()内部向ccm的CCGR寄存器写入11,即打开所有外设的时钟

arch_cpu_init()

设置了AIPS---Advanced IC Peripheral Bus 高级外设总线的开启

并且将其分频数值等设置好

并且关闭了一上电就默认开启的看门狗,防止打断后续过程

board_early_init_f()

设置复用uart

timer_init()

初始化U-Boot系统时钟,GPT是general purpose timer

该时钟提供整个 U-Boot 运行期间的时间基准

preloader_console_init()

这里串口被选为console

打开串口,方便后续输出启动前的所有信息到串口中

并且可以在U-Boot运行过程中通过串口进行交互

spl_dram_init()

初始化imx6ull自带的ddr3

memset(__bss_start, 0, __bss_end - __bss_start);

清零ZI-data的区域,因为SRAM上电后是随机值

静态RAM有这种特性

board_init_r(NULL, 0)

最后跳转到board_init_r开始执行

board_init_r

board_init_r:r的意思是"relocated",意思是重定位过了(设置了复制u-boot代码到DDR中的重定位位置)

该函数在/common/spl.c中定义,SOC厂商则提供回调函数给改函数

  • 作用:初始化各个子系统(各个存储设备、环境变量、网络),进入main_loop

    spl_boot_list是spl从对应设备加载u-boot的顺序列表

按理说从哪个设备载入程序执行是由bootRom决定的,但是bootRom不够灵活,所以这里可以自己定义更灵活的设备引导顺序

和bootRom的设计理念也不冲突,因为bootRom决定的是上电完成初始化后的第一行应用程序代码从哪里载入

这个应用程序的第一行代码跑起来后(往往是SPL或者SOC厂家自定义的初始化DDR代码),具体后面从哪里载入u-boot就不是bootRom能决定的了

spl_image是spl要载入的u-boot映像文件信息,包含os类型,入口地址,大小等

290行则是如果板子定义了 SPL 专用的 malloc 区域,则为其在ddr中申请这块区域,同时设置全局标志 GD_FLG_FULL_MALLOC_INIT,表示 malloc 已就绪(后续代码可以用 malloc)

295行则是检查是否调用过spl_init,没有的话就调用spl_init

spi_init中则会用到设备树,为spl要使用的设备进行初始化

307行,则是可以由SOC厂商加一点自定义代码进去.比如点个灯啥的

312行,获取到SPL加载u-boot的ROM设备顺序

这个board_boot_order内部会调用SOC厂商提供的回调函数,获取到加载顺序

321行,根据OS的类型,uboot,则跳出到334行继续执行

334行的条件,在.config文件中满足了,这里是一条调试信息,输出SPL阶段malloc了多少字节的数据

340行 ,imx6ull的u-boot文件中没写同名函数覆盖,这是个弱定义,所以啥都不做

因为关看门狗,初始化时钟,分频时钟,这些早都在board_init_f中完成了

341行,根据spl_image的入口地址跳转到u-boot执行,跳转后,SPL 阶段结束,控制权交给完整 U-Boot

该函数在imx-common/spl.c中定义,即所有imx.系列都用这个函数

参考

韦东山老师的直播公开课【u-boot完全分析与移植】 https://www.bilibili.com/video/BV1L24y187cK/?share_source=copy_web\&vd_source=a487d2d970df1f64a42c4883b55cb49f

相关推荐
盐真卿4 小时前
python第八部分:高级特性(二)
java·开发语言
茉莉玫瑰花茶4 小时前
C++ 17 详细特性解析(5)
开发语言·c++·算法
wVelpro4 小时前
如何在Pycharm 2025.3 版本实现虚拟环境“Make available to all projects”
linux·ide·pycharm
lly2024064 小时前
《堆的 shift down》
开发语言
黎雁·泠崖4 小时前
【魔法森林冒险】2/14 抽象层设计:Figure/Person类(所有角色的基石)
java·开发语言
程序员老舅5 小时前
C++高并发精髓:无锁队列深度解析
linux·c++·内存管理·c/c++·原子操作·无锁队列
雨中风华5 小时前
Linux, macOS系统实现远程目录访问(等同于windows平台xFsRedir软件的目录重定向)
linux·windows·macos
季明洵5 小时前
C语言实现单链表
c语言·开发语言·数据结构·算法·链表
墨雪不会编程5 小时前
C++之【深入理解Vector】三部曲最终章
开发语言·c++
浅念-5 小时前
C语言编译与链接全流程:从源码到可执行程序的幕后之旅
c语言·开发语言·数据结构·经验分享·笔记·学习·算法