uboot中启动linux内核的函数——do_bootm_linux函数解析

1、do_bootm_linux函数解析

do_bootm_linux函数是专门启动linux内核的,包括以下功能:
(1)确认当前的机器码,可以从全局变量gd或者环境变量machid中获取,其中环境变量machid的优先级高于gd中的机器码;
(2)计算出内核的入口地址;
(3)准备给内核的传参;不清楚uboot给内核传参的看博客:《uboot中命令体系详解》
(4)协处理器的相关操作,为启动内核初始化环境;
(5)启动内核;

2、启动内核入口的传参

(1)theKernel (0, machid, bd->bi_boot_params);
theKernel 是函数指针,指向内核的入口地址,也就是内核将要执行的第一句代码。其中要传三个参数,第一个参数固定为0,第二个参数是机器码,第三个参数是uboot传给内核参数所在的首地址。
(2)机器码
机器码可以算作是一块板子的id,嵌入式设备是高度定制的,内核也是可以高度裁剪的,所以每个版本的内核只能支持移植过开发板。在内核中维护有个表格,里面记录了支持的开发板的id,也就是机器码,如果启动内核时传入的机器码没有在表格中找到,说明内核不支持该开发板,启动失败。

3、do_bootm_linux函数源码

	#define CONFIG_BOOTARGS    	"console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3"
	
	void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
				 bootm_headers_t *images)
	{
    
    
		ulong	initrd_start, initrd_end;
		ulong	ep = 0;
		bd_t	*bd = gd->bd;
		char	*s;
		int	machid = bd->bi_arch_number; //从全局变量gd中获取机器码
		void	(*theKernel)(int zero, int arch, uint params);
		int	ret;

	#ifdef CONFIG_CMDLINE_TAG
		//对应宏:CONFIG_BOOTARGS,bootargs是启动内核的传的参数
		char *commandline = getenv ("bootargs"); 
	#endif

		/* find kernel entry point */
		if (images->legacy_hdr_valid) {
    
    
			ep = image_get_ep (&images->legacy_hdr_os_copy); //得到内核的入口地址
		} else {
    
    
			puts ("Could not find kernel entry point!\n");
			goto error;
		}
		//theKernel 是函数指针,指向内核的入口地址
		theKernel = (void (*)(int, int, uint))ep;
		
		//从环境变量获取机器码,这里可以看出环境变量里的机器码优先级高于全局变量gd里的机器码
		s = getenv ("machid"); 
		if (s) {
    
    
			machid = simple_strtoul (s, NULL, 16);
			printf ("Using machid 0x%x from environment\n", machid);
		}

		ret = boot_get_ramdisk (argc, argv, images, IH_ARCH_ARM, //虚拟内存盘相关
				&initrd_start, &initrd_end);
		if (ret)
			goto error;

		show_boot_progress (15);

		debug ("## Transferring control to Linux (at address %08lx) ...\n",
			   (ulong) theKernel);
			   
	/*	下面都是在准备给内核的传参     */
	#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
		defined (CONFIG_CMDLINE_TAG) || \
		defined (CONFIG_INITRD_TAG) || \
		defined (CONFIG_SERIAL_TAG) || \
		defined (CONFIG_REVISION_TAG) || \
		defined (CONFIG_LCD) || \
		defined (CONFIG_VFD) || \
		defined (CONFIG_MTDPARTITION)
		setup_start_tag (bd); //传递给内核参数的起始tag结构体
	#ifdef CONFIG_SERIAL_TAG
		setup_serial_tag (&params);
	#endif
	#ifdef CONFIG_REVISION_TAG
		setup_revision_tag (&params);
	#endif
	#ifdef CONFIG_SETUP_MEMORY_TAGS
		setup_memory_tags (bd);
	#endif
	#ifdef CONFIG_CMDLINE_TAG
		setup_commandline_tag (bd, commandline);
	#endif
	#ifdef CONFIG_INITRD_TAG
		if (initrd_start && initrd_end)
			setup_initrd_tag (bd, initrd_start, initrd_end);
	#endif
	#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
		setup_videolfb_tag ((gd_t *) gd);
	#endif

	#ifdef CONFIG_MTDPARTITION
		setup_mtdpartition_tag();
	#endif
		//传递给内核参数的结束tag结构体
		setup_end_tag (bd);
	#endif

		/* we assume that the kernel is in place */
		printf ("\nStarting kernel ...\n\n");

	#ifdef CONFIG_USB_DEVICE
		{
    
    
			extern void udc_disconnect (void);
			udc_disconnect ();
		}
	#endif

		/*启动内核前的设置,主要是协处理器的相关操作,包括关iCache和dCache等*/
		cleanup_before_linux ();
		
		/*这句是真正启动内核的语句,到此uboot的使命就结束了;
			theKernel是内核的入口地址,也就是内核执行的第一句代码;
			第1个参数固定为0,第2个参数是机器码,第3个参数是传递给内存的传参tag的首地址*/
		theKernel (0, machid, bd->bi_boot_params);
		/* does not return */
		return;

	error:
		do_reset (cmdtp, flag, argc, argv);
		return;
	}

おすすめ

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