uboot启动流程关键函数的介绍(一)

Uboot的入口在uboot根目录下的uboot.lds(只有编译u-boot才会出现)

Uboot.lds用来描述输出文件的内存布局,也就是确认程序的运行地址

Ubbot.lds为代码入口点:_start_start在文件arch/arm/lib/vectors.S中有定义。

 

Vectors段保存了中断向量表,vector.S的代码保存在vectors段中。

Arch/arm/cpu/armv7/start.s编译出来的代码放在中断向量表后。

其他的代码放在text段中。

 

Uboot.map是uboot的映像文件,可以从此文件看到某个文件或者函数链接到那个地址。

 

_start开始的是中断向量表

 

_start(arch/arm/lib/vectors.S)跳转 reset函数(arch/arm/cpu/armv7/start.S中),

reset函数跳转到save_boot_params函数(同样定义在arch/arm/cpu/armv7/start.S)

save_boot_params函数跳转到save_boot_params_ret函数中(同上文件)

         此函数先是完成设置cpu处于svc32模式,并且关闭FIQ/RIQ这两个中断。

         然后清除SCTLR寄存器中bit13位,此位是向量表控制位,置0可设置向量表重定位。

         最后分别调用cpu_init_cp15、cpu_init_crit_main

其中Cpu_init_cp15是用来设置cp15相关内容,比如关闭MMU等,

 

Cpu_init_crit仅调用lowlevel_init函数(此函数在arch/arm/cpu/armv7/lowlevel_init.S)。

         1、设置sp指针指向CONFIG_SYS_INIT_SP_ADDR(在include/configs/mx6ullevk.h中定义)

 

         CONFIG_SYS_INIT_RAM_ADDR=IRAM_BASE_ADDR=0X00900000

CONFIG_SYS_INIT_RAM_SIZE=IRAM_SIZE=0X20000=128KB        

GENERATED_GBL_DATA_SIZE=256

所以

CONFIG_SYS_INIT_SP_OFFSET=0X00020000-256=0X1FF00

CONFIG_SYS_INIT_SP_ADDR=0X00900000+0X1FF00=0X0091FF00(这属于imx6ull的内部ram

2sp指针减去GD_SIZE(248),此时sp指针的地址为0x91ff00-248=0x0091fe08

3、将sp指针地址放在r9寄存器中,将lr赋给pc调用s_init函数

 

S_init函数定义在在arch/arm/cpu/armv7/mx6/soc.c

         该函数主要判断当前CPU类型,然后直接返回,相当于是个空函数,从s_init返回到lowlevel_init函数,再返回到cpu_init_crit,再返回到save_boot_params_ret中。接下来执行save_boot_params_ret_main函数

 

 

_main函数arch/arm/lib/ct0.S

1、设置sp指针指向0x0091ff00,并保存到r0中,即r0=0x0091ff00

         调用函数borad_init_f_alloc_reservecommon/init/board_init.c中),传入参数r0               

                   该函数主要是留出早期的malloc内存区域和gd内存区域,其中CONFIG_SYS_MALLOC_F_LEN=0X400global_data=248;最后返回top=0X0091FA00。完成之后内存分布如图:

 

2r0保存着函数的返回值,将sp指针指向0X0091FA00

3、将r0的值写入到r9中,因为r9存放着全局变量gd的地址。Uboot定义了一个指向gd_t的指针,gd存放在r9中,因此gd是个全局变量。gd_t是个结构体。因此gd指向0x0091FA00

4调用board_init_f_init_reserve函数common/init/board_init.c),此函数是初始化gd,其实就是清零处理。此函数还设置gd->malloc_base0X0091FB00,就是early malloc的起始地址。

5、设置r00调用board_init_f函数(在common/board_f.c中),主要初始化DDR,定时器,完成代码的拷贝等。调用board_init_f函数主要实现以下两个工作:

①初始化一些外设,比如串口、定时器、或者打印一些信息等。

②初始化gd的各个成员变量,uboot会将自己重定位到DRAM最后面的地址区域,也就是将自己拷贝到DRAM最后面的内存区域中,这么做是为了给linux腾出空间,防止linux kernel覆盖掉uboot。再拷贝前肯定要给uboot各个部分分配内存位置和大小,比如gd应该放在哪个位置,malloc内存池应该放在那个位置等,这些信息都要保存在gd的成员变量值,最终形成一个完整的内存分配图,在后面重定位uboot的时候需要使用到这个内存图。

在board_init_f函数中调用了初始化序列init_sequence_f里面的一系列函数

1setup_mon_len函数设置了gdmon_len成员变量,即整个代码对的长度 0xa8e74

2initf_malloc函数初始化gdmalloc相关的变量,其中malloc_limit表示内存池的大小0x400

3arch_cpu_init函数初始化架构相关的内容,cpu级别的操作

4initf_dm驱动模块初始化

5board_early_init_f板子早期的一些初始化设置

6timer_init初始化定时器,内核时钟,给uboot提供时间

7board_postclk_init函数设置板子电压

8get_clocks函数获取时钟值,sd卡外设的时钟

9env_init函数设置gdenv_addr,就是环境变量的保存地址

10init_board_rate函数设置波特率、serial_init初始化串口

11concole_init_f设置gd->have_consloe1,表示有个控制台,此函数将暂存在缓冲区的数据打印到控制台

12display_options通过串口输出信息,display_text_info打印文本信息

13printf_cpuinfo打印CPU信息 show_board_info打印板子信息

14init_func_i2c初始化i2c

15dram_init 设置gd->ram_size并非真正的初始化ddr,对于i.m6ull emmc就是512MB

(16)post_init_f完成一些测试,初始化gd->post_init_f_time

17setup_dest_addr 设置目的地址,设置gd->ram_sizegd->ram_topgd->relocaddr这三个值。reserve_round_4k函数用于对gd->relocaddr4KB对齐。调整后不变。

18reserve_mmu 留出MMUTLB表的位置,分配MMUTLB表内存以后会对gd->relocaddr64KB字节对齐,完成以后gd->arch.tlb_sizegd_.arch.tlb_addrgd->relocaddr如图所示:

19reserve_uboot 留出重定位后的uboot所占的内存区域,uboot所占字节大小有ge->mon_len所之巅,并且重新设置gd->start_addr_sp

20reserve_malloc留出malloc区域调整gd->start_addr_sp位置,malloc区域有宏TOTAL_MALLOC_LEN定义,调整后的gd->start_addr_sp

21reserve_board留出板子bd所占的内存区,bd是结构体bd_t,大小为80字节

22reserve_global_data 保留gd_t的内存区域,gd_t结构体的大小为248B

23reserve_stacks留出栈空间,先对gd->start_addr_sp减去16,然后做16字节对齐,如果使能IRQ的话还要留出IRQ相应的内存

24setup_dram_config设置dram信息,就是设置gd->bd->bi_dram[0].startgd->bd->bi_dram[0].size后面传递个linux内核,告诉linux dram的起始地址和大小

25display_new_sp 显示新的sp位置,也就是gd->start_addr_sp

26setup_reloc,设置gd的其他成员变量,供后面定位使用,并且将一起的gd拷贝到gd->new_gd

6、重新设置环境(spgd)、获取gd->start_addr_sp的值赋给sp,在函数board_init_f中会初始化gd的所有成员变量,其中gd->start_addr_sp=0x9ef44e90。所以相当于设置sp= start_addr_sp=0x9ef44e900x9ef44e90ddr中的地址,说明新的spgd将会存在DDR中,GD_START_ADDR_SP=64

 

7、获取gd->bd的地址赋给r9,此时r9存放的是老的gd,新的gdbd下面,所以r9减去gd的大小就是新的gd的位置,获取到新的gd赋值给r9

8、设置lr寄存器为here,这样后面执行其他函数返回时就返回到here处。

9、读取gd->reloc_off的值给r0gd_reloc_off=68,lr寄存器的值加上r0的值,重新赋给lr,因为接下来要重新定位代码,也就是把代码拷贝到新的地方去(现在uboot的存放的起始地址为0x87800000,下面要把uboot拷贝ddr最后面的地址空间,将0x87800000开始的内存空出来),其中就包括here,因此lrhere要使用重定位的位置。

10、读取gd->relocaddr的值赋给r0,此时r0寄存器就保存着uboot拷贝到的目的地址,为0x9ff47000GD_RELOCADDR=48

11、调用函数relocate_code,也就是代码重定位函数,此函数负责将uboot拷贝到新的地方去。

函数relocate_code

①首先将r1=_image_copy_start,r1保存原地址,即0x878000000,r2保存代码拷贝之前的结束地址。r0=0x9FF47000,这个地址就是拷贝的目标地址,r4=r0-r1=0x18747000,即为偏移地址。通过r10和r11来进行数据的传输。比较r1与r2的值,检查拷贝是否完成。

②重定位段.rel.dyn段:

重定位就是uboot将自身拷贝到dram的另一个地址去继续运行(DRAM的高地址),一个可执行文件.bin文件,其链接地址和运行地址必须相等,就是链接到哪个地址,在运行之前就要拷贝到哪个地址去,现在进行了重地位,运行地址和链接地址不同了,这样寻址就会出现问题。

对于这个问题,采用第三方偏移地址,称为label,这就是重定位后运行不会出错的原因

12、调用函数relocate_vectors对中断向量表做重定位。

该函数实现对中断向量表的重定位,r0=gd->relocaddr,也就是重定位后uboot的首地址,向量表肯定是从这个地址开始存放的,然后将r0的值写入到cp15VB寄存器中,也就是将新的向量表首地址写入到寄存器VBAR,设置向量表偏移。

13、调用函数c_runtime_cpu_setup函数。

14、设置函数board_init_r的两个参数,第一个参数是gd,因此,读取gd保存到r0中。第二个参数是目标地址,因此r1=gd->rellocaddr

Board_init_f初始化了一些外设和gd的成员变量,但并没有初始化所有的外设,这时候就需要board_init_r函数来实现后续的工作。在board_init_r函数中调用了initcall_run_list函数来执行初始化序列init_sequence,这是一个函数的集合。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值