/********************************
uboot的第二階段就是初始化剩下的還沒在第一階段初始化的硬體。主要是SoC外部硬體(譬如 iNand 網卡晶片....... )uboot本身的一些東西(uboot的指令 環境變量等.....)。然後最終初始化完必要的東西後進入到uboot的指令行準備接受指令。
***********************************/
void start_armboot (void) //這個函數構成了uboot啟動的第二階段
{
/************************************
typedef int (init_fnc_t) (void);這是一個函數類型,是以init_fnc_t_ptr是一個二重函數指針,二重指針的作用有兩個,(一個是用來指向一重指針(指針的指針)),一個用來指向一個指針數組。是以這裡的init_fnc_t_ptr可以用來指向一個函數指針數組。
****************************************/
init_fnc_t **init_fnc_ptr;
char *s;
int mmc_exist = 0;
#if !defined(CFG_NO_FLASH) || defined (CONFIG_VFD) || defined(CONFIG_LCD)
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#if defined(CONFIG_BOOT_MOVINAND)
uint *magic = (uint *) (PHYS_SDRAM_1);
/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base; //gd在DDR記憶體配置設定中的起始位址
/************************gd(global data)的由來******************
每個用到檔案的開頭都有DECLARE_GLOBAL_DATA_PTR;這句,而這個宏在我們的uboot/include/asm-arm/Global_data.h中有定義(gd_t也定義在這個檔案中)
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
定義了一個全局變量名字為gd,這個全局變量是一個指針類型,占4個位元組,用volatile修飾表示可變的;用register修飾表示這個變量要盡量放到寄存器中;asm("r8")是gcc支援的一種文法,意思是要把gd放到寄存器r8中。
綜合分析:DECLARE_GLOBAL_DATA_PTR就是定義了一個要放在寄存器r8中的全局變量,名字叫gd,類型是一個指向gd_t類型變量的指針。gd_t中定義了很多全局變量,都是整個uboot使用的,其中有一個bd_t類型的指針bd,指向結構體bd_info,這個結構體裡面定義的是和開發闆硬體相關的全局變量(譬如 ip位址 序列槽波特率 等)
全局變量gd是uboot中很重要的一個全局變量(準确的說這個全局變量是一個結構體,裡面有很多内容,這些内容加起來構成的結構體就是uboot中常用的所有全局變量),由于gd經常被通路,是以放在寄存器中提升通路效率。
**************************/
/***************************************************
typedef struct global_data {
bd_t *bd; //指向bd_t的指針
unsigned long flags;
unsigned long baudrate; //波特率
unsigned long have_console; 是否有控制台(終端,或序列槽之類的)
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; // 環境變量的位址
unsigned long env_valid; //環境變量checksum是否有效
unsigned long fb_base; //frame buffer的基位址
void **jt; /* jump table */
} gd_t;
******************************************/
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
/******************************************
//uboot區:CFG_UBOOT_BASE (33e00000)+CFG_UBOOT_SIZE (uboot的實際大小,我們這裡給了2M)
堆區:長度為CFG_MALLOC_LEN ,實際長度為912KB
棧區:長度為CFG_STACK_SIZE,實際長度為512KB
gd:長度為sizeof(gd_t),實際為36位元組
bd:長度為sizeof(bd_t),實際長度為44位元組左右
是以gd_base的大概位置是離uboot起始位址(33e00000)往上走624KB的位置處
**************************************/
#ifdef CONFIG_USE_IRQ
gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif //CONFIG_USE_IRQ
gd = (gd_t*)gd_base; //上面已經得到gd_base是一個記憶體位址,然後通過強制類型轉換使其變為一個指針,并賦給gd,使得gd指向這段記憶體空間
#else //CONFIG_MEMORY_UPPER_CODE
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif //CONFIG_MEMORY_UPPER_CODE
/* compiler optimization barrier needed for GCC >= 3.4 */ //避免高版本的gcc的過度優化造成錯誤
__asm__ __volatile__("": : :"memory"); //内嵌彙編
/*
__asm__ :用于訓示編譯器在此插入彙編語句
__volatile__:用于告訴編譯器,嚴禁将此處的彙編語句與其它的語句重組合優化。即:原原本本按原來的樣子處理這這裡的彙編。
memory:強制gcc編譯器假設RAM所有記憶體單元均被彙編指令修改,這樣cpu中的registers和cache中已緩存的記憶體單元中的資料将廢棄。cpu将不得不在需要的時候重新讀取記憶體中的資料。這就阻止了cpu又将registers,cache中的資料用于去優化指令,而避免去通路記憶體。
*/
/*需要注意的是:
(1)這裡的#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")隻是定義了一個指針,也就說gd裡的這些全局變量并沒有配置設定記憶體,我們在使用gd之前要給他配置設定記憶體,否則gd隻是一個野指針
(2)gd和bd需要記憶體,記憶體目前沒有被人管理(因為沒有作業系統統一管理記憶體),大片的DDR記憶體(0x30000000-0x4fffffff共512MB)散放着可以随意使用(隻要使用記憶體位址直接去通路記憶體即可),但是因為後面的uboot還需要大片記憶體,是以這裡要本着夠用,緊湊排布的原則
****************************************************/
memset ((void*)gd, 0, sizeof (gd_t)); //申請的記憶體使用之前先進行清零
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); //根據bd的大小計算bd的起始位址,在gd下面緊挨着
//假如改為(int *)gd - sizeof(bd_t),則實際int * 類型指針-1相當于位址-4*1,這樣就會浪費一部分記憶體(雖然很 //小,因為這裡的sizeof(bd_t)裡面的就是bd的大小,是以位址直接減去這段記憶體即可。是以是char * gd)
memset (gd->bd, 0, sizeof (bd_t)); //申請的記憶體使用之前先進行清零
monitor_flash_len = _bss_start - _armboot_start;
/********************************************************
前面已經講到過init_fnc_ptr 是一個二重函數指針,可以指向init_sequence這個函數指針數組,而 init_sequence是一個函數指針數組,是以這個數組存放了很多函數指針,這些函數的類型都是init_fnc_ptr類型(typedef int (init_fnc_t) (void);)特殊是接收參數是void類型,傳回值是int類型。這裡面的内容都是函數名,也就是函數指針,這些函數都是闆級初始化的代碼。
*init_fnc_ptr:解引用,得到的就是一個函數指針
這個for循環的作用就是去周遊init_sequence這個函數指針數組,而周遊的目的就是去執行這個函數指針數組裡面的全部函數。
和普通數組類似,函數指針數組也可以通過下标個數組裡面的元素個數去周遊這個數組(for(i=0;i<n;i++ a[i])),但是這裡使用的是另一種思路:通過在數組的最後設定一個标志(NULL),周遊的時候從開頭開始,直到檢測到标志(NULL)結束,這樣的好處是不需要去确定數組元素的個數。
*****************************************/
用函數指針去調用函數(得到的是函數的傳回值),數組裡面的函數正确執行時傳回值都是0,如果錯誤是則挂起(hang();)啟動失敗
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifndef CFG_NO_FLASH
/*
Nor_FLASH初始化,但是我們的開發闆上面接的是NandFlash,是以這個是無效的,可以拿去
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
#ifdef CONFIG_VFD
uboot中自帶的LCD顯示架構,但是我們沒有使用uboot的LCD顯示架構,而是自己添加了一套LCD顯示架構
# 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 */
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
(1)mem_malloc_init函數用來初始化uboot的堆管理器。
(2)uboot中自己維護了一段堆記憶體,肯定自己就有一套代碼來管理這個堆記憶體。有了這些東西uboot中你也可以malloc、free這套機制來申請記憶體和釋放記憶體。我們在DDR記憶體中給堆預留了896KB的記憶體。
#else
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
//******************************//
// Board Specific 開發闆獨有的配置
三星用一套uboot滿足了多套開發闆,開發闆之間的差别就在這裡分析
// #if defined(CONFIG_SMDKXXXX)
#if defined(CONFIG_SMDK6410)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
/***********************************************************
(1)mmc_initialize():MMC相關的一些基礎的初始化,其實就是用來初始化SoC内部的SD/MMC控制器的。函數在uboot/drivers/mmc/mmc.c裡。
(2)mmc_initialize():是具體硬體架構無關的一個MMC初始化函數,
所有的使用了這套架構的代碼都掉用這個函數來完成MMC的初始化。
mmc_initialize中再調用board_mmc_init和cpu_mmc_init來完成具體的硬體的MMC控制器初始化工作。
if (mmc_exist != 0)
{
puts ("0 MB\n");
#else
#if defined(CONFIG_MMC)
puts("SD/MMC: ");
if (INF_REG3_REG == 0)
movi_ch = 0;
else
movi_ch = 1;
movi_set_capacity();
movi_init();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
#endif
if (INF_REG3_REG == BOOT_ONENAND) {
#if defined(CONFIG_CMD_ONENAND)
puts("OneNAND: ");
onenand_init();
/*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
} else {
puts("NAND: ");
nand_init();
if (INF_REG3_REG == 0 || INF_REG3_REG == 7)
setenv("bootcmd", "movi read kernel c0008000;movi read rootfs c0800000;bootm c0008000");
else
setenv("bootcmd", "nand read c0008000 80000 380000;bootm c0008000");
#endif /* CONFIG_SMDK6410 */
#if defined(CONFIG_SMDKC100)
//210SD卡相關配置
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
#if defined(CONFIG_CMD_NAND)
#endif /* CONFIG_SMDKC100 */
#if defined(CONFIG_X210)
//結束
#ifdef CONFIG_CHECK_X210CV3
check_flash_flag=0;//check inand error!
check_flash_flag=1;//check inand ok!
#if defined(CONFIG_MTD_ONENAND)
//puts("OneNAND: (FSR layer enabled)\n");
#endif /* CONFIG_X210 */
#if defined(CONFIG_SMDK6440)
if (INF_REG3_REG == 1) { /* eMMC_4.3 */
puts("eMMC: ");
movi_emmc = 1;
movi_init();
movi_set_ofs(0);
} else if (INF_REG3_REG == 7 || INF_REG3_REG == 0) { /* SD/MMC */
if (INF_REG3_REG & 0x1)
movi_ch = 1;
movi_ch = 0;
puts("SD/MMC: ");
movi_set_capacity();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
if (INF_REG3_REG == 2) {
/* N/A */
//setenv("bootcmd", "nand read c0008000 80000 380000;bootm c0008000");
#endif /* CONFIG_SMDK6440 */
#if defined(CONFIG_SMDK6430)
} else if (INF_REG3_REG == BOOT_NAND) {
if (INF_REG3_REG == 0 || INF_REG3_REG == 7)
setenv("bootcmd", "movi read kernel c0008000;movi read rootfs c0800000;bootm c0008000");
setenv("bootcmd", "nand read c0008000 80000 380000;bootm c0008000");
#endif /* CONFIG_SMDK6430 */
#if defined(CONFIG_SMDK6442)
#endif /* CONFIG_SMDK6442 */
#if defined(CONFIG_SMDK2416) || defined(CONFIG_SMDK2450)
#if defined(CONFIG_NAND)
puts("NAND: ");
nand_init();
#if defined(CONFIG_ONENAND)
puts("OneNAND: ");
onenand_init();
#if defined(CONFIG_BOOT_MOVINAND)
if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) {
printf("Boot up for burning\n");
movi_init();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
#endif /* CONFIG_SMDK2416 CONFIG_SMDK2450 */
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
/* initialize environment */
env_relocate ();
環境變量的重定位,将SD卡中的環境變量搬移到DDR中
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#ifdef CONFIG_SERIAL_MULTI
serial_initialize();
/* IP Address IP位址設定 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* MAC 位址設定 */
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;
#ifdef CONFIG_HAS_ETH1
i = getenv_r ("eth1addr", tmp, sizeof (tmp));
gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
/*裝置初始化,和核心類似*/
devices_init (); /* get the devices list going. */
#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
/*跳轉表*/
jumptable_init ();
#if !defined(CONFIG_SMDK6442)
/********************************************
控制台初始化的第二階段,之前講過console_init_f是第一階段初始化,實際上第一階段沒有做什麼實際的事情(隻是執行了gd->have_console = 1;将這個變量設定為1),而控制台的初始化代碼是在第二階段完成的
**********************************/
console_init_r (); /* fully init console as a device */
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
/* enable exceptions CPSR中的總中斷标志位使能,,通過條件編譯來選擇函數的實體,也就是說uboot中有多個enable_interrupts ();函數,然後通過宏來決定使用哪個*/
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_TI_EMAC
extern void dm644x_eth_set_mac_addr (const u_int8_t *addr);
if (getenv ("ethaddr")) {
dm644x_eth_set_mac_addr(gd->bd->bi_enetaddr);
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
smc_set_mac_addr(gd->bd->bi_enetaddr);
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
/* Initialize from environment loadaddr:核心啟動有關的環境變量 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
#if defined(CONFIG_CMD_NET)
/* bootfile:核心啟動有關的環境變量 */
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
#ifdef BOARD_LATE_INIT
board_late_init (); //開發闆後期(剩餘部分)的初始化
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug ("Reset Ethernet PHY\n");
reset_phy();
#if defined(CONFIG_CMD_IDE)
puts("IDE: ");
ide_init();
/****************lxg added**************/
#ifdef CONFIG_MPAD
extern int x210_preboot_init(void);
x210_preboot_init();
/****************end**********************/
/* check menukey to update from sd */
/*****************************************************
(1)uboot啟動的最後階段設計了一個自動更新的功能。就是:我們可以将要更新的鏡像放到SD卡的固定目錄中,然後開機時在uboot啟動的最後階段檢查更新标志(是一個按鍵。按鍵中标志為"LEFT"的那個按鍵,這個按鍵如果按下則表update mode,如果啟動時未按下則表示boot mode)。如果進入update mode則uboot會自動從SD卡中讀取鏡像檔案然後燒錄到iNand中;如果進入boot mode則uboot不執行update,直接啟動正常運作。
(2)這種機制能夠幫助我們快速燒錄系統,常用于量産時用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();
puts ("[LEFT UP] boot mode\n");
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
/* NOTREACHED - no way out of command loop except booting */
}
本文轉自 菜鳥養成記 51CTO部落格,原文連結:http://blog.51cto.com/11674570/1922441