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命令,顯示主菜單

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章