bootm分析(u-boot-2016.11)

目录

一、作用
二、使用方法
三、环境需求
四、流程分析
五、加载地址

一、作用


    boot application image stored in memory  //启动存放在内存中的镜像(二进制文件),一般就是启动内核镜像。
    在使用bootm命令之前,需要先将内核镜像放到内存里边,比如:1.从存储介质中读出 2.用tftp命令下载到指定的内存地址。


二、使用方法


bootm image_addr ramdisk_addr dtb_addr
image_addr:   内核镜像存放在DDR中的位置
ramdisk_addr: ramdisk地址
dtb_addr:     设备树地址
任何一项都可以没有,如果没有,用 “-’代替
用法1:bootm                                           //使用默认的镜像地址启动
用法2:bootm image_addr                     //指定镜像地址启动。一般是uImage
用法3:bootm image_addr - dtb_addr  //指定设备树地址


三、环境需求


arm 架构处理器对 linux 内核启动之前环境的五点需求
1、cpu 寄存器设置
    * R0 = 0
    * R1 = 板级 id
    * R2 = 启动参数在内存中的起始地址
2、cpu 模式
    * 禁止所有中断
    * 必须为SVC(超级用户)模式
3、缓存、MMU
    * 关闭 MMU
    * 指令缓存可以开启或者关闭
    * 数据缓存必须关闭并且不能包含任何脏数据
4、设备
    * DMA 设备应当停止工作
5、boot loader 需要跳转到内核镜像的第一条指令处

四、流程分析

bootm.c(cmd)
    do_bootm
        //如果argc大于1,则进行其他处理
        //bootm_headers_t  images;        //uboot_sdk/common        /*  pointers  to  os/initrd/fdt  images  */
        do_bootm_states(cmdtp,  flag,  argc,  argv,  BOOTM_STATE_START  |  BOOTM_STATE_FINDOS  |                                       
              BOOTM_STATE_FINDOTHER  | BOOTM_STATE_LOADOS  |  BOOTM_STATE_OS_PREP  |     
              BOOTM_STATE_OS_FAKE_GO  |  BOOTM_STATE_OS_GO,  &images,  1)      bootm.c(common)
            images->state  |=  states;
            /*  设置images.state为BOOTM_STATE_START;设置lmb  (管理镜像的内存)*/
            bootm_start(cmdtp,  flag,  argc,  argv)                      //  bootm.c(uboot_sdk/common)
                memset((void  *)&images,  0,  sizeof(images));
                images.verify  =  getenv_yesno("verify");
                boot_start_lmb(&images);      //logic  memory  block,用于管理镜像的内存
                    mem_start  =  getenv_bootm_low();        //获得内存sdram的起始地址
                    mem_size  =  getenv_bootm_size();        //获得内存sdram的大小
                    lmb_add(&images->lmb,  (phys_addr_t)mem_start,  mem_size);
                images.state  =  BOOTM_STATE_START;
                
            /*  根据header进行判断,设置images全局变量成员,打印header的信息  */
            bootm_find_os(cmdtp,  flag,  argc,  argv)
                const  void  *os_hdr;
                /*  images.os.image_start  :  有效数据地址    images.os.image_len:总长度(header和有效数据)  */
                os_hdr  =  boot_get_kernel(cmdtp,  flag,  argc,  argv,  &images,  &images.os.image_start,
                                                                      &images.os.image_len);
                    /*  确定kernel的地址,如果是0,则用默认地址,如果不是0,则设置为传进来的地址  */
                    img_addr  =  genimg_get_kernel_addr_fit(argc  <  1  ?  NULL  :  argv[0],  &fit_uname_config,  &fit_uname_kernel);
                    /*  如果配置了CONFIG_HAS_DATAFLASH则从dataflash中读出内核。本处,没有配置,一般是先用loadk命令
                     读出了  */
                    img_addr  =  genimg_get_image(img_addr);
                    *os_data  =  *os_len  =  0;        //images.os.image_start  =  0;  images.os.image_len  =  0
                    buf  =  map_sysmem(img_addr,  0);            //buf  =  (void  *)(unsigned  long  int)img_addr;
                    switch  (genimg_get_format(buf))  {            // 确定magic是否匹配(0x27051956),匹配则返回
                                                                                            // IMAGE_FORMAT_LEGACY
                        case  IMAGE_FORMAT_LEGACY:
                            hdr  =  image_get_kernel(img_addr,  images->verify)    
                                       //image_header_t  *hdr  =  (image_header_t  *)img_addr;
                                检测magic是否是0x27051956
                                检测头部的crc校验值
                                打印出head的内容:name,type,data  size,load  address,entry  point
                                    如果是IH_TYPE_MULTI或者IH_TYPE_SCRIPT类型,则打印其偏移值
                                检测实际数据的crc校验值(如果images->verify不是0,即环境变量verify不是0)
                                检测架构是否匹配(如果架构类型是0(invalid)则不匹配)
                                获得镜像的数据起始地址和长度 //先判断类型,后操作:IH_TYPE_KERNEL、
                                                                                    // IH_TYPE_KERNEL_NOLOAD,IH_TYPE_STANDALONE用同一组函数,                                                                                      // IH_TYPE_MULTI用另一组函数
                                    *os_data  =  image_get_data(hdr);                        //images.os.image_start  =  image_get_data(hdr)
                                        ((ulong)hdr  +  image_get_header_size());
                                    *os_len  =  image_get_data_size(hdr);                //images.os.image_len  =    image_get_data_size(hdr)
                                        (image_get_size(hdr)  +  image_get_header_size())
                                /*  把镜像的header拷贝到变量中,从而允许kernel解压时覆盖  */
                                memmove(&images->legacy_hdr_os_copy,  hdr,    sizeof(image_header_t));
                                images->legacy_hdr_os  =  hdr;
                                images->legacy_hdr_valid  =  1;
                        return  buf
                    images.os.type  =  image_get_type(os_hdr);            //映像类型
                    images.os.comp  =  image_get_comp(os_hdr);            //compression    压缩方式
                    images.os.os      =  image_get_os(os_hdr);                //  操作系统类型
                    
                    images.os.end    =  image_get_image_end(os_hdr);    //  结束地址
                    images.os.load  =  image_get_load(os_hdr);            //  头信息中定义的加载地址
                    images.os.arch  =  image_get_arch(os_hdr);            
                    /*  entry  point  of  os  */
                    images.ep  =  image_get_ep(&images.legacy_hdr_os_copy);
                    //如果类型是IH_TYPE_KERNEL_NOLOAD,有额外操作
                    images.os.start  =  map_to_sysmem(os_hdr);      //(unsiged  long)os_hdr    映像起始地址,包括头信息
                
                /*加载可用的ramfs,加载设备树
                bootm_find_other(cmdtp,  flag,  argc,  argv)
                
                关中断
                /*  将内核解压或者拷贝到加载地址  */
                bootm_load_os(images,  &load_end,  0);        bootm.c(common)
                    image_info_t  os  =  images->os;    
                    ulong  load  =  os.load;
                    ulong  image_len  =  os.image_len;

                    load_buf  =  map_sysmem(load,  0);
                    image_buf  =  map_sysmem(os.image_start,  image_len);
                    /*  解压内核  */
                    /*  #define  CONFIG_SYS_BOOTM_LEN  0x800000  */
                    bootm_decomp_image(os.comp,  load,  os.image_start,  os.type,  load_buf,  image_buf,  image_len,  
                                                              CONFIG_SYS_BOOTM_LEN,  load_end);
                        若压缩类型是:未压缩
                            若load  ==  image_start则退出,否则进行内存移动(将首地址为os.image_start的数据拷贝到首地址为
                                                                                                                       os.load)
                        如果有压缩,则解压到load_buf
                    /*  清dcache  */
                    flush_cache(load,  ALIGN(*load_end  -  load,  ARCH_DMA_MINALIGN));
                    判断映像文件存放地址和加载地址是否重叠
                lmb_reserve(&images->lmb,  images->os.load,  (load_end  -  images->os.load));
                
                重定位ramfs
                设置设备树区域
                /*  获得启动的函数(根据系统类型)。本处是do_bootm_linux  */
                boot_fn  =  bootm_os_get_boot_func(images->os.os);
                /*  设置启动标记  */
                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);
                    do_bootm_linux(BOOTM_STATE_OS_PREP,  argc,  argv,  images);            //bootm.c(arch/arm/lib)
                        /*  初始化启动的参数  */
                        boot_prep_linux(images);                                                                        //bootm.c(arch/arm/lib)
                            static  struct  tag  *params;
                            获得bootargs和dbg环境变量,把这两个变量放到一起(一个数组里)
                            setup_start_tag  (gd->bd);                                            /*  初始化参数列表起始符  */
                                    params  =  (struct  tag  *)bd->bi_boot_params;
                                    ...
                                    params  =  tag_next  (params);
                            setup_serial_tag  (¶ms);                                      /*  初始化串口参数  */
                                //传进来参数为tmp
                                设置params的成员
                                params  =  tag_next  (params);
                                *tmp  =  params;
                            setup_commandline_tag(gd->bd,  commandline);        /*  初始化命令参数  */
                                char  *p;
                                for  (p  =  commandline;  *p  ==  '  ';  p++);
                                params->hdr.tag  =  ATAG_CMDLINE;
                                params->hdr.size  =      (sizeof  (struct  tag_header)  +  strlen  (p)  +  1  +  4)  >>  2;
                                strcpy  (params->u.cmdline.cmdline,  p);
                                params  =  tag_next  (params);
                            setup_revision_tag  (¶ms);                                  /*  初始化版本参数  */
                            setup_memory_tags  (bd);                                              /*  初始化内存参数  */
                            setup_initrd_tag(gd->bd,  images->initrd_start,  images->initrd_end);    /*  初始化虚拟磁盘参数  */
                            setup_board_tags(¶ms);                                        /*  初始化fb参数  */
                            setup_end_tag(gd->bd);                                                /*  初始化参数列表结束符  */
                        boot_jump_linux(images,  flag);        //bootm.c(arch/arm/lib)
                            unsigned  long  machid  =  gd->bd->bi_arch_number;
                            char  *s;
                            void  (*kernel_entry)(int  zero,  int  arch,  uint  params);
                            unsigned  long  r2;
                            
                            kernel_entry  =  (void  (*)(int,  int,  uint))images->ep;
                            s  =  getenv("machid");
                            announce_and_cleanup(fake);
                                cleanup_before_linux()
                                    cleanup_before_linux_select(CBL_ALL)
                                        关中断
                                        关闭dcache
                                        关闭外部cache
                                        使所有dcache无效
                                        关闭icache
                                        使所有icache无效
                            r2  =  gd->bd->bi_boot_params
                            kernel_entry(0,  machid,  r2);


五、加载地址


    uImage的头部64字节信息,对应image_header_t,有个ih_load成员,代表image实际数据的地址(不包括头部),这个值是编译时就确定的,经过bootm命令处理以后,实际image会放到ih_load指定的地址上。最终会直接到ih_load指定的地址处运行。
对于未压缩的uImage:
    1.若运行bootm命令,默认加载地址不是ih_load指定的地址,则将默认加载地址处的数据移动到ih_load指定的位置                           (memmov)。
     2.若运行bootm  image_addr,image_addr不是ih_load指定的地址,则将image_addr处的数据移动到ih_load指定的位置
           (memmov)。
对于压缩的uImage
    1.若运行bootm命令,默认加载地址不是ih_load指定的地址,则将默认加载地址处的数据解压到ih_load。
    2.若运行bootm  image_addr,image_addr不是ih_load指定的地址,则将image_addr处的数据解压到ih_load。
总结:bootm  的image_addr可以是不影响uboot运行、不影响mmu映射、不影响ramdisk和dtb的任意内存地址。
代码流程分析如下:
bootm.c(cmd)
    do_bootm
        //如果argc大于1,则进行其他处理
        //bootm_headers_t  images;        //uboot_sdk/common        /*  pointers  to  os/initrd/fdt  images  */
        do_bootm_states(cmdtp,  flag,  argc,  argv,  BOOTM_STATE_START  |  BOOTM_STATE_FINDOS  |
                                 BOOTM_STATE_FINDOTHER  | BOOTM_STATE_LOADOS  |  BOOTM_STATE_OS_PREP  |
                                 BOOTM_STATE_OS_FAKE_GO  |  BOOTM_STATE_OS_GO,  &images,  1)      bootm.c(common)
            images->state  |=  states;
            /*  设置images.state为BOOTM_STATE_START;设置lmb  (管理镜像的内存)*/
            bootm_start(cmdtp,  flag,  argc,  argv)                      //  bootm.c(uboot_sdk/common)
                ...
            /*  根据header进行判断,设置images全局变量成员,打印header的信息  */
            bootm_find_os(cmdtp,  flag,  argc,  argv)
                const  void  *os_hdr;
                /*  images.os.image_start  :  有效数据地址    images.os.image_len:总长度(header和有效数据)  */
                os_hdr  =  boot_get_kernel(cmdtp,  flag,  argc,  argv,  &images,  &images.os.image_start,  &images.os.image_len);
                          /*  确定kernel的地址,如果是0,则用默认地址,如果不是0,则设置为传进来的地址  */
                          img_addr  =  genimg_get_kernel_addr_fit(argc  <  1  ?  NULL  :  argv[0],  &fit_uname_config,  
                                                  &fit_uname_kernel);
                          buf  =  map_sysmem(img_addr,  0);           // buf  =  (void  *)(unsigned  long  int)img_addr;
                          switch  (genimg_get_format(buf))  {            // 确定magic是否匹配(0x27051956),匹配则返回
                                                                                                   // IMAGE_FORMAT_LEGACY
                               case  IMAGE_FORMAT_LEGACY:
                                      hdr  =  image_get_kernel(img_addr,  images->verify)
                                             // image_header_t  *hdr  =  (image_header_t  *)img_addr;
                                       images.os.image_start  =  image_get_data(hdr)
                                             // images.os.image_start = ((ulong)hdr + image_get_header_size());

                             return  buf
                        images.os.load    =  image_get_load(os_hdr);
                        images.os.start  =  (unsiged  long)os_hdr   // 映像起始地址,包括头信息
                bootm_load_os(images,  &load_end,  0);
                      ulong  load  =  os.load;
                      load_buf  =  os.load
                      image_buf  =  os.image_start
                      bootm_decomp_image(os.comp,  load,  os.image_start,  os.type,  load_buf,  image_buf,  image_len,  
                                                                CONFIG_SYS_BOOTM_LEN,  load_end);
                      若没有压缩
                                  若load  ==  image_start则退出,否则进行内存移动(将os.image_start的数据移动到os.load)
                      若压缩了
                                  解压到os.load


你可能感兴趣的:(Linux)