Stage II过程分析
在Stage II中使用到了一些比较重要的数据结构,这里先对这些数据结构来进行下分析:
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 */ #ifdef CONFIG_VFD unsigned char vfd_type; /* display type */ #endif #if 0 unsigned long cpu_clk; /* CPU clock in Hz! */ unsigned long bus_clk; unsigned long ram_size; /* RAM size */ unsigned long reset_status; /* reset status register at boot */ #endif void **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 { int bi_baudrate; /* serial console baudrate */ unsigned long bi_ip_addr; /* IP Address */ unsigned char bi_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 */ #endif dram_init, /* configure available RAM banks 配置可用的RAM --board/smdk2410/smdk2410.c */ /* * 在这里开始进行配置SDRAM信息,用到结构体 bd->bi_dram[BANK_NR].start bd->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_IRQ IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; #endif return 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_FLASH ulong 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_IRQ gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); #endif gd = (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_ADDR addr = (void*)LCD_FRAMEBUFFER_ADDR; #else addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); /* 按页对其方式保留显存 */ #endif size = 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); #else mem_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: "); #endif nand_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_DATAFLASH AT91F_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_PU2 load_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_INIT board_late_init (); /* 该函数是开发板提供的,供不同的开发板做一些特有的初始化工作。 */ #endif /* * 网卡初始化 */ #if (CONFIG_COMMANDS & CFG_CMD_NET) #if defined(CONFIG_NET_MULTI) puts ("Net: "); #endif eth_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_PARSER static 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_PREBOOT char *p; #endif #ifdef CONFIG_BOOTCOUNT_LIMIT unsigned 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_SUPPORT if (do_mdm_init) bmp = 1; /* alternate bitmap */ #endif trab_vfd (bmp); #endif /* CONFIG_VFD && VFD_TEST_LOGO */ #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = 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_SUPPORT debug ("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_PARSER u_boot_hush_start (); #endif #ifdef CONFIG_AUTO_COMPLETE install_auto_complete(); #endif #ifdef CONFIG_PREBOOT if ((p = getenv ("preboot")) != NULL) { # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CFG_HUSH_PARSER run_command (p, 0); # else parse_string_outer(p, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_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_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (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_KEYED int prev = disable_ctrlc(1); /* disable Control C checking */ # endif # ifndef CFG_HUSH_PARSER run_command (s, 0); # else parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking */ # endif } # ifdef CONFIG_MENUKEY if (menukey == CONFIG_MENUKEY) { s = getenv("menucmd"); if (s) { # ifndef CFG_HUSH_PARSER run_command (s, 0); # else parse_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_PARSER parse_file_outer(); /* This point is never reached */ for (;;); #else for (;;) { #ifdef CONFIG_BOOT_RETRY_TIME if (rc >= 0) { /* Saw enough of a valid command to * restart the timeout. */ reset_cmd_timeout(); } #endif len = 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_TIME else 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); # else return; /* retry autoboot */ # endif } #endif if (len == -1) puts ("<INTERRUPT>\n"); else rc = 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_TAG char *commandline = getenv ("bootargs"); #endif theKernel = (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_DATAFLASH if (addr_dataflash (addr)) { read_dataflash (addr, sizeof (image_header_t), (char *) &header); } else #endif memcpy (&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_DATAFLASH if (addr_dataflash (addr)) { read_dataflash (data, len, (char *) CFG_LOAD_ADDR); data = CFG_LOAD_ADDR; } #endif if (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; } #ifdef DEBUG if (!data) { printf ("No initrd\n"); } #endif if (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_TAG setup_serial_tag (¶ms); #endif #ifdef CONFIG_REVISION_TAG setup_revision_tag (¶ms); #endif #ifdef CONFIG_SETUP_MEMORY_TAGS setup_memory_tags (bd); #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); #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 (); } #endif cleanup_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。
U-boot引导流程分析二