u-boot bootz 加载kernel 流程分析

  • u-boot 加载 kernel 的流程分析。


  • image重要结构体头文件

    // include/image.h
	*                                                                              
 	* Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()         
 	* routines.                                                                    
 	*/  
	// 这是正bootm 头部结构体  
	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;  

	// image 头部结构体
	typedef struct image_header {                                                   
	    __be32      ih_magic;   /* Image Header Magic Number    */                  
	    __be32      ih_hcrc;    /* Image Header CRC Checksum    */                  
	    __be32      ih_time;    /* Image Creation Timestamp */                      
	    __be32      ih_size;    /* Image Data Size      */                          
	    __be32      ih_load;    /* Data  Load  Address      */                      
	    __be32      ih_ep;      /* Entry Point Address      */                      
	    __be32      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;                                                               
	
	// image 信息结构体 
	typedef struct image_info {            
	    ulong       start, end;     /* start/end of blob */                         
	    ulong       image_start, image_len; /* start of image within blob, len of image 	*/
	    ulong       load;           /* load addr for the image */                   
	    uint8_t     comp, type, os;     /* compression, type of image, os type */   
	    uint8_t     arch;           /* CPU architecture */                          
	} image_info_t;                                                                                                                                                  

* #### 1. 这里分析的话从启动脚本开始分析 * #### 启动脚本写在 `include/configs/am335x_sbc7109.h` ```sh #define CONFIG_BOOTCOMMAND \ "run mmcboot;" \ "setenv mmcdev 1; " \ "setenv bootpart 1:2; " \ "run mmcboot;"
# 这里我们看到,他主要调用的是 mmcboot
# mmcboot 主要有如下操作
    "mmcboot=mmc dev ${mmcdev}; " \            
    "if mmc rescan; then " \                 
        "echo SD/MMC found on device ${mmcdev};" \    
        "if run loadbootenv; then " \   # 加载配置文件 uEnv.txt 
            "echo Loaded environment from ${bootenv};" \                
            "run importbootenv;" \  # 加载环境变量  
        "fi;" \                                                             
        "if test -n $uenvcmd; then " \      
            "echo Running uenvcmd ...;" \       
            "run uenvcmd;" \  # 这个上次有记录,是boot 命令  
        "fi;" \                                                             
        "if run loadimage; then " \   # 执行加载命令  
            "run mmcloados;" \       # ----> 这个在后面  
        "fi;" \                                                             
    "fi;\0" \   

# loadbootenv  
 "mmcdev=0\0" \   # 开始为零,如果第一次mmcboot 不行第二次就可以设置为1
 "bootenv=uEnv.txt\0" \
  "loadbootenv=load mmc ${mmcdev} ${loadaddr} ${bootenv}\0" \ 	

# importbootenv
   "importbootenv=echo Importing environment from mmc ...; " \ 
    "env import -t $loadaddr $filesize\0" \  

# loadimage
"loadimage=load mmc ${bootpart} ${loadaddr} ${bootdir}/${bootfile}\0" \ 

# 到最后的 ---->
# mmcloados
    "mmcloados=run mmcargs; " \                                                 
    "if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \          
        "if run loadfdt; then " \                                           
            "bootz ${loadaddr} - ${fdtaddr}; " \   #最后执行的是这里 
        "else " \                                                           
            "if test ${boot_fdt} = try; then " \                            
                "bootz; " \                                                 
            "else " \                                                       
                "echo WARN: Cannot load the DT; " \                         
            "fi; " \                                                        
        "fi; " \                                                            
    "else " \                                                               
        "bootz; " \                                                         
    "fi;\0" \      

# mmcargs
    "mmcargs=setenv bootargs console=${console} " \                             
    "${optargs} " \                                                         
    "${mtdparts} " \                                                        
    "root=${mmcroot} " \                                                    
    "rootfstype=${mmcrootfstype}\0" \                                       

#  loadfdt
   "loadfdt=load mmc ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile}\0" \   

# 最后执行   
        "bootz ${loadaddr} - ${fdtaddr}; " \  
# 结合下面的, 也就是 bootz  0x82000000 - 0x88000000 
    # ---> 接下面 bootz 命令 ,这里可以跳到 2. 

# 这里变量的定义最后我在 include/configs/ti_armv7_common.h 找到
	#define DEFAULT_LINUX_BOOT_ENV \                                                
    "loadaddr=0x82000000\0" \                                                   
    "kernel_addr_r=0x82000000\0" \                                              
    "fdtaddr=0x88000000\0" \                                                    
    "fdt_addr_r=0x88000000\0" \                                                 
    "rdaddr=0x88080000\0" \                                                     
    "ramdisk_addr_r=0x88080000\0" \                                             
    "scriptaddr=0x80000000\0" \                                                 
    "pxefile_addr_r=0x80100000\0" \                                             
    "bootm_size=0x10000000\0"             

# 这个宏被 CONFIG_EXTRA_ENV_SETTINGS  包含                                                                                                                                         

</br>
* #### 2. `bootz` 命令分析
```c
	// bootz 命令
	// cmd/bootm.c
	
	bootm_headers_t images;     /* pointers to os/initrd/fdt images */ 

	// bootz 命令	
	U_BOOT_CMD(            
	    bootz,  CONFIG_SYS_MAXARGS, 1,  do_bootz,                                   
	    "boot Linux zImage image from memory", bootz_help_text                      
	); 

	// bootz 调用的是 do_bootz
	// 接上面分析的 bootz  0x82000000 - 0x88000000 
	int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])         
	{                                                                               
	    int ret;                            
	                                               
	    /* Consume 'bootz' */                 
	    argc--; argv++;                
	    //  ----> 这里可以跳到 2.1 
	    if (bootz_start(cmdtp, flag, argc, argv, &images)) 
	        return 1;                
	        
	    /*     
	     * We are doing the BOOTM_STATE_LOADOS state ourselves, so must             
	     * disable interrupts ourselves                                             
	     */                                                                         
	    bootm_disable_interrupts();                                                 
	                                                                                
	    images.os.os = IH_OS_LINUX;   // 这个变量有关下面获取kernel 的进入函数
		// 在这里调用了 do_bootm_states() 
		// 状态是 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO 
	    ret = do_bootm_states(cmdtp, flag, argc, argv,   // ---->  这里可以跳到  2.2
	                  BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |                
	                  BOOTM_STATE_OS_GO,                                            
	                  &images, 1);             
	               
	    return ret;    
	}     

* #### 2.1 `do_bootz` 首先会执行 `bootz_statrt` 初始化一些东西 ```c // cmd/bootm.c static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], bootm_headers_t *images) { int ret; ulong zi_start, zi_end; // 第一次执行 do_bootm_states, 状态是 BOOTM_STATE_START // 这里可以跳到 2.2 ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, images, 1);
    /* Setup Linux kernel zImage entry point */                                 
    if (!argc) {                                                                
        images->ep = load_addr;                                                 
        debug("*  kernel: default image load address = 0x%08lx\n",              
                load_addr);                                                     
    } else {                                                                 
        images->ep = simple_strtoul(argv[0], NULL, 16); 
		// 在这里,参数不为0, 所以 image->ep == 0x82000000, 这个在后面被调用  
                  
        debug("*  kernel: cmdline image address = 0x%08lx\n",                   
            images->ep);                                                        
    }                                                                           
    // 这里把 0x8200000 作为第一个参数传进去,然后就有相关 kernel的显示
        // 这里跳到 2.1.1     
    ret = bootz_setup(images->ep, &zi_start, &zi_end);           
    if (ret != 0) 
        return 1;             
                                                                                
    lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);                   
                                                                                
    /*                                                                          
     * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not            
     * have a header that provide this informaiton. 
     */   // kernel, dts的获取就是在这个函数内进行的。
    if (bootm_find_images(flag, argc, argv))  
        return 1;    
                
    return 0;  
}        
* #### 2.1.1 bootz_setup
```c
	struct zimage_header {    
    	uint32_t    code[9];                                                        
    	uint32_t    zi_magic;                                                       
    	uint32_t    zi_start;                                                       
    	uint32_t    zi_end;                                                         
	};             

	#define LINUX_ARM_ZIMAGE_MAGIC  0x016f2818                                      
                                                                                
	int bootz_setup(ulong image, ulong *start, ulong *end)                          
	{                                                                               
    	struct zimage_header *zi;                                                   
                                                                                
    	zi = (struct zimage_header *)map_sysmem(image, 0);                          
    	if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) {                               
    	    puts("Bad Linux ARM zImage magic!\n");                                  
    	    return 1;                                                               
    	}                                                                           
  
    	*start = zi->zi_start;        
    	*end = zi->zi_end;      
        // 这里打印的信息
		// Kernel image @ 0x82000000 [ 0x000000 - 0x417248 ]  
    	printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n", image, *start,
          *end);  
           
    	return 0;     
	}                  

* #### 2.2 回到`do_bootz`, 再进去一次 `do_bootm_states` * #### 这里开始就是有关怎么一步步跳入kernel 的流程分析 ```c // common/bootm.c // 这个函数最主要的功能是获取kernel的启动函数,执行寻找设备树,并进入执行 kernel启动函数 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;         // 状态
    /*                                                                          
     * Work through the states and see how far we get. We stop on               
     * any error.                                                               
     */                                                                         
    if (states & BOOTM_STATE_START)     // 如果有开始状态,执行bootm_start  
        ret = bootm_start(cmdtp, flag, argc, argv); ---> 这里跳到  2.2.1 
                                                                                
    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);                        
        argc = 0;   /* consume the args */                                      
    }                                                                           
    /* Load the OS */                                                           
    if (!ret && (states & BOOTM_STATE_LOADOS)) {                                
        ulong load_end;                                                         
                                                                                
        iflag = bootm_disable_interrupts();                                     
        ret = bootm_load_os(images, &load_end, 0); 
        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;                                                            
#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY)       
        if (images->os.os == IH_OS_LINUX)                                       
            fixup_silent_linux();                                               
#endif                                                                          
    }    

	// ... ...                                                                       

    /* From now on, we need the OS boot function */                             
    if (ret) 
        return ret;  
	// 获取进入kernel 的函数, 上面可以获悉,参数是 IH_OS_LINUX 
    boot_fn = bootm_os_get_boot_func(images->os.os); // ---> 请跳到 2.2.2 
    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);                        
    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))                                 
        ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);  
     
	// ... ...
  
    /* Now run the OS! We hope this doesn't return */                           
    if (!ret && (states & BOOTM_STATE_OS_GO))   // 执行进去kernel,如果有返回则出错
    ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,  // ----> 请跳到 2.2.3
            images, boot_fn);                                        
// ... ... 成功则不执行下面的,失败则执行后面的函数

}


</br>
* #### 2.2.1  看到上面的 `bootm_start`, 第一次,状态为 `BOOTM_STATE_START`
* #### 我们需要调用  `bootm_start(cmdtp, flag, argc, argv); `
```c
	static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc,                    
               char * const argv[])                                             
	{   // 初始化清空 images 结构体的空间   
    	memset((void *)&images, 0, sizeof(images));   
    	images.verify = getenv_yesno("verify");  // 这个我设置为 n 了  
                           
    	boot_start_lmb(&images); // 暂时没有搞懂 
                                                                                
    	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");  // 空 
    	images.state = BOOTM_STATE_START;  // 设置了一个状态 
                                                                                
    	return 0;                                                                   
}                                                                               

* #### 2.2.2 跳回上面的`do_bootm_states`,看到`bootm_os_get_boot_func` * #### 获取kernel 的加载函数方式如下: ```c // common/bootm_os.c 获取kernel 加载函数 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];  
}                                                                               
```c
	// common/bootm_os.c
	static boot_os_fn *boot_os[] = {  
	    [IH_OS_U_BOOT] = do_bootm_standalone,   
	#ifdef CONFIG_BOOTM_LINUX           
	    [IH_OS_LINUX] = do_bootm_linux,  // 执行这个, 可以看 2.2.4 
	#endif         
	// ... ...
	}

* #### 2.2.3 我们先回到上面的 `do_bootm_states` 函数,跟到最后面的 `boot_selected_os` ```c 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); // 这里调用进入kernel 的函数 // 也就是上面返回的 do_bootm_linux()
    /* Stand-alone may return when 'autostart' is 'no' */                       
    if (images->os.type == IH_TYPE_STANDALONE ||                                
        state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */              
        return 0;                                                               
    bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED);                             
#ifdef DEBUG                                                                    
    puts("\n## Control returned to monitor - resetting...\n");                  
#endif                                                                          
    return BOOTM_ERR_RESET;                                                     
}
* #### 2.2.4  do_bootm_linux
```c
        // 参考 2.2.2 最后的返回值,执行的是这个
	// arch/arc/lib/bootm.c   // kernel 的加载函数长这个样子
	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);       
	        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);  // 这里跳入Linux  --> 2.2.5
	    return 0;                                                                   
	}                                                                               
  • 2.2.5 boot_jump_linux

	// arch/arc/lib/bootm.c
	/* Subcommand: GO */                                                            
	static void boot_jump_linux(bootm_headers_t *images, int flag)                  
	{                                                                               
	    unsigned long machid = gd->bd->bi_arch_number;                              
	    char *s;                                                                    
	    void (*kernel_entry)(int zero, int arch, uint params);                      
	    unsigned long r2;                                                           
	    int fake = (flag & BOOTM_STATE_OS_FAKE_GO);                                 
	    // 这个在前面被分析出来是 0x82000000                                                                           
	    kernel_entry = (void (*)(int, int, uint))images->ep;                        
	                                                                                
	    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);                                                 
	                                                                                
	    if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)                               
	        r2 = (unsigned long)images->ft_addr;                                    
	    else                                                                        
	        r2 = gd->bd->bi_boot_params;                                            
			// ... ...  
			// ... ...
			// 这里进入kernel 不再返回	
            kernel_entry(0, machid, r2);    
	// ... ...
}                                              
posted @ 2017-06-03 14:51  陈富林  阅读(5572)  评论(0编辑  收藏  举报