uboot启动内核的相关命令详解——boot、bootm

1、boot和bootm命令的联系

当我们进入uboot的命令终端后,可以利用boot和bootm来启动内核,但是命令的使用方式有区别:
直接输入boot命令就可以启动内核,如果使用bootm命令,后面还需要传入内核在DDR中的地址。
(1)boot命令:完成内核的重定位,将内核从外存加载到内存,然后启动内核;
(2)bootm命令:调用时一般要传入内核在内存中的地址,然后去该地址处启动内核;如果不传入内核在内存中的地址,bootm会用默认地址去启动内核,这样不保证能正确启动内核。
(3)bootm命令可以算作是boot命令的子命令,因为boot命令最终也是调用bootm去启动内核
补充:uboot命令体系和环境变量是理解下面源码实现的基础,不清楚的可以看博客:《uboot的命令体系详解》《uboot中环境变量的实现》

2、boot命令实现源码

	#define CONFIG_BOOTCOMMAND	"movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000"
	
	int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
	{
    
    
		int rcode = 0;
		
		//run_command是uboot中执行命令的函数,第一个参数就是要执行的命令
		#ifndef CFG_HUSH_PARSER
		//环境变量bootcmd的值默认就是CONFIG_BOOTCOMMAND
		if (run_command (getenv ("bootcmd"), flag) < 0)
				rcode = 1;
		#else
			if (parse_string_outer (getenv ("bootcmd"),
					FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0)
				rcode = 1;
		#endif
		
		return rcode;
	}

//利用U_BOOT_CMD宏注册命令
	U_BOOT_CMD(
		boot,	1,	1,	do_bootd,
		"boot    - boot default, i.e., run 'bootcmd'\n",
		NULL
	);

boot是启动内核的命令,boot命令的内容保存在环境变量bootcmd中,bootcmd的默认值来自于配置文件的CONFIG_BOOTCOMMAND宏定义。在main_loop函数中如果bootdelay时间内没有按下按键就会自动启动内核,可以把boot命令拆分成几个命令,其中包含重定位内核和启动内核。
(1)movi read kernel 30008000:用movi命令将外存的kernel分区读取到内存的0x30008000;
(2)movi read rootfs 30B00000 300000:用movi命令将外存的rootfs读取到内存的0x30B00000,读取长度0x300000
(3)bootm 30008000 30B00000:bootm是直接启动内核的指令,0x30008000是告诉bootm命令内核此时在内存的0x30008000地址处;

3、bootm命令实现源码

	int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
	{
    
    
		image_header_t	*hdr;
		ulong		addr;
		ulong		iflag;
		const char	*type_name;
		uint		unc_len = CFG_BOOTM_LEN;
		uint8_t		comp, type, os;

		void		*os_hdr;
		ulong		os_data, os_len;
		ulong		image_start, image_end;
		ulong		load_start, load_end;

	//判断内核是否是zImage
	#ifdef CONFIG_ZIMAGE_BOOT
	#define LINUX_ZIMAGE_MAGIC	0x016f2818 //这是一个魔术,用来判断是否是zImage
		/* find out kernel image address */
		if (argc < 2) {
    
    
			addr = load_addr;
			debug ("*  kernel: default image load address = 0x%08lx\n",
					load_addr);
		} else {
    
    
			 //argv[1]就是调用bootm命令时指定的内核所在地址
			addr = simple_strtoul(argv[1], NULL, 16);
			debug ("*  kernel: cmdline image address = 0x%08lx\n", img_addr);
		}
		 //判断镜像头的第37-40字节处是否存放的是LINUX_ZIMAGE_MAGIC
		 //这是zImage类型内核的特征
		if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
    
    
			printf("Boot with zImage\n");
			addr = virt_to_phys(addr);//得到内核的物理地址,因为uboot可能开启了虚拟地址映射
			hdr = (image_header_t *)addr;//得到内核的头信息
			hdr->ih_os = IH_OS_LINUX;//指明该内核是Linux
			hdr->ih_ep = ntohl(addr);//得到内核的入口

			memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));

			/* save pointer to image header */
			images.legacy_hdr_os = hdr;

			images.legacy_hdr_valid = 1; //标志位,如果不为1是用设备树的方式启动内核

			goto after_header_check;
		}
	#endif

	#if defined(CONFIG_ZIMAGE_BOOT)
	after_header_check:
		os = hdr->ih_os;
	#endif
	
		//根据操作类型判断调用不同的内核启动函数
		switch (os) {
    
    
		default:			/* handled by (original) Linux case */
		case IH_OS_LINUX:
	#ifdef CONFIG_SILENT_CONSOLE
		fixup_silent_linux();
	#endif
		do_bootm_linux (cmdtp, flag, argc, argv, &images); //启动linux内核
		break;

	case IH_OS_NETBSD:
		do_bootm_netbsd (cmdtp, flag, argc, argv, &images);
		break;
		}

		show_boot_progress (-9);

		if (iflag)
			enable_interrupts();

		return 1;
	}

摘抄自uboot的do_bootm函数中zImage内核启动部分的代码,为了便于理解内核启动的过程,其余关于uImage和设备树的代码都已经删除了。bootm函数的主要作用就是解析内核的头信息,区分出内核的类型和调用相应内核的启动函数。do_bootm 函数主要功能:
(1)识别出当前的启动方式:zImage、uImage或者设备树;
(2)根据不同的启动方式去初始化images全局变量和内核头信息;
(3)根据操作系统类型,调用不同的内核启动函数;
补充:常见的linux系统启动函数是do_bootm_linux (),细节请看博客:《do_bootm_linux函数解析》

おすすめ

転載: blog.csdn.net/weixin_42031299/article/details/121318451