U-boot引导流程分析二

来源:互联网 发布:琅琊榜细节 知乎 编辑:程序博客网 时间:2024/05/16 03:13

Stage II过程分析

在Stage II中使用到了一些比较重要的数据结构,这里先对这些数据结构来进行下分析:
typedefstructglobal_data {bd_t*bd;unsigned longflags;unsigned longbaudrate;unsigned longhave_console;/* serial_init() was called */unsigned longreloc_off;/* Relocation Offset */unsigned longenv_addr;/* Address  of Environment struct */unsigned longenv_valid;/* Checksum of Environment valid? */unsigned longfb_base;/* base address of frame buffer */#ifdef CONFIG_VFDunsigned charvfd_type;/* display type */#endif#if 0unsigned longcpu_clk;/* CPU clock in Hz!*/unsigned longbus_clk;unsigned longram_size;/* RAM size */unsigned longreset_status;/* reset status register at boot */#endifvoid**jt;/* jump table */} gd_t;

gd_t数据结构在include/asm-arm/global_data.h中定义,它用来存储全局数据区的数据。

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

U-Boot中使用了一个存储在寄存器中的指针gd来记录全局数据区的地址。DECLARE_GLOBAL_DATA_PTR定义了一个gd_t全局数据结构的指针,这个指针存放在寄存器r8中。这个声明也避免编译器把r8分配各其他变量。任何想要访问全局数据区的代码,只要开头加入"DECLARE_GLOBAL_DATA_PTR"这一行代码,然后就可以使用gd指针来访问全局数据区了。

根据U-Boot内存使用图可以计算gd的值: gd = TEXT_BASE - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)。

typedef struct bd_info {    intbi_baudrate;/* serial console baudrate */    unsigned longbi_ip_addr;/* IP Address */    unsigned charbi_enetaddr[6]; /* Ethernet adress */    struct environment_s       *bi_env;    ulong        bi_arch_number;/* unique id for this board */    ulong        bi_boot_params;/* where this board expects params */    struct/* RAM configuration */    {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;

db_t数据结构在include/asm-arm/u-boot.h中定义。U-Boot启动内核时要给内核传递参数,这时就要使用gd_t,db_t结构体中的信息来设置标记列表。

init_fnc_t *init_sequence[] = {cpu_init,/* basic cpu dependent setup 基本的处理器相关配置 --cpu/arm920t/cpu.c */board_init,/* basic board dependent setup 基本的板级相关配置 --board/smdk2410/smdk2410.c */interrupt_init,/* set up exceptions 初始化中断处理 --cpu/arm920t/s3c24x0/interrupts.c */env_init,/* initialize environment 初始化环境变量 --common/env_flash.c */init_baudrate,/* initialze baudrate settings 初始化波特率设置 --lib_arm/board.c */serial_init,/* serial communications setup 串口通讯设置 --cpu/arm920t/serial.c */console_init_f,/* stage 1 init of console 控制台初始化阶段1 -- common/console.c */display_banner,/* say that we are here 打印U-Boot版本、编译的时间 --lib_arm/board.c */#if defined(CONFIG_DISPLAY_CPUINFO)print_cpuinfo,/* display cpu info (and speed) */#endif#if defined(CONFIG_DISPLAY_BOARDINFO)checkboard,/* display board info */#endifdram_init,/* configure available RAM banks 配置可用的RAM --board/smdk2410/smdk2410.c *//* * 在这里开始进行配置SDRAM信息,用到结构体bd->bi_dram[BANK_NR].startbd->bi_dram[BANK_NR].size                                                                                                       bd->bi_dram[BANK_NR].size */display_dram_config,/* 显示SDRAM的配置信息 --lib_arm/board.c */NULL,};

U-Boot使用一个数组init_sequence来存储对于大多数开发板都要执行的函数指针。

int cpu_init (void){/* * setup up stacks if necessary */#ifdef CONFIG_USE_IRQIRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;#endifreturn 0;}

其中的cpu_init函数在cpu/arm920t/cpu.c中定义。

int board_init (void){............../* arch number of SMDK2410-Board */gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;/* SMDK2410开发板的机器码(板子ID) *//* adress of boot parameters */gd->bd->bi_boot_params = 0x30000100;/* 内核启动参数地址(绝对地址) */.............}

board_init设置了U-Boot机器码和内核启动参数地址。

/* 初始化内存RAM的信息,其实就是给gd->bd中内存信息表赋值而已 */int dram_init (void){/* 设置板级数据中的SDRAM开始地址和大小 */gd->bd->bi_dram[0].start = PHYS_SDRAM_1;gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;return 0;}

分析完上述数据结构,下面接着来分析Stage II入口函数start_armboot(实现在lib-arm/board.c中):

void start_armboot (void){init_fnc_t **init_fnc_ptr;char *s;#ifndef CFG_NO_FLASHulong size;#endif#if defined(CONFIG_VFD) || defined(CONFIG_LCD)unsigned long addr;#endif#if defined(CONFIG_BOOT_MOVINAND)uint *magic = (uint *) (PHYS_SDRAM_1);#endif/* * Pointer is writable since we allocated a register for it  * * 在重定位之后,即uboot的代码从flash拷到sdram 此时连接脚本里的_start 等于TEXT_BASE */#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ulong gd_base;gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);#ifdef CONFIG_USE_IRQgd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);#endifgd = (gd_t*)gd_base;#else/* 给全局变量gd分配空间大小且指定gd的位置 这里gd是一个结构体,在uboot内存分布中是CFG_GBL_DATA_SIZE一共128字节 */gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));/* 计算全局数据结构的地址gd *//* * 对全局数据区进行地址分配,_armboot_start为0x3f000000,CFG_MALLOC_LEN是堆大小+环境数据区大小, * include/configs/smdk2410.h中CFG_MALLOC_LEN大小定义为192KB。 */#endif/* compiler optimization barrier needed for GCC >= 3.4 */__asm__ __volatile__("": : :"memory");memset ((void*)gd, 0, sizeof (gd_t));/* gd指针所指向的空间清零  */gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));/* 给gd中的bd指针分配空间大小 */memset (gd->bd, 0, sizeof (bd_t));/* gd->bd 所指向的空间清零 */monitor_flash_len = _bss_start - _armboot_start;/* uboot镜像文件的大小 *//* * 顺序执行init_sequence数组中的初始化函数 * * 这里是调用了一系列的c函数指针,进行初始化。 * 比如cpu_init初始化完成各个gpio管脚初始化,board_init完成arch_number设置和boot_params约定存放地址,还有串口初始化等。 */for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}/* * 识别出来是哪一种Flash NOR还是NAND 如果定义了CFG_NO_FLASH这个宏,说明是NAND 否则是NOR  * * NOR型Flash的初始化 */#ifndef CFG_NO_FLASH/* configure available FLASH banks 配置可用的flash空间,并打印出相关信息 */size = flash_init ();/* NOR型Flash的初始化 */display_flash_config (size);#endif /* CFG_NO_FLASH *//* * 初始化VFD存储区(LCD显示相关)  *  * 定义显示类型 分vfd和lcd两种。vfd一般不用,我们用lcd的在这里定义了帧缓冲,也就显存的的地址和大小 */#ifdef CONFIG_VFD#ifndef PAGE_SIZE#  define PAGE_SIZE 4096/* 定义页大小为4K */#endif/* * reserve memory for VFD display (always full pages) *//* bss_end is defined in the board-specific linker script 把视频帧缓冲区设置在bss_end后面 */addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);size = vfd_setmem (addr);gd->fb_base = addr;#endif /* CONFIG_VFD *//* 初始化LCD显存在内存中配置一块帧缓冲区 */#ifdef CONFIG_LCD#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 */#ifdef LCD_FRAMEBUFFER_ADDRaddr =  (void*)LCD_FRAMEBUFFER_ADDR;#elseaddr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);/* 按页对其方式保留显存 */#endifsize = lcd_setmem (addr);/* 分配帧缓冲区的大小 */gd->fb_base = addr;/* 帧缓冲区的物理起始地址 */#endif /* CONFIG_LCD *//* armboot_start is defined in the board-specific linker script */#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);#elsemem_malloc_init (_armboot_start - CFG_MALLOC_LEN);/* 分配堆空间大小 *//* * 初始化堆空间(设置heap区,供malloc使用) * malloc可用内存由mem_malloc_start,mem_malloc_end指定。而当前分配的位置则是mem_malloc_brk。 * mem_malloc_init负责初始化这三个变量。malloc则通过sbrk函数来使用和管理这片内存。 --lib_arm/board.c */#endif#if defined(CONFIG_SMDK6400) || defined(CONFIG_SMDK6410) || defined(CONFIG_SMDK6430) || defined(CONFIG_SMDK2450) || defined(CONFIG_SMDK2416)/* * 初始化nand flash,这是在nand flash启动的s3c2410移植u-boot的关键,根据flash时序编写函数即可 * 在include/configs/smdk2410.h中的command definition中增加CONFIG_COMMANDS和CFG_CMD_NAND命令 * nand型flash的初始化 */#if defined(CONFIG_NAND)#ifdef FORLINX_DEBUG           printf("NandFlash Information:\n");#else           puts ("NAND:    ");#endifnand_init();/* go init the NAND 初始化Nand Flash控制器,获取NAND的基地址和大小信息 */#endif#if defined(CONFIG_ONENAND)/* 三星的一种特别的flash  onenand,类似于nand */puts ("OneNAND: ");onenand_init();/* go init the One-NAND 初始化OneNand */#endif#if defined(CONFIG_BOOT_MOVINAND)puts ("SD/MMC:  ");if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) {printf("Boot up for burning\n");} else {movi_set_capacity();movi_set_ofs(MOVI_TOTAL_BLKCNT);movi_init();}#endif#else/* * NAND Flash初始化  */#if (CONFIG_COMMANDS & CFG_CMD_NAND)puts ("NAND:    ");nand_init();/* go init the NAND */#endif#endif/* 初始化DataFlash */#ifdef CONFIG_HAS_DATAFLASHAT91F_DataflashInit();dataflash_print_info();#endif/* initialize environment 配置环境变量,重新定位 --common/env_common.c */env_relocate ();/* FrameBuffer初始化 */#ifdef CONFIG_VFD/* must do this after the framebuffer is allocated */drv_vfd_init();/* Video的初始化 */#endif /* CONFIG_VFD *//* * IP,MAC地址的初始化。主要是从环境中读,然后赋值给gd->bd对应域就OK了。  * * IP Address 从环境变量中获取IP地址(开发板IP) 以太网接口MAC地址 * 主要是初始化 gd->bd->bi_ip_addr和gd->bd->bi_enetaddr[] */gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");/* MAC Address 获得环境变量中的以太网接口MAC地址,设置到gd->bd-bi_enetaddr[reg]中 */{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));s = (i > 0) ? tmp : NULL;for (reg = 0; reg < 6; ++reg) {gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;if (s)s = (*e) ? e + 1 : e;}#endif}devices_init ();/* get the devices list going. 注册设备链表,其实也就只注册了一个串口设备 --common/devices.c */#ifdef CONFIG_CMC_PU2load_sernum_ethaddr ();#endif /* CONFIG_CMC_PU2 */jumptable_init ();/* 跳转表初始化 --common/exports.c */console_init_r ();/*  * fully init console as a device  * * 完整地初始化控制台设备 --common/console.c  * 到这里才可以从控制台看到数据打印出来 */ /*  * 从串口寄存器的设置到最终在终端上打印信息,是有以下函数组成的。  *在init_sequense里的三个函数和  *init_baudrate,   设置 gd->bd->bi_baudrate  *serial_init,     直接调用serial_setbrg函数初始化UART寄存器:8个数据位,一个开始位,一个停止位,无校验位。。。  *console_init_f,  控制台前期初始化  设置gd->have_console=1  *devices_init,    调用drv_system_init 注册串口设备  *console_init_r   控制台后期初始化,将串口设备指向控制台标准输入设备,标准输出设备,标准错误设备  *  *In:   serial      *Out: serial      *Err: serial     *打印这三行信息,表明串口作为标准输入设备,标准输出设备,标准错误输出设备,这样就能打印信息了  *默认情况下,键盘和鼠标默认为标准输入设备,显示器默认为标准输出设备和标准错误输出设备,printf为标准格式输出  *scanf为标准格式输入。标准输入,标准输出设备的重定向即将串口设备作为标准输入设备,将串口做为标准输出设备和  *标准错误输出设备。  */#if defined(CONFIG_MISC_INIT_R)/* miscellaneous platform dependent initialisations */misc_init_r ();/* 混杂设备的初始化 */#endif/* enable exceptions 使能中断处理 --cpu/arm920t/interrupts.c */enable_interrupts ();/* Perform network card initialisation if necessary */#ifdef CONFIG_DRIVER_CS8900//cs8900_get_enetaddr (gd->bd->bi_enetaddr);#endif/* 如果有网卡设备,设置网卡MAC和IP地址 */#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)if (getenv ("ethaddr")) {smc_set_mac_addr(gd->bd->bi_enetaddr);}#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 *//* Initialize from environment 通过环境变量初始化 */if ((s = getenv ("loadaddr")) != NULL) {load_addr = simple_strtoul (s, NULL, 16);}#if (CONFIG_COMMANDS & CFG_CMD_NET)if ((s = getenv ("bootfile")) != NULL) {copy_filename (BootFile, s, sizeof (BootFile));}#endif/* CFG_CMD_NET */#ifdef BOARD_LATE_INITboard_late_init ();/* 该函数是开发板提供的,供不同的开发板做一些特有的初始化工作。 */#endif/* * 网卡初始化 */#if (CONFIG_COMMANDS & CFG_CMD_NET)#if defined(CONFIG_NET_MULTI)puts ("Net:     ");#endifeth_initialize(gd->bd);/* 初始化以太网 */#endif    led_init();     /*led all off --forlinx add *//* 至此所有初始化工作已经完毕。main_loop在标准转入设备中接收命令行,然后分析,查找,执行 *//* main_loop() can return to retry autoboot, if so just run it again. main_loop()循环不断执行 */for (;;) {main_loop ();/* main_loop()函数在common/main.c中定义 */}/* NOTREACHED - no way out of command loop except booting */}
start_armboot()函数使用死循环调用main_loop()函数,作用是防止main_loop()函数开始的初始化代码如果调用失败后重新执行初始化操作,保证程序能进入到U-Boot的命令行。

main_loop()函数在common/main.c中。具体代码如下:

void main_loop (void){#ifndef CFG_HUSH_PARSERstatic char lastcommand[CFG_CBSIZE] = { 0, };int len;int rc = 1;int flag;#endif#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)char *s;int bootdelay;#endif#ifdef CONFIG_PREBOOTchar *p;#endif#ifdef CONFIG_BOOTCOUNT_LIMITunsigned long bootcount = 0;unsigned long bootlimit = 0;char *bcs;char bcs_set[16];#endif /* CONFIG_BOOTCOUNT_LIMIT */#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)ulong bmp = 0;/* default bitmap */extern int trab_vfd (ulong bitmap);#ifdef CONFIG_MODEM_SUPPORTif (do_mdm_init)bmp = 1;/* alternate bitmap */#endiftrab_vfd (bmp);#endif/* CONFIG_VFD && VFD_TEST_LOGO */#ifdef CONFIG_BOOTCOUNT_LIMITbootcount = bootcount_load();bootcount++;bootcount_store (bootcount);sprintf (bcs_set, "%lu", bootcount);setenv ("bootcount", bcs_set);bcs = getenv ("bootlimit");bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;#endif /* CONFIG_BOOTCOUNT_LIMIT */#ifdef CONFIG_MODEM_SUPPORTdebug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);if (do_mdm_init) {char *str = strdup(getenv("mdm_cmd"));setenv ("preboot", str);  /* set or delete definition */if (str != NULL)free (str);mdm_init(); /* wait for modem connection */}#endif  /* CONFIG_MODEM_SUPPORT */#ifdef CONFIG_VERSION_VARIABLE{extern char version_string[];setenv ("ver", version_string);  /* set version variable */}#endif /* CONFIG_VERSION_VARIABLE */#ifdef CFG_HUSH_PARSERu_boot_hush_start ();#endif#ifdef CONFIG_AUTO_COMPLETEinstall_auto_complete();#endif#ifdef CONFIG_PREBOOTif ((p = getenv ("preboot")) != NULL) {# ifdef CONFIG_AUTOBOOT_KEYEDint prev = disable_ctrlc(1);/* disable Control C checking */# endif# ifndef CFG_HUSH_PARSERrun_command (p, 0);# elseparse_string_outer(p, FLAG_PARSE_SEMICOLON |    FLAG_EXIT_FROM_LOOP);# endif# ifdef CONFIG_AUTOBOOT_KEYEDdisable_ctrlc(prev);/* restore Control C checking */# endif}#endif /* CONFIG_PREBOOT */#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)s = getenv ("bootdelay");bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);# ifdef CONFIG_BOOT_RETRY_TIMEinit_cmd_timeout ();# endif/* CONFIG_BOOT_RETRY_TIME */#ifdef CONFIG_BOOTCOUNT_LIMITif (bootlimit && (bootcount > bootlimit)) {printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",        (unsigned)bootlimit);s = getenv ("altbootcmd");}else#endif /* CONFIG_BOOTCOUNT_LIMIT */s = getenv ("bootcmd");debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");if (bootdelay >= 0 && s && !abortboot (bootdelay)) {# ifdef CONFIG_AUTOBOOT_KEYEDint prev = disable_ctrlc(1);/* disable Control C checking */# endif# ifndef CFG_HUSH_PARSERrun_command (s, 0);# elseparse_string_outer(s, FLAG_PARSE_SEMICOLON |    FLAG_EXIT_FROM_LOOP);# endif# ifdef CONFIG_AUTOBOOT_KEYEDdisable_ctrlc(prev);/* restore Control C checking */# endif}# ifdef CONFIG_MENUKEYif (menukey == CONFIG_MENUKEY) {    s = getenv("menucmd");    if (s) {# ifndef CFG_HUSH_PARSERrun_command (s, 0);# elseparse_string_outer(s, FLAG_PARSE_SEMICOLON |    FLAG_EXIT_FROM_LOOP);# endif    }}#endif /* CONFIG_MENUKEY */#endif/* CONFIG_BOOTDELAY */#ifdef CONFIG_AMIGAONEG3SE{    extern void video_banner(void);    video_banner();}#endif#ifdef CONFIG_BOOT_MOVINAND           ARMMenu();#endif#ifdef CONFIG_BOOT_NAND           NAND_ARMMenu();#endif/* * Main Loop for Monitor Command Processing */#ifdef CFG_HUSH_PARSERparse_file_outer();/* This point is never reached */for (;;);#elsefor (;;) {#ifdef CONFIG_BOOT_RETRY_TIMEif (rc >= 0) {/* Saw enough of a valid command to * restart the timeout. */reset_cmd_timeout();}#endiflen = readline (CFG_PROMPT);flag = 0;/* assume no special flags for now */if (len > 0)strcpy (lastcommand, console_buffer);else if (len == 0)flag |= CMD_FLAG_REPEAT;#ifdef CONFIG_BOOT_RETRY_TIMEelse if (len == -2) {/* -2 means timed out, retry autoboot */puts ("\nTimed out waiting for command\n");# ifdef CONFIG_RESET_TO_RETRY/* Reinit board to run initialization code again */do_reset (NULL, 0, 0, NULL);# elsereturn;/* retry autoboot */# endif}#endifif (len == -1)puts ("<INTERRUPT>\n");elserc = run_command (lastcommand, flag);if (rc <= 0) {/* invalid command or not repeatable, forget it */lastcommand[0] = 0;}}#endif /*CFG_HUSH_PARSER*/}
main_loop()函数进行与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。

用户不中断kernel引导的话,最后会调用run_command函数,它会解析输入的命令,然后根据命令的名字查找相应的函数进行调用。启动的时候需要调用两个命令:nand命令和bootm命令。


U-Boot如何给kernel传递参数?

U-Boot使用命令bootm来启动已经加载到内存中的内核。而bootm命令实际上调用的是bo_bootm函数。对于Linux内核,do_bootm函数会调用do_bootm_linux 函数来设置标记列表和启动内核。

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);/* * Check if there is an initrd image */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);#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 ();theKernel (0, bd->bi_arch_number, bd->bi_boot_params);}

Kernel如何读取U-Boot传递的相关参数呢?

对于Linux kernel,ARM平台启动时,先执行arch/arm/head.S,此文件会调用arch/arm/kernel/head-common.S中的函数,并最后调用start_kernel。


0 0
原创粉丝点击