S5PV210-uboot解析(三)-start_armboot解析

start_armboot解析

start_armboot函数在lib_arm/board.c文件里,除了函数本身,还有一些用到了的定义和宏。 S5PV210-uboot解析-start.S解析(二)-gd_t和init_sequence函数.note

typedef   int   (init_fnc_t) ( void );
这里定义的是一个 函数类型 ,不是函数指针。

下面是正式代码,我将没有用到的地方都去掉了:

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);

gd_base的地址就是 0x33e0_0000 + 2*1024*1024 - (0x4000 + 896*1024) - (512*1024) - (36 + 46 )
就是 基地址 + 2MB -(16KB + 896KB) - (512KB) - (82B)
也就是uboot的基地址+2MB-1424KB,相当于uboot基地址后面623KB多点的位置。
这之间的内存排布就是
CFG_UBOOT_SIZE 
宏定义的uboot大小(2MB)
实际上只有200多KB
gd_base
全局变量的区域
 
内存间隔
CFG_STACK_SIZE 
栈区
CFG_MALLOC_LEN 
堆区
CFG_UBOOT_BASE 
uboot基地址

     gd = (gd_t*)gd_base;
#else
     gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN -  sizeof (gd_t));
#endif
 
     /* compiler optimization barrier needed for GCC >= 3.4 */
     __asm__ __volatile__( "" : : : "memory" );

这句话是调用汇编的指令,为了防止高版本的GCC的优化造成错误。__asm__ 表示后面的代码为内嵌汇编,__volatile__防止编译器优化代码,括号内的是汇编指令。内嵌汇编语法如下:
__asm__(汇编语句模板: 输出部分: 输入部分: 破坏描述部分)
 
     memset   (( void *)gd, 0,  sizeof   (gd_t));
     gd->bd = (bd_t*)(( char *)gd -  sizeof (bd_t));
     memset   (gd->bd, 0,  sizeof   (bd_t));

memset初始化指针。
 
     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 ();
         }
     }
 
这个for循环用到了之前定义的函数类型的二重指针(相当于指针数组),遍历init_sequence数组内的元素,即运行每一个函数进行相关的初始化操作,这里面的函数都是初始化相关的。这里操作的时候先赋值init_fnc_ptr = init_sequence 循环的时候操作init_fnc_ptr 而不对init_sequence 进行操作。如果某一个函数初始化失败了,没有返回0,那么就进入hang函数挂起。hang函数内部就是一个死循环。如果uboot进入到hang函数了,只能重启。

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

这里是属于代码移植的遗留问题,在210板子中是没有norFlash的,但是这里还是保留了这段代码,而进行实际测试发现如果定义CFG_NO_FLASH的话会产生很多错误,所以没有定义CFG_NO_FLASH。这段代码只是在串口输出了Flash: 8 MB,没有其他的作用。
 
#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 */
 
#ifdef CONFIG_LCD
     /* board init may have inited fb_base */
     if   (!gd->fb_base) {
#       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 */

CONFIG_VFD和CONFIG_LCD都是和显示相关的,是uboot中自带的LCD显示的软件架构,但这里这两个宏都没有被定义,因为在后面我们使用了自己的LCD显示函数。
 
     /* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
     mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
     mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif

static   void   mem_malloc_init (ulong dest_addr)
{
     mem_malloc_start = dest_addr;
     mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
     mem_malloc_brk = mem_malloc_start;
 
     memset   (( void   *) mem_malloc_start, 0,
             mem_malloc_end - mem_malloc_start);
}
这个函数是用来初始化堆管理器的。 CFG_MALLOC_LEN 就是堆的大小,有896KB。

//******************************//
// Board Specific
// #if defined(CONFIG_SMDKXXXX)
//******************************//
 
接下来的一些代码都是和指定的板子相关的,通过定义宏来选择。

#if defined( CONFIG_X210 )
 
     #if defined( CONFIG_GENERIC_MMC )
         puts   ( "SD/MMC:  " );
         mmc_exist = mmc_initialize(gd->bd);
         if   (mmc_exist != 0)
         {
             puts   ( "0 MB\n" );

初始化MMC,其实是初始化SoC内部的MMC控制器。调用 mmc_initialize函数。
drivers/mmc/mmc.c
int   mmc_initialize(bd_t *bis)
{
     struct   mmc *mmc;
     int   err;
 
     INIT_LIST_HEAD(&mmc_devices);
     cur_dev_num = 0;
 
     if   (board_mmc_init(bis) < 0)
         cpu_mmc_init(bis);
 
#if defined(DEBUG_S3C_HSMMC)
     print_mmc_devices( ',' );
#endif
 
#ifdef CONFIG_CHECK_X210CV3
     mmc = find_mmc_device(1); //lqm
#else
     mmc = find_mmc_device(0);
#endif
     if   (mmc) {
         err = mmc_init(mmc);
         if   (err)
             err = mmc_init(mmc);
         if   (err) {
             printf ( "Card init fail!\n" );
             return   err;
         }
     }
     printf ( "%ldMB\n" , (mmc->capacity/(1024*1024/(1<<9))));
     return   0;
}
drivers/mmc/mmc.c
这段代码主要初始化的操作是这一段:
  if   (board_mmc_init(bis) < 0)
         cpu_mmc_init(bis);
跳转到board_mmc_init后
static   int   __def_mmc_init(bd_t *bis)
{
     return   -1;
}
 
int   cpu_mmc_init(bd_t *bis) __attribute__((weak, alias( "__def_mmc_init" )));
int   board_mmc_init(bd_t *bis) __attribute__((weak, alias( "__def_mmc_init" )));
board_mmc_init 后面的 __attribute__((weak, alias( "__def_mmc_init" ))); 表示
board_mmc_init 是 __def_mmc_init 的一个别名。而__def_mmc_init 返回-1,小于0,所以
初始化的时候实际执行的是 cpu_mmc_init。
cpu/s5pc11x/cpu.c
int   cpu_mmc_init(bd_t *bis)
{
#ifdef CONFIG_S3C_HSMMC
     setup_hsmmc_clock(); //设置时钟
     setup_hsmmc_cfg_gpio(); //设置GPIO
     return   smdk_s3c_hsmmc_init();
#else
     return   0;
#endif
}
__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
weak 和 alias 分别是两个属性。weak 使得 board_mmc_init 这个符号在目标文件中作为 weak symbol 而不是 global symbol。
而 alias 则使 board_mmc_init __def_mmc_init 的一个别名, __def_mmc_init board_mmc_init 必须在同一个编译单元中定义,否则会编译出错。
给函数加上weak属性时,即使函数没定义,函数被调用也可以编译成功。
当有两个函数同名时,则使用强符号(也叫全局符号,即没有加weak的函数)来代替弱符号(加weak的函数)。

#ifdef CONFIG_CHECK_X210CV3
             check_flash_flag=0; //check inand error!
#endif
         }
#ifdef CONFIG_CHECK_X210CV3
         else
         {
             check_flash_flag=1; //check inand ok! 
         }
#endif
     #endif
 
     #if defined(CONFIG_MTD_ONENAND)
         puts ( "OneNAND: " );
         onenand_init();
         /*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
     #else
         //puts("OneNAND: (FSR layer enabled)\n");
     #endif
 
     #if defined(CONFIG_CMD_NAND)
         puts ( "NAND:    " );
         nand_init();
     #endif
 
#endif /* CONFIG_X210 */

/* initialize environment */
env_relocate ();

重定位环境变量,因为环境变量最开始都烧录在SD卡中,这里将SD卡中的环境变量读取到DDR中。
将无效的宏定义去掉后:
void   env_relocate ( void )
{
     DEBUGF ( "%s[%d] offset = 0x%lx\n" , __FUNCTION__,__LINE__,
         gd->reloc_off);
  
     if   (gd->env_valid == 0) {
         set_default_env();
     }
     else   {
         env_relocate_spec ();
     }
     gd->env_addr = (ulong)&(env_ptr->data);

}
这样比较清楚的看到由于gd->env_valid的值是等于0的,所以执行的默认设置环境变量函数是
set_default_env:
void   set_default_env( void )
{
     if   ( sizeof (default_environment) > ENV_SIZE) {
         puts   ( "*** Error - default environment is too large\n\n" );
         return ;
     }
 
     memset (env_ptr, 0,  sizeof (env_t));
     memcpy (env_ptr->data, default_environment,
            sizeof (default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
     env_ptr->flags = 0xFF;
#endif
     env_crc_update ();
     gd->env_valid = 1;
}

可以看到第一次启动的时候uboot会使用默认的环境变量,用memset和memcpy进行清零后复制。
之后就把gd->env_valid赋值1,即有效。
/* IP Address */
     gd->bd->bi_ip_addr = getenv_IPaddr ( "ipaddr" );
 
     /* MAC Address */
     {
         int   i;
         ulong reg;
         char   *s, *e;
         char   tmp[64];
 
         i = getenv_r ( "ethaddr" , tmp,  sizeof   (tmp));
         s = (i > 0) ? tmp : NULL;
 
         for   (reg = 0; reg < 6; ++reg) {
             gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
             if   (s)
                 s = (*e) ? e + 1 : e;
         }
 
     }

从环境变量中获取IP地址和MAC地址, getenv_IPaddr 是调用 getenv 函数来获取环境变量中的字符串格式的IP地址,中间还用 string_to_ip 来将字符串格式的IP地址转换成字符串格式的点分十进制格式。

devices_init ();   /* get the devices list going. */

设备初始化函数,放在这里面初始化的设备都是驱动设备,作用就是集中执行各种硬件驱动的初始化函数。

jumptable_init ();

跳转表。应该是要实现函数指针到具体函数的映射,但是查找发现跳转表只是被赋值没有被引用,因此在uboot中没有使用。

#if !defined(CONFIG_SMDK6442)
     console_init_r (); /* fully init console as a device */
#endif

/* enable exceptions */
enable_interrupts ();

中断使能,实际是空函数。没有定义相关的宏,#else分支是空函数。

     /* Initialize from environment */
     if   ((s =  getenv   ( "loadaddr" )) != NULL) {
         load_addr = simple_strtoul (s, NULL, 16);
     }
#if defined(CONFIG_CMD_NET)
     if   ((s =  getenv   ( "bootfile" )) != NULL) {
         copy_filename (BootFile, s,  sizeof   (BootFile));
     }
#endif

这两个环境变量是和内核启动有关的,启动linux内核时会参考这两个变量。
 
#ifdef BOARD_LATE_INIT
     board_late_init ();
#endif

开发板晚期初始化的函数,在x210中,这个函数是空的。

****************lxg added**************/
#ifdef CONFIG_MPAD
     extern   int   x210_preboot_init( void );
     x210_preboot_init();
#endif

x210启动起来之前的一些初始化,主要是LCD相关。还有屏幕上的logo显示。
/****************end**********************/
 
     /* check menukey to update from sd */
     extern   void   update_all( void );
     if (check_menu_update_from_sd()==0) //update mode
     {
         puts   ( "[LEFT DOWN] update mode\n" );
         run_command( "fdisk -c 0" ,0);
         update_all();
     }
     else
         puts   ( "[LEFT UP] boot mode\n" );

这里设置了一个自动更新的功能,开机按下LEFT按键会进入 update mode,之后uboot会从SD卡中读取镜像烧录到iNand中。不按LEFT会进入boot mode,即正常启动。
 
     /* main_loop() can return to retry autoboot, if so just run it again. */
     for   (;;) {
         main_loop ();
     }
 
uboot启动的最后阶段,main_loop是一个死循环,如果开机倒计时不打断会自动引导内核加载系统,如果打断会进入uboot的命令行。

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

到这里uboot启动的第二阶段也分析完了。在start_armboot函数中,主要就是对开发板的硬件做了初始化,还有软件的一些数据结构做了初始化。
主要的函数及功能:
init_sequence
cpu_init 空的
board_init 网卡、机器码、内存传参地址
dm9000_pre_init 网卡
gd->bd->bi_arch_number 机器码
gd->bd->bi_boot_params 内存传参地址
interrupt_init 定时器
env_init
init_baudrate gd数据结构中波特率
serial_init 空的
console_init_f 空的
display_banner 打印启动信息
print_cpuinfo 打印CPU时钟设置信息
checkboard 检验开发板名字
dram_init gd数据结构中DDR信息
display_dram_config 打印DDR配置信息表
mem_malloc_init 初始化uboot自己维护的堆管理器的内存
mmc_initialize inand/SD卡的SoC控制器和卡的初始化
env_relocate 环境变量重定位
gd->bd->bi_ip_addr gd数据结构赋值
gd->bd->bi_enetaddr gd数据结构赋值
devices_init 空的
jumptable_init 不用关注的
console_init_r 真正的控制台初始化
enable_interrupts 空的
loadaddr、bootfile 环境变量读出初始化全局变量
board_late_init 空的
eth_initialize 空的
x210_preboot_init LCD初始化和显示logo
check_menu_update_from_sd 检查自动更新
main_loop 主循环
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值