uboot终极目标:启动内核
2014-09-01 20:25
239 查看
uboot总的启动linux内核的流程:启动文件start.S基本软硬件环境初始化好之后,调用arm_lib/board.c下的startarm_boot()函数,这个函数最终调用main_loop()函数,内核的调用肯定就在这里面完成了。
因此,首先分析common/main.c:的main_loop函数:
第一条:
s = getenv ("bootcmd"); //获取启动环境变量(包含两条命令)
getenv()返回的是指针,该指针指向字符串"bootcmd"指向的内容
bootcmd定义:environment.c和env_common.c都有:(数据结构--结构体定义)
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND"\0"
而#define CONFIG_BOOTCOMMAND "nboot 0x32000000 kernel; bootm 0x32000000"在tq2440.h里面定义
bootcmd=nboot 0x32000000 kernel; bootm 0x32000000 这里面一共有两条命令,他们的作用:
(1)nboot:从nand的kernel分区读出内核放到内存地址0x32000000 (2)bootm:再从内存地址0x32000000启动内核
第二条:
run_command(s,0); //运行上面得到的命令
1、从nand读出内核到SDRAM(uImage=头部+真正的内核)
--具体从哪里读? --从kernel分区(nand里边地址0x002000000处开始读,读0x00200000大小2M)
--读到哪里去? --内存的某个合适的位置,这个根据上面传入的地址参数
关于nand里边各个分区的定义在include\configs\tq2440.h头文件里面:
nboot命令:分析common/cmd_nand.c的do_nandboot()函数和nboot命令对应的结构体即可
2、bootm启动内核:该命令调用do_bootm()函数
1)先读内核的头部,从头部看要加载内核地址和(bootm+传入的内核地址)是否相同,如果不相同,要把真正的内核移到ih_load这个加载地址去,最后跳到ih_ep入口地址
内核头部结构体在image.h里面定义,主要关心:加载地址ih_load和入口地址ih_ep
首先bootm命令调用do_bootm函数:判断bootm xxxx 这个加载地址xxxx是否与指定的加载地址是否相同
如果不同,从这个地址开始提取出这个64byte的头部并对其进行校验分析,然后把去掉头部的内核复制到-a指定的ih_load加载地址中去运行,此时,ih_ep=ih_load
如果相同,让其原封不同的放在那,此时,-e指定的入口地址应该推后64byte,以跳过这64byte的头部,此时,ih_ep != ih_load
2)真正启动内核:(两个任务)
主要分析lib_arm/armlinux.c的do_bootm_linux()函数
(1)设置启动参数:armlinux.c里面定义参数的Tags函数,并在do_bootm_linux()函数调用完成
(2)跳到入口地址真正启动内核:do_bootm()函数调用do_bootm_linux函数:
//定义theKernel是一个c函数的指针
void (*theKernel)(int zero, int arch, uint params);
//给theKernel赋值指向entry point
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
//内核入口函数的调用,脱离u-boot,进入Linux内核中运行
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
到这里uboot启动内核的使命就完成了!
因此,首先分析common/main.c:的main_loop函数:
main_loop() { char *s; int bootdelay; embedsky_lcd_Init(); extern int mtdparts_init(void); if (!getenv("mtdparts")) { run_command("mtdparts default", 0); } else { mtdparts_init(); } s = getenv ("bootcmd"); if (bootdelay >= 0 && s && !abortboot (bootdelay)) { if (bBootFrmNORFlash()) //如果从Nor启动,即进入u-boot菜单下载模式,可以去分析bBootFrmNORFlash()这个函数 { run_command("menu", 0); } /* * Main Loop for Monitor Command Processing */ else //从nand启动:wince和linux的选择 { #ifdef CONFIG_SURPORT_WINCE //未定义 if (!TOC_Read())//wince匹配 { /* Launch wince */ char cmd_buf[16]; printf("Booting wince ...\n"); strcpy(cmd_buf, "wince"); run_command(cmd_buf, 0); } else//最终匹配加载Linux内核 #endif { printf("Booting Linux ...\n"); //boot_zImage(0x200000,0x200000); //启动内核 run_command (s, 0); } } } //run_command("menu", 0); /*进入menu模式后的:获取命令以及操作*/ for (;;) { len = readline (CFG_PROMPT); //获取命令的长度 flag = 0; /* assume no special flags for now */ if (len > 0) strcpy (lastcommand, console_buffer);//保存输入的命令数据 else if (len == 0) flag |= CMD_FLAG_REPEAT; //如果输入数据0,那么重复上次执行的命令,即只敲入回车键的情况 return; /* retry autoboot */ if (len == -1) //异常中断处理:遇到^C puts ("<INTERRUPT>\n"); else rc = run_command (lastcommand, flag);//执行命令并保存命令匹配情况 if (rc <= 0) {// 输入的命令不匹配时,清空记录 /* invalid command or not repeatable, forget it */ lastcommand[0] = 0; } } }uboot启动内核主要从main_loop函数里面的两条语句(如下)展开分析:
第一条:
s = getenv ("bootcmd"); //获取启动环境变量(包含两条命令)
getenv()返回的是指针,该指针指向字符串"bootcmd"指向的内容
bootcmd定义:environment.c和env_common.c都有:(数据结构--结构体定义)
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND"\0"
而#define CONFIG_BOOTCOMMAND "nboot 0x32000000 kernel; bootm 0x32000000"在tq2440.h里面定义
bootcmd=nboot 0x32000000 kernel; bootm 0x32000000 这里面一共有两条命令,他们的作用:
(1)nboot:从nand的kernel分区读出内核放到内存地址0x32000000 (2)bootm:再从内存地址0x32000000启动内核
第二条:
run_command(s,0); //运行上面得到的命令
1、从nand读出内核到SDRAM(uImage=头部+真正的内核)
--具体从哪里读? --从kernel分区(nand里边地址0x002000000处开始读,读0x00200000大小2M)
--读到哪里去? --内存的某个合适的位置,这个根据上面传入的地址参数
关于nand里边各个分区的定义在include\configs\tq2440.h头文件里面:
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bios)," \ "128k(params)," \ "128k(toc)," \ "512k(eboot)," \ "1024k(logo)," \ "2m(kernel)," \ "-(root)" //@0:起始地址为:0,接着下面的是大小,MTD:内存技术设备具体的nand读操作:
nboot命令:分析common/cmd_nand.c的do_nandboot()函数和nboot命令对应的结构体即可
2、bootm启动内核:该命令调用do_bootm()函数
1)先读内核的头部,从头部看要加载内核地址和(bootm+传入的内核地址)是否相同,如果不相同,要把真正的内核移到ih_load这个加载地址去,最后跳到ih_ep入口地址
内核头部结构体在image.h里面定义,主要关心:加载地址ih_load和入口地址ih_ep
typedef struct image_header { uint32_t ih_magic; /* Image Header Magic Number */ uint32_t ih_hcrc; /* Image Header CRC Checksum */ uint32_t ih_time; /* Image Creation Timestamp */ uint32_t ih_size; /* Image Data Size */ uint32_t ih_load; /* Data Load Address */ uint32_t ih_ep; /* Entry Point Address */ uint32_t ih_dcrc; /* Image Data CRC Checksum */ uint8_t ih_os; /* Operating System */ uint8_t ih_arch; /* CPU architecture */ uint8_t ih_type; /* Image Type */ uint8_t ih_comp; /* Compression Type */ uint8_t ih_name[IH_NMLEN]; /* Image Name */ } image_header_t; //一共64字节这个结构体里面的每个变量到底从哪里得到值?由内核制作工具tools/mkimage.c文件里面给予值
image_header_t header; image_header_t *hdr = &header; /* Build new header */ hdr->ih_magic = htonl(IH_MAGIC); hdr->ih_time = htonl(sbuf.st_mtime); hdr->ih_size = htonl(sbuf.st_size - sizeof(image_header_t)); hdr->ih_load = htonl(addr); hdr->ih_ep = htonl(ep); hdr->ih_dcrc = htonl(checksum); hdr->ih_os = opt_os; hdr->ih_arch = opt_arch; hdr->ih_type = opt_type; hdr->ih_comp = opt_comp;根据这个内核的头部判断是否需要将内核移到合适位置,下面是个人的小结。具体的分析看下篇博文:do_bootm()函数分析。
首先bootm命令调用do_bootm函数:判断bootm xxxx 这个加载地址xxxx是否与指定的加载地址是否相同
如果不同,从这个地址开始提取出这个64byte的头部并对其进行校验分析,然后把去掉头部的内核复制到-a指定的ih_load加载地址中去运行,此时,ih_ep=ih_load
如果相同,让其原封不同的放在那,此时,-e指定的入口地址应该推后64byte,以跳过这64byte的头部,此时,ih_ep != ih_load
2)真正启动内核:(两个任务)
主要分析lib_arm/armlinux.c的do_bootm_linux()函数
(1)设置启动参数:armlinux.c里面定义参数的Tags函数,并在do_bootm_linux()函数调用完成
(2)跳到入口地址真正启动内核:do_bootm()函数调用do_bootm_linux函数:
//定义theKernel是一个c函数的指针
void (*theKernel)(int zero, int arch, uint params);
//给theKernel赋值指向entry point
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
//内核入口函数的调用,脱离u-boot,进入Linux内核中运行
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
到这里uboot启动内核的使命就完成了!
![](http://static.blog.csdn.net/xheditor/xheditor_emot/default/smile.gif)
相关文章推荐
- 分析uboot是如何启动内核的
- Uboot的作用以及启动内核的过程
- uboot启动内核的流程分析
- uboot启动流程及内核参数传递
- uboot源码——内核启动分析
- 简要分析Uboot是如何启动内核! 分类: uboot深入浅出
- uboot命令及内核启动参数
- 简要分析Uboot是如何启动内核!
- 分析uboot是如何启动内核的
- uboot启动参数设置和内核引导方法总结
- uboot如何启动内核?
- uboot - 启动内核过程分析
- uboot如何启动内核
- uboot源码阅读(五)大佬的训导 uboot引导内核启动
- 【Linux】【cmdline】uboot传递给内核的root设备序号动态变化导致启动失败
- 简要分析Uboot是如何启动内核
- uboot源码阅读(六)大佬的命令 uboot引导内核启动
- UBOOT启动内核过程
- uboot和内核启动
- uboot启动内核的实现