• u-boot-1.1.6第2阶段入口函数start_armboot分析


    学习目标:

    1、分析u-boot-1.1.6第2阶段入口函数void start_armboot (void),熟悉该函数所实现的功能

    2、为后面能够掌握u-boot-1.1.6如何启动内核过程打下基础


    前面通过对uboot第一阶段代码的分析,我们了解的uboot第一阶段所做的一些工作,并且找到了其第二阶段的入口函数void start_armboot(void)。为了能够在清楚理解uboot启动内核的机制,还需要对第二阶段代码进行分析。第二阶段入口函数void start_armboot(void)存放在board.c文件中,该文件位于uboot根目录下的lib_arm文件夹中。

    1.gd_t数据结构分配内存

    /* Pointer is writable since we allocated a register for it */
    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));  //为gd_t结构体变量开辟空间     
    /* compiler optimization barrier needed for GCC >= 3.4 */      //并使得gd指针指向该空间初始位置
    __asm__ __volatile__("": : :"memory");
    
    memset ((void*)gd, 0, sizeof (gd_t));       //gd获取内存块清零     
    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); //为bd_t结构体变量开辟空间,并使得gd->bd指针指向bd_t的初始位置 
    memset (gd->bd, 0, sizeof (bd_t));          //bd_t内存范围清零

    start_armboot函数首先为gd_t数据类型分配相应的内存空间,并使得gd指针指向这块内存空间。gd指针在global_data.h文件(位于uboot根目录下的include/asm-arm文件夹)中声明,在start_armboot函数中定义,声明形式为:

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

    这个声明告诉编译器使用CPU寄存器r8来存储gd_t类型的指针gd,即这个定义声明了一个指针,并且指明了它的存储位置。register表示变量放在机器的寄存器,volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次使用直接读值。gd_t数据结构紧接在uboot自定义的堆区域下的全局变量区域进行存放,分配完成后使用memset函数对这块内存进行清零,uboot存储器映射图如下所示:

    2.硬件的初始化

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

    init_fnc_ptr是在start_armboot函数中定义的局部变量,该变量是一个二级指针,指向int (init_fnc_t) (void)类型函数的指针。init_sequence是一个指针数组的名称,指针数组init_sequence[]存放硬件初始化函数的地址。使用这种方式调用函数的好处是如果要添加初始化一些新的硬件时,只需写出新硬件初始化函数,将编写初始化函数名放在init_sequence[]中,不用修改其他代码,便能调用新的硬件初始化函数。指针数组内容如下:

    init_fnc_t *init_sequence[] = {
        cpu_init,        /* basic cpu dependent setup */
        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 */
    /* 未定义CONFIG_DISPLAY_CPUINFO宏,print_cpuinfo不被编译 */
    #if defined(CONFIG_DISPLAY_CPUINFO)
        print_cpuinfo,        /* display cpu info (and speed) */
    #endif
    /* 未定义CONFIG_DISPLAY_BOARDINFO宏,checkboard不被编译 */
    #if defined(CONFIG_DISPLAY_BOARDINFO)
        checkboard,        /* display board info */
    #endif
        dram_init,        /* configure available RAM banks */
        display_dram_config,
        NULL,
    };

    cpu_init函数功能:如果配置文件使能IRQ中断和FIQ中断,那么就获取uboot存储器映射中IRQ、FIQ堆栈的地址,将其保存在全局变量IRQ_STACK_START和FIQ_STACK_START中。

    board_init函数功能:设置系统时钟,初始化相应的GPIO端口,设置全局变量gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;(arch number of SMDK2410-Board),gd->bd->bi_boot_params =   0x30000100(传给内核启动参数的地址),使能指令缓存和数据缓存。

    interrupt_init函数功能:初始化系统定时器

    env_init函数功能:从flash中读取环境变量并进行crc校验,校验成功使用flash中环境变量的地址,校验失败使用默认环境变量的地址gd->env_addr  = (ulong)&default_environment[0];

    init_baudrate函数功能:读取环境变量中波特率的值

    serial_init函数功能:根据读取的波特率数值初始化串口

    console_init_f函数功能:设置串口控制台

    display_banner函数功能:串口打印uboot版本信息,以及uboot代码段、bss段链接地址

    dram_init函数功能:将sdram起始地址和大小存入gt指针所指向全局变量 gd->bd->bi_dram[0].start = PHYS_SDRAM_1;gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

    display_dram_config函数功能:串口打印sdram其实地址和大小的信息

    3.初始化nor flash和nand flash

    /* 未定义CFG_NO_FLASH宏,执行flash_init()初始化函数,打印flash容量 */
    #ifndef CFG_NO_FLASH
        /* configure available FLASH banks */
        size = flash_init ();                         //读出板载flash大小
        display_flash_config (size);                  //串口打印flash容量大小
    #endif /* CFG_NO_FLASH */
    
    /* 未定义CONFIG_VFD宏,不支持VFD液晶屏,此处代码不用分析 */
    #ifdef CONFIG_VFD                                 //液晶屏(不执行)
    #    ifndef PAGE_SIZE
    #      define PAGE_SIZE 4096
    #    endif
        /*
         * reserve memory for VFD display (always full pages)
         */
        /* bss_end is defined in the board-specific linker script */
        addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
        size = vfd_setmem (addr);
        gd->fb_base = addr;
    #endif /* CONFIG_VFD */
    
    /* 未定义CONFIG_LCD宏,不支持LCD液晶屏,此处代码不用分析 */
    #ifdef CONFIG_LCD                                //液晶屏(不执行) 
    #    ifndef PAGE_SIZE
    #      define PAGE_SIZE 4096
    #    endif
        /*
         * reserve memory for LCD display (always full pages)
         */
        /* bss_end is defined in the board-specific linker script */
        addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
        size = lcd_setmem (addr);
        gd->fb_base = addr;
    #endif /* CONFIG_LCD */
    
        /* 将malloc连接地址指定在指定内存位置 */
        /* armboot_start is defined in the board-specific linker script */
        mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);     
    
    /* 未定义CFG_CMD_NAND宏,此处代码不被编译 */
    #if (CONFIG_COMMANDS & CFG_CMD_NAND)             //开发板是否板载nand flash,若对nand flash 进行初始化
        puts ("NAND:  ");
        nand_init();        /* go init the NAND */
    #endif

    这段代码根据include/configs/smdk2410.h文件中的配置宏,选择性编译代码,初始化nor flash和nand flash,并读出flash的大小,通过串口打印相关信息到控制台。

    5.配置网络和使能中断

        devices_init ();    /* get the devices list going. */
    
    /* 未定义CONFIG_CMC_PU2,此处代码不编译 */
    #ifdef CONFIG_CMC_PU2
        load_sernum_ethaddr ();
    #endif /* CONFIG_CMC_PU2 */
    
        jumptable_init ();
    
        console_init_r ();    /* fully init console as a device */
    
    /* 未定义CONFIG_MISC_INIT_R,此处代码不编译 */
    #if defined(CONFIG_MISC_INIT_R)
        /* miscellaneous platform dependent initialisations */
        misc_init_r ();
    #endif
    
        /* enable exceptions */
        enable_interrupts ();
    
        /* Perform network card initialisation if necessary */
    #ifdef CONFIG_DRIVER_CS8900
        cs8900_get_enetaddr (gd->bd->bi_enetaddr);
    #endif
    
    /* 未定义CONFIG_DRIVER_SMC91111和CONFIG_DRIVER_LAN91C96,此处代码不编译 */
    #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
        if (getenv ("ethaddr")) {
            smc_set_mac_addr(gd->bd->bi_enetaddr);
        }
    #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
    
        /* Initialize from environment */
        if ((s = getenv ("loadaddr")) != NULL) {
            load_addr = simple_strtoul (s, NULL, 16);
        }
    #if (CONFIG_COMMANDS & CFG_CMD_NET)
        if ((s = getenv ("bootfile")) != NULL) {
            copy_filename (BootFile, s, sizeof (BootFile));
        }
    #endif    /* CFG_CMD_NET */
    
    /* 未定义BOARD_LATE_INIT,不编译此处代码 */
    #ifdef BOARD_LATE_INIT
        board_late_init ();
    #endif
    
    /* 未定义CFG_CMD_NET,此处不编译 */
    #if (CONFIG_COMMANDS & CFG_CMD_NET)
    #if defined(CONFIG_NET_MULTI)
        puts ("Net:   ");
    #endif
        eth_initialize(gd->bd);
    #endif

    5.跳转到main_loop函数

    /* main_loop() can return to retry autoboot, if so just run it again. */
        for (;;) {
            main_loop ();
        }
    }

    start_armboot函数经过一系列初始化之后,会再次跳转到main_loop函数中执行后续的操作。

    总结:start_armboot函数

    1、在uboot存储映射的全局变量区为全局结构gt_t分配内存空间,并让全局指针gt指向该分配内存空间入口地址

    2、完成一些硬件的初始化,例如:设置系统时钟和GPIO端口,使能指令Cache和数据Cache,设置串口,初始化flash等

    3、读取后续过程使用的信息,将其存储到gt指针指向的gt_t结构的内存空间中

    4、会再次跳转到main_loop函数中执行其他后续操作

  • 相关阅读:
    数据绑定(八)使用Binding的RelativeSource
    你是程序猿这块料吗?
    创建与删除索引
    Verilog分频器
    mysql触发器的作用及语法
    QoS令牌桶工作原理
    Android 开发新方向 Android Wear ——概述
    程序猿接私活经验总结,来自csdn论坛语录
    [Node.js]27. Level 5: URL Building & Doing the Request
    [Node.js]26. Level 5 : Route rendering
  • 原文地址:https://www.cnblogs.com/053179hu/p/10615552.html
Copyright © 2020-2023  润新知