学习 uboot 之四主流程main_loop分析
2018-01-16 21:25
435 查看
接着上一篇内容,本篇我们来分析main_loop的主要内容:
main_loop主要功能有两点:
1.没有进入调试模式的话,初始化一些参数,直接跳转到内核入口处执行
2.进入调试模式,执行串口输入的命令
上述代码从环境变量中获取bootcmd的命令,如下
这里我们分析下bootm命令,从cmd_bootm.c中我们能够找到bootm命令对应的函数为do_bootm。
下面重点分析下这个函数。
上面获取bootargs环境变量,定义内核入口函数theKernel,函数的地址在版本头ih_ep中指明,值得注意的是参数为int,int,uint最后一个参数并不是指针类型
目前boot向内核传递参数的常用方法便是标记列表法:
每个标记列表的结构体如下:
整个标记列表以ATAG_CORE开始,以ATAG_NONE结束,中间放入我们需要传递的参数
这里分析下setup_memory_tags参数,这个参数用于向内核传递RAM的起始地址和大小
CONFIG_NR_DRAM_BANKS为DRAM数目,每一个DRAM要单独定义一个TAG
紧接着调用内核入口函数
其中bd->bi_arch_number为ARCH_ID 在board_init中说明。至此uboot完成其任务,将控制权交给内核,退出了“”舞台”。后面几篇我会再补充一些uboot钟用到的关键知识点,完善uboot的体系
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的体系
相关文章推荐
- 十、uboot 代码流程分析---run_main_loop
- UBOOT 学习心得(UBOOT流程分析)
- uboot中main_loop分析
- uboot main_loop函数分析
- uboot中main_loop分析
- [UBOOT]main_loop函数分析!
- uboot main_loop()函数分析
- uboot main_loop()函数分析
- UBOOT 学习心得(UBOOT流程分析)
- uboot main_loop分析
- U-Boot 启动过程和源码分析(第二阶段)-main_loop分析
- 2014.4新版uboot启动流程分析 他的博客需要学习
- uboot main_loop函数分析
- QNX学习笔记-Neutrino-QNX-boot启动流程分析
- 嵌入式学习-uboot-lesson3-6410uboot启动流程分析
- uboot中main_loop分析
- coreboot学习5:启动流程跟踪之ramstage阶段主干分析
- uboot中main_loop分析
- uboot中main_loop分析
- uboot中main_loop分析