uboot第一部分

相关连接

[uboot] (番外篇)uboot 驱动模型_ooonebook的博客-CSDN博客_uboot 驱动模型uboot引入了驱动模型(driver model),这种驱动模型为驱动的定义和访问接口提供了统一的方法。提高了驱动之间的兼容性以及访问的标准型。uboot驱动模型和kernel中的设备驱动模型类似,但是又有所区别。在后续我们将驱动模型(driver model)简称为DM,其实在uboot里面也是这样简称的。这篇文章里介绍的DM的流程以及如何使用。https://blog.csdn.net/ooonebook/article/details/53234020

1.    UBOOT的编译:
a)    对于UBOOT的编译:cd /uboot/u-boot-2017.09; ./make.     rv1126;    其中的make.sh是RK封装好的一个脚本。
b)    Uboot也有dtb(设备树),但是可以控制dtb和uboot是否分开:

2.    芯片的内部有一段iROM,一般是芯片厂商写死的,用于引导启动程序。
所谓的iROM,即internal ROM,
IROM的流程:关中断和MMU,关D-cache,使能I-cache,失效TLB表,让CORE1进入空闲,初始化栈,初始化Zi,RW段,注册函数指针,判断启动方式,从对应启动设备中加载Bl1到SRAM中,然后对Bl1进行校验和检测,加密校验,解密处理,最后跳转到Bl1代码执行。
总结:iROM的作用是:iROM本身也称为Bl0

根据启动方式的不同(MMC或者NorFlash),从不同的存储介质中加载Bl1 到RAM
对加载的Bl1 进行检测校验
跳转到Bl1运行

3.    Bl1的作用:运行在芯片内的SRAM上
a)    重新初始化IRQ,
b)    判断启动方式(MMC,Nor)从对应的存储中加载Bl2到RAM中,
c)    如果是安全模式,则对Bl2进行校验,
d)    跳转到Bl2 运行

4.    Bl2的作用:运行在外扩SDRAM上(DDR),即uboot:
a)    重新初始化IRQ,设置时钟,初始化外扩RAM,
b)    判断启动设备,从对应的设备中读取OS或FW(firmware),
c)    跳转到OS或者FW中运行

5.    上述Blx为了执行正常,会分别占用一段内存,不同的公司不一样。上述的启动方式可选:nand  mmc  sd卡  USB等

--------------------·-------uboot流程概述-----------·-------------
1.    Boot的基本功能:核心功能是引导操作系统,其余部分工作如下:
a)    初始化部分硬件,时钟 内存等。
b)    加载内核到内存上
c)    启动操作系统
d)    命令行功能,即uboot下的命令,setenv  mmc write等。

2.    几种常见的bootloader:Uboot: 用于ARM,支持命令行(monitor)
3.    Uboot-spl:    即Bl1    spl.bin: 我司RK生成的spl.bin大小为 220KB左右。
主要工作有:
初始化部分时钟(和SDRAM相关)
初始化SDRAM
从flash读取Bl2(uboot)到SDRAM
跳转到Bl2(uboot)地址。

4.    uboot.bin   Bl2: 
a)    初始化硬件,包括时钟,内存等
b)    将kernel加载到内存上
c)    加载文件系统   dtb 到内存上。
d)    启动OS
e)    命令行:
i.    Flash的操作
ii.    Env的操作
iii.    启动相关的操作

-----------------------·-------RK平台 spl.bin的编译流程----------·--------
cd $(UBOOT_DIR); 【./make.sh $(UBOOT_CONFIG);】  ·  【./make.sh spl-s】  .
通用的来说,spl的编译是uboot编译的一部分,但是和uboot是分开编译的,一般是先编译uboot,然后编译spl.bin.

注意在编译过程中会通过一些 u-boot-spl.lds 的连接文件连接生成bin

------------------------·-----spl的流程--------------------·-
Lds文件:./arch/arm/cpu/u-boot-spl.lds
Lds即linker script 链接脚本,链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 连接器有个默认的内置连接脚本, 
即:lds文件决定了各部分内容在最终文件里的位置,
ENTRY(xxx) 即 最终文件的开头,放在最前面的文件,即最先被运行的部分
  通过查看得知,入口为“_start”
详见vectors.S 中定义的全局的符号 _start
 

1.    lib/ vectors.S
2.    armv7/start.S
    cpu_init_cp15 接口:设置 CP15 寄存器(缓存、MMU、TLB)。 除非定义了 CONFIG_SYS_ICACHE_OFF,否则将打开 I-cache。
    cpu_init_crit 接口:进行一些关键的初始化动作,也就是平台级和板级的初始化。其代码核心就是lowlevel_init:
* 检查一些复位状态
* 关闭看门狗
* 系统时钟的初始化    system_clock_init
* 内存DDR的初始化 mem_ctrl_asm_init
* 串口初始化(可选)
* Nand flash的初始化
  _main 位于crt0.s中

3. arch/arm/lib/crt0.S  该脚本的内容其实仅有一个函数 _main,
    所谓的crt0  即 c run time 即运行环境将由汇编向C语言转换。
main的主要目标是调用board_init_f进行先前的板级初始化动作,大致操作如下:

(1)    因为后面是C语言环境,首先是设置堆栈(汇编)
(2)    为GD分配空间:关于GD,也就是struct global_data,可以简单的理解为uboot的全局变量都放在了这里,比较重要
(3)    初始化GD空间:board_init_f_alloc_reserve:common/init/board_init.c
(4)    跳转到板级前期的初始化函数中  Bl  board_init_f
注意,经过上述四步后,就从spl里面跳转到 uboot里面了。board_init_f 一般各家的实现都不同. 

-----------------------·------uboot启动流程--------------·-----------
前面讲到,SPL执行的末尾会调用 board_init_f 自此转入uboot阶段。
该函数位 u-boot-2017.09/common/board_f.c 
1.    CPU刚上电。要设置很多状态: cpu状态、中断状态、MMU状态等。其次,初始化硬件资源。最后进入命令行状态,等待命令输入。
a)    arch级的初始化

b)    关闭中断,设置svc模式, 禁用MMU、TLB
c)    关键寄存器的设置,包括时钟、看门狗的寄存器
d)    堆栈环境的设置
e)    代码重定向之前的板级初始化,包括串口、定时器、环境变量、I2C\SPI等初始化
f)    进行代码重定向代码重定向之后的板级初始化,包括板级代码中定义的初始化操作、emmc、nand flash、网络、中断等等的初始化。
g)    进入命令行状态,等待终端输入命令以及对命令进行处理

2.    spl已对arch级进行了初始化,为什么uboot还要对arch再初始化?
回答:spl对启动uboot来说并不是必须的,某些情况下可能没有spl而直接运行boot,因此uboot不会考虑spl是否已对arch进行了初始化,uboot会完整的初始化一遍,以保证cpu处于所需状态下。

3.    Spl运行结束后,在_main里面会运行两个重要的函数 board_init_f 和 board_init_r 

4.    Board_init_f 的流程:
a)    setup_mon_len,  //计算整个镜像的长度
 
b)    env_init       环境变量初始化:
c)    init_baud_rate 波特率初始化
d)    serial_init,     串口初始化
e)    console_init_f, 控制台初始化
f)    init_func_i2c,  //IIC初始化
g)    init_func_spi,  //SPI始化
h)    reloc_fdt,     //fdt相关

5.    board_init_r的实现:
a)    initr_reloc, // global data 中一些关于relocate的标识的设置
b)    initr_malloc,   malloc内存池的设置
c)    interrupt_init,  //中断初始化
d)    board_init,    /* Setup chipselect不同厂家的各自的初始化函数 */
e)    board_late_init 
f)    initr_serial,    //串口初始化
g)    initr_flash, // // 如果使用emmc,那么这里需要对nand进行初始化
h)    initr_nand,   //如果使用nand flash,那么这里需要对nand进行初始
i)    initr_env,   //初始化环境变量
j)    timer_init,        /* initialize timer *///定时器初始化
k)    run_main_loop, //进死循环 ,在死循环里面处理终端的命令

-------------------------------·------uboot  relocation begin-----------------·--------------------
UBOOT即Bl2,需要拷贝到DDR上运行。拷贝Bl2 可以通过SPL来运行,但是有可能没有SPL。所以UBOOT自身也要支持拷贝到DDR。
即UBOOT的relocation实际就是将自身拷贝到DDR上的动作。
问题:既然boot会将自身拷贝到DDR上,相当于执行地址会变化,要求boot在r

elocate前后都可以运行,要求boot使用“位置无关代码”技术。

1.    位置无关代码技术:
a)    无论代码被加载到内存的什么地址,均可以运行。即加载地址和连接地址不同时,CPU可以通过相对寻址获得正确的指令。
b)    位置无关代码的生成:
i.    编译时 gcc的  -fpic 或 mword-relocations 选项

其中fPIC选项在编译应用层的时候也是常用的。
ii.    连接时 通过ld的 -fpie 选项。
iii.    ARM实际用 “-mword-relocations -fno-pic”  其中-fno-pic 意为 不使用PIC
iv.    ARM的连接阶段使用原生的LD的-fpie 选项
2.    位置无关的原理:

a)    uboot对自身relocate后,全局变量的地址会变化,因此,在relocate过程中要对全局变量的label中的地址进行修改,uboot将label的地址维护在.rel.dyn段中,然后再统一对.rel.dyn段指向的label进行修改。
3.    relocation的过程:
a)    把自己本身relocate到ddr的顶端地址使之不会和kernel的冲突。并非直接把uboot放到ddr的顶端,而有一定布局,预留空间给其他需要的功能使用。
4.    从高地址到低地址布局如下(并非所有区域都需要,可据宏定义确定)

prom页表区域            8192byte
logbuffer                LOGBUFF_RESERVE
pram区域                CONFIG_PRAM<<10
round_4k                用于4kb对齐
mmu页表区域                PGTABlE_SIZE
video buffer            不关心。但是是确定的。不会随着代码变化
lcd buffer                不关心。但是是确定的。不会随着代码变化
trace buffer            CONFIG_TRACE_BUFFER_SIZE
uboot代码区域            d->mon_len,并且对齐4KB对齐
malloc内存池            TOTAL_MALLOC_LEN
Board Info区域            sizeof(bd_t)
新global_data区域        sizeof(gd_t)
fdt区域                    gd->fdt_size
对齐                     16b对齐
堆栈区域                无限制

5.    Relocate代码流程:
* 对relocate进行空间规划
* 计算uboot代码空间到relocation的位置的偏移
* relocate旧的global_data到新的global_data的空间上
----- 旧空间--relocate_code ----新空间---
* relocate旧的uboot代码空间到新的空间上去
* 修改relocate之后全局变量的label。(不懂的话参考第二节)
* relocate中断向量表

综述:relocation的实现: arch/arm/lib/crt0.S 文件,详细参考链接,注意relocate_code之后,代码就在新的boot空间上运行了。

6.    具体代码分析:relocation功能的实现在init_sequence_f的后半部分,分析如下

如上图所示,所谓的规划,起始就是对各个分区的指针进行重定向。

7.    中断向量表: u-boot-2017.09/arch/arm/lib/vectors.S 的 relocation:
a)    arm的异常中断向量表需要复制到0x00000000处或者0xFFFF0000处。
b)    需把新的异常中断向量表复制到0x00000000处或者0xFFFF0000处。
这部分操作就是在relocate_vectors中进行。用汇编实现的。
=中断向量表relocate结束后,relocation就算正式结束了=

----------------·---------UBOOT启动kernel----------------------·----
1.    Bootm命令:是UBOOT下的一个命令

RK的: arm-linux-gnueabihf-objdump 经过尝试发现, 应用层的可以dump,但是内核和boot仅能dump  .o文件。
------------------------·------------------map相关 begin -------------------·----

经过测试,编译完uboot后,会在u-boot-2017.09 目录下生成一个u-boot.map文件,

 
经过测试,发现uboot uimage均会生成一个  .map 文件,里面是所有的sym的集合,

经过实测好用,可以定位到目录,并非准确的文件
1.    对map文件的用处的猜想:
    通过map定位函数所在的源文件
2.    Kernel下 有用的map文件为 linux4.1.9/System.map 文件,该文件区别于boot的map文件,内部仅是符号的集合,并没有具体的文件的目录。具体为按链接地址从小到大的顺序列出所有的符号:
 3.    关于map文件的另一种解释:所有的函数或者变量都会对应一个地址,内核使用函数时,比如malloc时,是去找malloc对应的地址。(此外gdb也是利用符号表,将地址转换为具体的接口名称),其实符号表可以做到符号和地址的相互转换。
a)    当编译新的镜像时,原来的 .map 或 .sym 就不能用了,需要用新产生的替换旧的。
b)    关于oops:内核oops时,会使用klogd解析map文件和ko的符号表,以返回给用户问题大概的出处。
扩展:关于内核的panic 和 oops

4.    相对于system.map的仅符号表,u-boot.map的内容更丰富:
a)    Uboot.map中的地址是运行时的地址,即uboot运行起来后打印函数名出现的地址与map中是相同的。

-------------------------------·-----------map相关 end ----------------·---------------------------

-------------------------------·----------UBOOT下fdt的介绍---------------·---------------------------
UBOOT中对FDT的操作:
1.    获取DTB的地址,并且验证DTB的合法性:
 
源码位于 u-boot-2017.09/lib/fdtdec.c文件,主要思路如下:
Fdt与UBOOT有两种方式:CONFIG_OF_EMBED  CONFIG_OF_SEPARATE,
CONFIG_OFEMBED:dtb集成到UBOOT的固定的字段中,通过该固定字段获取UBOOT
CONFIG_OF_SEPARATE:dtb追加到UBOOT的末尾,通过UBOOT的end作为fdt的起始位置。
获取到fdt后,通过判断前面几个字节是否为“magic“来判断fdt的合法性。

2.    为dtb分配新的内存空间:reserve_fdt: common/board_f.c
 
主要思路:如果是CONFIG_OF_EMBED即dtb在UBOOT中,则不需要额外为relocate分配空间。但是如果是CONFIG_OF_SEPARATE,则需要先获取fdt的size,然后分配size大小的内存。

3.    Relocate dtb:  common/board_f.c     接口 reloc_fdt

如果是CONFIG_OF_EMBED,则不处理(dtb在uboot中,不单独reloc)
如果是CONFIG_OF_SEPARATE,则拷贝就的dtb到新的dtb地址,然后更新一下gd(global_data)的相关字段。具体是  gd->uffdt_Blob(与配置相关) 
 

Uboot会通过gd->fdt_Blob提供fdt的API:
用节点在dtb中偏移地址表示该节点。也就是节点变量node中,存放的是节点的偏移地址
lib/fdtdec.c文件中:
1.    fdt_path_offset
int fdt_path_offset(const void *fdt, const char *path)
eg:node = fdt_path_offset(gd->fdt_Blob, “/aliases”);

功能:获得dtb下某个节点的路径path的偏移。这个偏移就代表了这个节点。
2.    fdt_getprop
const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp)
eg: mac = fdt_getprop(gd->fdt_Blob, node, “mac-address”, &len);

功能:获得节点node的某个字符串属性值。
3.    fdtdec_get_int_array、fdtdec_get_byte_array
int fdtdec_get_int_array(const void *Blob, int node, const char *prop_name, u32 *array, int count)
eg: ret = fdtdec_get_int_array(Blob, node, “interrupts”, cell, ARRAY_SIZE(cell))

功能:获得节点node的某个整形数组属性值。
4.    fdtdec_get_addr
fdt_addr_t fdtdec_get_addr(const void *Blob, int node, const char *prop_name)
eg:fdtdec_get_addr(Blob, node, “reg”);

功能:获得节点node的地址属性值。
5.    fdtdec_get_config_int、fdtdec_get_config_bool、fdtdec_get_config_string
功能:获得config节点下的整形属性、bool属性、字符串等等。

6.    fdtdec_get_chosen_node
int fdtdec_get_chosen_node(const void *Blob, const char *name)
功能:获得chosen下的name节点的偏移

7.    fdtdec_get_chosen_prop
const char *fdtdec_get_chosen_prop(const void *Blob, const char *name)
功能:获得chosen下name属性的值

lib/fdtdec_common.c中
1.    fdtdec_get_int
int fdtdec_get_int(const void *Blob, int node, const char *prop_name, int default_val)
eg: bus->udelay = fdtdec_get_int(Blob, node, “i2c-gpio,delay-us”, DEFAULT_UDELAY);
功能:获得节点node的某个整形属性值。
2.    fdtdec_get_uint
功能:获得节点node的某个无符号整形属性值。

-------------------------------·----------UBOOT下bootm---------------·------------------------
Bootm是uboot下的一个命令,用于启动OS,通过从镜像文件获取ARCH,OS类型,镜像类型及压缩方式,在内存中的加载地址,镜像文件的入口地址等,解压镜像,加载到指定地址,跳转运行OS。
 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值