您的位置:首页 > 其它

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

2016-03-04 22:29 435 查看
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(自启动)

如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: