Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1205252
  • 博文数量: 498
  • 博客积分: 1552
  • 博客等级: 上尉
  • 技术积分: 2424
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-23 18:24
文章分类

全部博文(498)

文章存档

2019年(5)

2018年(15)

2017年(1)

2016年(17)

2015年(14)

2014年(93)

2013年(233)

2012年(108)

2011年(1)

2009年(11)

分类: LINUX

2013-04-17 18:02:21

u-boot的启动过程比较简单,大致做下面的工作:
    1 cpu初始化
    2 时钟,串口,内存(ddr ram)初始化
    3 内存划分,分配栈,数据,配置参数,以及u-boot代码在内存中的位置。
    4 对u-boot代码做relocate
    5 初始化 malloc,flash,pci 以及外设(比如,网口)
    6 进入命令行或者直接启动Linux kernel

基本上,这就是u-boot的启动要做的事情,我也曾经大致看过arm的启动代码,也是类似。
不过,这里以mips作为例子进行介绍。

启动涉及到几个文件: start.S, cache.S, lowlevel_init.S 和 board.c  前三个都是汇编代码。

程 序从start.S的_start开始执行。首先,初始化中断向量,寄存器清零,大致包括32个通用寄存器reg0-reg31和协处理器的一些寄存 器:CP0_WATCHLO,     CP0_WATCHHI,     CP0_CAUSE, CP0_COUNT, CP0_COMPARE等等。

之后,配置寄存器CP0_STATUS,设置所使用的协处理器,中断以及cpu运行级别(核心级)。
配置gp寄存器,把GOT段的地址赋给gp寄存器。(gp寄存器的用处会在后面relocate code的部分详细解释)
这时,开始执行lowlevel_init.S的lowlevel_init,主要目的是工作频率配置,比如cpu的主频,总线(AHB),DDR工作频率等。
然 后,调用cache.S的mips_cache_reset对cache进行初始化。接着调用cache.S的mips_cache_lock。这个调用 的目的,起初让我不解,后来才知道。这时ddr ram并没有配置好,而如果直接调用c语言的函数必须完成栈的设置,而栈必定要在ram中。所以,只有先把一部分cache拿来当ram用。做法就是把一 部分cache配置为栈的地址,锁定。这样,当读写栈的内存空间时,只会访问cache,而不会访问真的ram地址了。

这时,配置栈的地址,进行调用函数board_init_f(board.c)
进入函数board_init_f后,首先做一系列初始化:
                timer_init    时钟初始化
                env_init    环境变量初始化(取得环境变量存放的地址)
                init_baudrate    串口速率
                serial_init        串口初始化
                console_init_f    配置控制台
                display_banner    显示u-boot启动信息,版本号等
                checkboard        执行board相关的操作。
                init_func_ram    初始化内存,配置ddr controller
这一系列工作完成后,串口和内存都已经可以用了。然后,就要把内存进行划分,
在内存的最后一部分,留出u-boot代码大小的空间,准备把u-boot代码从flash搬移到这里
然后,是堆的空间,malloc的内存就来自于这里。紧接着放两个全局数据结构bd_info global_data和环境变量boot_params。最后,是栈的空间。

内存划分好,就准备进行relocate code了
relocate code的意思是这样的。通常u-boot的执行代码肯定是在flash上(当调试的时候也可以放在ram上)。当启动起来以后,要把它从flash上搬移到ram里运行。这个工作就叫做relocate code。

但是,问题在于,flash上的地址和ram上的地址是不同的。当我们把代码从flash上搬移到ram上以后,当执行函数跳转时,代码里的函数地址还是flash上的地址,所以一跳就跳回去了。
这怎么办呢?
在u-boot里面用的是PIC(position-independent code)的方式解决这个问题。
简 单介绍一下其原理。当你用PIC方式时,在用gcc编译时需加上 -fpic的选项。编译器会为你的可执行代码建立一个GOT(global offset table)的段。一个地址在GOT表中有一项,里面存放地址的信息,而在使用这个地址时,只要根据这个地址的编号(也可以叫做偏移量offset)找到 表中相应的项目,就可以取得那个地址了。
而如果位置发生变化,只要对GOT表中的地址进行修改就可以了。
我们可以通过反汇编,看一个简单的函数调用例子:
lw    t9,1088(gp)
jalr    t9

这里,gp存放的就是GOT表的起始地址,而1088就是要调用函数的offset,也就是说GOT表的那个位置存放着它的地址。lw    t9,1088(gp) 把函数地址放入t9, 然后调用就可以了。

知道了PIC的原理,解释u-boot relocate code的方法就简单了。
简单的说就把u-boot的执行代码直接从flash里copy到ram的相应区域。
然后,把GOT表中的地址都加上一个偏移量,这个偏移量就是flash里的地址与ram里的地址的差。
还有其他一些工作比如:设置新的栈指针,从flash代码里跳转到ram代码里 等等。

之后,就进入board.c的board_init_r函数,在这个函数里初始化 malloc,flash,pci 以及外设(比如,网口),最后进入命令行或者直接启动Linux kernel。
这样,u-boot的启动工作就完成了。
阅读(898) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册