do_bootm()函数个人注释

本文详细解读了Linux内核中的do_bootm()函数,介绍了其在系统启动过程中的关键作用,包括如何加载和启动内核映像,以及与设备树的关系。通过对函数的逐行注释和流程分析,帮助读者深入理解该函数的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	ulong	iflag;
	ulong	addr;
	ulong	data, len, checksum;
	ulong  *len_ptr;
	uint	unc_len = CFG_BOOTM_LEN;
	int	i, verify;
	char	*name, *s;
	int	(*appl)(int, char *[]);
	image_header_t *hdr = &header;

/*为是否对镜像头做校验做准备,读取uboot的环境变量verify,
如果环境变量verify等于’n’,则局部变量verify赋值成为0;如果环境变量verify为空(即没有
定义环境变量verify)或者环境变量verify不等于’n’,则局部变量verify赋值成为1
*/
	s = getenv ("verify");
	verify = (s && (*s == 'n')) ? 0 : 1;

	if (argc < 2) //获取镜像存放的内存首地址,如果参数个数小于2(即只是输入了bootm),使用缺省加载地址CFG_LOAD_ADDR。
	{
		addr = load_addr;
	}
	 else  //否则使用第二个参数作为加载地址
	{
		addr = simple_strtoul(argv[1], NULL, 16);
	}

	SHOW_BOOT_PROGRESS (1);
	printf ("## Booting image at %08lx ...\n", addr);

	/* Copy header so we can blank CRC field for re-calculation */
#ifdef CONFIG_HAS_DATAFLASH
	if (addr_dataflash(addr)){
		read_dataflash(addr, sizeof(image_header_t), (char *)&header);
	} else
#endif

//从镜像内存首地址读取镜像头部,为下面的分析校验做准备
	memmove (&header, (char *)addr, sizeof(image_header_t));


//判断内核头部编号属性是否为IH_MAGIC,如果不是u-boot镜像识别的内核镜像格式,会输出提示信息”Bad Magic Number”
	if (ntohl(hdr->ih_magic) != IH_MAGIC)
	{
#ifdef __I386__	/* correct image format not implemented yet - fake it */
		if (fake_header(hdr, (void*)addr, -1) != NULL)
		{
			/* to compensate for the addition below */
			addr -= sizeof(image_header_t);
			/* turnof verify,
			 * fake_header() does not fake the data crc
			 */
			verify = 0;
		} 
		else
#endif	/* __I386__ */
	  {
			puts ("Bad Magic Number\n");
			SHOW_BOOT_PROGRESS (-1);
			return 1;
	  }
	}
	SHOW_BOOT_PROGRESS (2);

	data = (ulong)&header;
	len  = sizeof(image_header_t);

//对内核的镜像头部做CRC检验
	checksum = ntohl(hdr->ih_hcrc);
	hdr->ih_hcrc = 0;

	if (crc32 (0, (uchar *)data, len) != checksum) 
	{
		puts ("Bad Header Checksum\n");
		SHOW_BOOT_PROGRESS (-2);
		return 1;
	}
	SHOW_BOOT_PROGRESS (3);

#ifdef CONFIG_HAS_DATAFLASH
	if (addr_dataflash(addr))
	{
		len  = ntohl(hdr->ih_size) + sizeof(image_header_t);
		read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
		addr = CFG_LOAD_ADDR;
	}
#endif


	/* for multi-file images we need the data part, too */
	print_image_hdr ((image_header_t *)addr);

	data = addr + sizeof(image_header_t);
	len  = ntohl(hdr->ih_size);

	if (verify) //对镜像头部的数据做CRC校验
	{
		puts ("   Verifying Checksum ... ");
		if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
			printf ("Bad Data CRC\n");
			SHOW_BOOT_PROGRESS (-3);
			return 1;
		}
		puts ("OK\n");
	}
	SHOW_BOOT_PROGRESS (4);

	len_ptr = (ulong *)data;


//检验CPU的类型
#if defined(__PPC__)
	if (hdr->ih_arch != IH_CPU_PPC)
#elif defined(__ARM__)
	if (hdr->ih_arch != IH_CPU_ARM)
#elif defined(__I386__)
	if (hdr->ih_arch != IH_CPU_I386)
#elif defined(__mips__)
	if (hdr->ih_arch != IH_CPU_MIPS)
#elif defined(__nios__)
	if (hdr->ih_arch != IH_CPU_NIOS)
#elif defined(__M68K__)
	if (hdr->ih_arch != IH_CPU_M68K)
#elif defined(__microblaze__)
	if (hdr->ih_arch != IH_CPU_MICROBLAZE)
#elif defined(__nios2__)
	if (hdr->ih_arch != IH_CPU_NIOS2)
#elif defined(__blackfin__)
	if (hdr->ih_arch != IH_CPU_BLACKFIN)
#elif defined(__avr32__)
	if (hdr->ih_arch != IH_CPU_AVR32)
#else
# error Unknown CPU type
#endif
	{
		printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);
		SHOW_BOOT_PROGRESS (-4);
		return 1;
	}
	SHOW_BOOT_PROGRESS (5);

	switch (hdr->ih_type) {
	case IH_TYPE_STANDALONE:
		name = "Standalone Application";
		/* A second argument overwrites the load address */
		if (argc > 2) {
			hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
		}
		break;
	case IH_TYPE_KERNEL:
		name = "Kernel Image";
		break;
	case IH_TYPE_MULTI:
		name = "Multi-File Image";
		len  = ntohl(len_ptr[0]);
		/* OS kernel is always the first image */
		data += 8; /* kernel_len + terminator */
		for (i=1; len_ptr[i]; ++i)
			data += 4;
		break;
	default: printf ("Wrong Image Type for %s command\n", cmdtp->name);
		SHOW_BOOT_PROGRESS (-5);
		return 1;
	}
	SHOW_BOOT_PROGRESS (6);

	/*
	 * We have reached the point of no return: we are going to
	 * overwrite all exception vector code, so we cannot easily
	 * recover from any failures any more...
	 */

	iflag = disable_interrupts();

#ifdef CONFIG_AMIGAONEG3SE 
	/*
	 * We've possible left the caches enabled during
	 * bios emulation, so turn them off again
	 */
	icache_disable();  
	invalidate_l1_instruction_cache();
	flush_data_cache();
	dcache_disable();
#endif

switch (hdr->ih_comp) //开始判断是否对内核进行了压缩
{
	//首先清楚这几个地址的定义addr = CFG_LOAD_ADDR;  data = addr + sizeof(image_header_t);
	case IH_COMP_NONE:  //如果没有被压缩,只是在内核的前面加了一个头部信息
//在利用mkimage时指定-a选项后面就是指定的内核加载的地址,这个地址会被存放到镜像头结构的ih_load成员变量中,如在smdk2410中ih_load为0x30008000
//如果加载地址等于addr(即:bootm后面接的地址)就不移动内核,可以在这个addr上开始加载,此时的内核入口地址就是addr后的64字节,即data
		if(ntohl(hdr->ih_load) == addr)
		{ 
			printf ("   XIP %s ... ", name);
		}
/*
ih_load不等于addr,此时:data=addr+64还是指向真正的内核部分,这时候就要将内核拷贝到ih_load指定的地方才能执行,这种情况ih_load==ih_ep加载地址等于内核的入口地址
*/
		else 
		{
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
			size_t l = len;
			void *to = (void *)ntohl(hdr->ih_load);
			void *from = (void *)data;

			printf ("   Loading %s ... ", name);

			while (l > 0) {
				size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
				WATCHDOG_RESET();
				memmove (to, from, tail);
				to += tail;
				from += tail;
				l -= tail;
			}
#else	/* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
			memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
			//内核移动函数:将data地址处的真正内核复制到指定的ih_load处
			
#endif	/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
		}
		break;
		
	case IH_COMP_GZIP:  //内核文件有压缩成gzip格式情况:需要解压缩
		printf ("   Uncompressing %s ... ", name);
		if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,(uchar *)data, &len) != 0)
		{
			puts ("GUNZIP ERROR - must RESET board to recover\n");
			SHOW_BOOT_PROGRESS (-6);
			do_reset (cmdtp, flag, argc, argv);
		}
		break;
		
#ifdef CONFIG_BZIP2//内核文件有压缩成bzip2格式情况:需要解压缩
	case IH_COMP_BZIP2:
		printf ("   Uncompressing %s ... ", name);
		/*
		 * If we've got less than 4 MB of malloc() space,
		 * use slower decompression algorithm which requires
		 * at most 2300 KB of memory.
		 */
		i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
						&unc_len, (char *)data, len,
						CFG_MALLOC_LEN < (4096 * 1024), 0);
		if (i != BZ_OK) {
			printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);
			SHOW_BOOT_PROGRESS (-6);
			udelay(100000);
			do_reset (cmdtp, flag, argc, argv);
		}
		break;
		
#endif /* CONFIG_BZIP2 */
	default:
		if (iflag)
			enable_interrupts();
		printf ("Unimplemented compression type %d\n", hdr->ih_comp);
		SHOW_BOOT_PROGRESS (-7);
		return 1;
}
	puts ("OK\n");
	SHOW_BOOT_PROGRESS (7);

	switch (hdr->ih_type) {
	case IH_TYPE_STANDALONE:
		if (iflag)
			enable_interrupts();

		/* load (and uncompress), but don't start if "autostart"
		 * is set to "no"
		 */
		if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
			char buf[32];
			sprintf(buf, "%lX", len);
			setenv("filesize", buf);
			return 0;
		}
		appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
		(*appl)(argc-1, &argv[1]);
		return 0;
	case IH_TYPE_KERNEL:
	case IH_TYPE_MULTI:
		/* handled below */
		break;
	default:
		if (iflag)
			enable_interrupts();
		printf ("Can't boot image type %d\n", hdr->ih_type);
		SHOW_BOOT_PROGRESS (-8);
		return 1;
	}
	SHOW_BOOT_PROGRESS (8);

switch (hdr->ih_os) //确定内核的操作系统
{
	default:			/* handled by (original) Linux case */
	case IH_OS_LINUX: //选定为Linux操作系统
#ifdef CONFIG_SILENT_CONSOLE
	    fixup_silent_linux();
#endif

		//调用do_bootm_linux函数启动内核,接着分析armlinux.c文件下的这个函数,从这里传入的参数有:
	    do_bootm_linux  (cmdtp, flag, argc, argv,
			     addr, len_ptr, verify);
	    break;
	case IH_OS_NETBSD:
	    do_bootm_netbsd (cmdtp, flag, argc, argv,
			     addr, len_ptr, verify);
	    break;

#ifdef CONFIG_LYNXKDI
	case IH_OS_LYNXOS:
	    do_bootm_lynxkdi (cmdtp, flag, argc, argv,
			     addr, len_ptr, verify);
	    break;
#endif

	case IH_OS_RTEMS:
	    do_bootm_rtems (cmdtp, flag, argc, argv,
			     addr, len_ptr, verify);
	    break;

#if (CONFIG_COMMANDS & CFG_CMD_ELF)
	case IH_OS_VXWORKS:
	    do_bootm_vxworks (cmdtp, flag, argc, argv,
			      addr, len_ptr, verify);
	    break;
	case IH_OS_QNX:
	    do_bootm_qnxelf (cmdtp, flag, argc, argv,
			      addr, len_ptr, verify);
	    break;
#endif /* CFG_CMD_ELF */
#ifdef CONFIG_ARTOS
	case IH_OS_ARTOS:
	    do_bootm_artos  (cmdtp, flag, argc, argv,
			     addr, len_ptr, verify);
	    break;
#endif
}

	SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG
	puts ("\n## Control returned to monitor - resetting...\n");
	do_reset (cmdtp, flag, argc, argv);
#endif
	return 1;
}


<think>好的,用户想分析u-boot中的do_bootm_subcommand函数的功能和实现。首先,我需要回忆一下u-boot的启动流程,特别是bootm相关的部分。用户提供的引用里提到了do_bootmdo_bootm_states、bootz_start等函数,还有涉及到的启动状态和操作系统类型处理。不过用户的问题是关于do_bootm_subcommand的,这个函数在之前的引用里没有直接提到,可能需要结合u-boot的源码结构来分析。首先,根据常见的u-boot代码结构,bootm命令可能有子命令,比如bootminit、bootmstart等。do_bootm_subcommand可能是处理这些子命令的函数。需要确定它的作用,比如解析子命令参数,执行对应的操作步骤。接下来,功能方面,这个函数可能负责解析子命令,例如检查用户输入的是哪个子命令(如init、go等),然后调用相应的处理函数。根据引用[2]中的描述,bootm_states有不同的阶段,如BOOTM_STATE_START、BOOTM_STATE_OS_PREP等,子命令可能对应不同的状态处理。实现分析部分,可能需要考虑函数如何解析参数,如何根据子命令选择执行路径。比如,使用U_BOOT_CMD宏定义的命令,其处理函数通常会解析参数,然后调用对应的子函数。引用[4]中的U_BOOT_CMD宏用于注册bootz命令,处理函数do_bootz,类似的,do_bootm_subcommand可能也是这样的结构。另外,引用[3]提到do_bootm函数中重定位了boot_os数组,这可能与子命令处理有关,比如不同的子命令对应不同的操作系统启动方式。do_bootm_subcommand可能负责根据子命令参数选择对应的启动函数,如do_bootm_linux。可能还需要考虑子命令如何影响启动流程,比如初始化内存、加载镜像、准备参数等步骤。用户提到的do_bootm_states函数处理不同状态,可能子命令会触发不同的状态转换。需要确保回答的结构清晰,分功能和实现两部分,并结合引用中的信息。例如,引用[1]说明do_bootmbootm的处理函数,而子命令可能在该函数中被分解处理。引用[2]提到根据操作系统类型选择启动函数,这可能与子命令的参数解析有关。最后,生成相关问题时要围绕函数功能、实现细节、在u-boot中的作用以及相关函数的关系展开,帮助用户深入理解。</think>### do_bootm_subcommand函数分析与实现 #### 一、功能定位 `do_bootm_subcommand`是U-Boot中处理`bootm`命令子功能的核心函数,主要用于**解析和执行`bootm`的子命令参数**(如`init`、`start`、`loados`等),并协调内核启动流程中的关键步骤[^1][^2]。 #### 二、实现分析 1. **参数解析** 函数通过`argc`和`argv`接收命令行参数,识别子命令类型(例如`bootm initramfs@addr`中的`initramfs`),并匹配对应的处理逻辑[^3]。 2. **子命令路由** 根据子命令类型调用不同的处理模块: - `init`:初始化内存布局和镜像信息(如解析设备树、加载内核镜像) - `loados`:解压内核并准备运行环境 - `go`:跳转到内核入口地址 示例代码片段: ```c switch (subcmd) { case BOOTM_SUBCMD_INIT: bootm_init(...); break; case BOOTM_SUBCMD_LOADOS: bootm_load_os(...); break; } ``` 3. **状态机驱动** 与`do_bootm_states`协同工作,通过状态标志(如`BOOTM_STATE_OS_PREP`)控制启动阶段[^2]。例如: ```c states = BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_LOADOS; boot_fn = do_bootm_states(...); ``` 4. **多架构支持** 通过函数指针表(如`boot_os[]`)支持不同操作系统类型(Linux、VxWorks等),根据`images->os.os`选择对应的启动函数[^1][^2]。 #### 三、关键代码特征 - **重定位处理**:在U-Boot重定位后,通过`gd->reloc_off`调整函数指针地址[^3] - **错误回滚**:对每个子命令执行结果校验,失败时调用`bootm_os_cleanup`清理资源 - **日志追踪**:通过`debug()`宏输出详细启动日志(需开启`CONFIG_DEBUG_BOOTM`) #### 四、典型调用流程 ``` do_bootm() └─ do_bootm_subcommand("init") └─ bootm_init() └─ bootm_load_os() # 加载内核镜像 └─ do_bootm_subcommand("go") └─ boot_fn = do_bootm_linux() # 执行Linux启动函数 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值