uboot启动之第一次运行C函数到uboot重定位

接上一篇博文《uboot启动流程之上电启动到第一次准备好C语言运行环境》,本文从board_init_f()开始。

board_init_f定义在uboot/common/board_f.c中(CONFIG_SYS_GENERIC_BOARD=y)。

这里全局变量gd定义在uboot/arch/arm/include/asm/global_data.h中,在29行间接包含(uboot/include/common.h ->uboot/arch/arm/include/asm/global_data.h):

uboot/common/board_f.c

uboot/include/common.h

这个#include中的asm在uboot编译工程中讲过,是一个软连接,连接到uboot/arch/arm/include/asm/

我们去这个uboot/arch/arm/include/asm/global_data.h中看看全局变量gd的定义:

uboot/arch/arm/include/asm/global_data.h

从定义看,gd是一个全局的寄存器变量,占用的寄存器位r8, gd即r8。

前面分析过,r8存储了的数据为地址(0x80000000+16K – GD_SIZE), 即全局变量gd指向(0x80000000+16K – GD_SIZE)。

在行1020-1022在栈上定义一个gd_t变量,并且让gd指向这个地址。

这里为什么要在栈上定义一个gd_t,然后再让gd指向它?这不是改变了gd的指向了吗?

实际上,并没有改变gd指向,gd还是指向r8保存的栈地址。因为arm使用的是空递减栈,即sp指向的是下一次使用的内存地址。在crt0.S中,我们让sp预留了gd_t的内存,即这个预留出的内存就是这里定义的data局部变量。

1025行设置gd->flags为0(r0寄存器值在C函数board_init_f被调用前清零了)。

1027行通过initcall_run_list循环调用init_sequence_f的函数列表。

uboot/lib/initcall.c

init_sequence_f定义了很多函数list:

uboot/common/board_f.c

zero_global_data:将gd指向的内存清零。

mark_bootstage: 设置bootstage为board_init_f。

env_init: 设置环境变量保存的地址。在uboot/common/Makefile中定义如下:

                 CONFIG_ENV_IS_IN_SPI在uboot/include/autoconf.mk中定义:

                 

                 所以, env_init为定义在uboot/common/env_spi.c中的env_init, 如下:

                

                 default_environment[]定义在uboot/include/env_default.h, 定义了默认的环境变量的值。

                 所有的默认boot环境变量配置都在文件uboot/include/common.cfg中定义,比如:

                 bootcmd=bootfmh

                 bootdelay=0

                 uboot/include/env_default.h

                

init_baud_rate: 配置串口波特率为默认值115200

                

                  CONFIG_BAUDRATE定义在uboot/include/common.cfg中:

                  

                  这个CONFIG_SPX_FEATURE_BAUDRATE_CONSOLE_TTY在工程配置文件*.PRJ定义:

                 

                  这个是特定厂商的配置文件,不做进一步介绍。总之,这个*.PRJ定义的东西,会替换

                  common.h中对应的宏。

serial_init:初始化一个串口,为uboot cmd交互提供交互控制台。

                      定义在uboot/drivers/serial/serial.c

                    

                   

                     serial_init直接调用default_serial_console串口设备start()函数。

                     在ast2500evb板子中,使用的UART为ns16550, 且CONFIG_SYS_NS16550_SERIAL=y

                     故, 这里这个default_serial_console定义在uboot/drivers/serial/serial_ns16550.c,

                    且配置的CONFIG_CONS_INDEX为5.

                   

                    所以,serial_init就是调用了eserial5_device->start(), 即eserial5_init().

                   

                     

                     

                     100行中serial_ports[port-1]为CONFIG_SYS_NS16550_COM5(0x1E784000):

                    

                     serial_ns16550.c -> uboot/include/common.h -> uboot/include/config.h

                           ->  uboot/include/ configs/ast2500evb.h  -> uboot/include/configs/ast.cfg

                                          

                  calc_divisor()用于计算每传输一个码元需要的时钟滴答数,计算方式如下:

                 

                   CONFIG_SYS_NS16550_CLK定义:

                   uboot/include/configs/ast.cfg

                  

                   uboot/arch/arm/cpu/astcommon/ast_clk.c.

                 

                   计算出clock_divisor后,调用NS16550_init()处理:

                   uboot/drivers/serial/ns16550.c

                   

                   

                   NS16550_init就是对UART port的寄存器进行配置。

console_init_f:

                   这个时候还没有std,所以,前面的UART串口就用作console。

                   如果配置了CONFIG_PRE_CONSOLE_BUFFER=y, 那么

                   console_init_f就是向UART口输出打印。

                   uboot/common/console.c

                  

                  

                    这个putc就是向UART口发送一个字符。

                   

                   

                    如果没有配置CONFIG_PRE_CONSOLE_BUFFER=y,

                   那这个print_pre_console_buffer()就是空,什么都不做。

                 

init_func_i2c:

                   初始化i2c总线。

                   

                    对于ast2500evb, i2c_init什么也不做,见uboot/arch/arm/cpu/astcommon/ast_i2c.c

                    

                      uboot/arch/arm/cpu/astcommon/Makefile

                     

                      uboot/include/autoconf.mk

                     

dram_init:

                     这里dram初始化就是sdram的信息记录,定义如下:

                     uboot/board/ast2500evb/ast2500evb.c

                     

                     sdram寄存器的配置和初始化在*.S中就做过了

                    (不然你怎么能跑到C语言环境,堆栈指针可是指向这个)

                    这里保存了sdram的基址CONFIG_SYS_SDRAM_BASE(2G),

                   大小为CONFIG_SYS_SDRAM_SYS_USED(480M)。

setup_dest_addr:

                   这里主要是设置uboot relocate到sdram中的地址为sdram的最大地址: 2G+480M.

                  

                  

reserve_mmu:

                   reserve_mmu主要是在sdram顶端预留TLB的内存(16k).

                  

reserve_uboot:

                  reserve_uboot主要就是设置uboot monitor relocate的内存地址(整个uboot image size)

                  大小为bss_end - _start, 见setup_mon_len()

                  

                   uboot/arch/arm/cpu/arm1176/start.S

                 

                 

reserve_malloc:

                  reserve malloc区间内存,大小为TOTAL_MALLOC_LEN, 即64k+64k

                

reserve_board:

                reserve gd->bd内存。

               

reserve_global_data:

                  在sdram中reserve全局变量内存struct global_data *gd.

                 

                  uboot/include/common.h

                 

                  这里将新的gd放在TLB, uboot monitor下面。

reserve_fdt:

                  reserve FDT(flat device tree)内存。

                 

reserve_stacks:

                  reserve 中断IRQ使用的堆栈。

                 

                    

                     

                      IRQ使用的堆栈大小为CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ, 即4k + 4k.

setup_dram_config:

                     将sdram的基址,以及大小,保存到gd->bd中。

                    

                     

                    

setup_baud_rate:

                      保存波特率配置到gd->bd.

                     

setup_board_extra:

                      保存额外信息到gd->bd, 比如处理器时钟,plb和pci总线频率等。

                      

reloc_fdt:

                     将FDT信息拷贝到新的FDT内存地址处。

                    

setup_reloc:

                   计算uboot被relocated后的地址和原始地址之间的偏移offset.

                  

至此,board_init_f的所有工作完成。下面我们总结一下,relocate之后的内存布局:

在uboot/arch/arm/lib/crt0.S, 我们使用了”bl”来跳转到board_init_f,当board_init_f执行完后,程序回到了该跳转语句的下一条指令:

上面的代码很直观:

100行,改变sp到gd->start_addr_sp地址处,即上图中的sp域高地址处。

102-103行,r8为gd新地址处,即上图中的gd域。

105行设置返回地址为”here”。

106-107行改变lr为uboot relocate之后返回地址(当前here地址+uboot relocate offset)。

108行设置uboot relocate到的新地址。

109行跳转到relocate_code处执行.

我们先来看relocate_code怎么做的(注意,此时的sp已经指向sdram内存了,见上图的sp).

uboot/arch/arm/lib/relocate.S

40行设置uboot image当前的start地址。

41行计算relocate addr和当前uboot image的start地址的偏移。

43行设置uboot image当前的end地址。

46-49行从r1(uboot image 当前start地址)处将数据加载到r10-r11, 然后再将r10-r11寄存器的数据存到r0(uboot新地址)处,即完成一次uboot image数据的一次搬移;之后重复这个动作,直到uboot image的__image_copy_start到__image_copy_end段的数据全部被搬移到新地址。

从50行一直到69行在做的就是将uboot image中的”Label”段fix成relocate之后的地址。有关这一段的描写,参考https://blog.csdn.net/skyflying2012/article/details/37660265

下图是uboot.lds描述的uboot段:

搬运完之后,来到relocate_done。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值