U-boot會給Linux Kernel傳遞很多參數,如:串口,RAM,videofb、MAC地址等。而Linux kernel也會讀取和處理這些參數。兩者之間通過struct tag來傳遞參數。U-boot把要傳遞給kernel的東西保存在struct tag數據結構中,啓動kernel時,把這個結構體的物理地址傳給kernel;Linux kernel通過這個地址,用parse_tags分析出傳遞過來的參數。
U-Boot啓動Linux過程
U-Boot使用標記列表(tagged list)的方式向Linux傳遞參數。標記的數據結構式是tag,在U-Boot源代碼目錄include/asm-arm/setup.h中定義如下:
struct tag_header {
u32 size; /* 表示tag數據結構的聯合u實質存放的數據的大小*/
u32 tag; /* 表示標記的類型 */
};
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
U-Boot使用命令bootm來啓動已經加載到內存中的內核。而bootm命令實際上調用的是do_bootm函數。對於Linux內核,do_bootm函數會調用do_bootm_linux函數來設置標記列表和啓動內核。do_bootm_linux函數在lib_arm/bootm.c
中定義如下:
59 int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
60 {
61 bd_t *bd = gd->bd;
62 char *s;
63 int machid = bd->bi_arch_number;
64 void (*theKernel)(int zero, int arch, uint params);
65
66 #ifdef CONFIG_CMDLINE_TAG
67 char *commandline = getenv ("bootargs"); /* U-Boot環境變量bootargs */
68 #endif
… …
73 theKernel = (void (*)(int, int, uint))images->ep; /* 獲取內核入口地址 */
… …
86 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
87 defined (CONFIG_CMDLINE_TAG) || \
88 defined (CONFIG_INITRD_TAG) || \
89 defined (CONFIG_SERIAL_TAG) || \
90 defined (CONFIG_REVISION_TAG) || \
91 defined (CONFIG_LCD) || \
92 defined (CONFIG_VFD)
93 setup_start_tag (bd); /* 設置ATAG_CORE標誌
*/
… …
100 #ifdef CONFIG_SETUP_MEMORY_TAGS
101 setup_memory_tags (bd); /* 設置內存標記 */
102 #endif
103 #ifdef CONFIG_CMDLINE_TAG
104 setup_commandline_tag (bd, commandline); /* 設置命令行標記 */
105 #endif
… …
113 setup_end_tag (bd); /* 設置ATAG_NONE標誌*/
114 #endif
115
116 /* we assume that the kernel is in place */
117 printf ("\nStarting kernel ...\n\n");
… …
126 cleanup_before_linux (); /* 啓動內核前對CPU作最後的設置 */
127
128 theKernel (0, machid, bd->bi_boot_params); /* 調用內核 */
129 /* does not return */
130
131 return 1;
132 }
其中的setup_start_tag,setup_memory_tags,setup_end_tag函數在lib_arm/bootm.c中定義如下:
(1)setup_start_tag函數
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params; /* 內核的參數的開始地址 */
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}
標記列表必須以ATAG_CORE開始,setup_start_tag函數在內核的參數的開始地址設置了一個ATAG_CORE標記。
(2)setup_memory_tags函數
static void setup_memory_tags (bd_t *bd)
{
int i;
/*設置一個內存標記 */
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = bd->bi_dram.start;
params->u.mem.size = bd->bi_dram.size;
params = tag_next (params);
}
}
setup_memory_tags函數設置了一個ATAG_MEM標記,該標記包含內存起始地址,內存大小這兩個參數。
(3)setup_end_tag函數
static
void setup_end_tag (bd_t *bd)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
標記列表必須以標記ATAG_NONE結束,setup_end_tag函數設置了一個ATAG_NONE標記,表示標記列表的結束。
U-Boot設置好標記列表後就要調用內核了。但調用內核前,CPU必須滿足下面的條件:
(1)
CPU寄存器的設置
? r0=0
? r1=機器碼
? r2=內核參數標記列表在RAM中的起始地址
(2)
CPU工作模式
? 禁止IRQ與FIQ中斷
? CPU爲SVC模式
(3)
使數據Cache與指令Cache失效
do_bootm_linux中調用的cleanup_before_linux函數完成了禁止中斷和使Cache失效的功能。cleanup_before_linux函數在cpu/arm920t/cpu.中定義:
int cleanup_before_linux
(void)
{
/*
*
this is called just before we call linux
*
it prepares the processor for linux
*
*
we turn off caches etc ...
*/
disable_interrupts (); /* 禁止FIQ/IRQ中斷 */
/* turn off I/D-cache */
icache_disable(); /* 使指令Cache失效 */
dcache_disable(); /* 使數據Cache失效 */
/* flush I/D-cache */
cache_flush(); /* 刷新Cache */
return 0;
}
由於U-Boot啓動以來就一直工作在SVC模式,因此CPU的工作模式就無需設置了。
do_bootm_linux中:
64
void (*theKernel)(int zero, int arch, uint params);
… …
73
theKernel = (void (*)(int, int, uint))images->ep;
… …
128
theKernel (0, machid, bd->bi_boot_params);
第73行代碼將內核的入口地址“images->ep”強制類型轉換爲函數指針。根據ATPCS規則,函數的參數個數不超過4個時,使用r0~r3這4個寄存器來傳遞參數。因此第128行的函數調用則會將0放入r0,機器碼machid放入r1,內核參數地址bd->bi_boot_params放入r2,從而完成了寄存器的設置,最後轉到內核的入口地址。
到這裏,U-Boot的工作就結束了,系統跳轉到Linux內核代碼執行。