从零开始之十、移植uboot2017.01(十、bootm原理分析)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/81990070

通常我们启动内核是使用bootm命令,所以这里就分析一下内核的启动流程。

基础知识

vmlinux、Image、uImage 和zImage

kernel镜像格式:vmlinux
   vmlinuz是可引导的、可压缩的内核镜像,vm代表Virtual Memory.Linux支持虚拟内存,因此得名vmlinux它是由用户对内核源码编译得到,实质是elf格式的文件.也就是说,vmlinux是编译出来的最原始的内核文件,未压缩.这种格式的镜像文件多存放在PC机上

kernel镜像格式:Image
   Image是经过objcopy处理的只包含二进制数据的内核代码,它已经不是elf格式了,但这种格式的内核镜像还没有经过压缩.

kernel镜像格式:zImage
   zImage是ARM linux常用的一种压缩镜像文件,它是由vmlinux加上解压代码经gzip压缩而成,命令格式是#make zImage.这种格式的Linux镜像文件多存放在flash上.

kernel镜像格式:uImage
    uImage是uboot专用的镜像文件,它是在zImage之前加上一个长度为0x40的头信息,在头信息内说明了该镜像文件的类型、加载 位置、生成时间、大小等信息.换句话说,若直接从uImage的0x40位置开始执行,则zImage和uImage没有任何区别.命令格式是#make  uImage.这种格式的Linux镜像文件多存放在flash上.

kernel镜像格式:xipImage
   这种格式的Linux镜像文件多存放在NorFlash上,且运行时不需要拷贝到内存SDRAM中,可以直接在NorFlash中运行.

可以让我们启动使用的内核有Image、zImage、uImage、xipImage 因为我们没norflash,所以我们只能使用前三种。

我们前面一直移植的是uboot,所以我们就加载uImage启动内核。

下面是image头的格式

typedef struct image_header {
	__be32		ih_magic;	/* Image Header Magic Number,魔数	*/
	__be32		ih_hcrc;	/* Image Header CRC Checksum,整个64字节头的crc校验码	*/
	__be32		ih_time;	/* Image Creation Timestamp,uImage的时间	*/
	__be32		ih_size;	/* Image Data Size,zImage的字节数		*/
	__be32		ih_load;	/* Data	 Load  Address,uImage要加载的地址		*/
	__be32		ih_ep;		/* Entry Point Address,zImage的入口位置 = lode + 64		*/
	__be32		ih_dcrc;	/* Image Data CRC Checksum,整个zImage的crc校验码	*/
	uint8_t		ih_os;		/* Operating System,操作系统代码		*/
	uint8_t		ih_arch;	/* CPU architecture,芯片类型		*/
	uint8_t		ih_type;	/* Image Type, 镜像类型			*/
	uint8_t		ih_comp;	/* Compression Type,压缩类型		*/
	uint8_t		ih_name[IH_NMLEN];	/* Image Name,32字节的名字		*/
} image_header_t;

而bootm的作用就是根据uImage头的64个字节数据信息,来启动linux内核。

bootm命令的定义

U_BOOT_CMD(
	bootm,	CONFIG_SYS_MAXARGS,	1,	do_bootm,
	"boot application image from memory", bootm_help_text
);

通常使用是bootm xxxxxxxx  或 bootm 

bootm的实现


/*******************************************************************/
/* bootm - boot application image from image in memory */
/*******************************************************************/

int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC        /* 没定义 */
	static int relocated = 0;

	if (!relocated) {
		int i;

		/* relocate names of sub-command table */
		for (i = 0; i < ARRAY_SIZE(cmd_bootm_sub); i++)
			cmd_bootm_sub[i].name += gd->reloc_off;

		relocated = 1;
	}
#endif

	/* determine if we have a sub command */
	argc--; argv++;
	if (argc > 0) {        /* 假设我们传了一个参数"30008000" */
		char *endp;

		simple_strtoul(argv[0], &endp, 16);      /* 为数字,endp = NULL */
		/* endp pointing to NULL means that argv[0] was just a
		 * valid number, pass it along to the normal bootm processing
		 *
		 * If endp is ':' or '#' assume a FIT identifier so pass
		 * along for normal processing.
		 *
		 * Right now we assume the first arg should never be '-'
		 */
		if ((*endp != 0) && (*endp != ':') && (*endp != '#'))    /* 这里进不去 */
			return do_bootm_subcommand(cmdtp, flag, argc, argv);
	}

	return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
		BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
		BOOTM_STATE_LOADOS |
#if defined(CONFIG_PPC) || defined(CONFIG_MIPS)    /* 这两个都没定义 */
		BOOTM_STATE_OS_CMDLINE |
#endif
		BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
		BOOTM_STATE_OS_GO, &images, 1);        //images是一个比较重要的全局变量
}

这里记录一下BOOTM的状态,方便后面查找

下面列出bootm_headers的内容,等会一个一个填充

/*
 * Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()
 * routines.
 */
typedef struct bootm_headers {
	/*
	 * Legacy os image header, if it is a multi component image
	 * then boot_get_ramdisk() and get_fdt() will attempt to get
	 * data from second and third component accordingly.
	 */
	image_header_t	*legacy_hdr_os;		/* image header pointer */
	image_header_t	legacy_hdr_os_copy;	/* header copy */
	ulong		legacy_hdr_valid;

#if IMAGE_ENABLE_FIT        /* 没定义 */
	const char	*fit_uname_cfg;	/* configuration node unit name */

	void		*fit_hdr_os;	/* os FIT image header */
	const char	*fit_uname_os;	/* os subimage node unit name */
	int		fit_noffset_os;	/* os subimage node offset */

	void		*fit_hdr_rd;	/* init ramdisk FIT image header */
	const char	*fit_uname_rd;	/* init ramdisk subimage node unit name */
	int		fit_noffset_rd;	/* init ramdisk subimage node offset */

	void		*fit_hdr_fdt;	/* FDT blob FIT image header */
	const char	*fit_uname_fdt;	/* FDT blob subimage node unit name */
	int		fit_noffset_fdt;/* FDT blob subimage node offset */

	void		*fit_hdr_setup;	/* x86 setup FIT image header */
	const char	*fit_uname_setup; /* x86 setup subimage node name */
	int		fit_noffset_setup;/* x86 setup subimage node offset */
#endif

#ifndef USE_HOSTCC
	image_info_t	os;		/* os image info */
	ulong		ep;		/* entry point of OS */

	ulong		rd_start, rd_end;/* ramdisk start/end */

	char		*ft_addr;	/* flat dev tree address */
	ulong		ft_len;		/* length of flat device tree */

	ulong		initrd_start;
	ulong		initrd_end;
	ulong		cmdline_start;
	ulong		cmdline_end;
	bd_t		*kbd;
#endif

	int		verify;		/* getenv("verify")[0] != 'n' */

#define	BOOTM_STATE_START	(0x00000001)
#define	BOOTM_STATE_FINDOS	(0x00000002)
#define	BOOTM_STATE_FINDOTHER	(0x00000004)
#define	BOOTM_STATE_LOADOS	(0x00000008)
#define	BOOTM_STATE_RAMDISK	(0x00000010)
#define	BOOTM_STATE_FDT		(0x00000020)
#define	BOOTM_STATE_OS_CMDLINE	(0x00000040)
#define	BOOTM_STATE_OS_BD_T	(0x00000080)
#define	BOOTM_STATE_OS_PREP	(0x00000100)
#define	BOOTM_STATE_OS_FAKE_GO	(0x00000200)	/* 'Almost' run the OS */
#define	BOOTM_STATE_OS_GO	(0x00000400)
	int		state;

#ifdef CONFIG_LMB             /* 没定义 */
	struct lmb	lmb;		/* for memory mgmt */
#endif
} bootm_headers_t;

1.do_bootm_states函数

int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
		    int states, bootm_headers_t *images, int boot_progress)
{
	boot_os_fn *boot_fn;
	ulong iflag = 0;
	int ret = 0, need_boot_fn;

	images->state |= states;        /* images->state之前没被赋值过,就是上面传下来的几个 */

	/*
	 * Work through the states and see how far we get. We stop on
	 * any error.
	 */
	if (states & BOOTM_STATE_START)        /* 被定义,要执行 */
		ret = bootm_start(cmdtp, flag, argc, argv);    /* 得到verify值,见1.1小节分析 */

	if (!ret && (states & BOOTM_STATE_FINDOS))    /* 要执行 ,见1.2小节分析* /
		ret = bootm_find_os(cmdtp, flag, argc, argv);

	if (!ret && (states & BOOTM_STATE_FINDOTHER))    /* 要执行,里面没什么实际操作 */
		ret = bootm_find_other(cmdtp, flag, argc, argv);

	/* Load the OS */
	if (!ret && (states & BOOTM_STATE_LOADOS)) {
		ulong load_end;

		iflag = bootm_disable_interrupts();            /* 关中断 */
		ret = bootm_load_os(images, &load_end, 0);     /* 加载os,见1.3分析 */
		if (ret == 0)
			lmb_reserve(&images->lmb, images->os.load,
				    (load_end - images->os.load));    /* 空函数 */
		else if (ret && ret != BOOTM_ERR_OVERLAP)
			goto err;
		else if (ret == BOOTM_ERR_OVERLAP)
			ret = 0;
	}

	/* Relocate the ramdisk */
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH        /* 没定义 */
	if (!ret && (states & BOOTM_STATE_RAMDISK)) {
		ulong rd_len = images->rd_end - images->rd_start;

		ret = boot_ramdisk_high(&images->lmb, images->rd_start,
			rd_len, &images->initrd_start, &images->initrd_end);
		if (!ret) {
			setenv_hex("initrd_start", images->initrd_start);
			setenv_hex("initrd_end", images->initrd_end);
		}
	}
#endif
#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB)   /* 没定义 */
	if (!ret && (states & BOOTM_STATE_FDT)) {
		boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
		ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,
					&images->ft_len);
	}
#endif

	/* From now on, we need the OS boot function */
	if (ret)
		return ret;
	boot_fn = bootm_os_get_boot_func(images->os.os);        /* 得到我们要运行的os的函数,见1.4分析 */
	need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
			BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
			BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);    /* 我们后三个os相关的都有 */
	if (boot_fn == NULL && need_boot_fn) {
		if (iflag)
			enable_interrupts();
		printf("ERROR: booting os '%s' (%d) is not supported\n",
		       genimg_get_os_name(images->os.os), images->os.os);
		bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
		return 1;
	}


	/* Call various other states that are not generally used */
	if (!ret && (states & BOOTM_STATE_OS_CMDLINE))        /* 没有,不执行 */
		ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
	if (!ret && (states & BOOTM_STATE_OS_BD_T))            /* 没有,不执行 */
		ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
	if (!ret && (states & BOOTM_STATE_OS_PREP)) {        /* 有 */
#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY)    /* 没定义 */
		if (images->os.os == IH_OS_LINUX)
			fixup_silent_linux();
#endif
		ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); /* 这里要执行了,见1.5分析,注意我们这里只传了一个BOOTM_STATE_OS_PREP ,1.5分析后只执行了boot_prep_linux后又回到这里向下继续执行*/
	}

#ifdef CONFIG_TRACE            /* 没定义 */
	/* Pretend to run the OS, then run a user command */
	if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {
		char *cmd_list = getenv("fakegocmd");

		ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
				images, boot_fn);
		if (!ret && cmd_list)
			ret = run_command_list(cmd_list, -1, flag);
	}
#endif

	/* Check for unsupported subcommand. */
	if (ret) {        /* 上面的返回值是0,不执行 */
		puts("subcommand not supported\n");
		return ret;
	}

	/* Now run the OS! We hope this doesn't return */
	if (!ret && (states & BOOTM_STATE_OS_GO))     /* 定义了,这里继续指行,见1.6分析,也是只传了一个参数BOOTM_STATE_OS_GO */       
		ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
				images, boot_fn);

	/* Deal with any fallout */
err:
	if (iflag)
		enable_interrupts();

	if (ret == BOOTM_ERR_UNIMPLEMENTED)
		bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL);
	else if (ret == BOOTM_ERR_RESET)
		do_reset(cmdtp, flag, argc, argv);

	return ret;
}

1.1、bootm_start

static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc,
		       char * const argv[])
{
    /* 把之前的全局变量images清0,包括state */
	memset((void *)&images, 0, sizeof(images));        
	images.verify = getenv_yesno("verify");       /* 得到环境变量verify,定义的时候是n表示不对zImage进行crc校验,为y表示要校验 */

	boot_start_lmb(&images);    /* 没定义CONFIG_LMB,空函数 */

	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");
	images.state = BOOTM_STATE_START;    /* 标记状态 */

	return 0;
}

1.2、bootm_find_os


static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
			 char * const argv[])
{
	const void *os_hdr;
	bool ep_found = false;
	int ret;

	/* get kernel image header, start address and length,注释说的很明白得到image头信息,起始地址长度等 */
	os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,
			&images, &images.os.image_start, &images.os.image_len);    //os里面和imgae里面的一些信息我们已经标记了
	if (images.os.image_len == 0) {
		puts("ERROR: can't get kernel image!\n");
		return 1;
	}

	/* get image parameters */
	switch (genimg_get_format(os_hdr)) {  /* 已经分析过返回值是IMAGE_FORMAT_LEGACY */
#if defined(CONFIG_IMAGE_FORMAT_LEGACY)
	case IMAGE_FORMAT_LEGACY:
		images.os.type = image_get_type(os_hdr);    //填充os中的各种信息
		images.os.comp = image_get_comp(os_hdr);
		images.os.os = image_get_os(os_hdr);

		images.os.end = image_get_image_end(os_hdr);
		images.os.load = image_get_load(os_hdr);
		images.os.arch = image_get_arch(os_hdr);
		break;
#endif
#if IMAGE_ENABLE_FIT        /* 没定义 */
	case IMAGE_FORMAT_FIT:
        ......
		break;
#endif
#ifdef CONFIG_ANDROID_BOOT_IMAGE        /* 没定义 */
	case IMAGE_FORMAT_ANDROID:
		images.os.type = IH_TYPE_KERNEL;
		images.os.comp = IH_COMP_NONE;
		images.os.os = IH_OS_LINUX;

		images.os.end = android_image_get_end(os_hdr);
		images.os.load = android_image_get_kload(os_hdr);
		images.ep = images.os.load;
		ep_found = true;
		break;
#endif
	default:
		puts("ERROR: unknown image format type!\n");
		return 1;
	}

	/* If we have a valid setup.bin, we will use that for entry (x86) */
	if (images.os.arch == IH_ARCH_I386 ||
	    images.os.arch == IH_ARCH_X86_64) {
		ulong len;

		ret = boot_get_setup(&images, IH_ARCH_I386, &images.ep, &len);
		if (ret < 0 && ret != -ENOENT) {
			puts("Could not find a valid setup.bin for x86\n");
			return 1;
		}
		/* Kernel entry point is the setup.bin */
	} else if (images.legacy_hdr_valid) {
		images.ep = image_get_ep(&images.legacy_hdr_os_copy);    /* 前面标记过有效了,这里对ep赋值0x30008040 */
#if IMAGE_ENABLE_FIT
	} else if (images.fit_uname_os) {
		int ret;

		ret = fit_image_get_entry(images.fit_hdr_os,
					  images.fit_noffset_os, &images.ep);
		if (ret) {
			puts("Can't get entry point property!\n");
			return 1;
		}
#endif
	} else if (!ep_found) {
		puts("Could not find kernel entry point!\n");
		return 1;
	}
    /* 可以运行在任何地址的os,会直接使用当前地址作为os的load地址,我们不是那种内核 */
	if (images.os.type == IH_TYPE_KERNEL_NOLOAD) {
		images.os.load = images.os.image_start;
		images.ep += images.os.load;
	}

	images.os.start = map_to_sysmem(os_hdr);    /* os.start还是0x30008000 */

	return 0;
}

1.2.1、boot_get_kernel

/*  这里的参数images就是全局变量images,os_data是images.os.image_start,这里的os_len是images.os.image_len */
static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
				   char * const argv[], bootm_headers_t *images,
				   ulong *os_data, ulong *os_len)
{
#if defined(CONFIG_IMAGE_FORMAT_LEGACY)
	image_header_t	*hdr;
#endif
	ulong		img_addr;
	const void *buf;
	const char	*fit_uname_config = NULL;
	const char	*fit_uname_kernel = NULL;
#if IMAGE_ENABLE_FIT
	int		os_noffset;
#endif

    /* 我们的参数是"30008000",所以argc = 1,第一个参数是argv[0],结果返回整数0x30008000函数见下面分析 */
	img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0],
					      &fit_uname_config,
					      &fit_uname_kernel);

	bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC);       /* 标记状态 */

	/* copy from dataflash if needed */
	img_addr = genimg_get_image(img_addr);    /* 我们没dataflash,这里为空还是返回0x30008000 */

	/* check image type, for FIT images get FIT kernel node */
	*os_data = *os_len = 0;        /* 这里先清空images.os里的两个参数 */
	buf = map_sysmem(img_addr, 0);    /* 强制类型转换buf = 0x30008000 */
	switch (genimg_get_format(buf)) {        /* buf传入这里还是0x30008000地址 */
#if defined(CONFIG_IMAGE_FORMAT_LEGACY)
	case IMAGE_FORMAT_LEGACY:        /* 魔数校验成功就是返回IMAGE_FORMAT_LEGACY */
		printf("## Booting kernel from Legacy Image at %08lx ...\n",
		       img_addr);    //打印信息见下图
		hdr = image_get_kernel(img_addr, images->verify);    /* 对image进行校验,并且打印出相关信息,该函数下面就分析 */
		if (!hdr)
			return NULL;
		bootstage_mark(BOOTSTAGE_ID_CHECK_IMAGETYPE);

		/* get os_data and os_len */
		switch (image_get_type(hdr)) {    /* 这边是得到os type类型,我们就是kernel */
		case IH_TYPE_KERNEL:
		case IH_TYPE_KERNEL_NOLOAD:
			*os_data = image_get_data(hdr);        /* 即0x30008000+0x40,这里的os_data就是os.image_start即得到,zImage当前在内存中的地址 */
			*os_len = image_get_data_size(hdr);    /* 0x40 + zImage大小,这里的os_len就是os.image_len即uImage的大小 */
			break;
		case IH_TYPE_MULTI:
			image_multi_getimg(hdr, 0, os_data, os_len);
			break;
		case IH_TYPE_STANDALONE:
			*os_data = image_get_data(hdr);
			*os_len = image_get_data_size(hdr);
			break;
		default:
			printf("Wrong Image Type for %s command\n",
			       cmdtp->name);
			bootstage_error(BOOTSTAGE_ID_CHECK_IMAGETYPE);
			return NULL;
		}

		/*
		 * copy image header to allow for image overwrites during
		 * kernel decompression.
		 */
		memmove(&images->legacy_hdr_os_copy, hdr,
			sizeof(image_header_t));    /* 拷贝一份image_herfer_t,今后就直接用全局变量里面的了 */

		/* save pointer to image header */
		images->legacy_hdr_os = hdr;        /* 0x30008000 */

		images->legacy_hdr_valid = 1;       /* 标记我们的hdr经过前面的校验,有效 */
		bootstage_mark(BOOTSTAGE_ID_DECOMP_IMAGE);
		break;
#endif
#if IMAGE_ENABLE_FIT        /* 没定义,不管 */
	case IMAGE_FORMAT_FIT:
		os_noffset = fit_image_load(images, img_addr,
				&fit_uname_kernel, &fit_uname_config,
				IH_ARCH_DEFAULT, IH_TYPE_KERNEL,
				BOOTSTAGE_ID_FIT_KERNEL_START,
				FIT_LOAD_IGNORED, os_data, os_len);
		if (os_noffset < 0)
			return NULL;

		images->fit_hdr_os = map_sysmem(img_addr, 0);
		images->fit_uname_os = fit_uname_kernel;
		images->fit_uname_cfg = fit_uname_config;
		images->fit_noffset_os = os_noffset;
		break;
#endif
#ifdef CONFIG_ANDROID_BOOT_IMAGE  /* 没定义,不管 */
	case IMAGE_FORMAT_ANDROID:
		printf("## Booting Android Image at 0x%08lx ...\n", img_addr);
		if (android_image_get_kernel(buf, images->verify,
					     os_data, os_len))
			return NULL;
		break;
#endif
	default:
		printf("Wrong Image Format for %s command\n", cmdtp->name);
		bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO);
		return NULL;
	}

	debug("   kernel data at 0x%08lx, len = 0x%08lx (%ld)\n",
	      *os_data, *os_len, *os_len);

	return buf;            /* 返回值还是switch前面的0x30008000
}

我当初开发板自带的zImage,我使用uboot自带的mkinage工具生成的uImage

./mkimage -n "my kernel" -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage

mkimage的使用方法百度介绍很多。

虽然我生成的uImage目前不能启动,但可以方便我们调试观察uboot中bootm的动作。这是一个2.6.35.7的内核。

static image_header_t *image_get_kernel(ulong img_addr, int verify)
{
	image_header_t *hdr = (image_header_t *)img_addr;

	if (!image_check_magic(hdr)) {    /* 魔数前面校验过了,正常肯定能通过的 */
		puts("Bad Magic Number\n");
		bootstage_error(BOOTSTAGE_ID_CHECK_MAGIC);
		return NULL;
	}
	bootstage_mark(BOOTSTAGE_ID_CHECK_HEADER);

	if (!image_check_hcrc(hdr)) {    /* 前64字节的crc,正常肯定能通过的 */
		puts("Bad Header Checksum\n");
		bootstage_error(BOOTSTAGE_ID_CHECK_HEADER);
		return NULL;
	}

	bootstage_mark(BOOTSTAGE_ID_CHECK_CHECKSUM);
	image_print_contents(hdr);    /* 打印头信息 */

	if (verify) { /* zimage比较大,所以通过这个verify环境变量来定义要不要校验 */
		puts("   Verifying Checksum ... ");
		if (!image_check_dcrc(hdr)) {
			printf("Bad Data CRC\n");
			bootstage_error(BOOTSTAGE_ID_CHECK_CHECKSUM);
			return NULL;
		}
		puts("OK\n");
	}
	bootstage_mark(BOOTSTAGE_ID_CHECK_ARCH);

	if (!image_check_target_arch(hdr)) {    /* arch我们是arm,正常都是可以过的 */
		printf("Unsupported Architecture 0x%x\n", image_get_arch(hdr));
		bootstage_error(BOOTSTAGE_ID_CHECK_ARCH);
		return NULL;
	}
	return hdr;
}

image_print_contents打印uImage的头信息,实际头信息键上图

void image_print_contents(const void *ptr)
{
	const image_header_t *hdr = (const image_header_t *)ptr;        /* 0x30008000 */
	const char __maybe_unused *p;

	p = IMAGE_INDENT_STRING;
	printf("%sImage Name:   %.*s\n", p, IH_NMLEN, image_get_name(hdr));    /* 打印hdr的name */
	if (IMAGE_ENABLE_TIMESTAMP) {
		printf("%sCreated:      ", p);
		genimg_print_time((time_t)image_get_time(hdr));
	}
	printf("%sImage Type:   ", p);        /* 打印image类型提示符 */
	image_print_type(hdr);       /* 打印实际image类型 */
	printf("%sData Size:    ", p);        /* 打印zImaage的大小提示符 */
	genimg_print_size(image_get_data_size(hdr));     /* 打印实际image大小 */
	printf("%sLoad Address: %08x\n", p, image_get_load(hdr));    /* 加载地址 */
	printf("%sEntry Point:  %08x\n", p, image_get_ep(hdr));      /* 入口地址 */

	if (image_check_type(hdr, IH_TYPE_MULTI) ||
			image_check_type(hdr, IH_TYPE_SCRIPT)) {
		int i;
		ulong data, len;
		ulong count = image_multi_count(hdr);

		printf("%sContents:\n", p);
		for (i = 0; i < count; i++) {
			image_multi_getimg(hdr, i, &data, &len);

			printf("%s   Image %d: ", p, i);
			genimg_print_size(len);

			if (image_check_type(hdr, IH_TYPE_SCRIPT) && i > 0) {
				/*
				 * the user may need to know offsets
				 * if planning to do something with
				 * multiple files
				 */
				printf("%s    Offset = 0x%08lx\n", p, data);
			}
		}
	} else if (image_check_type(hdr, IH_TYPE_FIRMWARE_IVT)) {
		printf("HAB Blocks:   0x%08x   0x0000   0x%08x\n",
				image_get_load(hdr) - image_get_header_size(),
				image_get_size(hdr) + image_get_header_size()
						- 0x1FE0);
	}
}

可以看一下当前image的头信息

1.1.1.1、genimg_get_kernel_addr_fit

ulong genimg_get_kernel_addr_fit(char * const img_addr,
			     const char **fit_uname_config,
			     const char **fit_uname_kernel)
{
	ulong kernel_addr;

	/* find out kernel image address */
	if (!img_addr) {    /* 我们传的有值,所以进不来这里 */
        /*  ulong load_addr = CONFIG_SYS_LOAD_ADDR;	如果我们使用bootm不带参数,就会使用头文件默认的加载地址 */
		kernel_addr = load_addr;   

		debug("*  kernel: default image load address = 0x%08lx\n",
		      load_addr);
#if CONFIG_IS_ENABLED(FIT)        /* 没定义 */
	} else if (fit_parse_conf(img_addr, load_addr, &kernel_addr,
				  fit_uname_config)) {
		debug("*  kernel: config '%s' from image at 0x%08lx\n",
		      *fit_uname_config, kernel_addr);
	} else if (fit_parse_subimage(img_addr, load_addr, &kernel_addr,
				     fit_uname_kernel)) {
		debug("*  kernel: subimage '%s' from image at 0x%08lx\n",
		      *fit_uname_kernel, kernel_addr);
#endif
	} else {
		kernel_addr = simple_strtoul(img_addr, NULL, 16);    /* 把"30008000"字符串转换成16进制数 */
		debug("*  kernel: cmdline image address = 0x%08lx\n",
		      kernel_addr);
	}

	return kernel_addr;
}

1.2.1.2、genimg_get_format

/* 上面传过来的地址还是0x30008000 */
int genimg_get_format(const void *img_addr)
{
#if defined(CONFIG_IMAGE_FORMAT_LEGACY)
	const image_header_t *hdr;

	hdr = (const image_header_t *)img_addr;
	if (image_check_magic(hdr))        /* 如果是uImage的话,0x30008000第一个位置放的是一个魔数 */
		return IMAGE_FORMAT_LEGACY;
#endif
#if IMAGE_ENABLE_FIT || IMAGE_ENABLE_OF_LIBFDT
	if (fdt_check_header(img_addr) == 0)
		return IMAGE_FORMAT_FIT;
#endif
#ifdef CONFIG_ANDROID_BOOT_IMAGE
	if (android_image_check_header(img_addr) == 0)
		return IMAGE_FORMAT_ANDROID;
#endif

	return IMAGE_FORMAT_INVALID;
}

/* 得到传过来的0x30008000地址的第一个32位整数和IH_MAGIC比较,判断是不是魔数 */
static inline int image_check_magic(const image_header_t *hdr)
{
	return (image_get_magic(hdr) == IH_MAGIC);
}

1.2.1.2.1、image_get_magic

#define image_get_hdr_l(f) \
	static inline uint32_t image_get_##f(const image_header_t *hdr) \
	{ \
		return uimage_to_cpu(hdr->ih_##f); \
	}
image_get_hdr_l(magic)		/* image_get_magic */
image_get_hdr_l(hcrc)		/* image_get_hcrc */
image_get_hdr_l(time)		/* image_get_time */
image_get_hdr_l(size)		/* image_get_size */
image_get_hdr_l(load)		/* image_get_load */
image_get_hdr_l(ep)		/* image_get_ep */
image_get_hdr_l(dcrc)		/* image_get_dcrc */

前面已经分析过类似的宏,这里就不带入分析了,可以直接看出来,这个宏的作用就是通过传入的参数地址hdr
返回image_header_t 里面不同变量偏移地址的4字节数

注意:前面uImage中的image_hander是以大端格式存储的以下图的30008000为例来看,而我们arm中则是以小端格式存储,所以uimage_to_cpu函数还要对数值的高低字节进行调整。

32位数,调整大小端算法

#define uimage_to_cpu(x)		be32_to_cpu(x)

# define be32_to_cpu(x)		uswap_32(x)

#define uswap_32(x) \
	((((x) & 0xff000000) >> 24) | \
	 (((x) & 0x00ff0000) >>  8) | \
	 (((x) & 0x0000ff00) <<  8) | \
	 (((x) & 0x000000ff) << 24))

得到magic魔数为0x27051956,而系统定义的魔数也是0x27051956,所以只要是uImage,第一个魔数校验就可以成功。

#define IH_MAGIC	0x27051956	/* Image Magic Number		*/

下图标记一下头信息

1.3、bootm_load_os

先看一下图

static int bootm_load_os(bootm_headers_t *images, unsigned long *load_end,
			 int boot_progress)
{
	image_info_t os = images->os;    
	ulong load = os.load;    /* 0x30008000 */
	ulong blob_start = os.start;    /* 0x30008000 */
	ulong blob_end = os.end;    /*  0x30008000 + uImage大小*/
	ulong image_start = os.image_start;    /* 0x30008040 */
	ulong image_len = os.image_len;    /* uImage大小 */
	bool no_overlap;
	void *load_buf, *image_buf;
	int err;

	load_buf = map_sysmem(load, 0);    /* 强制类型转换 */
	image_buf = map_sysmem(os.image_start, image_len);     /* 强制类型转换 */
	err = bootm_decomp_image(os.comp, load, os.image_start, os.type,
				 load_buf, image_buf, image_len,
				 CONFIG_SYS_BOOTM_LEN, load_end);    /* 解压缩 */
	if (err) {
		bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE);
		return err;
	}
	flush_cache(load, ALIGN(*load_end - load, ARCH_DMA_MINALIGN));

	debug("   kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);
	bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED);

    /* 我们在uboot中没再压缩,所以不执行下面的 */
	no_overlap = (os.comp == IH_COMP_NONE && load == image_start);

	if (!no_overlap && (load < blob_end) && (*load_end > blob_start)) {
		debug("images.os.start = 0x%lX, images.os.end = 0x%lx\n",
		      blob_start, blob_end);
		debug("images.os.load = 0x%lx, load_end = 0x%lx\n", load,
		      *load_end);

		/* Check what type of image this is. */
		if (images->legacy_hdr_valid) {
			if (image_get_type(&images->legacy_hdr_os_copy)
					== IH_TYPE_MULTI)
				puts("WARNING: legacy format multi component image overwritten\n");
			return BOOTM_ERR_OVERLAP;
		} else {
			puts("ERROR: new format image overwritten - must RESET the board to recover\n");
			bootstage_error(BOOTSTAGE_ID_OVERWRITTEN);
			return BOOTM_ERR_RESET;
		}
	}

	return 0;
}

1.3.1、bootm_decomp_image

1.3.2、bootm_decomp_image

err = bootm_decomp_image(os.comp, load, os.image_start, os.type,
				 load_buf, image_buf, image_len,
				 CONFIG_SYS_BOOTM_LEN, load_end);    /* 解压缩 */

load_buf = 0x30008000   image_buf = 0x30008040
image_len = uImage长度   load_end = 0x30008000 + uImage

int bootm_decomp_image(int comp, ulong load, ulong image_start, int type,
		       void *load_buf, void *image_buf, ulong image_len,
		       uint unc_len, ulong *load_end)
{
	int ret = 0;

	*load_end = load;
	print_decomp_msg(comp, type, load == image_start);    /* 我们这里不相等 */

	/*
	 * Load the image to the right place, decompressing if needed. After
	 * this, image_len will be set to the number of uncompressed bytes
	 * loaded, ret will be non-zero on error.
	 */
	switch (comp) {
	case IH_COMP_NONE:
		if (load == image_start)    /* 一致,我门从flash或网络下载的地址和要运行的地址一致 */
			break;
		if (image_len <= unc_len)    /* 不一致则要移动从image_buf地址移动image_len到load_buf地址 */
			memmove_wd(load_buf, image_buf, image_len, CHUNKSZ);
		else
			ret = 1;
		break;
#ifdef CONFIG_GZIP
	case IH_COMP_GZIP: {
		ret = gunzip(load_buf, unc_len, image_buf, &image_len);
		break;
	}
#endif /* CONFIG_GZIP */
#ifdef CONFIG_BZIP2
	case IH_COMP_BZIP2: {
		
        /* 解压缩算法 */
        ........
		
		break;
	}
#endif /* CONFIG_BZIP2 */
#ifdef CONFIG_LZMA
	case IH_COMP_LZMA: {
		 
        /* 解压缩算法 */
        ........

		break;
	}
#endif /* CONFIG_LZMA */
#ifdef CONFIG_LZO
	case IH_COMP_LZO: {
		/* 解压缩算法 */
        ........
		break;
	}
#endif /* CONFIG_LZO */
#ifdef CONFIG_LZ4
	case IH_COMP_LZ4: {
		/* 解压缩算法 */
        ........
		break;
	}
#endif /* CONFIG_LZ4 */
	default:
		printf("Unimplemented compression type %d\n", comp);
		return BOOTM_ERR_UNIMPLEMENTED;
	}

	if (ret)
		return handle_decomp_error(comp, image_len, unc_len, ret);
	*load_end = load + image_len;

	puts("OK\n");

	return 0;
}


/* 我们的没有压缩,所以是IH_COMP_NONE,xip代表我们的加载地址和uImage地址刚好一致,否则loading代表要搬移 */
static void print_decomp_msg(int comp_type, int type, bool is_xip)
{
	const char *name = genimg_get_type_name(type);

	if (comp_type == IH_COMP_NONE)
		printf("   %s %s ... ", is_xip ? "XIP" : "Loading", name);
	else
		printf("   Uncompressing %s ... ", name);
}

从上面的搬移我们可以看到,假如我们下载的uImage的地址不是uImage将来要运行的load地址,bootm命令会把我们下载的地址的uboot通过uImage的头信息的里面的load地址搬移到load地址。

1.4、bootm_os_get_boot_func

boot_os_fn *bootm_os_get_boot_func(int os)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC    /* 没定义 */
	static bool relocated;

	if (!relocated) {
		int i;

		/* relocate boot function table */
		for (i = 0; i < ARRAY_SIZE(boot_os); i++)
			if (boot_os[i] != NULL)
				boot_os[i] += gd->reloc_off;

		relocated = true;
	}
#endif
	return boot_os[os];        /* 根据os类型,返回对应的函数指针 */
}
enum {
	IH_OS_INVALID		= 0,	/* Invalid OS	*/
	IH_OS_OPENBSD,			/* OpenBSD	*/
	IH_OS_NETBSD,			/* NetBSD	*/
	IH_OS_FREEBSD,			/* FreeBSD	*/
	IH_OS_4_4BSD,			/* 4.4BSD	*/
	IH_OS_LINUX,			/* Linux	*/
	IH_OS_SVR4,			/* SVR4		*/
	IH_OS_ESIX,			/* Esix		*/
    .....
};


/* 函数指针数组见下图 */

static boot_os_fn *boot_os[] = {
	[IH_OS_U_BOOT] = do_bootm_standalone,
#ifdef CONFIG_BOOTM_LINUX
	[IH_OS_LINUX] = do_bootm_linux,    /* 我们的刚好是这个 */
#endif
#ifdef CONFIG_BOOTM_NETBSD
	[IH_OS_NETBSD] = do_bootm_netbsd,
#endif
#ifdef CONFIG_LYNXKDI
	[IH_OS_LYNXOS] = do_bootm_lynxkdi,
#endif
#ifdef CONFIG_BOOTM_RTEMS
	[IH_OS_RTEMS] = do_bootm_rtems,
#endif
#if defined(CONFIG_BOOTM_OSE)
	[IH_OS_OSE] = do_bootm_ose,
#endif
#if defined(CONFIG_BOOTM_PLAN9)
	[IH_OS_PLAN9] = do_bootm_plan9,
#endif
#if defined(CONFIG_BOOTM_VXWORKS) && \
	(defined(CONFIG_PPC) || defined(CONFIG_ARM))
	[IH_OS_VXWORKS] = do_bootm_vxworks,
#endif
#if defined(CONFIG_CMD_ELF)
	[IH_OS_QNX] = do_bootm_qnxelf,
#endif
#ifdef CONFIG_INTEGRITY
	[IH_OS_INTEGRITY] = do_bootm_integrity,
#endif
#ifdef CONFIG_BOOTM_OPENRTOS
	[IH_OS_OPENRTOS] = do_bootm_openrtos,
#endif
};

这里学到一点,数组可以通过指定下标,赋值,即如下
static boot_os_fn *boot_os[] = {
    [IH_OS_LINUX] = do_bootm_linux,    
}

static boot_os_fn *boot_os[] = {
    [5] = do_bootm_linux,    
}

1.5、boot_selected_os

int boot_selected_os(int argc, char * const argv[], int state,
		     bootm_headers_t *images, boot_os_fn *boot_fn)
{
	arch_preboot_os();    /* 空函数 */
	boot_fn(state, argc, argv, images);    /* 执行do_bootm_linux */

	/* Stand-alone may return when 'autostart' is 'no' */
	if (images->os.type == IH_TYPE_STANDALONE ||
	    IS_ENABLED(CONFIG_SANDBOX) ||
	    state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */
		return 0;
	bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED);
	debug("\n## Control returned to monitor - resetting...\n");

	return BOOTM_ERR_RESET;
}

1.6、do_bootm_linux

/* 前面的传参flag = BOOTM_STATE_OS_PREP*/

int do_bootm_linux(int flag, int argc, char * const argv[],
		   bootm_headers_t *images)
{
	/* No need for those on ARM */
	if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
		return -1;

	if (flag & BOOTM_STATE_OS_PREP) {
		boot_prep_linux(images);    /* 执行了一次 boot_prep_linux 直接退出*/
		return 0;
	}

	if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
		boot_jump_linux(images, flag);
		return 0;
	}

	boot_prep_linux(images);
	boot_jump_linux(images, flag);
	return 0;
}

1.6.1、boot_prep_linux  uboot的tag传参形式

static void boot_prep_linux(bootm_headers_t *images)
{
	char *commandline = getenv("bootargs");    /* 得到bootargs环境变量 */

	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
#ifdef CONFIG_OF_LIBFDT
		debug("using: FDT\n");
		if (image_setup_linux(images)) {
			printf("FDT creation failed! hanging...");
			hang();
		}
#endif
	} else if (BOOTM_ENABLE_TAGS) {
		debug("using: ATAGS\n");
		setup_start_tag(gd->bd);        /* 设置起始头 */
		if (BOOTM_ENABLE_SERIAL_TAG)
			setup_serial_tag(&params);    /* 设置串口信息 */
		if (BOOTM_ENABLE_CMDLINE_TAG)
			setup_commandline_tag(gd->bd, commandline);    /* 命令行参数,很重要 */
		if (BOOTM_ENABLE_REVISION_TAG)
			setup_revision_tag(&params);    /* 版本,不重要 */
		if (BOOTM_ENABLE_MEMORY_TAGS)
			setup_memory_tags(gd->bd);    /* 内存信息,比较重要 */
		if (BOOTM_ENABLE_INITRD_TAG) {
			/*
			 * In boot_ramdisk_high(), it may relocate ramdisk to
			 * a specified location. And set images->initrd_start &
			 * images->initrd_end to relocated ramdisk's start/end
			 * addresses. So use them instead of images->rd_start &
			 * images->rd_end when possible.
			 */
			if (images->initrd_start && images->initrd_end) {
				setup_initrd_tag(gd->bd, images->initrd_start,
						 images->initrd_end);
			} else if (images->rd_start && images->rd_end) {
				setup_initrd_tag(gd->bd, images->rd_start,
						 images->rd_end);
			}
		}
		setup_board_tags(&params); 
		setup_end_tag(gd->bd);    /* 结束tag */
	} else {
		printf("FDT and ATAGS support not compiled in - hanging\n");
		hang();
	}
}
/* tag的起始标记 */
static void setup_start_tag (bd_t *bd)
{
	params = (struct tag *)bd->bi_boot_params;    /* 在uboot的后半部分的board_init被初始化 */

	params->hdr.tag = ATAG_CORE;
	params->hdr.size = tag_size (tag_core);

	params->u.core.flags = 0;
	params->u.core.pagesize = 0;
	params->u.core.rootdev = 0;

	params = tag_next (params);
}

int board_init(void)
{
	/* Set Initial global variables */
	gd->bd->bi_arch_number = MACH_TYPE_SMDKV210;
	gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100;   /* 0x30000100 */

	return 0;
}

上面函数执行完后,会把uboot给linux内核的参数以tag的形式,依次放在0x30000100地址后面。将来只需要给linux内核传0x30000100地址就可以了。

1.7、跳转到linux内核

/* 参数flag  = BOOTM_STATE_OS_GO */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64        /* 不是64位的 */
	void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
			void *res2);
	int fake = (flag & BOOTM_STATE_OS_FAKE_GO);

	kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
				void *res2))images->ep;

	debug("## Transferring control to Linux (at address %lx)...\n",
		(ulong) kernel_entry);
	bootstage_mark(BOOTSTAGE_ID_RUN_OS);

	announce_and_cleanup(fake);

	if (!fake) {
#ifdef CONFIG_ARMV8_PSCI       
		armv8_setup_psci();
#endif
		do_nonsec_virt_switch();

		update_os_arch_secondary_cores(images->os.arch);

#ifdef CONFIG_ARMV8_SWITCH_TO_EL1        
		armv8_switch_to_el2((u64)images->ft_addr, 0, 0,
				    (u64)switch_to_el1, ES_TO_AARCH64);
#else
		if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
		    (images->os.arch == IH_ARCH_ARM))
			armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number,
					    (u64)images->ft_addr,
					    (u64)images->ep,
					    ES_TO_AARCH32);
		else
			armv8_switch_to_el2((u64)images->ft_addr, 0, 0,
					    images->ep,
					    ES_TO_AARCH64);
#endif
	}
#else
	unsigned long machid = gd->bd->bi_arch_number;    /* board_init中MACH_TYPE_SMDKV210 */
	char *s;
	void (*kernel_entry)(int zero, int arch, uint params);
	unsigned long r2;
	int fake = (flag & BOOTM_STATE_OS_FAKE_GO);        /* 不是这个 */

	kernel_entry = (void (*)(int, int, uint))images->ep;    /* 0x30008040 */

	s = getenv("machid");    /* 环境变量里面定义了的话,优先使用环境变量的 */
	if (s) {
		if (strict_strtoul(s, 16, &machid) < 0) {
			debug("strict_strtoul failed!\n");
			return;
		}
		printf("Using machid 0x%lx from environment\n", machid);
	}

	debug("## Transferring control to Linux (at address %08lx)" \
		"...\n", (ulong) kernel_entry);
	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
	announce_and_cleanup(fake);    /* 启动Linux内核前的最后信息和清理 */

	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)    /* 没定义 */
		r2 = (unsigned long)images->ft_addr;
	else
		r2 = gd->bd->bi_boot_params;    /* 0x30000100 */

	if (!fake) {
#ifdef CONFIG_ARMV7_NONSEC
		if (armv7_boot_nonsec()) {
			armv7_init_nonsec();
			secure_ram_addr(_do_nonsec_entry)(kernel_entry,
							  0, machid, r2);
		} else
#endif
			kernel_entry(0, machid, r2);    /* 跳到0x30008040执行,参数分别是0,SMDKV210的机器码,以及参数地址0x30000100 */
	}
#endif
}



static void announce_and_cleanup(int fake)
{
	printf("\nStarting kernel ...%s\n\n", fake ?        /* 打印启动前的最后一个信息 */
		"(fake run for tracing)" : "");
	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
#ifdef CONFIG_BOOTSTAGE_FDT        /* 没定义 */
	bootstage_fdt_add_report();
#endif
#ifdef CONFIG_BOOTSTAGE_REPORT    /* 没定义 */
	bootstage_report();
#endif

#ifdef CONFIG_USB_DEVICE        /* 没定义 */
	udc_disconnect();
#endif

	board_quiesce_devices();    /* 空函数 */

	cleanup_before_linux();     /* 清cache,关中断等 */
}

分析我自己手动生成放入uImage不能启动内核的原因。(用厂家提供的旧的1.3.4版本的uboot可以启动zImage,说明kernel没有问题)

 1.没有设置命令行参数,比如启动了也不一定打印到这个串口2

设置一个我之前用过的,在smdkv210.h中定义,主要注释掉头文件里面原有的CONFIG_BOOTARGS 

#define CONFIG_BOOTARGS "root=/dev/nfs nfsroot=192.168.0.107:   \
/home/run/work/rootfs/rootfs ip=192.168.0.20:192.168.0.107:192.168.0.1:\
255.255.255.0::eth0:off init=/linuxrc console=ttySAC2,115200"

发现仍然不能启动,故排除命令行问题

2.uImage本身不对

经过仔细分析源码发现,zImage的入口地址本应该是0x30008000(由kernel决定,一小节移植kernel时说明)

load地址是0x30008000,而load的时候我们是tftp uImage到0x30008000,所以在bootm命令中要把0x30008040地址后面的zImage搬移到0x30008000位置

同时,此时应该ep应该指向0x30008000,而不是0x30008040。而我们的ep设置的还是0x3008040,这就让跳转到zImage时不是从第一条指令开始运行,所以会跑飞。

接下来我们重新生成uImage再次尝试启动。

./mkimage -n "my kernel" -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage 

继续死掉

向前64字节,依然死掉

这一次是加载地址是XIP的,即不需要移动,说明我们分析的uImage比zImage前64字节,而zImge刚好在0x30008000就是XIP

实在没辙了重新编译了一次老的内核,用命令生成uImage

依然是死掉。

看来老的内核确实是有毒。

接一来,从下一节开始移植一个新的内核和文件系统,今后的驱动就用新的内核编写学习。

顺便测试一下新uboot配新内核能否启动。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/81990070