从零开始的UBOOT的学习6--start_armboot

时间:2022-08-27 04:45:19


1、start_armboot函数简介

(1)这个函数的是在UBOOT/lib_arm/board.c的第444行开始到908行结束
(2)450行里面还不是全部的函数,因为里面调用了很多函数。
(3)此函数构成了UBOOT启动部分的第二阶段。

(4)宏观分析:UBOOT的第二阶段应该做什么?
4.1、概括来讲UBOOT的第一阶段主要就是初始化SOC内部的一些部件(比如看门狗、时钟),然后初始化DDR并且完成重定位。
4.2、从宏观分析来讲,UBOOT的第二阶段就是要初始化剩下的还没有被初始化的硬件。主要是SOC外部硬件(比如INAND、网卡芯片。。。)
4.3、UBOOT本身的一些东西(uboot的命令、环境变量等。。。)
然后最终初始化完成必要的东西后进入UBOOT的命令行准备接受命令。

2、UBOOT第二阶段完结于何处?

(1)uboot启动后自动运行打印出很多信息:这些信息就是UBOOT在第一和第二阶段不断进行初始化时打印出来的信息。然后进入了倒数bootdelay秒然后执行bootcmd对应的启动命令。

(2)如果用户没有干涉则会执行bootcmd进入自动启动内核流程(uboot就死掉了);此时用户可以按下回车键打断UBOOT的自动启动进入UBOOT的命令行下。

(3)uboot的命令行就是一个死循环,循环体内不断重复:接收命令、解析命令、执行命令。这就是UBOOT最终的归宿。

3、start_armboot解析1

(1)分析数据类型:init_fnc_t
(2)typedef int (init_fnc_t)(void); //这是一个函数类型
(3)init_fnc_ptr是一个二重函数指针,回顾高级C语言中讲过:二重指针的作用有2个,其中一个是用来指向一重指针,一个是用来指向指针数组。因此这里的init_fuc_ptr可以用来指向一个函数指针数组。

init_fnc_t **init_fnc_ptr;

CFG_UBOOT_BASE : 0xc3e00000
CFG_UBOOT_SIZE : 210241024
CFG_MALLOC_LEN : 9161024
CFG_STACK_SIZE : 512
1024
sizeof(gd_t) : 32BYTE

刚刚的gd_base是一个gd结构体的基地址。
然后把这个基地址的数据,

定义一个全局结构体的地址:然后把gd的结构体赋值过去。
然后用内存拷贝的函数memset函数将0填充到这个函数里面。

void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr;
    char *s;
    int mmc_exist = 0;

    /* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
    ulong gd_base;

    gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
#ifdef CONFIG_USE_IRQ
    gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
    gd = (gd_t*)gd_base;
#else
    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif

    memset ((void*)gd, 0, sizeof (gd_t));
    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
    memset (gd->bd, 0, sizeof (bd_t));

    monitor_flash_len = _bss_start - _armboot_start;

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

#ifndef CFG_NO_FLASH
    /* configure available FLASH banks */
    size = flash_init ();
    display_flash_config (size);
#endif /* CFG_NO_FLASH */

关键部分的分析

(1)初始化数组的实现
下面的部分我把一些条件编译去掉了之后,让这个函数指针数组看起来清爽了很多。

我们是使用函数指针不断的后移来调用和执行指针的,如果函数的返回值不等于0的话,你就挂起这个函数。函数指针不断的后移,直到遇到 NULL这个空指针,结束这个循环。

init_fnc_t *init_sequence[] = {
    cpu_init,
    reloc_init,    
    board_init,        
    interrupt_init,    
    env_init,        
    init_baudrate,    
    serial_init,
    console_init_f,    
    display_banner,    
    print_cpuinfo,
    checkboard,    
    init_func_i2c,
    dram_init,        /* configure available RAM banks */
    display_dram_config,
    NULL,
};

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

(2)全局数据的实现

typedef    struct    global_data {
    bd_t        *bd;
    unsigned long    flags;
    unsigned long    baudrate;
    unsigned long    have_console;    
    unsigned long    reloc_off;    
    unsigned long    env_addr;    
    unsigned long    env_valid;    
    unsigned long    fb_base;    
    void        **jt;        /* jump table */
} gd_t;

gd_t  gd;

(1)#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

把这个gd的结构体,放置在r8的寄存器里面。
这个全局的数据结构,定义了UBOOT的很多重要的信息,在后面的移植和给内核传参的时候起到的关键的作用。

4、start_armboot的解析2

(1)内存使用排布
1、为什么要分配内存?
(1)uboot区: CFG_UBOOT_BASE --2MB
(2)堆区: 长度为CFG_MALLOC_LEN,实际为912KB
(3)栈区: 长度为CFG_STACK_SIZE,实际为512KB
(4)gd: 长度为sizeof(gd_t),实际为36字节
(5)bd: 长度为sizeof(bd_t),实际为44字节
(6)内存间隔: 为了防止高版本的gcc的优化造成错误

2、解析gd结构体的bd结构体
这个结构体里面定义了很多和开发板有关的信息:
比如波特率、IP地址,单板的信息,BootLoader的启动参数

比如bi_boot_params是单板的机器码。
(1)所谓的机器码就是UBOOT给这个开发板定义的一个唯一编号。
(2)机器码的主要作用就是UBOOT和Linux内核之间进行比对和适配。
(3)嵌入式设备中每一个设备的硬件都是定制化的,不能通用,嵌入式设备的高度定制化导致硬件和软件不能随便的适配使用,这就告诉我们:这个开发板移植的内核镜像绝对不能下载到另一个开发板去,否则也不能启动,就算启动也不能正常的工作,有很多隐患,因此Linux做了个设置,给每个开发板做个唯一编号(机器码)然后开发板、UBOOT、Linux三者去比对机器码,如果机器码对上了就启动,否则就不启动。

经过计算得知:bi_boot_params:的值就是0x30000100
这个内存地址就被分配用来做内核传参了,所以在UBOOT的其他地方使用内存时要注意。千万不能把这个地址给弄没了。

typedef struct bd_info {
    int            bi_baudrate;    
    unsigned long    bi_ip_addr;    
    unsigned char    bi_enetaddr[6]; 
    struct environment_s           *bi_env;
    ulong            bi_arch_number;    
    ulong            bi_boot_params;    
    struct                /* RAM configuration */
    {
    ulong start;
    ulong size;
    }            bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
    /* second onboard ethernet port */
    unsigned char   bi_enet1addr[6];
#endif
} bd_t;

5、start_armboot的解析3

(1)对UBOOT的源代码进行修改,修改内容根据自己的理解和分析进行修改。
(2)make disclean然后make x210_sd_config然后make
(3)编译完得到u-boot.bin,然后去烧录,烧录方法按照裸机的第三部分讲的Linux下使用dd命令来烧写。
(4)烧写过程:
第一步:进入sd_fusing目录下
第二步:make clean
第三步:make
第四步:插入SD卡,ls /dev/sd*得到SD卡在Ubuntu中的设备号。
注意SD卡要链接到虚拟机Ubuntu中,不要链接到WINDOW中。
第五步:./sd_fusing.sh /dev/sdb完成烧录,注意不是sd_fusing2.sh。

总结:UBOOT就是一个庞大点的复杂点的裸机程序而已,我们完全可以对它进行调式,调式的方法就是按照上面步奏,根据自己对代码的分析和理解对代码进行更改,然后重新编译烧录运行,根据运行的结果来执行。

6、start_armboot的解析4

(1)console_init_r:
console_init_f是控制台的第一阶段初始化,console_init_r是第二阶段初始化。实际上第一阶段初始化并没有实质性的工作,第二阶段初始化才进行了实质性的工作。

(2)console_init_r就是纯软件架构方面的初始化,说白了就是给console相关的数据结构中填充相应的值,所以属于纯软件配置类型的初始化。

(3)剩下的初始化函数就不再进行详细的说明。
最后程序会运行到一个死循环里面不断的解析命令,执行命令。
开机倒数自动执行,命令补全。