U-Boot之LINUX内核引导
基于samsung的Exynos 4412,U-Boot版本为2010.03
前面我们介绍了u-boot的第一阶段和第二阶段,今天我们来介绍u-boot引导内核,这也是u-boot的最后一个阶段,也就是说,这个阶段过后,u-boot的任务就彻底结束了,Linux内核将接管一切软硬件资源。下面开始我们的分析。
u-boot引导内核分为两个阶段
- 将内核镜像从FLASH加载到内存
- 启动内核
将内核镜像从FLASH加载到内存
,这个目标一般是通过一个叫做do_xxx的函数实现的,xxx是存储介质的名称,比如do_emmc,do_nand等,我们这里使用的是emmc,所以按道理说应该是do_emmc,但是我看了源码,里面并没有按照这种方式来定义,而是用了do_emmcops的名称来替代,do_emmc有别的功能,至于为什么这样,管他呢。do_emmcops这个函数在cmd_mmc.c这个文件中,下面是与读
有关的代码
else if (strcmp(argv[1], "read") == 0) { int dev = simple_strtoul(argv[2], NULL, 10); void *addr = (void *)simple_strtoul(argv[3], NULL, 16); u32 cnt = simple_strtoul(argv[5], NULL, 16); u32 blk = simple_strtoul(argv[4], NULL, 16); u32 n; struct mmc *mmc = find_mmc_device(dev); if (!mmc) return 1; printf("\nMMC read: dev # %d, block # %d, count %d ...", dev, blk, cnt); n = mmc->block_dev.block_read(dev, blk, cnt, addr); /* flush cache after read */ flush_cache((ulong)addr, cnt * 512); /* FIXME */ printf("%d blocks read: %s\n", n, (n==cnt) ? "OK" : "ERROR"); return (n == cnt) ? 0 : 1;
addr是由我们传递的argv[3]解析得到的,mmc->block_dev.block_read把对应的内核镜像从emmc读到addr处,
U_BOOT_CMD( mmc, 6, 1, do_mmcops, "MMC sub system", "read <device num> addr blk# cnt\n" "mmc write <device num> addr blk# cnt\n" "mmc rescan <device num>\n" "mmc erase <boot | user> <device num> <start block> <block count>\n" "mmc list - lists available devices");
除此之外,这个函数还支持其它功能,如写入、擦除、扫描等,通过传入的参数来进行功能选择
启动内核
,在上一步中, 内核镜像已经被加载到了内存中,但这个内核镜像还不一定能用,因为它还没被解压缩,也有可能不在正确的内存地址上,所以,do_bootm的首要工作是将内核解压缩并加载到正确的内存地址上,这些功能主要在bootm_load_os这个函数中完成
ret = bootm_load_os(images.os, &load_end, 1);
当内核准备好之后,我们就可以启动内核了,这个目标主要是通过do_bootm_linux这个函数来完成
/* boot the linux kernel */ int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t * images) { char *bootargs; ulong initrd_start, initrd_end; ulong rd_len; unsigned int data, len, checksum; unsigned int initrd_addr, kernend; void (*kernel) (struct linux_romvec *, void *); struct lmb *lmb = &images->lmb; int ret; if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) return 1; /* Get virtual address of kernel start */ linux_hdr = (void *)images->os.load; /* */ kernel = (void (*)(struct linux_romvec *, void *))images->ep; /* check for a SPARC kernel */ if ((linux_hdr->hdr[0] != 'H') || (linux_hdr->hdr[1] != 'd') || (linux_hdr->hdr[2] != 'r') || (linux_hdr->hdr[3] != 'S')) { puts("Error reading header of SPARC Linux kernel, aborting\n"); goto error; } #ifdef PRINT_KERNEL_HEADER printf("## Found SPARC Linux kernel %d.%d.%d ...\n", linux_hdr->linuxver_major, linux_hdr->linuxver_minor, linux_hdr->linuxver_revision); #endif #ifdef CONFIG_USB_UHCI usb_lowlevel_stop(); #endif /* set basic boot params in kernel header now that it has been * extracted and is writeable. */ /* Calc length of RAM disk, if zero no ramdisk available */ rd_len = images->rd_end - images->rd_start; if (rd_len) { ret = boot_ramdisk_high(lmb, images->rd_start, rd_len, &initrd_start, &initrd_end); if (ret) { puts("### Failed to relocate RAM disk\n"); goto error; } /* Update SPARC kernel header so that Linux knows * what is going on and where to find RAM disk. * * Set INITRD Image address relative to RAM Start */ linux_hdr->hdr_input.ver_0203.sparc_ramdisk_image = initrd_start - CONFIG_SYS_RAM_BASE; linux_hdr->hdr_input.ver_0203.sparc_ramdisk_size = rd_len; /* Clear READ ONLY flag if set to non-zero */ linux_hdr->hdr_input.ver_0203.root_flags = 1; /* Set root device to: Root_RAM0 */ linux_hdr->hdr_input.ver_0203.root_dev = 0x100; linux_hdr->hdr_input.ver_0203.ram_flags = 0; } else { /* NOT using RAMDISK image, overwriting kernel defaults */ linux_hdr->hdr_input.ver_0203.sparc_ramdisk_image = 0; linux_hdr->hdr_input.ver_0203.sparc_ramdisk_size = 0; /* Leave to kernel defaults linux_hdr->hdr_input.ver_0203.root_flags = 1; linux_hdr->hdr_input.ver_0203.root_dev = 0; linux_hdr->hdr_input.ver_0203.ram_flags = 0; */ } /* Copy bootargs from bootargs variable to kernel readable area */ bootargs = getenv("bootargs"); prepare_bootargs(bootargs); /* turn on mmu & setup context table & page table for process 0 (kernel) */ srmmu_init_cpu((unsigned int)kernel); /* Enter SPARC Linux kernel * From now on the only code in u-boot that will be * executed is the PROM code. */ kernel(kernel_arg_promvec, (void *)images->ep); /* It will never come to this... */ while (1) ; error: return 1; }
这个函数主要完成了两个功能:传递参数给Linux内核
、跳转到linux入口地址
到这里,u-boot的任务就彻底完成了,下面就该Linux上场了
注:
这里有很多细节没有介绍,但我对于这种工具性知识的学习一向讲求先整体、后细节。
如果有什么不对的地方,请指正,万分感谢。