U-Boot與Linux核心的互動
說明:本文所使用的U-Boot的版本是1.1.6,平台是S3C2440。
目錄一、簡介
1.1標記列表
二、設定標記存放的地址
2.1相關的結構體定義
2.2標記存放地址的設定
三、標記的設定
3.1設定標記ATAG_CORE
3.2設定記憶體標記ATAG_MEM
3.3設定命令列標記ATAG_CMDLINE
3.4設定ATAG_NONE一、簡介
U-Boot與Linux核心的互動是單向的,U-Boot將各類參數傳遞給核心。由於他們不能同時運行,傳遞辦法只能有一個個:U-Boot將參數放在某個約定的地方之後,在啟動核心,核心啟動後從這個地方獲得參數。
1.1標記列表
除了約定好參數存放的地方外,還要規定參數的結構。Linux2.4.x以後的核心都以標記列表(tagged list)的形式來傳遞參數。標記就是一種資料結構;標記列表就是挨著存放的多個標記。標記列表以標記ATAG_CORE開始,以ATAGE_NONE結束。
標記的資料結構為tag,它是偶一個tag_header結構和一個聯合體(union)組成。tag_header結構體表示標記的類型及長度,比如是表示記憶體還是表示命令列參數等。對於不同類型的標記使用不同的聯合體,比如表示記憶體=時使用tag_men32,表示命令列時使用tag_cmdline。其定定義在include/asm-arm/setup.c檔案中。
/* * The new way of passing information: a list of tagged entries *//* The list ends with an ATAG_NONE node. */#define ATAG_NONE0x00000000struct tag_header {u32 size;u32 tag;};/* The list must start with an ATAG_CORE node */#define ATAG_CORE0x54410001struct tag_core {u32 flags;/* bit 0 = read-only */u32 pagesize;u32 rootdev;};/* it is allowed to have multiple ATAG_MEM nodes */#define ATAG_MEM0x54410002struct tag_mem32 {u32size;u32start;/* physical start address */};/* VGA text type displays */#define ATAG_VIDEOTEXT0x54410003struct tag_videotext {u8x;u8y;u16video_page;u8video_mode;u8video_cols;u16video_ega_bx;u8video_lines;u8video_isvga;u16video_points;};/* describes how the ramdisk will be used in kernel */#define ATAG_RAMDISK0x54410004struct tag_ramdisk {u32 flags;/* bit 0 = load, bit 1 = prompt */u32 size;/* decompressed ramdisk size in _kilo_ bytes */u32 start;/* starting block of floppy-based RAM disk image */};/* describes where the compressed ramdisk image lives (virtual address) *//* * this one accidentally used virtual addresses - as such, * its depreciated. */#define ATAG_INITRD0x54410005/* describes where the compressed ramdisk image lives (physical address) */#define ATAG_INITRD20x54420005struct tag_initrd {u32 start;/* physical start address */u32 size;/* size of compressed ramdisk image in bytes */};/* board serial number. "64 bits should be enough for everybody" */#define ATAG_SERIAL0x54410006struct tag_serialnr {u32 low;u32 high;};/* board revision */#define ATAG_REVISION0x54410007struct tag_revision {u32 rev;};/* initial values for vesafb-type framebuffers. see struct screen_info * in include/linux/tty.h */#define ATAG_VIDEOLFB0x54410008struct tag_videolfb {u16lfb_width;u16lfb_height;u16lfb_depth;u16lfb_linelength;u32lfb_base;u32lfb_size;u8red_size;u8red_pos;u8green_size;u8green_pos;u8blue_size;u8blue_pos;u8rsvd_size;u8rsvd_pos;};/* command line: \0 terminated string */#define ATAG_CMDLINE0x54410009struct tag_cmdline {charcmdline[1];/* this is the minimum size */};/* acorn RiscPC specific information */#define ATAG_ACORN0x41000101struct tag_acorn {u32 memc_control_reg;u32 vram_pages;u8 sounddefault;u8 adfsdrives;};/* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */#define ATAG_MEMCLK0x41000402struct tag_memclk {u32 fmemclk;};struct tag {struct tag_header hdr;union {struct tag_corecore;struct tag_mem32mem;struct tag_videotextvideotext;struct tag_ramdiskramdisk;struct tag_initrdinitrd;struct tag_serialnrserialnr;struct tag_revisionrevision;struct tag_videolfbvideolfb;struct tag_cmdlinecmdline;/* * Acorn specific */struct tag_acornacorn;/* * DC21285 specific */struct tag_memclkmemclk;} u;};#define tag_next(t)((struct tag *)((u32 *)(t) + (t)->hdr.size))#define tag_size(type)((sizeof(struct tag_header) + sizeof(struct type)) >> 2) //???
二、設定標記存放的地址2.1相關的結構體定義
結構體bd中儲存了標記存放的地址。bd結構體是gd結構體的一項,我們先看gd結構體,其定義在include/asm-arm/global_data.h檔案中:
typedefstructglobal_data {bd_t*bd;//開發板相關參數 ,結構體變數,參考u-boot.h unsigned longflags;//指示標誌,如裝置已經初始化標誌等unsigned longbaudrate;//串列口通訊速率unsigned longhave_console;/* serial_init() was called 如果執行了該函數,則設定為1 */unsigned longreloc_off;/* *Relocation Offset 重定位位移,就是實際定向的位置與編譯串連時指定的位置之差,一般為0 */unsigned longenv_addr;/* 環境參數地址*/unsigned longenv_valid;/* 環境參數CRC檢驗有效標誌*/unsigned longfb_base;/*框架緩衝區基地址*/#ifdef CONFIG_VFDunsigned charvfd_type;/* 顯示類型*/#endif#if 0unsigned longcpu_clk;/*cpu時鐘*/unsigned longbus_clk; //匯流排時鐘unsigned longram_size;/* RAM size */unsigned longreset_status;/* reset status register at boot */#endifvoid**jt;/* jump table 跳轉表,用來登記"函數調用地址"*/} gd_t;
接來下我們來看一下bd結構體,這個結構體定義在include/asm-arm/u-boot.h檔案中:
typedef struct bd_info { intbi_baudrate;/* 串口傳輸速率*/ unsigned longbi_ip_addr;/* IP 位址*/ unsigned charbi_enetaddr[6]; /* MAC地址*/ struct environment_s *bi_env; ulong bi_arch_number;/* 板子的id*/ ulong bi_boot_params;/* 啟動參數*/ struct/* RAM 配置*/ {ulong start;ulong size; }bi_dram[CONFIG_NR_DRAM_BANKS];#ifdef CONFIG_HAS_ETH1 /* second onboard ethernet port */ unsigned char bi_enet1addr[6];#endif} bd_t;
2.2標記存放地址的設定
在board/smdk2410/smdk2410.c的board_init 函數設定了bi_boot_params 參數:
int board_init (void){S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();//擷取時鐘和電源配置寄存器的第一個寄存器的地址,寄存器的地上是連續的S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();//擷取GPIO配置寄存器的第一個寄存器的地址/* to reduce PLL lock time, adjust the LOCKTIME register */clk_power->LOCKTIME = 0xFFFFFF;/* configure MPLL */clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);/* some delay between MPLL and UPLL */delay (4000);/* configure UPLL */clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);/* some delay between MPLL and UPLL */delay (8000);/* set up the I/O ports */gpio->GPACON = 0x007FFFFF;gpio->GPBCON = 0x00044555;gpio->GPBUP = 0x000007FF;gpio->GPCCON = 0xAAAAAAAA;gpio->GPCUP = 0x0000FFFF;gpio->GPDCON = 0xAAAAAAAA;gpio->GPDUP = 0x0000FFFF;gpio->GPECON = 0xAAAAAAAA;gpio->GPEUP = 0x0000FFFF;gpio->GPFCON = 0x000055AA;gpio->GPFUP = 0x000000FF;gpio->GPGCON = 0xFF95FFBA;gpio->GPGUP = 0x0000FFFF;gpio->GPHCON = 0x002AFAAA;gpio->GPHUP = 0x000007FF;/* arch number of SMDK2410-Board */gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;/* adress of boot parameters */gd->bd->bi_boot_params = 0x30000100;icache_enable(); //調用cpu/arm920t/cpu.c中的函數dcache_enable();return 0;}
三、標記的設定
U-Boot通過bootm命令引導Linux核心,bootm命令對吼調用do_bootm_linux函數來引導核心。在do_bootm_linux函數就設定了標記,該函數的定義在lib_arm/armlinux.c中:
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], ulong addr, ulong *len_ptr, int verify){ulong len = 0, checksum;ulong initrd_start, initrd_end;ulong data;void (*theKernel)(int zero, int arch, uint params);image_header_t *hdr = &header;bd_t *bd = gd->bd;#ifdef CONFIG_CMDLINE_TAGchar *commandline = getenv ("bootargs");#endiftheKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);設定kernal載入地址/* * Check if there is an initrd image */使用者自訂了initrd之後需要載入進來,整個過程需要進行頭部以及整個資料內部校,類似於核心的載入校正,這裡省略了。initial RAM disk Linux初始 RAM磁碟(initrd)是在系統引導過程中掛載的一個臨時根檔案系統,用來支援兩階段的引導過程。initrd檔案中包含了各種可執行程式和驅動程式,它們可以用來掛載實際的根檔案系統,然後再將這個 initrd RAM 磁碟卸載,並釋放記憶體。在很多嵌入式Linux 系統中,initrd 就是最終的根檔案系統。if (argc >= 3) {SHOW_BOOT_PROGRESS (9);addr = simple_strtoul (argv[2], NULL, 16);printf ("## Loading Ramdisk Image at %08lx ...\n", addr);/* Copy header so we can blank CRC field for re-calculation */#ifdef CONFIG_HAS_DATAFLASHif (addr_dataflash (addr)) {read_dataflash (addr, sizeof (image_header_t),(char *) &header);} else#endifmemcpy (&header, (char *) addr,sizeof (image_header_t));if (ntohl (hdr->ih_magic) != IH_MAGIC) {printf ("Bad Magic Number\n");SHOW_BOOT_PROGRESS (-10);do_reset (cmdtp, flag, argc, argv);}data = (ulong) & header;len = sizeof (image_header_t);checksum = ntohl (hdr->ih_hcrc);hdr->ih_hcrc = 0;if (crc32 (0, (unsigned char *) data, len) != checksum) {printf ("Bad Header Checksum\n");SHOW_BOOT_PROGRESS (-11);do_reset (cmdtp, flag, argc, argv);}SHOW_BOOT_PROGRESS (10);print_image_hdr (hdr);data = addr + sizeof (image_header_t);len = ntohl (hdr->ih_size);#ifdef CONFIG_HAS_DATAFLASHif (addr_dataflash (addr)) {read_dataflash (data, len, (char *) CFG_LOAD_ADDR);data = CFG_LOAD_ADDR;}#endifif (verify) {ulong csum = 0;printf (" Verifying Checksum ... ");csum = crc32 (0, (unsigned char *) data, len);if (csum != ntohl (hdr->ih_dcrc)) {printf ("Bad Data CRC\n");SHOW_BOOT_PROGRESS (-12);do_reset (cmdtp, flag, argc, argv);}printf ("OK\n");}SHOW_BOOT_PROGRESS (11);if ((hdr->ih_os != IH_OS_LINUX) || (hdr->ih_arch != IH_CPU_ARM) || (hdr->ih_type != IH_TYPE_RAMDISK)) {printf ("No Linux ARM Ramdisk Image\n");SHOW_BOOT_PROGRESS (-13);do_reset (cmdtp, flag, argc, argv);}#if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)/* *we need to copy the ramdisk to SRAM to let Linux boot */memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);data = ntohl(hdr->ih_load);#endif /* CONFIG_B2 || CONFIG_EVB4510 *//* * Now check if we have a multifile image */} else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {ulong tail = ntohl (len_ptr[0]) % 4;int i;SHOW_BOOT_PROGRESS (13);/* skip kernel length and terminator */data = (ulong) (&len_ptr[2]);/* skip any additional image length fields */for (i = 1; len_ptr[i]; ++i)data += 4;/* add kernel length, and align */data += ntohl (len_ptr[0]);if (tail) {data += 4 - tail;}len = ntohl (len_ptr[1]);} else {/* * no initrd image */SHOW_BOOT_PROGRESS (14);len = data = 0;}#ifdefDEBUGif (!data) {printf ("No initrd\n");}#endifif (data) {initrd_start = data;initrd_end = initrd_start + len;} else {initrd_start = 0;initrd_end = 0;}SHOW_BOOT_PROGRESS (15);debug ("## Transferring control to Linux (at address %08lx) ...\n", (ulong) theKernel);#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,用於傳遞參數給Linux#ifdef CONFIG_SERIAL_TAGsetup_serial_tag (¶ms);#endif#ifdef CONFIG_REVISION_TAGsetup_revision_tag (¶ms);#endif#ifdef CONFIG_SETUP_MEMORY_TAGSsetup_memory_tags (bd);#endif#ifdef CONFIG_CMDLINE_TAGsetup_commandline_tag (bd, commandline);#endif#ifdef CONFIG_INITRD_TAGif (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);#endifsetup_end_tag (bd);#endif/* we assume that the kernel is in place */printf ("\nStarting kernel ...\n\n");列印資訊#ifdef CONFIG_USB_DEVICE{extern void udc_disconnect (void);udc_disconnect ();}#endifcleanup_before_linux ();啟動之前先做一些清理工作cpu/arm920t/cpu.c調用核心需要傳遞的參數如下:R0:必須為0R1:機器類型ID,本機為ARM(bd->bi_arch_number)R2:啟動參數列表在記憶體中的位置(bd->bi_boot_params)theKernel (0, bd->bi_arch_number, bd->bi_boot_params);}
3.1設定標記ATAG_CORE
標記列表以標記ATAG_CORE開始
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);//指向當前標記的末尾}
3.2設定記憶體標記ATAG_MEM
在board/smdk2410/smdk2410.c的dram_init函數設定了bd的bi_dram結構體:
int dram_init (void){gd->bd->bi_dram[0].start = PHYS_SDRAM_1;gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;return 0;}
下面是這邊記憶體標記的結構體:
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);}}
3.3設定命令列標記ATAG_CMDLINE
命令列就是一個字串,用來控制核心的一些行為。比如“root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 ”表示根檔案系統在MTD2分區上系統啟動後執行的第一個程式為/linuxrc,控制台是ttySAC0 。
static void setup_commandline_tag (bd_t *bd, char *commandline){char *p;if (!commandline)return;/* eat leading white space */for (p = commandline; *p == ' '; p++);/* skip non-existent command lines so the kernel will still * use its default command line. */if (*p == '\0')return;params->hdr.tag = ATAG_CMDLINE;params->hdr.size =(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;strcpy (params->u.cmdline.cmdline, p);params = tag_next (params);}
3.4設定ATAG_NONE
標記列表以標記ATAG_NONE介紹。
static void setup_end_tag (bd_t *bd){params->hdr.tag = ATAG_NONE;params->hdr.size = 0;}