加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

mips平台的u-boot启动流程

发布时间:2020-12-15 06:56:11 所属栏目:百科 来源:网络整理
导读:转载自:? http://hi.baidu.com/rbdr/blog/item/63bffb5476360f4c564e00bf.htmlu-boot 启动流程(mips) 2011-06-13 17:55 u-boot的启动过程比较简单,大致做下面的工作: ??? 1 cpu初始化 ??? 2 时钟,串口,内存(ddr ram)初始化 ??? 3 内存划分,分配栈
转载自:?
http://hi.baidu.com/rbdr/blog/item/63bffb5476360f4c564e00bf.htmlu-boot 启动流程(mips)
2011-06-13 17:55

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
和其他启动过程下次再介绍。

上次讲到,内存划分好,准备进行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,pci 以及外设(比如,网口),最后进入命令行或者直接启动Linux kernel。
这样,u-boot的启动工作就完成了。


流程分析
********************************************************
1.最开始系统上电后
?ENTRY(_start)程序入口点是 _start ??? ?board/mingddie/u-boot.lds

2._start:???????????? ??? ??? ??? ?cpu/mips/start.S

3. la????? t9,board_init_f ?? ? ?? ?将函数board_init_f地址赋予t9
?? j?????? t9?? ??? ???? ??? ??? ?跳转到t9寄存器中保存的地址指向的指令
?? ??? ??? ??????? ??? ??? ?即跳转到RAM 中执行 C 代码
?? ??? ??? ??? ??? ?这里会打印一些信息。
?? ??? ?
? 3.1? board_init_f()??? ??? ??? ?lib_mips/board.c
??? ??? ??? ? ?? ??? ??? 初始化外部内存
?????? relocate_code()?? ??? ??? ?回到cpu/mips/start.S中继续执行


4.la t9,board_init_r ??? ??? ? ?? ? cpu/mips/start.S
? j? t9?? ??? ??? ??? ??? ?将函数board_init_r地址赋予t9
?? ??? ?????????????????? ??? ??? ?跳转到t9寄存器中保存的地址指向的指令
??? ??? ??????????? ??? ??? ??? ?? 即跳转到RAM 中执行 C 代码
?? ??? ??? ??? ??? ? 这里会打印一些信息

? 4.1? board_init_r() 函数??????? ??? ??? ? lib_mips/board.c

? 4.2? main_loop()???????? ??? ??? ? common/main.c ?
?? ?s=getenv ("bootcmd")?? ? ?? ? 取得环境变量中的启动命令行,如bootcmd=bootm 0xbf020000
?? ?run_command (s,0);?? ??? ?? //执行这个命令行,即bootm?? ??? ?
?? ??? ?

? 4.3? do_bootm()?????????? ??? ??? ??? ?common/cmd_bootm.c
????? // printf ("## Booting image at %08lx ...n",addr);?? //比如

5. bootm 启动内核
?? 5.1 do_bootm_linux()??? lib_mips/mips_linux.c




函数解析
***************************************************
1.board_init_f()

?1.1
void board_init_f(ulong bootflag)
{


?? ?for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
?? ??? ?if ((*init_fnc_ptr)() != 0) {
?? ??? ??? ?hang ();
?? ??? ?}
?? ?}
?? ???? //调用init_sequence 函数队列,对板子进行一些初始化,详细见后面
?? ?
???????? 初始化external memory,初始化堆栈用cache作堆栈,为


?? ?relocate_code (addr_sp,id,addr);?? //回到cpu/mips/start.S 中? ?

?? ?/* NOTREACHED - relocate_code() does not return */
}


?1.2
typedef int (init_fnc_t) (void);

init_fnc_t *init_sequence[] = {

?? ?clx_board_init,?? ??? ?//初始化GPIO,CPU速度,PLL,SDRAM 等
?? ?timer_init,???????????? //时钟初始化
?? ?env_init,?? ??? ?//环境变脸初始化
?? ?incaip_set_cpuclk,?? ?//根据环境变量设置CPU 时钟
?? ?init_baudrate,?? ??? ?//初始化串口波特率
?? ?serial_init,?? ??? ?/* serial communications setup */
?? ?console_init_f,???????? //串口初始化,后面才能显示
?? ?display_banner,?? ??? ?//在屏幕上输出一些显示信息
?? ?checkboard,
?? ?init_func_ram,
?? ?NULL,
};




2.board_init_r()
(1)调用一系列的初始化函数。
(2)初始化Flash设备。
(3)初始化系统内存分配函数。
(4)如果目标系统拥有NAND设备,则初始化NAND设备。
(5)如果目标系统有显示设备,则初始化该类设备。
(6)初始化相关网络设备,填写IP、MAC地址等。
(7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作
void board_init_r (gd_t *id,ulong dest_addr)
{



?? ?/* configure available FLASH banks */?? //配置可用的flash单元
?? ?size = flash_init();???????????????? //初始化flash
?? ?display_flash_config (size);?? ??? ?//显示flash 的大小


?? ?/* initialize malloc() area */
?? ?mem_malloc_init();
?? ?malloc_bin_reloc();


?? ?puts ("NAND:");
?? ?nand_init();?? ??? ?/* go init the NAND */? //NAND初始化


?? ?/* relocate environment function pointers etc. */
?? ?env_relocate();??????????????? //初始化环境变量

?? ?/* board MAC address */
?? ?s = getenv ("ethaddr");??????? //以太网MAC地址
?? ?for (i = 0; i < 6; ++i) {
?? ??? ?bd->bi_enetaddr[i] = s ? simple_strtoul (s,&e,16) : 0;
?? ??? ?if (s)
?? ??? ??? ?s = (*e) ? e + 1 : e;
?? ?}

?? ?/* IP Address */
?? ?bd->bi_ip_addr = getenv_IPaddr("ipaddr");


?? ?pci_init();???????? //pci初始化配置


/** leave this here (after malloc(),environment and PCI are working) **/
?? ?/* Initialize devices */
?? ?devices_init ();
?? ?
?? ?jumptable_init ();

?? ?/* Initialize the console (after the relocation and devices init) */
?? ?console_init_r ();????????????? //串口初始化



?? ?/* miscellaneous platform dependent initialisations */
?? ?misc_init_r ();



?? ?puts ("Net:?? ");
?? ?eth_initialize(gd->bd);


?? ?/* main_loop() can return to retry autoboot,if so just run it again. */
?? ?for (;;) {
?? ??? ?main_loop ();? //循环执行,试图自动启动,接受用户从串口输入的命令,
?????????????????????????????????????????????????????????????????? 然后进行相应的工作,设置延时时间,确定目标板是进入下载模式还是启动加载模式
?? ?}

?? ?/* NOTREACHED - no way out of command loop except booting */
}

?
3.main_loop()

void main_loop (void)
{

?? ?s = getenv ("bootdelay");? //从环境变量中取得bootdelay 内核等待延时
?? ?bootdelay = s ? (int)simple_strtol(s,NULL,10) : CONFIG_BOOTDELAY;

?? ?debug ("### main_loop entered: bootdelay=%dnn",bootdelay);


?? ??? ?s = getenv ("bootcmd");? //从环境变量中取得bootcmd 启动命令行
?? ??? ??? ??? ??? ??? 如bootcmd=tftp;bootm 或者 bootcmd=bootm 0xbf020000
?? ??? ?char *s1 = getenv ("bootargs"); //从环境变量中取得bootargs 启动参数

?? ?debug ("### main_loop: bootcmd="%s"n",s ? s : "<UNDEFINED>");

?? ??? ?run_command (s,0);??? //执行启动命令




??? //手动输入命令


?? ?for (;;) {

?? ??? ?len = readline (CFG_PROMPT); //读取键入的命令到CFG_PROMPT 中

?? ??? ?rc = run_command (lastcommand,flag);? //执行这个命令

?? ??? ?
?? ?}
#endif /*CFG_HUSH_PARSER*/
}

4.do_bootm()

int do_bootm (cmd_tbl_t *cmdtp,int flag,int argc,char *argv[])
这个函数看着挺长的,其实无非就是将内核解压缩,然后调用do_bootm_linux引导内核


5.do_bootm_linux()??? lib_mips/mips_linux.c

打印信息Starting kernel ...

void do_bootm_linux (cmd_tbl_t * cmdtp,char *argv[],
?? ??? ????? ulong addr,ulong * len_ptr,int verify)
{
?? ?
?? ?char *commandline = getenv ("bootargs");
?? ?

?? ?theKernel =
?? ??? ?(void (*)(int,char **,int *)) ntohl (hdr->ih_ep);

????????? //hdr为指向image header的指针,hdr->ih_ep就是我们用mkimage创建image时-e选项的参数:内核的入口地址

?? ?linux_params_init (UNCACHED_SDRAM (gd->bd->bi_boot_params),commandline);


?? ?/* we assume that the kernel is in place */
?? ?printf ("nStarting kernel ...nn");


?? ?theKernel (linux_argc,linux_argv,linux_env,0);????????? //启动内核


}

u-boot向内核传递启动参数由一系列在include/configs/.h中的宏控制,启动参数传递的地址在board_init中初始化

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读