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
    #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  包含                                                                                                                                         

  • 2. bootz 命令分析
    // 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 初始化一些东西
    //  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
    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 的流程分析
    // 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);                                        
    // ... ... 成功则不执行下面的,失败则执行后面的函数
}                                          

  • 2.2.1 看到上面的 bootm_start, 第一次,状态为 BOOTM_STATE_START
  • 我们需要调用 bootm_start(cmdtp, flag, argc, argv);
    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 的加载函数方式如下:
    // 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];  
    }                                                                               
    // 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
    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
        // 参考 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);    
    // ... ...
}                                              

转载于:https://www.cnblogs.com/chenfulin5/p/6937334.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值