先讲一下分区的概念:
嵌入式linux:flash上是没有分区表的。只能在源码里写死,我们关心的是地址。
对于分区,uboot的配置在includeconfigs100ask24x0.h里面进行配置的
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader),"
"128k(params),"
"2m(kernel),"
"-(root)"
mtdparts=nandflash0 :表示分区在nandflash0上
256k@0(bootloader) :表示从地址0开始 偏移256k的大小的空间为bootloader
128k(params) :从bootloader末尾地址开始 偏移128k的大小空间为启动参数所用
2m(kernel) :再后面2M的空间为kernel所使用
-(root) :后面的空间全为rootfs使用
可以在uboot命令行中使用mtd命令查看
启动内核需要两个命令:
nand read.jffs2 0x30007FC0 kernel (调用do_nand)
bootm 0x30007FC0 (调用do_bootm)
烧写到flash上的内核是uImage
uImage组成:
一个头部 + 真正的内核
这个头部在uboot中有个结构体可以描述:image_header_t *hdr = &header;
image_header_t 这个结构体的重要成员为ih_load与in_ep。
ih_load:内核的加载地址,表示内核运行时要将它放在那里
in_ep:入口地址,运行内核的时候直接跳到这个地址就可以了
在执行bootm 地址的时候,bootm会去读uImage的头部,从而知道真正内核的加载地址和入口地址,如果它发现真正的内核不在加载地址的话,它就会把内核移到加载地址(in_load)上去,然后跳转到入口地址(in_ep)去执行
在我们使用的开发板真正内核的加载地址是0x30008000(问题:从哪得知的???),而我们将uImage读到了0x30007FC0(这个很奇怪的地址),原因是什么?
使用0x30008000 - 0x30007FC0 = 64字节,这说明头部刚好就是64字节。这样就不用去移动内核了,加快了运行速度。
下面分析下do_bootm:
1.首先会判断下argc是不是小于2(bootm 0x30007FC0 ====> argc == 2)
if (argc < 2) {
addr = load_addr;//不会使用默认的load_addr
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
2.真正内核的地址:
data = addr + sizeof(image_header_t);
先分析一下image_header_t的成员在哪里进行赋值的。在函数fake_header中:
fake_header(hdr, (void*)addr, -1)
fake_zimage_header(hdr, ptr, size)
hdr->ih_magic = htonl(IH_MAGIC);
hdr->ih_time = 0;
hdr->ih_size = htonl(size);
hdr->ih_load = htonl(ZIMAGE_LOAD);
hdr->ih_ep = 0;
hdr->ih_dcrc = htonl(checksum);
hdr->ih_os = IH_OS_LINUX;
hdr->ih_arch = IH_CPU_I386;
hdr->ih_type = IH_TYPE_KERNEL;
hdr->ih_comp = IH_COMP_NONE;
strncpy((char *)hdr->ih_name, "(none)", IH_NMLEN)
hdr->ih_hcrc = htonl(checksum);
在下面的swith语句:
IH_TYPE_KERNEL name = "Kernel Image";
IH_COMP_NONE 如果内核的加载地址和存放的地址一样的话,直接执行printf (" XIP %s ... ", name);如果地址不一样,就会做搬移操作:memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
IH_OS_LINUX do_bootm_linux (cmdtp, flag, argc, argv,addr, len_ptr, verify);//到这里,内核就要启动了
总结一下bootm的作用:
1.根据头部,移动真正的内核到合适的地方
2.启动内核:
重要函数;do_bootm_linux,作用:
1.uboot告诉内核一些参数--->设置启动参数
在某个地址(bd->bi_boot_params:3000100)按某种格式(TAG)保存数据。内核启动后,就会到0x30000100的地址读取这些参数
2.跳到入口地址启动内核
void (*theKernel)(int zero, int arch, uint params);//定义一个函数指针
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);//指定了这个指针的地址为hdr->ih_ep
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//跳到ih_ep的地址执行
下面分析一下do_bootm_linux
1.获取bootargs的参数:
char *commandline = getenv ("bootargs");
2.设置内核启动的入口地址:
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
3.设置TAGS
setup_start_tag (bd); //设置开始tags
params = (struct tag *) bd->bi_boot_params;
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);
setup_memory_tags (bd); //设置内存的大小tags
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);
}
setup_commandline_tag (bd, commandline);//设置bootargs的TAG
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size =(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
setup_end_tag (bd); //设置结束TAGS
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
4.theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
启动内核,并且将机器ID(bd->bi_arch_number)和启动参数(bd->bi_boot_params,存放tags的地址)传到内核中,以让内核知道。
bootcmd的设置:
setenv bootcmd 'nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0'
问题一:为什么setenv 'bootcmd nand read 0x30007FC0 60000 500000;bootm 0x30007FC0'可以启动?而setenv 'bootcmd nand read 0x30008000 60000 500000;bootm 0x30008000'启动不了,不是在内核中会进行搬移操作吗?
如果使用后者会在启动时停在start_kernel不动。求解。。。。