u-boot 2011.09 调用kernel 的流程

时间:2023-03-09 16:17:21
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 最后的一些工作,

  #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);

这里是设置系统的启动延时的时间。
如果在include/configs/ok335x.h 里面设置了宏定义CONFIG_BOOTDELAY大于零
那么在延时期间中断便可进入shell 终端界面。

          s = getenv ("bootcmd");                                                 

      debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");           

      if (bootdelay >=  && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, 0);

这里面就有关于u-boot 调用kernel 的方法。
Bootcmd 的定义在common/env_common.c 里面。

  #ifdef  CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"

这是关于bootcmd 的定义。
在c语言中一个字符串其实本质上代表的就是的指针。
Bootcmd 指向的是CONFIG_BOOTCOMMAND 这个地址。

CONFIG_BOOTCOMMAND 这个宏的定义在include/configs/ok335x.h 里面。

 #define CONFIG_BOOTCOMMAND \
" if test $bootdev = MMC; then " \
"mmc dev ${mmcdev}; mmc rescan;"\
"echo SD/MMC found on device ${mmcdev};" \
"if run loadbootenv; then " \
"echo Loaded environment from ${bootenv};" \
"run importbootenv;" \
"fi;" \
"if test -n $uenvcmd; then " \
"echo Running uenvcmd ...;" \
"run uenvcmd;" \
"fi;" \
"if run loaduimagefat; then " \
"run mmcboot;" \
"elif run loaduimage; then " \
"run mmcboot;" \
"else " \
"echo Could not find ${bootfile} ;" \
"fi;" \
"else " \
"run nandboot;" \
"fi;" \

这边的话我们是直接运行run nandboot ;

     "nandboot=echo Booting from nand ...; " \
"run nandargs; " \
"nandecc hw 2;"\
"nand read ${loadaddr} ${nandsrcaddr} ${nandimgsize}; " \
"bootm ${loadaddr}\0" \

最后运行bootm ${loadaddr} ,就可以运行kernel 。

3. run_command() 函数
这个函数的实现在common/main.c 里面。
运行run_command 函数,如果成功运行,返回1或者0,如果没有运行则返回-1。
命令结构体。

  struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
char *usage; /* Usage message (short) */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *c mdv[]);
#endif
}; typedef struct cmd_tbl_s cmd_tbl_t;

最后运行这条命令。

         /* OK - call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -;
}

4. bootm 命令分析

在run_command 里面,以及前面的分析可以得出,最终,他会调用
bootm 命令,并且指向kernel 的地址。
bootm 命令的实现是在common/cmd_bootm.c 中实现。

  /*******************************************************************/
/* bootm - boot application image from image in memory */
/*******************************************************************/ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{

其实这个函数做了三个很重要的功能。
bootm_start : 内核启动前的准备工作,获取内核的信息并检查。
bootm_load_os : 检查内核的文件格式,是否需要解压。
boot_fn : 内核启动函数的调用, 我们调用的是do_bootm_linux 函数。

1) bootm_start
从do_bootm 调用了这一个函数。

      if (bootm_start(cmdtp, flag, argc, argv))
return ;

有时候我们加载内核不成功,很多时候会报can’t get kernel image就是在这里报的。
从下可以看到,我们又调用了boot_get_kernel 函数。

  static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
void *os_hdr;
int ret; memset ((void *)&images, , sizeof (images));
images.verify = getenv_yesno ("verify"); bootm_start_lmb(); /* get kernel image header, start address and length */
os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
&images, &images.os.image_start, &images.os.image_len);
if (images.os.image_len == ) {
puts ("ERROR: can't get kernel image!\n");
return ;
}

这个函数的作用其实是:找到kernel 镜像。

  static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]     ,
bootm_headers_t *images, ulong *os_data, ulong *os_len)
{
image_header_t *hdr;
ulong img_addr;
} else {
img_addr = simple_strtoul(argv[], NULL, );
debug ("* kernel: cmdline image address = 0x%08lx\n", img_addr);
} show_boot_progress (); /* copy from dataflash if needed */
img_addr = genimg_get_image (img_addr); /* check image type, for FIT images get FIT kernel node */
*os_data = *os_len = ;
switch (genimg_get_format ((void *)img_addr)) {
case IMAGE_FORMAT_LEGACY:
printf ("## Booting kernel from Legacy Image at %08lx ...\n",
img_addr);
hdr = image_get_kernel (img_addr, images->verify);

打印一句调试信息。
然后通过image_get_kernel 获取kernel.

  static image_header_t *image_get_kernel (ulong img_addr, int verify)
{
image_header_t *hdr = (image_header_t *)img_addr; if (!image_check_magic(hdr)) {
puts ("Bad Magic Number\n");
show_boot_progress (-);
return NULL;
}
show_boot_progress (); if (!image_check_hcrc (hdr)) {
puts ("Bad Header Checksum\n");
show_boot_progress (-);
return NULL;
} show_boot_progress ();
image_print_contents (hdr); if (verify) {
puts (" Verifying Checksum ... ");
if (!image_check_dcrc (hdr)) {
printf ("Bad Data CRC\n");
show_boot_progress (-);
return NULL;
}
puts ("OK\n");
}
show_boot_progress (); if (!image_check_target_arch (hdr)) {
printf ("Unsupported Architecture 0x%x\n", image_get_arch (hdr));
show_boot_progress (-);
return NULL;
}
return hdr;
}
  void image_print_contents (const void *ptr)
{
const image_header_t *hdr = (const image_header_t *)ptr;
const char *p; #ifdef USE_HOSTCC
p = "";
#else
p = " ";
#endif printf ("%sImage Name: %.*s\n", p, IH_NMLEN, image_get_name (hdr));
#if defined(CONFIG_TIMESTAMP) || defined(CONFIG_CMD_DATE) || defined(USE_HOSTCC)
printf ("%sCreated: ", p);
genimg_print_time ((time_t)image_get_time (hdr));
#endif
printf ("%sImage Type: ", p);
image_print_type (hdr);
printf ("%sData Size: ", p);
genimg_print_size (image_get_data_size (hdr));
printf ("%sLoad Address: %08x\n", p, image_get_load (hdr));
321 printf ("%sEntry Point: %08x\n", p, image_get_ep (hdr)); if (image_check_type (hdr, IH_TYPE_MULTI) ||
image_check_type (hdr, IH_TYPE_SCRIPT)) {
int i;
ulong data, len;
ulong count = image_multi_count (hdr); printf ("%sContents:\n", p);
for (i = ; i < count; i++) {
image_multi_getimg (hdr, i, &data, &len); printf ("%s Image %d: ", p, i);
genimg_print_size (len); if (image_check_type (hdr, IH_TYPE_SCRIPT) && i > ) {
/*
338 * the user may need to know offsets
339 * if planning to do something with
340 * multiple files
341 */
printf ("%s Offset = 0x%08lx\n", p, data);
}
}
}
}

打印内核信息。打印校验和校验是否正确。

OK,到此从image_get_kernel - > boot_get_kernel 回来。

      if (!image_check_target_arch (hdr)) {
printf ("Unsupported Architecture 0x%x\n", image_get_arch (hdr));
show_boot_progress (-);
return NULL;
}
return hdr;
}

返回内核地址。

再从boot_get_kernel 回到bootm_start

      /* get image parameters */
switch (genimg_get_format (os_hdr)) {
case IMAGE_FORMAT_LEGACY:
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);
break;

获取内核的参数并保存。

      default:
puts ("ERROR: unknown image format type!\n");
return ;

有时候我们加载内核失败会报这个错误。

      /* find kernel entry point */
if (images.legacy_hdr_valid) {
images.ep = image_get_ep (&images.legacy_hdr_os_copy);

找到内核的入口。

      if (((images.os.type == IH_TYPE_KERNEL) ||
(images.os.type == IH_TYPE_MULTI)) &&
(images.os.os == IH_OS_LINUX)) {
/* find ramdisk */
ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH,
&images.rd_start, &images.rd_end);
if (ret) {
puts ("Ramdisk image is corrupt or invalid\n");
return ;
}

获取虚拟磁盘。

      images.os.start = (ulong)os_hdr;
322 images.state = BOOTM_STATE_START; return ;
}

保存内核开启地址,并给开始的状态。

从bootm_start 回到do_bootm.

      if (bootm_start(cmdtp, flag, argc, argv))
return ;

正常情况下bootm_start 是返回0,所以不会执行return 1;
2) bootm_load_os 函数

      ret = bootm_load_os(images.os, &load_end, );                               

获取文件类型,打印调试信息。

  #define BOOTM_ERR_RESET     -1
#define BOOTM_ERR_OVERLAP -2
#define BOOTM_ERR_UNIMPLEMENTED -3
static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress)
{
uint8_t comp = os.comp;
ulong load = os.load;
ulong blob_start = os.start;
ulong blob_end = os.end;
ulong image_start = os.image_start;
ulong image_len = os.image_len;
uint unc_len = CONFIG_SYS_BOOTM_LEN;
  #if defined(CONFIG_LZMA) || defined(CONFIG_LZO)
int ret;
#endif /* defined(CONFIG_LZMA) || defined(CONFIG_LZO) */ const char *type_name = genimg_get_type_name (os.type); switch (comp) {
case IH_COMP_NONE:
if (load == blob_start || load == image_start) {
printf (" XIP %s ... ", type_name);
} else {
printf (" Loading %s ... ", type_name);
memmove_wd ((void *)load, (void *)image_start,
image_len, CHUNKSZ);
}
*load_end = load + image_len;
puts("OK\n");
break;
      flush_cache(load, (*load_end - load) * sizeof(ulong));                      

      puts ("OK\n");
debug (" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);
if (boot_progress)
show_boot_progress (); if ((load < blob_end) && (*load_end > blob_start)) {
debug ("images.os.start = 0x%lX, images.os.end = 0x%lx\n", blob_start, blob_end );
debug ("images.os.load = 0x%lx, load_end = 0x%lx\n", load, *load_end); return BOOTM_ERR_OVERLAP;
} return ;
}

从bootm_load_os 回到do_bootm

      ret = bootm_load_os(images.os, &load_end, 1);                               

      if (ret < ) {
if (ret == BOOTM_ERR_RESET)
do_reset (cmdtp, flag, argc, argv);
if (ret == BOOTM_ERR_OVERLAP) {
if (images.legacy_hdr_valid) {
if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)
puts ("WARNING: legacy format multi component "
"image overwritten\n");
} else {
puts ("ERROR: new format image overwritten - "
"must RESET the board to recover\n");
show_boot_progress (-);
do_reset (cmdtp, flag, argc, argv);
}
}
if (ret == BOOTM_ERR_UNIMPLEMENTED) {
if (iflag)
enable_interrupts();
show_boot_progress (-);
return ;
}
}

如果刚才出错,那么检查错误编号,打印错误信息。

      boot_fn = boot_os[images.os.os];                                            

      if (boot_fn == NULL) {
if (iflag)
enable_interrupts();
printf ("ERROR: booting os '%s' (%d) is not supported\n",
genimg_get_os_name(images.os.os), images.os.os);
show_boot_progress (-);
return ;
} arch_preboot_os(); boot_fn(0, argc, argv, &images);

其实这一步调用的是do_bootm_linux

  static boot_os_fn *boot_os[] = {
#ifdef CONFIG_BOOTM_LINUX
[IH_OS_LINUX] = do_bootm_linux,
#endif
#ifdef CONFIG_BOOTM_NETBSD
[IH_OS_NETBSD] = do_bootm_netbsd,
#endif
#ifdef CONFIG_LYNXKDI
[IH_OS_LYNXOS] = do_bootm_lynxkdi,
#endif
#ifdef CONFIG_BOOTM_RTEMS
[IH_OS_RTEMS] = do_bootm_rtems,
#endif
#if defined(CONFIG_BOOTM_OSE)
[IH_OS_OSE] = do_bootm_ose,
#endif
#if defined(CONFIG_CMD_ELF)
[IH_OS_VXWORKS] = do_bootm_vxworks,
[IH_OS_QNX] = do_bootm_qnxelf,
#endif
#ifdef CONFIG_INTEGRITY
[IH_OS_INTEGRITY] = do_bootm_integrity,
#endif
};

do_bootm_linux 函数其实在arch/arm/lib/bootm.c里面

  int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
bd_t *bd = gd->bd;
char *s;
int machid = bd->bi_arch_number;
void (*kernel_entry)(int zero, int arch, uint params); #ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif if ((flag != ) && (flag != BOOTM_STATE_OS_GO))
return ; s = getenv ("machid");
if (s) {
machid = simple_strtoul (s, NULL, );
printf ("Using machid 0x%x from environment\n", machid);
} show_boot_progress (); #ifdef CONFIG_OF_LIBFDT
if (images->ft_len)
return bootm_linux_fdt(machid, images);
#endif kernel_entry = (void (*)(int, int, uint))images->ep; debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong) kernel_entry);

声明一个函数指针。
把镜像入口给kernel_entry 这个函数指针。

 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG)
setup_start_tag (bd);
#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);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (images->rd_start && images->rd_end)
setup_initrd_tag (bd, images->rd_start, images->rd_end);
#endif
setup_end_tag(bd);
#endif

初始化开始标签。
初始化串口。
初始化版本信息。
初始化内存。
初始化命令行。
End.

     announce_and_cleanup();                                                     

     kernel_entry(0, machid, bd->bi_boot_params);
/* does not return */ return ;
}

正式进入内核。

参考:http://blog.csdn.net/g_salamander/article/details/8463854