uboot引導linux內核過程 卡死Starting kernel ...(下載地址,加載地址,入口地址的修改)(uImage和zImage的區別)

1uImagezImage\color{#0000FF}{1、uImage和zImage的區別}

make 之後會生成三個文件,一個vmlinux ,一個Image,一個zImage,vmlinux是ELF格式的不能直接運行,Image 就是使用 objcopy 取消掉 vmlinux 中的一些其他信息得到的,而zImage爲內核的一種進一步映像壓縮文件,Image大約爲4M,而zImage不到2M。

然後make uImage就會生成 uImage文件,uImage顧名思義,u代表uboot,
uImageubootlinux使bootmuImage\color {#FF0000}{所以uImage是uboot引導linux的鏡像文件,使用bootm 命令是隻支持uImage鏡像的}
如果要引導zImage鏡像需要修改uboot do_bootm函數

uImagemkimagezImage64\color {#FF0000}{uImage是用mkimage工具根據zImage製作而來的,其實就是加了如下64字節頭部信息}

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、下載地址,加載地址,入口地址,移植內核重點}

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

我們在uboot環境變量裏面一般會有這麼一句,
boocmd=movireadkernel0x46000000,bootm0x46000000\color{#FF0000}{boocmd = movi read kernel 0x46000000 , bootm 0x46000000}

它的意思就是把內核下載(讀)到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;

編譯的時候可以指定加載地址
在這裏插入圖片描述
在這裏插入圖片描述

圖中可以看出加載地址 = 入口地址

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

ubootlinux\color{#FF0000}{我們知道,uboot在引導linux內核前,會檢查下載地址和加載地址是否相同,}
zImage\color{#FF0000}{如果不同,就需要把內核拷貝從下載地址拷貝到加載地址,注意,拷貝部分是去了掉頭部的zImage,}
\color{#FF0000}{如果相同,就不作拷貝}

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

此時可以正常啓動

2、在加載地址和入口地址相同 且 下載地址和加載地址相同情況下:

此時無法正常啓動,因爲此時uboot不會去掉頭部64字節,如果直接從入口地址(==加載地址)運行肯定會失敗,正確的做法就是不要讓下載地址和加載地址相同

\color{#FF0000}{注意:下載地址和加載地址不同時,兩個地址差值儘量大一點,}

uboot\color{#FF0000}{否知在uboot重新定位內核的時候可能會出現覆蓋的情況}

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

圖片來源:https://www.cnblogs.com/Oude/p/12217750.html
在這裏插入圖片描述

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==>setloadaddresstoaddr(hex)\color{#FF0000}{-a ==> set load address to 'addr' (hex) 指定加載地址}

e==>setentrypointtoep(hex)\color{#FF0000}{-e ==> set entry point to 'ep' (hex) 指定內核入口運行地址}

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

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

-x ==> set XIP (execute in place,即不進行文件的拷貝,在當前位置執行)

在這裏插入圖片描述

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

在這裏插入圖片描述
在這裏插入圖片描述

編譯信息如下:
說明上面的改法是對的
在這裏插入圖片描述
在這裏插入圖片描述

3ubootuImage\color{#0000FF}{3、uboot引導uImage過程}

​​​​在這裏插入圖片描述
​​

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;
}

kernelentry\color{#FF0000}{kernel_entry 傳遞參數啓動內核}



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
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章