您的位置:首页 > 大数据 > 人工智能

学习 uboot 之四主流程main_loop分析

2018-01-16 21:25 435 查看
接着上一篇内容,本篇我们来分析main_loop的主要内容:

main_loop主要功能有两点:

1.没有进入调试模式的话,初始化一些参数,直接跳转到内核入口处执行

2.进入调试模式,执行串口输入的命令

s = getenv ("bootcmd");
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
{
printf("Booting Linux ...\n");
run_command (s, 0);
}

}


上述代码从环境变量中获取bootcmd的命令,如下

#define CONFIG_BOOTCOMMAND  "nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0"


这里我们分析下bootm命令,从cmd_bootm.c中我们能够找到bootm命令对应的函数为do_bootm。

下面重点分析下这个函数。

1.do_bootm分析

1.获取内核版本在RAM中的位置,并取出前64个字节的uImage版本头

if (argc < 2) {
addr = load_addr;
} else {
/*获取uImage版本在内存中的地址*/
addr = simple_strtoul(argv[1], NULL, 16);/*bootm 0x30007FC0,argv[1]代表kernel开始的地址*/
}

SHOW_BOOT_PROGRESS (1);
printf ("## Booting image at %08lx ...\n", addr);

/* Copy header so we can blank CRC field for re-calculation */
memmove (&header, (char *)addr, sizeof(image_header_t));/*直接得到头部*/


2.根据得到的版本头信息做一些校验,主要包括版本魔数,CRC等

if (ntohl(hdr->ih_magic) != IH_MAGIC) { /*版本魔数不对*/
{
puts ("Bad Magic Number\n");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}

data = (ulong)&header;/*内核地址*/
len  = sizeof(image_header_t);/*版本头部*/

checksum = ntohl(hdr->ih_hcrc);/*网络序到主机序--计算版本头部的CRC*/
hdr->ih_hcrc = 0;

/*计算CRC校验码并判断*/
if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksum\n");
SHOW_BOOT_PROGRESS (-2);
return 1;
}

data = addr + sizeof(image_header_t);/*计算正式版本的地址*/
len  = ntohl(hdr->ih_size);          /*得到版本的大小*/

/*计算版本的CRC*/
if (verify) {
puts ("   Verifying Checksum ... ");
if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRC\n");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
puts ("OK\n");
}


3.根据生成内核时采用的压缩方式解压内核

switch (hdr->ih_comp) {
.
......
.
case IH_COMP_GZIP:/*如果是压缩方式GZIP,解压版本*/
printf ("   Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
puts ("GUNZIP ERROR - must RESET board to recover\n");
SHOW_BOOT_PROGRESS (-6);
do_reset (cmdtp, flag, argc, argv);
}
break;


4.下面是跳转到内核入口的处理函数,根据os类型,本篇使用的linux,do_bootm_linux定义在lib_arm目录下的armlinux.c

switch (hdr->ih_os) {
default:            /* handled by (original) Linux case */
case IH_OS_LINUX:
do_bootm_linux  (cmdtp, flag, argc, argv,
addr, len_ptr, verify);/*addr 代表LOAD地址,len_ptr 版本数据正式开始的地方*/


2.do_bootm_linux分析

1.启动参数获取,内核入口函数定义

void (*theKernel)(int zero, int arch, uint params);
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif

/*得到内核的入口地址*/
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);


上面获取bootargs环境变量,定义内核入口函数theKernel,函数的地址在版本头ih_ep中指明,值得注意的是参数为int,int,uint最后一个参数并不是指针类型

2.设置标记列表,内核传递参数

#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_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif

#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif

setup_end_tag (bd);
#endif


目前boot向内核传递参数的常用方法便是标记列表法:

每个标记列表的结构体如下:

struct tag {
struct tag_header hdr;
union {
/*这里定义联合体,分别可以是tag_mem32,cmd_line*/
struct tag_core     core;
struct tag_mem32    mem;
......
struct tag_cmdline  cmdline;
......
} u;
};

struct tag_header {
u32 size; /*整个tag的大小*/
u32 tag;  /*tag的类型*/
};


整个标记列表以ATAG_CORE开始,以ATAG_NONE结束,中间放入我们需要传递的参数

params->hdr.tag = 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);

/**/
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;


这里分析下setup_memory_tags参数,这个参数用于向内核传递RAM的起始地址和大小

for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);

params->u.mem.start = bd->bi_dram[i].start;
params->u.mem.size = bd->bi_dram[i].size;

params = tag_next (params);
}


CONFIG_NR_DRAM_BANKS为DRAM数目,每一个DRAM要单独定义一个TAG

紧接着调用内核入口函数

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);


其中bd->bi_arch_number为ARCH_ID 在board_init中说明。至此uboot完成其任务,将控制权交给内核,退出了“”舞台”。后面几篇我会再补充一些uboot钟用到的关键知识点,完善uboot的体系
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: