基于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上场了
注:
这里有很多细节没有介绍,但我对于这种工具性知识的学习一向讲求先整体、后细节。
如果有什么不对的地方,请指正,万分感谢。
原文地址:https://www.cnblogs.com/YinShijia/p/12036877.html