S5PV210-uboot分析(三)

2020.02.08

 

 

 

 

  • Uboot启动过程分析

1.启动的大概流程

 

 

注意:开发板刚上电时,cpu会首先从0x00000000地址处取值,所有会由硬件电路将高地址的IROM映射到0x00000000处,然后去执行

 

第零步:设置OM PIN引脚,就可以选择启动方式了,譬如从SD卡启动,emmc启动,norflash启动等等,我们这里假设从NAND启动。

第一步:IROM内的代码(BL0)对SOC进行初步的初始化工作

第二步:从NAND中复制BL1SOC内的SRAM,执行BL1内的代码

第三步:从NAND中复制BL2SOC内的SRAM,执行BL2内的代码(作用:初始化SDRAM控制器,那样我们就可以用DDR内存了)

第四步:把我们的操作系统OS复制到SDRAM

第五步:我们在BL2中进行一个长跳转,跳转到SDRAM中去执行程序,这样我们整个启动过程就完成了

 

另外阐述另外一个比较容易忽略的问题

 

向量地址映射:根据ARM手册,一般异常发生以后,地址会被映射到0x00000000或者0xffff0000处,这两个地址可以通过CP15协处理器进行配置。即IRAM高地址区域会被映射到0x00000000处。

 

如何确定在0x00000004处;打开S5PV210 ApplicationNote.pdf文档第13页,BL1处加载镜像文件,地址为0xD0020000,则第一条指令地址为0xD0020010,0x00000010部分为校验和,ldr,pl等指令会被放在0xD0020014,发现异常以后,ARM会跳转到0x00000004运行,而不是0xD0020014处,要解释这个问题,牵扯到另外一个比较大的知识点:异常向量和地址的映射问题

 

二、uboot第二阶段代码分析

 

  1. 分析start_armboot这个函数(逐行分析)

init_fnc_t **init_fnc_ptr;

char *s;

int mmc_exist = 0;

ulong size;

分析:

此处定义了四个变量

ulong gd_base;

 

gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);

分析:

CFG_UBOOT_BASE     c3e0_0000

CFG_UBOOT_SIZE      3*1024*1024

CFG_MALLOC_LEN     0X8000 + 1024*1024

CFG_STACK_SIZE       512*1024

具体见下图分析:

 

 

         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;

 

分析:

_bss_start是在链接文件中指定, _armboot_start的值就是代码段开始的地方

        

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

                   if ((*init_fnc_ptr)() != 0) {

                            hang ();

                   }

}

分析:

init_sequenceboard.c文件中定义

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 */

#ifndef CONFIG_DIS_BOARD_INFO

         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

#endif

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

         init_func_i2c,

#endif

         dram_init,                  /* configure available RAM banks */

         display_dram_config,

         NULL,

};

分析:

下面逐个分析

Cpu_init

这个里面啥也没干

board_init

{

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

 

}

这个里面主要完成网卡的初步初始化,还有就是arch_number 的赋值(2345),还有boot_params赋值 0x2000_0000 + 0x100

interrupt_init

这个里面主要是中断相关的初始化

env_init

这个函数比较重要,主要完成环境变量相关的初始化

{

         #if defined(ENV_IS_EMBEDDED)  //此处未定义,直接跳过

         size_t total;

         int crc1_ok = 0, crc2_ok = 0;

         env_t *tmp_env1, *tmp_env2;

 

         total = CFG_ENV_SIZE;  //0x4000

        

         tmp_env1 = env_ptr;

         tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE);

         //env_ptr指向一个全局数组的第一个元素

extern uchar environment[];

env_t *env_ptr = (env_t *)(&environment[0]);

 

 

         crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);

         crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);

 

         if (!crc1_ok && !crc2_ok)

                   gd->env_valid = 0;

         else if(crc1_ok && !crc2_ok)

                   gd->env_valid = 1;

         else if(!crc1_ok && crc2_ok)

                   gd->env_valid = 2;

         else {

                   /* both ok - check serial */

                   if(tmp_env1->flags == 255 && tmp_env2->flags == 0)

                            gd->env_valid = 2;

                   else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)

                            gd->env_valid = 1;

                   else if(tmp_env1->flags > tmp_env2->flags)

                            gd->env_valid = 1;

                   else if(tmp_env2->flags > tmp_env1->flags)

                            gd->env_valid = 2;

                   else /* flags are equal - almost impossible */

                            gd->env_valid = 1;

         }

 

         if (gd->env_valid == 1)

                   env_ptr = tmp_env1;

         else if (gd->env_valid == 2)

                   env_ptr = tmp_env2;

#else /* ENV_IS_EMBEDDED */

         gd->env_addr  = (ulong)&default_environment[0];  //对环境变量赋值,使用默认值

         gd->env_valid = 1;

#endif /* ENV_IS_EMBEDDED */

 

         return (0);

}

init_baudrate

完成串口波特率的初始化

{

         char tmp[64];  /* long enough for environment variables */

         int i = getenv_r ("baudrate", tmp, sizeof (tmp));

         //从环境变量中获取波特率这个字段的值,具体获取方法不做研究

         gd->bd->bi_baudrate = gd->baudrate = (i > 0)

                            ? (int) simple_strtoul (tmp, NULL, 10)

                            : CONFIG_BAUDRATE;

         //对获取到的波特率进行赋值

         return (0);

}

紧接着就是

dram_init

{

                  DECLARE_GLOBAL_DATA_PTR;

 

         gd->bd->bi_dram[0].start = PHYS_SDRAM_1;

         gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

         //dram参数赋值

#if defined(PHYS_SDRAM_2)

         gd->bd->bi_dram[1].start = PHYS_SDRAM_2;

         gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE;

#endif

}

mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);

分析:

此处为初始化了malloc起始地址和结束地址

#if defined(CONFIG_TQ210)    //定义了该宏,本开发板为TQ210

 

         #if defined(CONFIG_GENERIC_MMC)  

#ifndef CONFIG_DIS_BOARD_INFO

                   puts ("SD/MMC:  ");

#endif

                   mmc_exist = mmc_initialize(gd->bd);   //mmc初始化

                   if (mmc_exist != 0)

                   {

#ifndef CONFIG_DIS_BOARD_INFO

                            puts ("0 MB\n");

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

#ifndef CONFIG_DIS_BOARD_INFO

                   puts("NAND:    ");

#endif

                   nand_init();    //nandflash初始化

         #endif

        

#endif /* CONFIG_TQ210 */

/* initialize environment */

         env_relocate ();

分析:

{

#ifdef ENV_IS_EMBEDDED    //定义了该宏

         /*

          * The environment buffer is embedded with the text segment,

          * just relocate the environment pointer

          */

         env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);   // gd->reloc_off的值为0

         DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);

#else

         /*

          * We must allocate a buffer for the environment

          */

         env_ptr = (env_t *)malloc (CFG_ENV_SIZE);

         DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);

#endif

 

         if (gd->env_valid == 0) {

#if defined(CONFIG_GTH)        || defined(CFG_ENV_IS_NOWHERE)       /* Environment not changable */

                   puts ("Using default environment\n\n");

#else

                   puts ("*** Warning - bad CRC, using default environment\n\n");

                   show_boot_progress (-60);

#endif

                   set_default_env();

         }

         else {

                   env_relocate_spec (); //环境变量重定位,内部啥也没干

         }

         gd->env_addr = (ulong)&(env_ptr->data); //对环境变量地址进行赋值

 

#ifdef CONFIG_AMIGAONEG3SE

         disable_nvram();

#endif

}

 

 

#ifdef CONFIG_SERIAL_MULTI

         serial_initialize();

#endif

分析:

完成串口的初始化

         /* IP Address */

         gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");  //获取开发板的IP地址

 

         /* MAC Address */

         {

                   int i;

                   ulong reg;

                   char *s, *e;

                   char tmp[64];

 

                   i = getenv_r ("ethaddr", tmp, sizeof (tmp));  //获取开发板的MAC地址

                   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;

                   }

 

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

分析:

这个里面主要实现开发板外接设备的初始化

 

         jumptable_init ();

分析:

进行好一些回到函数的设置

{

                  int i;

 

         gd->jt = (void **) malloc (XF_MAX * sizeof (void *));

         for (i = 0; i < XF_MAX; i++)

                   gd->jt[i] = (void *) dummy;

 

         gd->jt[XF_get_version] = (void *) get_version;

         gd->jt[XF_malloc] = (void *) malloc;

         gd->jt[XF_free] = (void *) free;

         gd->jt[XF_getenv] = (void *) getenv;

         gd->jt[XF_setenv] = (void *) setenv;

         gd->jt[XF_get_timer] = (void *) get_timer;

         gd->jt[XF_simple_strtoul] = (void *) simple_strtoul;

         gd->jt[XF_udelay] = (void *) udelay;

         gd->jt[XF_simple_strtol] = (void *) simple_strtol;

         gd->jt[XF_strcmp] = (void *) strcmp;

#if defined(CONFIG_I386) || defined(CONFIG_PPC)

         gd->jt[XF_install_hdlr] = (void *) irq_install_handler;

         gd->jt[XF_free_hdlr] = (void *) irq_free_handler;

#endif       /* I386 || PPC */

#if defined(CONFIG_CMD_I2C)

         gd->jt[XF_i2c_write] = (void *) i2c_write;

         gd->jt[XF_i2c_read] = (void *) i2c_read;

}

#if !defined(CONFIG_SMDK6442)

         console_init_r ();      /* fully init console as a device */

#endif

分析:

主要完成回话窗口的设置,stdin/stdout/stderr

         /* enable exceptions */

         enable_interrupts ();

分析:

开启中断异常

         /* Initialize from environment */

         if ((s = getenv ("loadaddr")) != NULL) {

                   load_addr = simple_strtoul (s, NULL, 16);

         }

分析:

本开发板loadaddr环境变量未设置

#if defined(CONFIG_CMD_NET)

         if ((s = getenv ("bootfile")) != NULL) {

                   copy_filename (BootFile, s, sizeof (BootFile));

         }

#endif

分析:

Bootfile未定义

#ifdef BOARD_LATE_INIT

         board_late_init ();

#endif

分析:

啥也没干,直接返回0

 

 

 

 

 

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

         for (;;) {

                   main_loop ();

         }

此处开始进行uboot启动菜单的显示,即主循环,先获取bootdelay的值,超时等待delay变为0,如果在delay时间内没有任何输入,则会去获取bootcmd参数,根据这个参数去加载内核到内存中,然后去启动内核,如果在delay结束之前有键盘输入,则会触发menu命令,显示主菜单

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值