(学习笔记,有错见谅,欢迎指出)
正文
-
首先已知启动内核在程序中的核心代码为
s = getenv ("bootcmd"); ... printf("Booting Linux ...\n"); run_command (s, 0);
其中,变量s为:
nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0<INTERRUPT>
1.1 读取操作:表示在nand_flash中从kernel分区读取.jffs2到0x30007FC0。
1.2 启动操作:在地址0x30007FC0 启动内核。 -
分区:
就是将nand划分为几个区域,一般如下: bootloader-》params-》kernel-》root
这些分区的划分是在/include/configs/mini2440.h中写死的:#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \ "128k(params)," \ "2m(kernel)," \ "-(root)"
注:@0表示从0地址开始,256k的bootloader分区可能对某些uboot不够用,这里只是举例而已。
将上面的信息换算成十六进制:# name 大小 在nand上的起始地址 0 bootloader 0x00040000 0x00000000 1 params 0x00020000 0x00040000 2 kernel 0x00200000 0x00060000 3 root 0xfda00000 0x00260000 那么上面的nand read.jffs2 0x30007FC0 kernel就等价于: nand read.jffs2 0x30007FC0
0x00060000 0x00200000
注:这里的read.jffs2并不是指定要什么特定的格式,而是用read.jffs2不需要块/页对齐,所以这个kernel的分区大小可以随意定。[引用来源]
-
do_nand
nand read.jffs2功能对应的功能函数为在cmd_nand.c中的int do_nand(),该功能的U_BOOT_CMD宏定义如下:"nand read[.jffs2] - addr off|partition size\n"
-
do_bootm
bootm对应的功能函数为在cmd_bootm.c下的int do_bootm()。
首先来看存储在flahs上的系统文件.uimage。由于:
image=头部+内核
其中头部定义关键比变量如下:typedef struct image_header { ... uint32_t ih_load; /* Data Load Address */ uint32_t ih_ep; /* Entry Point Address */ ... } image_header_t;
即系统文件的加载地址(运行时先放置的地址)和入口(运行内核是跳转的地址)地址。当运行bootm xxx时候,函数会先读取该头部以获取该uImage的加载和入口地址,当发现该uImage目前所处的内存地址不等于它的加载地址时,该函数会将该uImage移动到它的加载地址上,故将加载地址与存储地址一致以节省启动时间。
由于视频对应的内核放置在0x3000 8000处(为什么???感觉好像是固定的),故将加载地址减去头部文件的64字节,正好等于0x3000 7FC0 。 -
do_bootm_linux
在启动内核前要传递相应参数至cpu(设置启动参数),然后再跳到入口地址启动内核。
do_bootm_linux函数的主要功能为:
5.1 设置启动参数
在0x3000100处以tag格式保存好数据,待U-boot启动内核后,内核读取出数据(大概理解)。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);
每一个启动参数对应一个tag结构体,所谓的设置传递参数其实就是初始化这些tag的值。
5.1.1 setup_start_tag(bd);params = (struct tag *) bd->bi_boot_params; //在board.c中有gd->bd->bi_boot_params = 0x3000 0100 params->hdr.tag = ATAG_CORE;//ATAG_CORE=0x5441 0001 params->hdr.size = tag_size (tag_core);//tag_size = tag_header(2)+tag_core(3) params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = tag_next (params);//指向下一个tag
5.1.2 setup_memory_tags(bd);
嵌入式的memory的start与size信息写定即可,无需动态判断。params->hdr.tag = ATAG_MEM;//#define ATAG_MEM 0x54410002 params->hdr.size = tag_size (tag_mem32); params->u.mem.start = bd->bi_dram[i].start;//0x3000 0000 params->u.mem.size = bd->bi_dram[i].size;//64M params = tag_next (params);
5.1.3 setup_commandline_tag (bd, commandline);
commandline就是U-boot中的bootargs功能。char *p; if (!commandline) return; for (p = commandline; *p == ' '; p++); if (*p == '\0') return; params->hdr.tag = ATAG_CMDLINE;//0x544 10009 params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;//头部+命令行字符串长度参数(p) strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); //还会存储字符串的长度和bootargs环境变量
5.1.4 setup_end_tag(bd);
表示tag结束params->hdr.tag = ATAG_NONE; params->hdr.size = 0;
Linux内核启动时就会去读取这些tag参数
5.2 跳到入口地址,启动内核
再分析功能二,如何跳到入口地址并启动,主要功能由theKernel()函数完成:theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);//将入口地址赋给theKernel theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//调用thekernel
thekernel的入口参数有三,后两者的含义是
bi_arch_number:机器码,uboot机器码需要与内核的机器码相同
bi_boot_params:uboot需传递给内核的启动参数所位于的地址总结
U-boot目的:启动内核
- 从flash中读取内核
- 启动内核
2.1 设置启动参数
2.2 跳到入口启动