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 檔案中, 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 (&params);


#endif




#ifdef CONFIG_REVISION_TAG


setup_revision_tag (&params);


#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 中的函數,并最后呼叫 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







//補充一下:


來自:http://hi.baidu.com/armfans/blog/item/306cd5035f24ff084afb514b.html






bootloader巧妙地利用函數指標及傳參規格將R0:0x0R1:機器號,R2:參數位址傳遞給核心.由于R0R1比對簡單,不需要再作說明.需要花點時間了解的是R2暫存器.


  R2暫存器傳遞的是一個指標,這個指標指向一個TAG區域.UBOOTLinux核心之間正是通過這個延伸了的TAG區域來進行複雜參數的傳遞,如 command line,檔案系統資訊等等,用戶也可以延伸這個TAG來進行更多參數的傳遞.TAG區域存放的位址,也就是R2的值,是在/board /yourboard/youboard.c裡的board_init函數中起始化的,如在UB4020中起始化為:gd->bd->bi_boot_params = 0x30000100;,這是一個絕對位址.


  TAG區的架構比對簡單,可以視為一個一個TAG的排列(陣列?),每一個TAG傳遞一種特定類別的參數.各種系統TAG的定義可以參照./include/asm-arm/setup.h


  下面是一個TAG區的例子:


  0x30000100 00000005 54410001 00000000 00000000


  0x30000110 00000000 0000000F 54410009 746F6F72


  0x30000120 65642F3D 61722F76 7220306D 6F632077


  0x30000130 6C6F736E 74743D65 2C305379 30303639


  0x30000140 696E6920 6C2F3D74 78756E69 EA006372


  0x30000150 00000004 54420005 30300040 00200000


  0x30000160 00000000 00000000


  我們可以看到一共有三個TAG


  第一個TAG的長度是5個字,類別是ATAG_CORE54410001),有三個元素,均為全零.TAG區必須以這個TAG開頭.


  第二個TAG的長度是F個字,類別是ATAG_CMDLINE54410009),這是一個字串,是向核心傳遞的kernel command line


  第三個TAG的長度是4個字,類別是ATAG_INITRD254410005),有兩個元素,第一個是start:3030004030300000+40),第二個是size:2000002M


  如果說還有第四個TAG,那就是末尾的兩個全零,這是TAG結束的旗標.


  這些TAG是在./lib_arm/arm_linux.c中的do_bootm_linux函數中建立起來的.具體建立哪些TAG,由相應的控制巨集決定.具體可以參照相應程式碼.例子中第一個TAG是起始TAG,如果環境變數中有bootargs,則建立第二個TAG,如果bootm有兩個參數(啟動檔案系統),則會讀取檔案系統頭部的必要資訊,建立第三個TAG


  核心啟動后,將根據R2暫存器的值找到這些TAG,并根據TAG類別,呼叫相應的處理函數進行處理,從而抓取核心執行的必要資訊.








本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/lanmanck/archive/2009/06/24/4294685.aspx

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()