學習目標: 1、分析u-boot-1.1.6第2階段入口函數void start_armboot (void),熟悉該函數所實現的功能 2、為後面能夠掌握u-boot-1.1.6如何啟動內核過程打下基礎 前面通過對uboot第一階段代碼的分析,我們瞭解的uboot第一階段所做的一些工作,並且找到了其 ...
學習目標:
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函數中執行其他後續操作