make 之後會生成三個文件,一個vmlinux ,一個Image,一個zImage,vmlinux是ELF格式的不能直接運行,Image 就是使用 objcopy 取消掉 vmlinux 中的一些其他信息得到的,而zImage爲內核的一種進一步映像壓縮文件,Image大約爲4M,而zImage不到2M。
然後make uImage就會生成 uImage文件,uImage顧名思義,u代表uboot,
,
如果要引導zImage鏡像需要修改uboot do_bootm函數
,
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;
我們在uboot環境變量裏面一般會有這麼一句,
它的意思就是把內核下載(讀)到0x46000000地方,0x46000000就是下載地址
typedef struct image_header {
uint32_t ih_load; /* Data Load Address加載地址 */
uint32_t ih_ep; /* Entry Point Address入口地址 */
} image_header_t;
編譯的時候可以指定加載地址
圖中可以看出加載地址 = 入口地址
1、在加載地址和入口地址相同 且 下載地址和加載地址不同情況下:
此時可以正常啓動
2、在加載地址和入口地址相同 且 下載地址和加載地址相同情況下:
此時無法正常啓動,因爲此時uboot不會去掉頭部64字節,如果直接從入口地址(==加載地址)運行肯定會失敗,正確的做法就是不要讓下載地址和加載地址相同
圖片來源:https://www.cnblogs.com/Oude/p/12217750.html
1、入口地址和加載地址不同(正常是相差64直接),下載地址和加載地址相同
正常啓動,因爲此時uboot不會去掉頭部也不做拷貝,然後正好從加載地址+64字節的入口地址啓動內核
2、入口地址和加載地址不同,下載地址和加載地址不同的情況
無法正常啓動,因爲此時uboot會去掉頭部64字節拷貝到加載地址,但是去掉64字節已經就是zImage了,在從加載地址+64字節地方啓動就不是正在的入口地址了
/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’
-n ==> set image name to ‘name’
-d==> use image data from ‘datafile’
-x ==> set XIP (execute in place,即不進行文件的拷貝,在當前位置執行)
vim ./scripts/Makefile.lib 修改Makfile.lib文件
編譯信息如下:
說明上面的改法是對的
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;
}
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
}