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上场了

注:这里有很多细节没有介绍,但我对于这种工具性知识的学习一向讲求先整体、后细节。

如果有什么不对的地方,请指正,万分感谢。

posted @ 2019-12-13 19:04  ShijiaYin  阅读(551)  评论(0)    收藏  举报