• u-boot 流程分析


     

    u-boot 介绍:

      对于计算机来说 , 从一开始上机通电是无法直接启动操作系统的 , 这中间需要一个引导过程 , 嵌入式Linux系统同样离不开引导程序 ,  这个启动程序就叫启动加载程序(Bootloader)  ,Bootloader 主要是进行一些基础必要硬件的初始化 (cpu_init ,memory_init , UART_init ...) , 为最终调用 kernel 作准备 .

      对于嵌入式系统而言 , Bootloader 是基于特定的硬件平台实现的 . 因此 , 几乎不可能有一个Bootloader 能适用所有的嵌入式系统 , 不同的处理器架构都有不同的Bootloader , 不同的硬件也需要不同的Bootloader , Bootloader 不仅仅依赖CPU的体系架构 , 而且依赖嵌入式开发板设备的配置 , 也就是说 , 就算是应用相同cpu 体系架构的 两块开发版 , 要想让运行在一块板子上的 Bootloader 运行在另一块板子上 , 一般都需要修改Bootloader 的源程序 .

      但是 , 大部分的Bootloader 他们都有相似之处 , 而且相同cpu体系架构的Bootloader 很多源代码都有相似之处 , 所以 , 就产生了u - boot , u - boot 能支持PowerPC , ARM , MIPS ,和 X86 等体系结构 , 支持的板子也多达上百种 , 我们通过自己的修改也可以使用自己的板子 .

      

    u-boot 源码结构:

      u-boot 的 顶层目录下有30多个子目录 , 分别存放和管理不同的源程序 . 这些目录存放的文件也有其规则, 可以分为3类 .

      1 . 第一类目录与处理器体系架构和开发板硬件直接相关 .

      2 . 第二类目录是一些通用函数或驱动程序 .

      3 . 第三类是u-boot的应用程序, 工具 , 文档 .

      下面是相关目录的简介

      board       :  存放的开发板相关的目录 , 如 board/freescale 等目录.

      cpu        :  存放的cpu相关的目录 , 如 cpu/arm_cortexa8 等目录.

      lib_xxx     :  与体系架构相关的库文件 , 如与arm相关的库就存放在 lib_arm 目录当中 .

      include     : u-boot 使用的头文件 , 还有支持各种硬件平台的汇编文件 , 系统的相关配置文件和支持文件系统的文件 , 该目录下的configs 目录中有与开发板相关的配置文件 .

      common   : 实现u-boot 命令行下的支持的命令 , 每一条命令都对应一个文件 , 例如bootm 命令对应的就是cmd_bootm.c

      lib_generic  : 通用库函数的实现 .

      net       : 与网络协议相关的代码 , BOOTP , TFTP , RARP , 和NFS文件系统的实现 .

      fs       : 支持的文件系统 , 如 cramfs . fat . fdos . jffs2 和 registerfs.

      drivers             : u-boot 支持的设备驱动程序都放在该目录中 , 如串口驱动 , usb驱动 , 网卡驱动 .

      disk      : 对磁盘的支持.

      doc       :  文档目录 .

      tools     : 生成u-boot 的工具 .

      examples  : 一些独立运行的运用程序的例子 .

     

    u-boot 配置编译 :

      u-boot 的源码是通过makefile 来整体编译的 , 顶层目录下的Makefile 完成对开发板的整体配置 , 然后递归调用各级子目录下的Makefile , 最后把所有编译过的程序链接成u-boot 的映像 , 下面以arm_cortexa8处理器的 mx6q_sabresd 为例 , 介绍u - boot 的配置编译方法 , 并简单的分析其原理 .

      

      1 . 基本的配置编译方法

      配置编译u-boot 的方法十分简单 ,  只需要在u-boot 顶层目录下执行如下两个命令.

      # make <board_name>_config

      # mkae

      第一条命令为配置u-boot , 例如我们板子的的名字是mx6q_sabresd , 我们的命令就是 make mx6q_sabresd_config , 第二条命令是编译 , 一般我会在其后面加上 , make -j100 (请不要轻易尝试, 如果是处理器不是很好的情况下) .

      

      2 . 在编译完成后 , 我们会得到以下文件

        a . 在make mx6q_sabresd_config 后 , 我们会执行顶层目录的Makefile , 并执行mx6q_sabresd_config  规则:

        有如下代码      

     1 mx6solo_sabresd_config                                                         
     2 mx6solo_sabresd_mfg_config                                                     
     3 mx6solo_sabresd_android_config                                                 
     4 mx6dl_sabresd_config                                                           
     5 mx6dl_sabresd_mfg_config                                                       
     6 mx6dl_sabresd_android_config                                                   
     7 mx6q_sabresd_config                                                            
     8 mx6q_sabresd_android_config                                                    
     9 mx6q_sabresd_mfg_config                                                        
    10 mx6q_sabresd_iram_config    : unconfig                                          
    11     @[ -z "$(findstring iram_,$@)" ] ||                                     
    12         { echo "TEXT_BASE = 0x00907000" >$(obj)board/freescale/mx6q_sabresd/config.tmp ; 
    13           echo "... with iram configuration" ;                                 
    14         }                                                                       
    15     @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 mx6q_sabresd freescale mx6      

     

    unconfig 这个规则其实就是将以前的生成的配置文件全删除掉: (@的作用就是不显示)

    1 unconfig:                                                                       
    2     @rm -f $(obj)include/config.h $(obj)include/config.mk                      
    3         $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp                    
    4         $(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep       

     

     

      在这里,我们将调用$(MKCONFIG)

      也就是:

    1 MKCONFIG    := $(SRCTREE)/mkconfig 

      其实就是执行了 ./mkconfig    arm arm_cortexa8 mx6q_sabresd freescale mx6

      就是顶层目录下的mkconfig 文件:

      所以就有如下代码:         

     1 #                                                                               
     2 # Create include file for Make                                                  
     3 #                                                                               
     4 echo "ARCH   = $2" >  config.mk                                                 
     5 echo "CPU    = $3" >> config.mk                                                 
     6 echo "BOARD  = $4" >> config.mk                                                 
     7                                                                                 
     8 [ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk               
     9                                                                                 
    10 [ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk               
    11                                                                                 
    12 #                                                                                                                         

     

     

      我们会得到在 include/config.mk文件.

      这个文件的内容是  

    1 ARCH   = arm                                                                    
    2 CPU     = arm_cortexa8                                                           
    3 BOARD  = mx6q_sabresd                                                           
    4 VENDOR = freescale                                                              
    5 SOC    = mx6     

     

      根据上面的文件 , 我们可以得到在mx6q_sabresd  平台下 , 我们的相关目录如下

                    board/freescale/mx6q_sabresd/

                    cpu/arm_cortexa8/

                    cpu/arm_cortexa8/mx6/

                    lib_arm/

                    include/asm-arm/

                    include/configs/mx6q_sabresd.h

      上面这几个目录 ,就是我们真个u-boot在 mx6q_sabresd 这个平台下的主线所在.

        

      下面将一下 make 命令的执行过程

      首先说一下如果没有经过make mx6q_sabresd_config 会发生什么情况,

      它会报一个:

      System not configured  - see README   的错误.

      它生成的过程是这样的:

      

     1 ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))    /* 判断这几个文件是否存在 */
     2 ......
     3 all:                                                                          
     4 sinclude $(obj)include/autoconf.mk.dep                                          
     5 sinclude $(obj)include/autoconf.mk   
     6 
     7 ......
     8 
     9 else    # !config.mk                                                            
    10 all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin                        
    11 $(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot                                 
    12 $(SUBDIRS) $(TIMESTAMP_FILE) $(VERSION_FILE) gdbtools updater env depend       
    13 dep tags ctags etags cscope $(obj)System.map:                                   
    14     @echo "System not configured - see README" >&2                              
    15     @ exit 1                                                                    
    16 endif   # config.mk      

     

           

    u-boot 启动流程分析

      u-boot 的启动流程可以分为两个阶段 .

        一 . 第一阶段 .

         1 . 硬件设备初始化 (cpu)

         2 . 加载u- boot 第二阶段代码到RAM空间

         3 . 设置栈 , 为跳转c code 作准备.

         4 . 跳转到第二阶段代码入口.

        二 . 第二阶段 

         1 . 初始化本阶段使用的硬件设备 .

         2 . 为内核设置参数.

         3 .准备调用内核

     

     

    在这里我忘了一个文件, 在顶层目录中的u-boot.lds

    这是一个很重要的文件, 我么可以靠这个文件找到真正的入口

    里面有内容:

     1  . = 0x00000000;                                                                
     2  . = ALIGN(4);                                                                  
     3  .text :                                                                        
     4  {                                                                              
     5    board/freescale/mx6q_sabresd/flash_header.o (.text.flasheader)               
     6    cpu/arm_cortexa8/start.o                                                     
     7    board/freescale/mx6q_sabresd/libmx6q_sabresd.a (.text)                       
     8    lib_arm/libarm.a (.text)                                                     
     9    net/libnet.a (.text)                                                         
    10    drivers/mtd/libmtd.a (.text)                                                 
    11    drivers/mmc/libmmc.a (.text)                                                 
    12    . = DEFINED(env_offset) ? env_offset : .;                                    
    13    common/env_embedded.o(.text)                                                 
    14    *(.text)                       

    可以看到我们的入口文件是在  board/freescale/mx6q_sabresd/flash_header.o (.text.flasheader)

    参考: http://blog.csdn.net/kris_fei/article/details/50463018

    这里面启动了rom 并跳转到了start.S

    再介绍一个文件 , 在cpu/arm_cortexa8/u-boot.lds 文件中 , 该文件内容如下.

     

     27 /*  指定输出可执行文件是32位的ARM指令,小端模式的ELF格式 */                  
     28 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")      
     29 OUTPUT_ARCH(arm)        /*  指定可执行文件平台为ARM */                      
     30 ENTRY(_start)           /*  指定程序的入口为_start  */                      
     31 SECTIONS                                                                    
     32 {                                                                           
     33     . = 0x00000000;     /*  指明目标代码的起始代码地址为0x0位置开始,"."代表>    当前位置    */
     34                                                                             
     35     . = ALIGN(4);       /*  表示此处4字节对其       */                      
     36     .text   :                                                               
     37     {                                                                       
     38         cpu/arm_cortexa8/start.o    (.text)     /*  表示start.o是代码段的第>    一个.o文件  */
     39         *(.text)        /*  表示这是代码段的其余部分    */                  
     40     }                                                                       
     41                                                                             
     42     . = ALIGN(4);                                                           
     43     .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }   /* 这是只读段数据  */
     43     .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }   /* 这是只读段数据  */
     44                                                                             
     45     . = ALIGN(4);
     46     .data : { *(.data) }    /*  这表示数据段  */
     47
     48     . = ALIGN(4);
     49     .got : { *(.got) }
     50
     51     __u_boot_cmd_start = .;
     52     .u_boot_cmd : { *(.u_boot_cmd) }
     53     __u_boot_cmd_end = .;
     54
     55     . = ALIGN(4);
     56     __bss_start = .;
     57     .bss : { *(.bss) }
     58     _end = .;
     59 }

     

     

    如上注释 ,  第28行 , 指定输出可执行文件是32位的ARM指令,小端模式的ELF格式.

         第29行 , 指定指定可执行文件平台为ARM. 

         第30行 , 指定程序的入口为_start .

         第33行 , 指明目标代码的起始代码地址为0x0位置开始,"."代表>    当前位置 

         第38行 , 表示start.o是代码段的第>    一个.o文件 .

         从而得出 , 我们的入口文件是 cpu/arm_cortexa8/start.o , 因此 ,u-boot 的入口代码是对应源文件cpu/arm_cortexa8/start.S 

     

    cpu/arm_cortexa8/start.S 源码分析:

    一开始:      

     

     1 .globl _start                                                                   
     2 _start: b   reset                                                               
     3     ldr pc, _undefined_instruction                                              
     4     ldr pc, _software_interrupt                                                 
     5     ldr pc, _prefetch_abort                                                     
     6     ldr pc, _data_abort                                                         
     7     ldr pc, _not_used                                                           
     8     ldr pc, _irq                                                                
     9     ldr pc, _fiq                                                                
    10                                                                                 
    11 _undefined_instruction: .word undefined_instruction                             
    12 _software_interrupt:    .word software_interrupt                                
    13 _prefetch_abort:    .word prefetch_abort                                        
    14 _data_abort:        .word data_abort                                            
    15 _not_used:      .word not_used                                                  
    16 _irq:           .word irq                                                       
    17 _fiq:           .word fiq                                                       
    18 _pad:           .word 0x12345678 /* now 16*4=64 */                              
    19 .global _end_vect                                                               

     

    这一段代码其实从一开始的 跳转到 reset 函数  , 但是 , 这段代码设置了一个异常向量表 , 意思就是cpu 如果执行了异常的指令就会直接跳到这里 , 比如说除于0这种代码 .

     

    在37行直接跳到 reset  函数

      一开始  ,  u-boot 便把 cpu 设置为SVC模式(管理模式)

     

    1 reset:                                                                          
    2     /*                                                                          
    3      * set the cpu to SVC32 mode                                                
    4      */                                                                         
    5     mrs r0, cpsr                                                                
    6     bic r0, r0, #0x1f                                                           
    7     orr r0, r0, #0xd3                                                           
    8     msr cpsr,r0                                                                 

     

    跳到低水平的cpu_init : 

     

    1     bl  cpu_init_crit      //到里边看看                                        

     

    bl  指令是会回来的:

     

     1 /*************************************************************************      
     2  *                                                                              
     3  * CPU_init_critical registers                                                  
     4  *                                                                              
     5  * setup important registers                                                    
     6  * setup memory timing                                                          
     7  *  @cpu 最开始的初始化                                                         
     8  *************************************************************************/     
     9 cpu_init_crit:                                                                  
    10     /*                                                                          
    11      * Invalidate L1 I/D                                                        
    12      */                                                                         
    13     mov r0, #0          @ set up for MCR                                        
    14     mcr p15, 0, r0, c8, c7, 0   @ invalidate TLBs                               
    15     mcr p15, 0, r0, c7, c5, 0   @ invalidate icache                             
    16                                                                                 
    17     /*                                                                          
    18      * disable MMU stuff and caches //关闭mmu以及caches高速缓存器               
    19      */                                                                         
    20     mrc p15, 0, r0, c1, c0, 0                                                   
    21     bic r0, r0, #0x00002000 @ clear bits 13 (--V-)                              
    22     bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)    
    23     orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align                            
    24     orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB                             
    25     mcr p15, 0, r0, c1, c0, 0                                                   
    26                                                                                 
    27     /*                                                                          
    28      * Jump to board specific initialization...                                 
    29      * The Mask ROM will have already initialized                               
    30      * basic memory. Go here to bump up clock rate and handle                   
    31      * wake up conditions.                                                      
    32      */                                                                         
    33     mov ip, lr          @ persevere link reg across call                        
    34     bl  lowlevel_init       @ go setup pll,mux,memory  //跳到低水平的初始化     
    35     mov lr, ip          @ restore link                                          
    36     mov pc, lr          @ back to my caller                                                            

    在34行 , 我们又跳入lowlevel_init

      

     1 .globl lowlevel_init                                                            
     2 lowlevel_init:                                                                  
     3                                                                                 
     4     inv_dcache                           /*invalidate the D-CACHE */                                          
     5                                                                                 
     6     init_l2cc                            /* disable L2Cache */                                        
     7                                                                                 
     8     init_aips                            /* APIS  setup  */                                      
     9                                                                                 
    10     init_clock                          /*初始化时钟*/                                           
    11                                                                                 
    12     mov pc, lr                                                                  
    13                                                                                

     

    跳回start.S  

    过后直接设置栈   

     

     1     /* Set up the stack */                                                      
     2 stack_setup:                                                                    
     3     ldr r0, _TEXT_BASE      @ upper 128 KiB: relocated uboot                    
     4     sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area                            
     5     sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo                              
     6 #ifdef CONFIG_USE_IRQ            /* 跳过  */                                               
     7     sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)                  
     8 #endif                                                                          
     9     sub sp, r0, #12     @ leave 3 words for abort-stack                         
    10     and sp, sp, #~7     @ 8 byte alinged for (ldr/str)d                      

       清除bss段

     

    1 clear_bss:                                                                      
    2     ldr r0, _bss_start      @ find start of bss segment                         
    3     ldr r1, _bss_end        @ stop here                                         
    4     mov r2, #0x00000000     @ clear value                                       
    5 clbss_l:                                                                        
    6     str r2, [r0]        @ clear BSS location                                    
    7     cmp r0, r1          @ are we at the end yet                                 
    8     add r0, r0, #4      @ increment clear index pointer                         
    9     bne clbss_l         @ keep clearing till at end                             

     

       最后就便是跳到c  语言代码  

    1     ldr pc, _start_armboot  @ jump to C code                                    
    2                                                                                 
    3 _start_armboot: .word start_armboot   @跳入c语言代码,lib_arm/board.c            

     

     

    lib_arm/board.c 源码分析:

        首先介绍两个重要的结构体以及一个重要的数组.

    gd_t 结构体 , 该结构体是用来储存全局数据区的数据 , 这个结构体在 include/asm-arm/global_data.h 中定义如下

     

     1 /*                                                                              
     2  * The following data structure is placed in some memory wich is                
     3  * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or            
     4  * some locked parts of the data cache) to allow for a minimum set of           
     5  * global variables during system initialization (until we have set             
     6  * up the memory controller so that we can use RAM).                            
     7  *                                                                              
     8  * Keep it *SMALL* and remember to set CONFIG_SYS_GBL_DATA_SIZE > sizeof(gd_t)  
     9  */                                                                            
    10 
    11 typedef struct  global_data {                                                   
    12     bd_t        *bd;                                                            
    13     unsigned long   flags;                                                      
    14     unsigned long   baudrate;                                                   
    15     unsigned long   have_console;   /* serial_init() was called */     (被serial_init() 调用)         
    16     unsigned long   reloc_off;  /* Relocation Offset */                         
    17     unsigned long   env_addr;   /* Address  of Environment struct */            
    18     unsigned long   env_valid;  /* Checksum of Environment valid? */            
    19     unsigned long   fb_base;    /* base address of frame buffer */              
    20     void        **jt;       /* jump table */                                    
    21 } gd_t;                                                                         

     

    bd_t 结构体用于存放板级相关的全局数据 , 是gd_t 结构体指针成员 bd 的结构体类型 , 在 include/asm-arm/u-boot.h中定义如下:

     

    typedef struct bd_info {                                                        
        int         bi_baudrate;    /* serial console baudrate */        /*串口波特率*/           
        unsigned long   bi_ip_addr; /* IP Address */                                
        struct environment_s           *bi_env;                                     
        ulong           bi_arch_number; /* unique id for this board */     /*开发板机器码*/           
        ulong           bi_boot_params; /* where this board expects params */  /*内核参数开始地址*/     
        struct              /* RAM configuration */                 /*内存配置信息结构体*/                
        {                                                                           
        ulong start;                                                                
        ulong size;                                                                 
        }           bi_dram[CONFIG_NR_DRAM_BANKS];                                  
    } bd_t;                                                                         

     

    init_sequence 数组:

     

     1 //一系列的初始化                                                                
     2 init_fnc_t *init_sequence[] = {                                                 
     3 #if defined(CONFIG_ARCH_CPU_INIT)                                               
     4     arch_cpu_init,      /* basic arch cpu dependent setup   */                  
     5 #endif                                                                          
     6     board_init,     /* basic board dependent setup */                           
     7 #if defined(CONFIG_USE_IRQ)                                                     
     8     interrupt_init,     /* set up exceptions */   //不进入                      
     9 #endif                                                                          
    10     timer_init,     /* initialize timer */  //初始化定时器                      
    11     env_init,       /* initialize environment */                                
    12     init_baudrate,      /* initialze baudrate settings, 波特率由用户自己设定 */ 
    13     serial_init,        /* serial communications setup */                       
    14     console_init_f,     /* stage 1 init of console */                           
    15     display_banner,     /* say that we are here */                              
    16 #if defined(CONFIG_DISPLAY_CPUINFO)                                             
    17     print_cpuinfo,      /* display cpu info (and speed) */                      
    18 #endif                                                                          
    19 #if defined(CONFIG_DISPLAY_BOARDINFO)                                           
    20     checkboard,     /* display board info */                                    
    21 #endif                       
    22 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)                        
    23     init_func_i2c,                                                              
    24 #endif                                                                          
    25     dram_init,      /* configure available RAM banks */                         
    26 #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)                             
    27     arm_pci_init,                                                               
    28 #endif                                                                          
    29     display_dram_config,                                                        
    30     NULL,                                                                       
    31 };                                                                                       

     

     

    下面介绍start_armboot 这段代码的流程 :

    首先直接进行一系列的初始化:

     

    1     //这个是个小关键,这里面会运行一系列的初始化           ,就是上面那个数组内一个个函数的运行 , 有时间我会将一个个函数详细的讲解                      
    2     for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {         
    3         if ((*init_fnc_ptr)() != 0) {                                           
    4             hang ();                                                            
    5         }                                                                       
    6     }                                                                           

     

    上面这段代码 , 会直接运行init_sequence 数组内的每一个函数, cpu  , 时钟 , 串口 , 控制台 , 等一系列初始化.

     

    1     //标准输入输出初始化                                                        
    2     stdio_init ();  /* get the devices list going. */                           

     

    1     jumptable_init ();      /* jump table init (chen) */                        
    1     console_init_r ();  /* fully init console as a device */                    
    2     /* 对所有的控制台进行初始化并打印信息,主要是标准输入输出以及错误输出*/      
    1     /* enable exceptions */                                                     
    2     enable_interrupts ();       /*配置寄存器使中断使能  */                      
    1     board_late_init ();     /* mainly do one thing,setup i2c  (chen) */        
    1     /* main_loop() can return to retry autoboot, if so just run it again. */    
    2     //最后的函数                                                                
    3     for (;;) {                                                                  
    4         main_loop ();                                                           
    5     }                                                                           


    今天由于时间关系 , 只能暂时写到这里 . 之后我会将该流程详细的讲解.

  • 相关阅读:
    centos7安装docker-ce最新版
    输出第一个hello word程序(day1)
    centos7安装python3及ipython
    华为防火墙ping不通直连设备
    cisco网络设备基本命令
    linux中ftp
    查找你的域名DNS服务器
    Grafana 安装配置启动
    Jmeter 循环控制器
    CentOs 7查看端口占用情况,以及出现未找到命令的情况
  • 原文地址:https://www.cnblogs.com/chenfulin5/p/5676266.html
Copyright © 2020-2023  润新知