Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析

来源:互联网 发布:mac版本的greenvpn 编辑:程序博客网 时间:2024/05/15 23:51

转载自http://blog.csdn.net/zqixiao_09/article/details/50805720

uboot启动流程分析如下:

第一阶段:

a -- 设置cpu工作模式为SVC模式
b -- 关闭中断,mmu,cache
v -- 关看门狗
d -- 初始化内存,串口
e -- 设置栈
f -- 代码自搬移
g -- 清bss
h -- 跳c

第二阶段

a -- 初始化外设,进入超循环
b -- 超循环处理用户命令


可见, U-Boot 属于两阶段的Bootloader

第一阶段的文件:

arch/arm/cpu/armv7 /start.S                       平台相关,CPU工作模式设为SVC模式,关MMU,关icahce(CPU相关)

board/samsung/fs4412/lowlevel_init.S     开发板相关:关看门狗,内存初始化,时钟初始化,串口初始化(board相关,初始化最基本设备)


第二阶段的文件:

arch/arm/lib/crt0.S                     _main 函数所在处,初始化SP,为C语言准备,代码重定位,清BSS,设置R0 R1 R2 R8相应寄存器

arch/arm/lib/board.c                   board_init_f 函数 ,填充GD结构体,初始化外设, main_loop()函数超循环

arch/arm/cpu/armv7 /start.S 代码自搬移时会用到

针对uboot2013启动流程图如下:



下面是具体分析:

一、U-Boot 第一阶段代码分析

通常我们通过连接文件知晓程序入口点,入口查看 u-boot.lds


通过链接脚本可知入口为_start,位于arch/arm/cpu/armv7/start.o


第一阶段开始:

1、进入arch/arm/cpu/armv7/start.S

a -- 异常向量表设置



b -- 设置CPU处于SVC工作模式



d -- 协处理器 p15 的 c12 寄存器来重新定位



e、Bl  cpu_init_cp15(使分支预测无效,数据)


关闭数据预取功能;

DSB:多核CPU对数据处理指令

ISB:流水线清空指令;


关闭MMU,使能I-cache

NOTE:

分支预测:在流水线里,会将后面的代码优先加载到处理器中,由于是循环,会使后面加载的代码无效,故出现了分支预测技术。(统计跳的次数来选择装载循环的代码还是下面的代码)。


f、Bl  cpu_init_crit




2、跳到Low_level_init,位于board/samsung/fs4412/lowlevel_init.S

a、关闭看门狗


b、比较当前pc指针域TEXT_BASE的高8位是否一样来判断,当前代码是否在内存中



c、对系统时钟初始化



d、对内存初始化


e、对串口初始化


结束后返回 start.S

第一阶段结束,总结如下:

1 前面总结过的部分,初始化异常向量表,设置svc模式

2 配置cp15,初始化mmu cache tlb

3 板级初始化,clk,memory,uart初始化


二、第二阶段开始:


按"CTRL + ] ", 发现 _main 在两处有定义:


这里我们选择第一个Bl  _main ,跳转到arch/arm/lib/crt0.S


1、初始c运行环境(看注释就知道,初始化C运行环境,并调用board_init_f 函数


功能:

初始化sp ,为支持C语言做准备;

保存128B 放GD结构体,存放全局信息,GD的地址存放在r8中;

跳转到 board_init_f 函数,其在arch/arm/lib/board.c 处定义;


2、跳转到arch/arm/lib/board.c


功能:

对全局信息GD结构体进行填充:

291行:mon_len 通过链接脚本可以知道存放的是uboot代码大小;

294行:fdt_blob 存放设备数地址;

303行:循环执行init_fnc_t数组的函数,作硬件初始化;


a -- init_fnc_t数组的函数定义

     初始化硬件


b -- Dram_init初始化成功之后,剩余代码将会对sdram空间进行规划。


可以看到addr的值由CONFIG_SYS_SDRAM_BASE加上ram_size。也就是到了可用sdram的顶端。


e--继续对gd结构体填充


如果icahe 与 dcache 是打开的,就留出 64K 的空间作为 tlb 空间,最后 addr 就是tlb 地址,4K对齐。


f --填充完成将信息拷贝到内存指定位置




2 -- 继续回到 _main

按"CTRL + O"回到跳转前的函数,即 arch/arm/lib/crt0.S


功能:

将 r8 指向新的 gd 地址;

代码重定位;

 对lr 的操作为了让返回时,返回的是重定位的here处


3 -- 代码自搬移

代码自搬移,防止与内核冲突,代码位于arch/arm/cpu/armv7/start.S

循环将代码搬移到指定高地址

这里只是将链接脚本中_image_copy_end_start中的代码,其它段还没有操作。

在这里我们有疑惑就是将代码重定位到高地址,那运行的地址不就和链接地址不一样了,那运行可能不正常?这个疑惑就是.rel.dyn帮我们解决了,主要还是编译器帮我们做的工作,在链接中有如下:【参考:http://blog.csdn.net/skyflying2012/article/details/37660265


4 -- 重定位到高地址之后,再次回到 _main(arch/arm/lib/crt0.S)

      此时回到的是刚才的重定位的 here 处


关 icache,保证数据从SDRAM中更新,更新异常向量表,因为代码被重定位了;

清BBS;



调用board_init_r主要是对外设的初始化。

R0=gd

R1=RELOCADDR


5 -- Main_loop 函数进入超循环(arch/arm/lib/board.c)


Main_loop函数主要功能是处理环境变量,解析命令

install_auto_complete();  //安装自动补全的函数,分析如下 

getenv(bootcmd)

bootdelay(自启动)

如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。


0 0