uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)

1 、 u I m a g e 和 z I m a g e 的 区 别 \color{#0000FF}{1、uImage和zImage的区别} 1uImagezImage

make 之后会生成三个文件,一个vmlinux ,一个Image,一个zImage,vmlinux是ELF格式的不能直接运行,Image 就是使用 objcopy 取消掉 vmlinux 中的一些其他信息得到的,而zImage为内核的一种进一步映像压缩文件,Image大约为4M,而zImage不到2M。

然后make uImage就会生成 uImage文件,uImage顾名思义,u代表uboot,
所 以 u I m a g e 是 u b o o t 引 导 l i n u x 的 镜 像 文 件 , 使 用 b o o t m 命 令 是 只 支 持 u I m a g e 镜 像 的 \color {#FF0000}{所以uImage是uboot引导linux的镜像文件,使用bootm 命令是只支持uImage镜像的} uImageubootlinux使bootmuImage
如果要引导zImage镜像需要修改uboot do_bootm函数

u I m a g e 是 用 m k i m a g e 工 具 根 据 z I m a g e 制 作 而 来 的 , 其 实 就 是 加 了 如 下 64 字 节 头 部 信 息 \color {#FF0000}{uImage是用mkimage工具根据zImage制作而来的,其实就是加了如下64字节头部信息} uImagemkimagezImage64

typedef struct image_header {
	uint32_t	ih_magic;	/* Image Header Magic Number	*/
	uint32_t	ih_hcrc;	/* Image Header CRC Checksum	*/
	uint32_t	ih_time;	/* Image Creation Timestamp	*/
	uint32_t	ih_size;	/* Image Data Size		*/
	uint32_t ih_load;       /* Data Load Address加载地址 */ 
     uint32_t ih_ep;        /* Entry Point Address入口地址 */
	uint32_t	ih_dcrc;	/* Image Data CRC Checksum	*/
	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		*/
} image_header_t;

2 、 下 载 地 址 , 加 载 地 址 , 入 口 地 址 , 移 植 内 核 重 点 \color{#0000FF}{2、下载地址,加载地址,入口地址,移植内核重点} 2

下 载 地 址 \color{#00FF00}{下载地址}

我们在uboot环境变量里面一般会有这么一句,
b o o c m d = m o v i r e a d k e r n e l 0 x 46000000 , b o o t m 0 x 46000000 \color{#FF0000}{boocmd = movi read kernel 0x46000000 , bootm 0x46000000} boocmd=movireadkernel0x46000000,bootm0x46000000

它的意思就是把内核下载(读)到0x46000000地方,0x46000000就是下载地址

入 口 地 址 , 和 加 载 地 址 \color{#00FF00}{入口地址,和加载地址}

typedef struct image_header {

	uint32_t ih_load;       /* Data Load Address加载地址 */ 
     uint32_t ih_ep;        /* Entry Point Address入口地址 */
} image_header_t;

编译的时候可以指定加载地址
uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第1张图片
uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第2张图片

图中可以看出加载地址 = 入口地址

入 口 地 址 , 和 加 载 地 址 相 同 的 情 况 下 \color{#00FF00}{入口地址,和加载地址相同的情况下}

我 们 知 道 , u b o o t 在 引 导 l i n u x 内 核 前 , 会 检 查 下 载 地 址 和 加 载 地 址 是 否 相 同 , \color{#FF0000}{我们知道,uboot在引导linux内核前,会检查下载地址和加载地址是否相同,} ubootlinux
如 果 不 同 , 就 需 要 把 内 核 拷 贝 从 下 载 地 址 拷 贝 到 加 载 地 址 , 注 意 , 拷 贝 部 分 是 去 了 掉 头 部 的 z I m a g e , \color{#FF0000}{如果不同,就需要把内核拷贝从下载地址拷贝到加载地址,注意,拷贝部分是去了掉头部的zImage,} zImage
如 果 相 同 , 就 不 作 拷 贝 \color{#FF0000}{如果相同,就不作拷贝}

1、在加载地址和入口地址相同 且 下载地址和加载地址不同情况下:

此时可以正常启动

2、在加载地址和入口地址相同 且 下载地址和加载地址相同情况下:

此时无法正常启动,因为此时uboot不会去掉头部64字节,如果直接从入口地址(==加载地址)运行肯定会失败,正确的做法就是不要让下载地址和加载地址相同

注 意 : 下 载 地 址 和 加 载 地 址 不 同 时 , 两 个 地 址 差 值 尽 量 大 一 点 , \color{#FF0000}{注意:下载地址和加载地址不同时,两个地址差值尽量大一点,}

否 知 在 u b o o t 重 新 定 位 内 核 的 时 候 可 能 会 出 现 覆 盖 的 情 况 \color{#FF0000}{否知在uboot重新定位内核的时候可能会出现覆盖的情况} uboot

入 口 地 址 , 加 载 地 址 不 相 同 的 情 况 下 \color{#00FF00}{入口地址,加载地址不相同的情况下}

图片来源:https://www.cnblogs.com/Oude/p/12217750.html
uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第3张图片

1、入口地址和加载地址不同(正常是相差64直接),下载地址和加载地址相同

正常启动,因为此时uboot不会去掉头部也不做拷贝,然后正好从加载地址+64字节的入口地址启动内核

2、入口地址和加载地址不同,下载地址和加载地址不同的情况

无法正常启动,因为此时uboot会去掉头部64字节拷贝到加载地址,但是去掉64字节已经就是zImage了,在从加载地址+64字节地方启动就不是正在的入口地址了

入 口 地 址 , 加 载 地 址 修 改 方 式 \color{#00FF00}{入口地址,加载地址修改方式}

/tools/mkimage.c解析

-A ==> set architecture to ‘arch’

-O ==> set operating system to ‘os’

-T ==> set image type to ‘type’ “kernel或是ramdisk”

-C ==> set compression type ‘comp’

− a = = > s e t l o a d a d d r e s s t o ′ a d d r ′ ( h e x ) 指 定 加 载 地 址 \color{#FF0000}{-a ==> set load address to 'addr' (hex) 指定加载地址} a==>setloadaddresstoaddr(hex)

− e = = > s e t e n t r y p o i n t t o ′ e p ′ ( h e x ) 指 定 内 核 入 口 运 行 地 址 \color{#FF0000}{-e ==> set entry point to 'ep' (hex) 指定内核入口运行地址} e==>setentrypointtoep(hex)

-n ==> set image name to ‘name’

-d==> use image data from ‘datafile’

-x ==> set XIP (execute in place,即不进行文件的拷贝,在当前位置执行)

uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第4张图片

vim ./scripts/Makefile.lib 修改Makfile.lib文件

在这里插入图片描述
uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第5张图片

编译信息如下:
说明上面的改法是对的
uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第6张图片
uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第7张图片

3 、 u b o o t 引 导 u I m a g e 过 程 \color{#0000FF}{3、uboot引导uImage过程} 3ubootuImage

​​​​uboot引导linux内核过程 卡死Starting kernel ...(下载地址,加载地址,入口地址的修改)(uImage和zImage的区别)_第8张图片
​​

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

	return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
		BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
		BOOTM_STATE_LOADOS |
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
		BOOTM_STATE_RAMDISK |
#endif
#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);
}

bootm_start(cmdtp, flag, argc, argv); 对内核头部分析

bootm_find_other(cmdtp, flag, argc, argv); 查找内存中是否有设备树文件或者是文件系统

bootm_load_os(images, &load_end, 0); 加载内核到加载地址处,如果下载地址和加载地址相同则不需要加载

int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
		    int states, bootm_headers_t *images, int boot_progress)
{
	
	if (states & BOOTM_STATE_START)
		ret = bootm_start(cmdtp, flag, argc, argv);

	if (!ret && (states & BOOTM_STATE_FINDOS))
		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)) {
	
		ret = bootm_load_os(images, &load_end, 0);   
	
	}

	/* Relocate the ramdisk */
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
	if (!ret && (states & BOOTM_STATE_RAMDISK)) {

		ret = boot_ramdisk_high(&images->lmb, images->rd_start,
	
		}
	}
#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);
    
     
		ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);  //调用do_bootm_linux函数跳转
	}

}

int do_bootm_linux(int flag, int argc, char * const argv[],
		   bootm_headers_t *images)
{
	
	boot_prep_linux(images);
	boot_jump_linux(images, flag);
	return 0;
}

k e r n e l e n t r y 传 递 参 数 启 动 内 核 \color{#FF0000}{kernel_entry 传递参数启动内核} kernelentry



static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
	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;    //得到入口函数地址


if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
		r2 = (unsigned long)images->ft_addr;  //得到设备树地址
	else
		r2 = gd->bd->bi_boot_params;

			kernel_entry(0, machid, r2);  //给内核传递三个参数,机器码,设备树地址
	}
#endif
}

你可能感兴趣的:(linux驱动开发)