u-boot第二阶段分析(二)

来源:互联网 发布:琅琊榜飞剑进阶数据 编辑:程序博客网 时间:2024/06/06 03:35

start_armboot函数(二)

注:本次分析的u-boot是九鼎官方的u-boot代码
下载地址:链接:http://pan.baidu.com/s/1gfpDZqj 密码:7cqe


接着上一章节的内容,继续往下分析start_armboot函数;

1.for循环执行init_sequence
代码如下:

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {        if ((*init_fnc_ptr)() != 0)         {            hang ();        }}

分析代码:
(1)init_fnc_ptr二重指针指向了init_sequence,init_sequence是一个函数指针数组,数组中存储了很多个函数指针,这些指向指向的函数都是init_fnc_t类型(特征是接收参数是void类型,返回值是int)。
init_sequence在定义时就同时给了初始化,初始化的函数指针都是一些函数名。(函数名的实质就是函数指针)
代码如下:

typedef int (init_fnc_t) (void);init_fnc_t *init_sequence[] = {    cpu_init,       /* basic cpu dependent setup */#if defined(CONFIG_SKIP_RELOCATE_UBOOT)    reloc_init,     /* Set the relocation done flag, must                   do this AFTER cpu_init(), but as soon                   as possible */#endif    board_init,     /* basic board dependent setup */    interrupt_init,     /* set up exceptions */    env_init,       /* initialize environment */    init_baudrate,      /* initialze baudrate settings */    serial_init,        /* serial communications setup */    console_init_f,     /* stage 1 init of console */    display_banner,     /* say that we are here */#if defined(CONFIG_DISPLAY_CPUINFO)    print_cpuinfo,      /* display cpu info (and speed) */#endif#if defined(CONFIG_DISPLAY_BOARDINFO)    checkboard,     /* display board info */#endif#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)    init_func_i2c,#endif    dram_init,      /* configure available RAM banks */    display_dram_config,    NULL,};

(2)for循环的条件是*init_fnc_ptr,也就是*init_fnc_ptr != NULL;
遍历指针数组一般有两种方法,一种是根据数组下标来进行遍历,第二种是在数组的有效元素末尾放一个标志,依次遍历到标准处即可截至。代码中的for循环遍历方式采用的是第二种方式,这张遍历方式的好处是不用知道数组元素的个数,可以在数组中进行增减元素。

(3)init_fnc_t的这些函数的返回值定义方式一样的,都是:函数执行正确时返回0,不正确时返回-1.所以我们在遍历时去检查函数返回值,如果遍历中有一个函数返回值不等于0则hang()挂起。从分析hang函数可知:uboot启动过程中初始化板级硬件时不能出任何错误,只要有一个错误整个启动就终止,除了重启开发板没有任何办法。hang()代码如下:

void hang (void){    puts ("### ERROR ### Please RESET the board ###\n");    for (;;);}

下面对数组里的函数名逐个分析:

2.cpu_init
这个是cpu内部的初始化,在u-boot的第一阶段已经完成了,所以这里的函数体为空的;

3.board_init
board_init在uboot/board/samsung/x210/x210.c中,这个看名字就知道是x210开发板相关的初始化,代码如下:

int board_init(void){    DECLARE_GLOBAL_DATA_PTR;#ifdef CONFIG_DRIVER_SMC911X    smc9115_pre_init();#endif#ifdef CONFIG_DRIVER_DM9000    dm9000_pre_init();#endif    gd->bd->bi_arch_number = MACH_TYPE;    gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);    return 0;}

(1)我们使用的网卡型号是dm9000,所以dm9000_pre_init()可以得到运行,函数主要功能是网卡的初始化。CONFIG_DRIVER_DM9000这个宏是x210_sd.h中定义的,这个宏用来配置开发板的网卡的。开发板移植uboot时,如果要移植网卡,主要的工作就在这里。

这个函数中主要是网卡的GPIO和端口的配置,而不是驱动。因为网卡的驱动都是现成的正确的,移植的时候驱动是不需要改动的,关键是这里的基本初始化,因为这些基本初始化是硬件相关的。

(2)gd->bd->bi_arch_number = MACH_TYPE;
这句代码是将机器码的值赋值给bi_arch_number。
(1)bi_arch_number是board_info中的一个元素,含义是:开发板的机器码。所谓机器码就是uboot给这个开发板定义的一个唯一编号。
(2)机器码的主要作用就是在uboot和linux内核之间进行比对和适配。
(3)嵌入式设备中每一个设备的硬件都是定制化的,不能通用。嵌入式设备的高度定制化导致硬件和软件不能随便适配使用。这就告诉我们:这个开发板移植的内核镜像绝对不能下载到另一个开发板去,否则也不能启动,就算启动也不能正常工作,有很多隐患。因此linux做了个设置:给每个开发板做个唯一编号(机器码),然后在uboot、linux内核中都有一个软件维护的机器码编号。然后开发板、uboot、linux三者去比对机器码,如果机器码对上了就启动,否则就不启动(因为软件认为我和这个硬件不适配)。
(4)MACH_TYPE在x210_sd.h中定义,值是2456,并没有特殊含义,只是当前开发板对应的编号。这个编号就代表了x210这个开发板的机器码,将来这个开发板上面移植的linux内核中的机器码也必须是2456,否则就启动不起来。
(5)uboot中配置的这个机器码,会作为uboot给linux内核的传参的一部分传给linux内核,内核启动过程中会比对这个接收到的机器码,和自己本身的机器码相对比,如果相等就启动,如果不想等就不启动。
(6)理论上来说,一个开发板的机器码不能自己随便定。理论来说有权利去发放这个机器码的只有uboot官方,所以我们做好一个开发板并且移植了uboot之后,理论上应该提交给uboot官方审核并发放机器码(好像是免费的)。但是国内的开发板基本都没有申请(主要是因为国内开发者英文都不行,和国外开源社区接触比较少),都是自己随便编号的。随便编号的问题就是有可能和别人的编号冲突,但是只要保证uboot和kernel中的编号是一致的,就不影响自己的开发板启动。

(3)gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
这句代码是linux内核启动时的传参的内存地址赋值给bi_boot_params。
(1)bd_info中另一个主要元素,bi_boot_params表示uboot给linux kernel启动时的传参的内存地址。也就是说uboot给linux内核传参的时候是这么传的:uboot事先将准备好的传参(字符串,就是bootargs)放在内存的一个地址处(就是bi_boot_params),然后uboot就启动了内核(uboot在启动内核时真正是通过寄存器r0 r1 r2来直接传递参数的,其中有一个寄存器中就是bi_boot_params)。内核启动后从寄存器中读取bi_boot_params就知道了uboot给我传递的参数到底在内存的哪里。然后自己去内存的那个地方去找bootargs。
(2)PHYS_SDRAM_1是第一个内存片的起始地址,大小为0x30000000。所以经过计算,X210中bi_boot_params的值为0x30000100

原创粉丝点击