u-boot 2011.09 调用kernel 的流程
这段时候我总是觉得有个问题,u-boot 的存在是不是就是为了调用kernel 而存在的。
所以,粗浅的跟了一下这个流程,还有很多细节上的东西没有做好,往指正。
u-boot-2011.9 调用内核代码跟踪
1. _start board_init_r main_loop.
这个流程是u-boot 的一个整体的流程。
2. main_loop()
这个函数主要执行一些u-boot 最后的一些工作,
347 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) 348 s = getenv ("bootdelay"); 349 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; 350 351 debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); 352
这里是设置系统的启动延时的时间。
如果在include/configs/ok335x.h 里面设置了宏定义CONFIG_BOOTDELAY大于零
那么在延时期间中断便可进入shell 终端界面。
371 s = getenv ("bootcmd"); 372 373 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); 374 375 if (bootdelay >= 0 && s && !abortboot (bootdelay)) { 376 # ifdef CONFIG_AUTOBOOT_KEYED 377 int prev = disable_ctrlc(1); /* disable Control C checking */ 378 # endif 379 380 # ifndef CONFIG_SYS_HUSH_PARSER 381 run_command (s, 0);
这里面就有关于u-boot 调用kernel 的方法。
Bootcmd 的定义在common/env_common.c 里面。
51 #ifdef CONFIG_BOOTARGS 52 "bootargs=" CONFIG_BOOTARGS "\0"
这是关于bootcmd 的定义。
在c语言中一个字符串其实本质上代表的就是的指针。
Bootcmd 指向的是CONFIG_BOOTCOMMAND 这个地址。
CONFIG_BOOTCOMMAND 这个宏的定义在include/configs/ok335x.h 里面。
193 #define CONFIG_BOOTCOMMAND \ 194 " if test $bootdev = MMC; then " \ 195 "mmc dev ${mmcdev}; mmc rescan;"\ 196 "echo SD/MMC found on device ${mmcdev};" \ 197 "if run loadbootenv; then " \ 198 "echo Loaded environment from ${bootenv};" \ 199 "run importbootenv;" \ 200 "fi;" \ 201 "if test -n $uenvcmd; then " \ 202 "echo Running uenvcmd ...;" \ 203 "run uenvcmd;" \ 204 "fi;" \ 205 "if run loaduimagefat; then " \ 206 "run mmcboot;" \ 207 "elif run loaduimage; then " \ 208 "run mmcboot;" \ 209 "else " \ 210 "echo Could not find ${bootfile} ;" \ 211 "fi;" \ 212 "else " \ 213 "run nandboot;" \ 214 "fi;" \
这边的话我们是直接运行run nandboot ;
149 "nandboot=echo Booting from nand ...; " \ 150 "run nandargs; " \ 151 "nandecc hw 2;"\ 152 "nand read ${loadaddr} ${nandsrcaddr} ${nandimgsize}; " \ 153 "bootm ${loadaddr}\0" \
最后运行bootm ${loadaddr} ,就可以运行kernel 。
3. run_command() 函数
这个函数的实现在common/main.c 里面。
运行run_command 函数,如果成功运行,返回1或者0,如果没有运行则返回-1。
命令结构体。
46 struct cmd_tbl_s { 47 char *name; /* Command Name */ 48 int maxargs; /* maximum number of arguments */ 49 int repeatable; /* autorepeat allowed? */ 50 /* Implementation function */ 51 int (*cmd)(struct cmd_tbl_s *, int, int, char * const []); 52 char *usage; /* Usage message (short) */ 53 #ifdef CONFIG_SYS_LONGHELP 54 char *help; /* Help message (long) */ 55 #endif 56 #ifdef CONFIG_AUTO_COMPLETE 57 /* do auto completion on the arguments */ 58 int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *c mdv[]); 59 #endif 60 }; 61 62 typedef struct cmd_tbl_s cmd_tbl_t;
最后运行这条命令。
1372 /* OK - call function to do the command */ 1373 if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { 1374 rc = -1; 1375 }
4. bootm 命令分析
在run_command 里面,以及前面的分析可以得出,最终,他会调用
bootm 命令,并且指向kernel 的地址。
bootm 命令的实现是在common/cmd_bootm.c 中实现。
585 /*******************************************************************/ 586 /* bootm - boot application image from image in memory */ 587 /*******************************************************************/ 588 589 int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 590 {
其实这个函数做了三个很重要的功能。
bootm_start : 内核启动前的准备工作,获取内核的信息并检查。
bootm_load_os : 检查内核的文件格式,是否需要解压。
boot_fn : 内核启动函数的调用, 我们调用的是do_bootm_linux 函数。
1) bootm_start
从do_bootm 调用了这一个函数。
625 if (bootm_start(cmdtp, flag, argc, argv)) 626 return 1;
有时候我们加载内核不成功,很多时候会报can’t get kernel image就是在这里报的。
从下可以看到,我们又调用了boot_get_kernel 函数。
214 static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 215 { 216 void *os_hdr; 217 int ret; 218 219 memset ((void *)&images, 0, sizeof (images)); 220 images.verify = getenv_yesno ("verify"); 221 222 bootm_start_lmb(); 223 224 /* get kernel image header, start address and length */ 225 os_hdr = boot_get_kernel (cmdtp, flag, argc, argv, 226 &images, &images.os.image_start, &images.os.image_len); 227 if (images.os.image_len == 0) { 228 puts ("ERROR: can't get kernel image!\n"); 229 return 1; 230 }
这个函数的作用其实是:找到kernel 镜像。
839 static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] , 840 bootm_headers_t *images, ulong *os_data, ulong *os_len) 841 { 842 image_header_t *hdr; 843 ulong img_addr; 869 } else { 870 img_addr = simple_strtoul(argv[1], NULL, 16); 871 debug ("* kernel: cmdline image address = 0x%08lx\n", img_addr); 872 } 873 874 show_boot_progress (1); 875 876 /* copy from dataflash if needed */ 877 img_addr = genimg_get_image (img_addr); 878 879 /* check image type, for FIT images get FIT kernel node */ 880 *os_data = *os_len = 0; 881 switch (genimg_get_format ((void *)img_addr)) { 882 case IMAGE_FORMAT_LEGACY: 883 printf ("## Booting kernel from Legacy Image at %08lx ...\n", 884 img_addr); 885 hdr = image_get_kernel (img_addr, images->verify);
打印一句调试信息。
然后通过image_get_kernel 获取kernel.
741 static image_header_t *image_get_kernel (ulong img_addr, int verify) 742 { 743 image_header_t *hdr = (image_header_t *)img_addr; 744 745 if (!image_check_magic(hdr)) { 746 puts ("Bad Magic Number\n"); 747 show_boot_progress (-1); 748 return NULL; 749 } 750 show_boot_progress (2); 751 752 if (!image_check_hcrc (hdr)) { 753 puts ("Bad Header Checksum\n"); 754 show_boot_progress (-2); 755 return NULL; 756 } 757 758 show_boot_progress (3); 759 image_print_contents (hdr); 760 761 if (verify) { 762 puts (" Verifying Checksum ... "); 763 if (!image_check_dcrc (hdr)) { 764 printf ("Bad Data CRC\n"); 765 show_boot_progress (-3); 766 return NULL; 767 } 768 puts ("OK\n"); 769 } 770 show_boot_progress (4); 771 772 if (!image_check_target_arch (hdr)) { 773 printf ("Unsupported Architecture 0x%x\n", image_get_arch (hdr)); 774 show_boot_progress (-4); 775 return NULL; 776 } 777 return hdr; 778 }
300 void image_print_contents (const void *ptr) 301 { 302 const image_header_t *hdr = (const image_header_t *)ptr; 303 const char *p; 304 305 #ifdef USE_HOSTCC 306 p = ""; 307 #else 308 p = " "; 309 #endif 310 311 printf ("%sImage Name: %.*s\n", p, IH_NMLEN, image_get_name (hdr)); 312 #if defined(CONFIG_TIMESTAMP) || defined(CONFIG_CMD_DATE) || defined(USE_HOSTCC) 313 printf ("%sCreated: ", p); 314 genimg_print_time ((time_t)image_get_time (hdr)); 315 #endif 316 printf ("%sImage Type: ", p); 317 image_print_type (hdr); 318 printf ("%sData Size: ", p); 319 genimg_print_size (image_get_data_size (hdr)); 320 printf ("%sLoad Address: %08x\n", p, image_get_load (hdr)); 321 printf ("%sEntry Point: %08x\n", p, image_get_ep (hdr)); 322 323 if (image_check_type (hdr, IH_TYPE_MULTI) || 324 image_check_type (hdr, IH_TYPE_SCRIPT)) { 325 int i; 326 ulong data, len; 327 ulong count = image_multi_count (hdr); 328 329 printf ("%sContents:\n", p); 330 for (i = 0; i < count; i++) { 331 image_multi_getimg (hdr, i, &data, &len); 332 333 printf ("%s Image %d: ", p, i); 334 genimg_print_size (len); 335 336 if (image_check_type (hdr, IH_TYPE_SCRIPT) && i > 0) { 337 /* 338 * the user may need to know offsets 339 * if planning to do something with 340 * multiple files 341 */ 342 printf ("%s Offset = 0x%08lx\n", p, data); 343 } 344 } 345 } 346 } 347
打印内核信息。打印校验和校验是否正确。
OK,到此从image_get_kernel - > boot_get_kernel 回来。
772 if (!image_check_target_arch (hdr)) { 773 printf ("Unsupported Architecture 0x%x\n", image_get_arch (hdr)); 774 show_boot_progress (-4); 775 return NULL; 776 } 777 return hdr; 778 }
返回内核地址。
再从boot_get_kernel 回到bootm_start
232 /* get image parameters */ 233 switch (genimg_get_format (os_hdr)) { 234 case IMAGE_FORMAT_LEGACY: 235 images.os.type = image_get_type (os_hdr); 236 images.os.comp = image_get_comp (os_hdr); 237 images.os.os = image_get_os (os_hdr); 238 239 images.os.end = image_get_image_end (os_hdr); 240 images.os.load = image_get_load (os_hdr); 241 break;
获取内核的参数并保存。
275 default: 276 puts ("ERROR: unknown image format type!\n"); 277 return 1;
有时候我们加载内核失败会报这个错误。
280 /* find kernel entry point */ 281 if (images.legacy_hdr_valid) { 282 images.ep = image_get_ep (&images.legacy_hdr_os_copy);
找到内核的入口。
297 if (((images.os.type == IH_TYPE_KERNEL) || 298 (images.os.type == IH_TYPE_MULTI)) && 299 (images.os.os == IH_OS_LINUX)) { 300 /* find ramdisk */ 301 ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH, 302 &images.rd_start, &images.rd_end); 303 if (ret) { 304 puts ("Ramdisk image is corrupt or invalid\n"); 305 return 1; 306 }
获取虚拟磁盘。
321 images.os.start = (ulong)os_hdr; 322 images.state = BOOTM_STATE_START; 323 324 return 0; 325 }
保存内核开启地址,并给开始的状态。
从bootm_start 回到do_bootm.
625 if (bootm_start(cmdtp, flag, argc, argv)) 626 return 1;
正常情况下bootm_start 是返回0,所以不会执行return 1;
2) bootm_load_os 函数
648 ret = bootm_load_os(images.os, &load_end, 1);
获取文件类型,打印调试信息。
327 #define BOOTM_ERR_RESET -1 328 #define BOOTM_ERR_OVERLAP -2 329 #define BOOTM_ERR_UNIMPLEMENTED -3 330 static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress) 331 { 332 uint8_t comp = os.comp; 333 ulong load = os.load; 334 ulong blob_start = os.start; 335 ulong blob_end = os.end; 336 ulong image_start = os.image_start; 337 ulong image_len = os.image_len; 338 uint unc_len = CONFIG_SYS_BOOTM_LEN;
339 #if defined(CONFIG_LZMA) || defined(CONFIG_LZO) 340 int ret; 341 #endif /* defined(CONFIG_LZMA) || defined(CONFIG_LZO) */ 342 343 const char *type_name = genimg_get_type_name (os.type); 344 345 switch (comp) { 346 case IH_COMP_NONE: 347 if (load == blob_start || load == image_start) { 348 printf (" XIP %s ... ", type_name); 349 } else { 350 printf (" Loading %s ... ", type_name); 351 memmove_wd ((void *)load, (void *)image_start, 352 image_len, CHUNKSZ); 353 } 354 *load_end = load + image_len; 355 puts("OK\n"); 356 break;
436 flush_cache(load, (*load_end - load) * sizeof(ulong)); 437 438 puts ("OK\n"); 439 debug (" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end); 440 if (boot_progress) 441 show_boot_progress (7); 442 443 if ((load < blob_end) && (*load_end > blob_start)) { 444 debug ("images.os.start = 0x%lX, images.os.end = 0x%lx\n", blob_start, blob_end ); 445 debug ("images.os.load = 0x%lx, load_end = 0x%lx\n", load, *load_end); 446 447 return BOOTM_ERR_OVERLAP; 448 } 449 450 return 0; 451 }
从bootm_load_os 回到do_bootm
648 ret = bootm_load_os(images.os, &load_end, 1); 649 650 if (ret < 0) { 651 if (ret == BOOTM_ERR_RESET) 652 do_reset (cmdtp, flag, argc, argv); 653 if (ret == BOOTM_ERR_OVERLAP) { 654 if (images.legacy_hdr_valid) { 655 if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI) 656 puts ("WARNING: legacy format multi component " 657 "image overwritten\n"); 658 } else { 659 puts ("ERROR: new format image overwritten - " 660 "must RESET the board to recover\n"); 661 show_boot_progress (-113); 662 do_reset (cmdtp, flag, argc, argv); 663 } 664 } 665 if (ret == BOOTM_ERR_UNIMPLEMENTED) { 666 if (iflag) 667 enable_interrupts(); 668 show_boot_progress (-7); 669 return 1; 670 } 671 }
如果刚才出错,那么检查错误编号,打印错误信息。
690 boot_fn = boot_os[images.os.os]; 691 692 if (boot_fn == NULL) { 693 if (iflag) 694 enable_interrupts(); 695 printf ("ERROR: booting os '%s' (%d) is not supported\n", 696 genimg_get_os_name(images.os.os), images.os.os); 697 show_boot_progress (-8); 698 return 1; 699 } 700 701 arch_preboot_os(); 702 703 boot_fn(0, argc, argv, &images); 704
其实这一步调用的是do_bootm_linux
134 static boot_os_fn *boot_os[] = { 135 #ifdef CONFIG_BOOTM_LINUX 136 [IH_OS_LINUX] = do_bootm_linux, 137 #endif 138 #ifdef CONFIG_BOOTM_NETBSD 139 [IH_OS_NETBSD] = do_bootm_netbsd, 140 #endif 141 #ifdef CONFIG_LYNXKDI 142 [IH_OS_LYNXOS] = do_bootm_lynxkdi, 143 #endif 144 #ifdef CONFIG_BOOTM_RTEMS 145 [IH_OS_RTEMS] = do_bootm_rtems, 146 #endif 147 #if defined(CONFIG_BOOTM_OSE) 148 [IH_OS_OSE] = do_bootm_ose, 149 #endif 150 #if defined(CONFIG_CMD_ELF) 151 [IH_OS_VXWORKS] = do_bootm_vxworks, 152 [IH_OS_QNX] = do_bootm_qnxelf, 153 #endif 154 #ifdef CONFIG_INTEGRITY 155 [IH_OS_INTEGRITY] = do_bootm_integrity, 156 #endif 157 };
do_bootm_linux 函数其实在arch/arm/lib/bootm.c里面
96 int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) 97 { 98 bd_t *bd = gd->bd; 99 char *s; 100 int machid = bd->bi_arch_number; 101 void (*kernel_entry)(int zero, int arch, uint params); 102 103 #ifdef CONFIG_CMDLINE_TAG 104 char *commandline = getenv ("bootargs"); 105 #endif 106 107 if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) 108 return 1; 109 110 s = getenv ("machid"); 111 if (s) { 112 machid = simple_strtoul (s, NULL, 16); 113 printf ("Using machid 0x%x from environment\n", machid); 114 } 115 116 show_boot_progress (15); 117 118 #ifdef CONFIG_OF_LIBFDT 119 if (images->ft_len) 120 return bootm_linux_fdt(machid, images); 121 #endif 122 123 kernel_entry = (void (*)(int, int, uint))images->ep; 124 125 debug ("## Transferring control to Linux (at address %08lx) ...\n", 126 (ulong) kernel_entry); 127
声明一个函数指针。
把镜像入口给kernel_entry 这个函数指针。
128 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ 129 defined (CONFIG_CMDLINE_TAG) || \ 130 defined (CONFIG_INITRD_TAG) || \ 131 defined (CONFIG_SERIAL_TAG) || \ 132 defined (CONFIG_REVISION_TAG) 133 setup_start_tag (bd); 134 #ifdef CONFIG_SERIAL_TAG 135 setup_serial_tag (¶ms); 136 #endif 137 #ifdef CONFIG_REVISION_TAG 138 setup_revision_tag (¶ms); 139 #endif 140 #ifdef CONFIG_SETUP_MEMORY_TAGS 141 setup_memory_tags (bd); 142 #endif 143 #ifdef CONFIG_CMDLINE_TAG 144 setup_commandline_tag (bd, commandline); 145 #endif 146 #ifdef CONFIG_INITRD_TAG 147 if (images->rd_start && images->rd_end) 148 setup_initrd_tag (bd, images->rd_start, images->rd_end); 149 #endif 150 setup_end_tag(bd); 151 #endif
初始化开始标签。
初始化串口。
初始化版本信息。
初始化内存。
初始化命令行。
End.
153 announce_and_cleanup(); 154 155 kernel_entry(0, machid, bd->bi_boot_params); 156 /* does not return */ 157 158 return 1; 159 }
正式进入内核。
参考:http://blog.csdn.net/g_salamander/article/details/8463854