main_loop中执行的最重要的操作便是引导内核。引导分两步。第一步先读取内核镜像uImage到内存。然后再使用bootm引导内核。
读取内核只是进行内存复制,因此仅仅需要分析bootm。
/*******************************************************************/
/* bootm - boot application image from image in memory */
/*******************************************************************/
引导应用程序在内存中的镜像。
common/cmd_bootm.c
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong load_end = 0;
int ret;
boot_os_fn *boot_fn;
/* determine if we have a sub command */检查是否有子命令
if (argc > 1) {
char *endp;
simple_strtoul(argv[1], &endp, 16); //调用字符串转无符号整形的函数,endp指向尾字符。
/* endp pointing to NULL means that argv[1] was just a
* valid number, pass it along to the normal bootm processing 以空字符结尾则当作是合法的数,作为正常引导的参数
*
* If endp is ':' or '#' assume a FIT identifier so pass
* along for normal processing.
*
* Right now we assume the first arg should never be '-'
*/
if ((*endp != 0) && (*endp != ':') && (*endp != '#')) //是否有子命令。一般不会有,所以这个路径不会执行。
return do_bootm_subcommand(cmdtp, flag, argc, argv);
}
if (bootm_start(cmdtp, flag, argc, argv)) //bootm_start的分析见下一篇文章。这个函数执行成功返回0,失败返回1。这个函数主要用于获取镜像的信息并存入images。
return 1;
/*
* We have reached the point of no return: we are going to
* overwrite all exception vector code, so we cannot easily
* recover from any failures any more...
*/
iflag = disable_interrupts();//禁用中断。
ret = bootm_load_os(images.os, &load_end, 1);//将镜像的数据从images.os.image_start复制到images.os.load 打印:Loading Kernel Image ... OK
if (ret < 0) {//上个函数调用失败才会进入这个路径
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 (-113);
do_reset (cmdtp, flag, argc, argv);
}
}
if (ret == BOOTM_ERR_UNIMPLEMENTED) {
if (iflag)
enable_interrupts();
show_boot_progress (-7);
return 1;
}
}
lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));//由于没有定义CONFIG_LMB,这个函数调用被定义为一个空的宏。
if (images.os.type == IH_TYPE_STANDALONE) {//处理单独的程序镜像。显然引导内核时不会进入这个路径。
if (iflag)
enable_interrupts();
/* This may return when 'autostart' is 'no' */
bootm_start_standalone(iflag, argc, argv);
return 0;
}
show_boot_progress (8);
boot_fn = boot_os[images.os.os];//根据操作系统的类型获取引导操作系统的函数, boot_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 (-8);
return 1;
}
arch_preboot_os();//禁用中断,重设中断向量
boot_fn(0, argc, argv, &images);//调用do_bootm_linux,分析在下面。
show_boot_progress (-9);
#ifdef DEBUG
puts ("\n## Control returned to monitor - resetting...\n");
#endif
do_reset (cmdtp, flag, argc, argv);
return 1;
}
2。boot_os
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_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
};
3。do_bootm_linux
之前所做的引导工作都是与操作系统无关的,接下来都是针对linux 的。
可以看到,参数flag传过来的是0。
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 (*theKernel)(int zero, int arch, uint params);
#ifdef CONFIG_CMDLINE_TAG//如果定义了这个宏,将会把bootargs传给内核。
char *commandline = getenv ("bootargs");//获取bootargs环境变量。
#endif
if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
return 1;
theKernel = (void (*)(int, int, uint))images->ep;//镜像的入口地址处也就是需要执行的函数入口
s = getenv ("machid");
if (s) {
machid = simple_strtoul (s, NULL, 16);
printf ("Using machid 0x%x from environment\n", machid);
}
show_boot_progress (15);
debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong) theKernel);
//下面将会初始化传给内核的标签(tag),这些标签里包含了必要的环境变量和参数。
#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_SETUP_MEMORY_TAGS
setup_memory_tags (bd);//内存有关的定义
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);//将bootargs传给tag
#endif
#ifdef CONFIG_INITRD_TAG
if (images->rd_start && images->rd_end)//没有ramdisk所以不会执行这个路径
setup_initrd_tag (bd, images->rd_start, images->rd_end);
#endif
setup_end_tag (bd);
#endif
/* we assume that the kernel is in place */
printf ("\nStarting kernel ...\n\n");//引导内核之前最后一次打印信息。
cleanup_before_linux ();//禁用中断和cache.
theKernel (0, machid, bd->bi_boot_params);//跳到内核中执行。<<<<全剧终>>>>
/* does not return */
return 1;
}
4.
arch/arm/include/asm
/* The list must start with an ATAG_CORE node */
#define ATAG_CORE 0x54410001
struct tag_core {
u32 flags; /* bit 0 = read-only */
u32 pagesize;
u32 rootdev;
};
/* command line: \0 terminated string */
#define ATAG_CMDLINE 0x54410009
struct tag_cmdline {//bootargs保存在它的后面
char cmdline[1]; /* this is the minimum size */
};
/* describes where the compressed ramdisk image lives (virtual address) */
/*
* this one accidentally used virtual addresses - as such,
* its depreciated.
*/
#define ATAG_INITRD 0x54410005
/* describes where the compressed ramdisk image lives (physical address) */
#define ATAG_INITRD2 0x54420005
struct tag_initrd {//描述ramdisk的分布
u32 start; /* physical start address */
u32 size; /* size of compressed ramdisk image in bytes */
};
/* it is allowed to have multiple ATAG_MEM nodes */
#define ATAG_MEM 0x54410002
struct tag_mem32 {
u32 size;
u32 start; /* physical start address */
};
struct tag_header {
u32 size;
u32 tag;
};
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
5.
arch/arm/include/asm/setup.h
#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))//跳到下一个tag地址处
arch/arm/lib/bootm.c
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params; //CFG_ENV_ADDR(0x80000100)设置环境变量的头地址。
params->hdr.tag = ATAG_CORE; //第一个tag默认为 tag_core。ATAG_CORE 是默认的开始类型。
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params); //跳到下一个tag地址处
}
#ifdef CONFIG_SETUP_MEMORY_TAGS
static void setup_memory_tags (bd_t *bd)//设置内存分布的tag
{
int i;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {//如果有多个内存芯片,则会循环多次。对3250来说,只有一片。
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = bd->bi_dram[i].start;//内存块的开始地址。PHYS_SDRAM_1;在smartarm3250.h中,起始地址(0x80000000)
params->u.mem.size = bd->bi_dram[i].size;//内存块的大小
params = tag_next (params);//跳到下一个tag地址处
}
}
#endif /* CONFIG_SETUP_MEMORY_TAGS */
static void setup_commandline_tag (bd_t *bd, char *commandline)
{
char *p;
if (!commandline)
return;
/* eat leading white space */
for (p = commandline; *p == ' '; p++);//跳过开头的空格
/* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if (*p == '\0')
return;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size =
(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);//将bootargs复制到cmdline后面。
params = tag_next (params);//跳到下一个tag地址处
}
#ifdef CONFIG_INITRD_TAG
static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end)
{
/* an ATAG_INITRD node tells the kernel where the compressed
* ramdisk can be found. ATAG_RDIMG is a better name, actually.
*/
params->hdr.tag = ATAG_INITRD2;
params->hdr.size = tag_size (tag_initrd);
params->u.initrd.start = initrd_start;
params->u.initrd.size = initrd_end - initrd_start;
params = tag_next (params);
}
#endif /* CONFIG_INITRD_TAG */
static void setup_end_tag (bd_t *bd)
{
params->hdr.tag = ATAG_NONE;//最后一个tag是一个空类型,标志这tag的结束。
params->hdr.size = 0;
}
uboot从bootm跳到内核

最新推荐文章于 2024-08-19 20:19:38 发布
