U-boot會給Linux Kernel傳遞很多參數,如:串口,RAM,videofb等。而Linux kernel也會讀取和處理這些參數。兩者之間通過struct tag來傳遞參數。U-boot把要傳遞給kernel的東西保存在struct tag數據結構中,啟動kernel時,把這個結構體的物理地址傳給kernel;Linux kernel通過這個地址,用parse_tags分析出傳遞過來的參數。
這里主要以U-boot傳遞RAM和Linux kernel讀取RAM參數為例進行說明。
1、u-boot給kernel傳RAM參數
./common/cmd_bootm.c文件中(指Uboot的根目錄),bootm命令對應的do_bootm函數,當分析uImage中信息發現OS是Linux時,調用./lib_arm/bootm.c文件中的do_bootm_linux函數來啟動Linux kernel。
在do_bootm_linux函數中:
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],\
ulong addr, ulong *len_ptr, int verify)
{
......
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd); //初始化tag結構體開始
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (?ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (?ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd); //設置RAM參數
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd); //初始化tag結構體結束
#endif
......
......
theKernel (0, machid, bd->bi_boot_params);
//傳給Kernel的參數= (struct tag *)型的bd->bi_boot_params
//bd->bi_boot_params在board_init函數中初始化,如對于at91rm9200,初始化在at91rm9200dk.c的board_init中進行:bd->bi_boot_params=PHYS_SDRAM + 0x100;
//這個地址也是所有taglist的首地址,見下面的setup_start_tag函數
}
對于setup_start_tag和setup_memory_tags函數說明如下。
函數setup_start_tag也在此文件中定義,如下:
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params;
//初始化(struct tag *)型的全局變量params為bd->bi_boot_params的地址,之后的setup tags相關函數如下面的setup_memory_tags就把其它tag的數據放在此地址的偏移地址上。
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);
}
RAM相關參數在bootm.c中的函數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[i].start;
params->u.mem.size = bd->bi_dram[i].size;
params = tag_next (params);
} //初始化內存相關tag
}
2、Kernel讀取U-boot傳遞的相關參數
對于Linux Kernel,ARM平臺啟動時,先執行arch/arm/kernel/head.S,此文件會調用arch/arm/kernel/head-common.S和arch/arm/mm/proc-arm920.S中的函數,并最后調用start_kernel:
......
b start_kernel
......
init/main.c中的start_kernel函數中會調用setup_arch函數來處理各種平臺相關的動作,包括了u-boot傳遞過來參數的分析和保存:
start_kernel()
{
......
setup_arch(&command_line);
......
}
其中,setup_arch函數在arch/arm/kernel/setup.c文件中實現,如下:
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;
setup_processor();
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
if(__atags_pointer)
//指向各種tag起始位置的指針,定義如下:
//unsigned int __atags_pointer __initdata;
//此指針指向__initdata段,各種tag的信息保存在這個段中。
tags = phys_to_virt(__atags_pointer);
else if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
if (tags->hdr.tag != ATAG_CORE)
tags = (struct tag *)&init_tags;
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);
//處理各種tags,其中包括了RAM參數的處理。
//這個函數處理如下tags:
__tagtable(ATAG_MEM, parse_tag_mem32);
__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
__tagtable(ATAG_SERIAL, parse_tag_serialnr);
__tagtable(ATAG_REVISION, parse_tag_revision);
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
}
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(cmdline_p, from);//處理編譯內核時指定的cmdline或u-boot傳遞的cmdline
paging_init(&meminfo, mdesc);
request_standard_resources(&meminfo, mdesc);
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
cpu_init();
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
early_trap_init();
}
對于處理RAM的tag,調用了parse_tag_mem32函數:
static int __init parse_tag_mem32(const struct tag *tag)
{
......
arm_add_memory(tag->u.mem.start, tag->u.mem.size);
......
}
__tagtable(ATAG_MEM, parse_tag_mem32);
上述的arm_add_memory函數定義如下:
static void __init arm_add_memory(unsigned long start, unsigned long size)
{
struct membank *bank;
size -= start & ~PAGE_MASK;
bank = &meminfo.bank[meminfo.nr_banks++];
bank->start = PAGE_ALIGN(start);
bank->size = size & PAGE_MASK;
bank->node = PHYS_TO_NID(start);
}
如上可見,parse_tag_mem32函數調用arm_add_memory函數把RAM的start和size等參數保存到了meminfo結構的meminfo結構體中。最后,在setup_arch中執行下面語句:
paging_init(&meminfo, mdesc);
對沒有MMU的平臺上調用arch/arm/mm/nommu.c中的paging_init,否則調用arch/arm/mm/mmu.c中的paging_init函數。這里暫不分析mmu.c中的paging_init函數。
3、關于U-boot中的bd和gd
U-boot中有一個用來保存很多有用信息的全局結構體--gd_t(global data縮寫),其中包括了bd變量,可以說gd_t結構體包括了u-boot中所有重要全局變量。最后傳遞給內核的參數,都是從gd和bd中來的,如上述的setup_memory_tags函數的作用就是用bd中的值來初始化RAM相應的tag。
對于ARM平臺這個結構體的定義大致如下:
include/asm-arm/global_data.h
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
void **jt; /* jump table */
} gd_t;
在U-boot中使用gd結構之前要用先用宏DECLARE_GLOBAL_DATA_PTR來聲明。這個宏的定義如下:
include/asm-arm/global_data.h
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
從這個宏的定義可以看出,gd是一個保存在ARM的r8寄存器中的gd_t結構體的指針。
本文的版本為U-boot-1.3.4、Linux-2.6.28,平臺是ARM。
-
RAM
+關注
關注
8文章
1344瀏覽量
114215 -
Linux
+關注
關注
87文章
11123瀏覽量
207912 -
u-boot
+關注
關注
0文章
119瀏覽量
38136 -
Kernel
+關注
關注
0文章
48瀏覽量
11094
原文標題:Uboot與Linux之間的參數傳遞詳解
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論