u-boot启动linux内核

时间:2022-01-29 16:44:26

1、

U_BOOT_CMD(
   bootm, CFG_MAXARGS, 1,do_bootm,
  "bootm   - boot application image from memory\n",
  "[addr [arg ...]]\n    - boot application image stored in memory\n"
  "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
  "\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n"
"\ta bd_info struct will be passed instead\n"
#endif
);

根据上面可知:bootm命令的执行函数是do_bootm,函数的最大参数是CFG_MAXARGS。

2、do_bootm函数源码摘要:

SHOW_BOOT_PROGRESS (8);


#if defined(CONFIG_ZIMAGE_BOOT) || defined(CONFIG_IMAGE_BOOT)
after_header_check:
#endif
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
   fixup_silent_linux();
#endif
   do_bootm_linux  (cmdtp, flag, argc, argv,
    addr, len_ptr, verify);
   break;

case IH_OS_NETBSD:
   do_bootm_netbsd (cmdtp, flag, argc, argv,
    addr, len_ptr, verify);
   break;


#ifdef CONFIG_LYNXKDI
case IH_OS_LYNXOS:
   do_bootm_lynxkdi (cmdtp, flag, argc, argv,
    addr, len_ptr, verify);
   break;
#endif


case IH_OS_RTEMS:
   do_bootm_rtems (cmdtp, flag, argc, argv,
    addr, len_ptr, verify);
   break;


#if (CONFIG_COMMANDS & CFG_CMD_ELF)
case IH_OS_VXWORKS:
   do_bootm_vxworks (cmdtp, flag, argc, argv,
     addr, len_ptr, verify);
   break;
case IH_OS_QNX:
   do_bootm_qnxelf (cmdtp, flag, argc, argv,
     addr, len_ptr, verify);
   break;
#endif /* CFG_CMD_ELF */
#ifdef CONFIG_ARTOS
case IH_OS_ARTOS:
   do_bootm_artos  (cmdtp, flag, argc, argv,
    addr, len_ptr, verify);
   break;
#endif
}

可知,do_bootm函数调用do_bootm_linux函数启动Linux内核,当定义了CONFIG_PPC是,将使用common/cmd_bootm.c文件中的do_bootm_linux;当没有定义时,将调用lib_arm/armlinux.c文件中的do_bootm_linux函数。

3、lib_arm/armlinux.c中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);//设置kernal加载地址


/*
* Check if there is an initrd image用户定义了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_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 (&params);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (&params);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS  //设置memory tags
setup_memory_tags (bd);
#endif

#ifdef CONFIG_CMDLINE_TAG  //设置启动命令行tags
setup_commandline_tag (bd, commandline);
#endif

#ifdef CONFIG_INITRD_TAG  //存在ramdisk时设置initrdtag
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);
}

调用内核需要传递的参数如下:

(1)R0:必须为0

(2)R1:机器类型ID,bd->bi_arch_number

(3)R2:启动参数列表在内存中的位置bd->bi_boot_params

4、清理函数

int cleanup_before_linux (void)
{
/*
* this function is called just before we call linux
* it prepares the processor for linux
*
* we turn off caches etc ...
*/


unsigned long i;


disable_interrupts (); //禁止中断


/* turn off I/D-cache */关闭指令和数据cache,读控制寄存器
asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i));


i &= ~(C1_DC | C1_IC);//设置指令和数据cache
asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i));


/* flush I/D-cache */
i = 0;
asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i));//写会控制寄存器


return (0);

}

清理工作,调用内核之前必须满足一下条件:

1、确定CPU模式

2、必须禁止中断IRQ和FIQ

3、CPU必须SVC模式

4、Cache和MMU的设置

5、MMU必须关闭

6、指令Cacge可以打开也可关闭

7、数据Cache必须关闭