zoukankan      html  css  js  c++  java
  • 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;"    
    
    # loadbootenv  
     "mmcdev=0"    # 开始为零,如果第一次mmcboot 不行第二次就可以设置为1
     "bootenv=uEnv.txt" 
      "loadbootenv=load mmc ${mmcdev} ${loadaddr} ${bootenv}"  	
    
    # importbootenv
       "importbootenv=echo Importing environment from mmc ...; "  
        "env import -t $loadaddr $filesize"   
    
    # loadimage
    "loadimage=load mmc ${bootpart} ${loadaddr} ${bootdir}/${bootfile}"  
    
    # 到最后的 ---->
    # 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;"       
    
    # mmcargs
        "mmcargs=setenv bootargs console=${console} "                              
        "${optargs} "                                                          
        "${mtdparts} "                                                         
        "root=${mmcroot} "                                                     
        "rootfstype=${mmcrootfstype}"                                        
    
    #  loadfdt
       "loadfdt=load mmc ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile}"    
    
    # 最后执行   
            "bootz ${loadaddr} - ${fdtaddr}; "   
    # 结合下面的, 也就是 bootz  0x82000000 - 0x88000000 
        # ---> 接下面 bootz 命令 ,这里可以跳到 2. 
    
    # 这里变量的定义最后我在 include/configs/ti_armv7_common.h 找到
    	#define DEFAULT_LINUX_BOOT_ENV                                                 
        "loadaddr=0x82000000"                                                    
        "kernel_addr_r=0x82000000"                                               
        "fdtaddr=0x88000000"                                                     
        "fdt_addr_r=0x88000000"                                                  
        "rdaddr=0x88080000"                                                      
        "ramdisk_addr_r=0x88080000"                                              
        "scriptaddr=0x80000000"                                                  
        "pxefile_addr_r=0x80100000"                                              
        "bootm_size=0x10000000"             
    
    # 这个宏被 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
    ",              
                    load_addr);                                                     
        } else {                                                                 
            images->ep = simple_strtoul(argv[0], NULL, 16); 
    		// 在这里,参数不为0, 所以 image->ep == 0x82000000, 这个在后面被调用  
                      
            debug("*  kernel: cmdline image address = 0x%08lx
    ",                   
                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!
    ");                                  
        	    return 1;                                                               
        	}                                                                           
      
        	*start = zi->zi_start;        
        	*end = zi->zi_end;      
            // 这里打印的信息
    		// Kernel image @ 0x82000000 [ 0x000000 - 0x417248 ]  
        	printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]
    ", 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
    ",                
                   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("
    ## Control returned to monitor - resetting...
    ");                  
    #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!
    ");                                  
    	            return;                                                             
    	        }                                                                       
    	        printf("Using machid 0x%lx from environment
    ", machid);                
    	    }                                                                           
    	                                                                                
    	    debug("## Transferring control to Linux (at address %08lx)"                
    	        "...
    ", (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);    
    	// ... ...
    }                                              
    
  • 相关阅读:
    彻底弄懂GMT、UTC、时区和夏令时
    揭秘webpack loader
    揭秘webpack plugin
    封装axios
    webpack优化之玩转代码分割和公共代码提取
    node.js操作数据库之MongoDB+mongoose篇
    GitHub项目徽标
    Java中容易遗漏的小知识点( 一 )(为了和小白一样马上要考试的兄弟准备的,希望小白和大家高过不挂)
    QNX Message Passing,一个让人头秃的 IPC BUG
    【百面】02_模型评估
  • 原文地址:https://www.cnblogs.com/chenfulin5/p/6937334.html
Copyright © 2011-2022 走看看