首页 > 代码库 > bootloader

bootloader

[Makefile]
[1] 规则
    目标: 依赖
    [TAB]命令(命令名 参数 依赖 目标)
    
[2] 难点
    1. 自动变量
       作用域在一个规则中, 如: $@(目标, 每条规则都只有一个目标), $<(第一个依赖), $^(所有依赖)
       例:
       hello.o world.o: hello.h
       等价于
       hello.o: hello.h
       world.o: hello.h
       
    2. 模式规则
       %.o: %.c
       [TAB]$(CC) $(CFLAGS) -c -o $@ $<
       
    3. 自动推倒
       要生成的目标没有显示的规则,这时会去找通用规则(模式规则),找通用规则的过程叫自动推导
       寻找规则的过程:
       1. 在Makefile中,寻找生成hello.o的规则,如果找到...
          如:
          hello.o: hello.c 
          
       2. 在Makefile中, 寻找模式规则, 如果找到匹配的模式规则, 需要查看当前目录下是否有模式规则需要的依赖文件
          如果有,则使用模式规则生成目标,如果没有继续寻找下一个模式规则...
          如:
          %.o: %.c
          %.o: %.S
          
       3. 在Make程序内部,寻找模式规则(隐式规则),生成目标
       
[ARM工程]
[1] 目录
    board         跟主板相关的配置和源码代码(主板的初始化代码)
    common        主程序源码及配置
    cpu           启动代码及配置
    drivers       主板无关的设备驱动及配置
    include       头文件
    lib           库源码及配置
    
[2] 源文件
    *.c           C语言源代码
    *.S           带预处理命令的汇编源代码
    map.lds       链接脚本
    Makefile      编译规则
    config.mk     编译命令及编译参数(是Makefile的一部分), 会影响编译命令和参数的硬件目录,都需要config.mk
    
[3] 目标文件
    fsc100        ELF格式的可执行文件
    fsc100.bin    Binary格式的可执行文件
    fsc100.dis    ELF格式的可执行文件反汇编文件
    fsc100.map    详细的符号表文件
    System.map    符号表文件
    
[4] 工程讲解(Makefile和config.mk)
    Makefile和config.mk编译源代码为可执行文件
    注意:每个模块打包成静态库的好处:
          1. 便于管理
          2. 链接成可执行程序时,没有被启动代码调用的代码不会被链接到可执行文件中
    
[5] 如何添加驱动模块?
    1. 添加源代码
    2. 添加Makefile
       拷贝别的目录下的Makefile,修改下列变量的值:
       LIB                 最终生成的*.a
       SOBJS               *.S源代码编译出来的*.o
       COBJS               *.c源代码编译出来的*.o
       START               启动代码生成的*.o


    3. 修改顶层目录下的Makefile,添加新目录生成的目标文件到工程
       OBJS                启动代码的目标文件(*.o)
       LIBS                非启动代码的目标文件(*.a)
       
[u-boot]
[1] 目录
    cpu(cpu/名称/...)                  启动代码、cpu内部驱动和配置
    board(board/芯片厂家/主板名/...)   启动代码中要使用的硬件的驱动代码,主板的初始化代码
    common                             命令输入、解析和执行代码
    net                                网络协议
    drivers                            通用硬件设备驱动
    fs                                 文件系统源码
    disk                               磁盘分区驱动
    lib_arm                            arm架构的cpu都需要的代码,主板的初始化代码和库代码 
    tools                              工具软件源码  
    doc                                作者写的源代码说明
    README                             简单的u-boot介绍、目录树说明、配置项说明
    
[2] 文件
    1. 源文件
       *.c/*.S/*.h                     源代码文件
       Makefile                        编译规则(有源代码的目录下都会有)
       config.mk                       编译命令和编译参数(cpu、board、lib_cpu架构、nand_spl)
       *.lds                           链接脚本
       rules.mk                        产生*.c(*.S): xxx.h ...
       mkconfig                        配置的shell脚本
       见《文件作用表.bmp》
       
    2. 目标文件
       u-boot                          ELF可执行文件
       u-boot.bin                      Binary可执行文件
       u-boot.map                      详细的符号表
       System.map                      符号表
    
[3] 特点
    1. 支持很多CPU
    2. 支持很多主板
    3. 支持很多通用硬件
    
    4. 工程管理和代码比较混乱
    何一款嵌入式产品,都不会用到所有的源代码,所以用u-boot的源码生成目标文件的步骤:
    1. 选择需要的源代码
    2. 编译选取的源代码
    
[4] 编译(必须掌握)
    1. 配置--根据主板的硬件配置和需要的命令,选取需要的源代码
       # make fsc100(主板名)_config
       
    2. 编译--编译选取的源代码
       # make
       
    3. 清除编译
       # make clean
       
    4. 清除配置
       # make clobber
    
[5] 配置原理
    1. 过程
       见《u-boot配置流程图》
       
    2. 结果
       include/config.mk                                     选取源代码所在的目录
       include/config.h                                      选取源代码
          include/configs/fsc100.h                           主板的配置项
             include/config_cmd_default.h                    编译到u-boot可执行程序的命令的配置项
             (include/config_cmd_all.h                       u-boot源码中支持的所有命令的配置项)
             
     3. 原理
        (1) 配置命令
           # make fsc100_config
           
           命令原理
           fsc100_config: unconfig
           @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 fsc100 samsung s5pc1xx
           @mkconfig fsc100(主板名) arm(架构) arm_cortexa8(cpu名) fsc100 samsung(芯片厂家) s5pc1xx(SOC名)
           
        (2) 配置项原理 
            1. 控制源代码文件是否编译到u-boot中的配置项--选取文件
               例: 在include/configs/fsc100.h定义:
                   #define CONFIG_DRIVER_DM9000  1  (顶层目录下的Makefile会转换CONFIG_DRIVER_DM9000宏定义为一个Makefile变量)
                                                 1  (CONFIG_DRIVER_DM9000 = y) 
                                                 0  (CONFIG_DRIVER_DM9000 = 0) 
                                                 
                   宏定义(include/configs/fsc100.h)    被转化为(include/autoconf.mk)                 
                   #define CONFIG_DRIVER_DM9000  1     CONFIG_DRIVER_DM9000 = y     
                   
                   Makefile
                      config.mk
                        include/autoconf.mk
                                        
               在drivers/net/Makefile中引用
                 CONFIG_DRIVER_DM9000=y (include/autoconf.mk)   
                 
                COBJS-$(CONFIG_DRIVER_DM9000) += dm9000x.o
                ...
                
                COBJS := $(COBJS-y)
                OBJS := $(COBJS)
                  $(LIBS): .depend $(OBJS)
                        $(AR) ...
                        
               研究方法:
               1. 查找CONFIG_DRIVER_DM9000
                  grep CONFIG_DRIVER_DM9000 * -r
                  在autoconf.mk文件中发现"CONFIG_DRIVER_DM9000=y”c
                        
               2. 找出autoconf.mk文件生成方法
                  grep autoconf.mk * -r
                  在Makefile中找到生成autoconf.mk的规则


               3. 查看生成autoconf.mk的规则
               
           2. 条件编译原理选取源代码--选取文件中的一部分代码
              例: 在include/configs/fsc100.h定义:
                  #define CONFIG_CMD_PING  1
                  
           3. 提供数据给源代码的配置项
              宏替换原理
              例: 在include/configs/fsc100.h定义:
                  #define CONFIG_DM9000_BASE 0x88000000
                  在drivers/net/dm9000x.c里面引用
             
[6] 编译原理
    1. 修改代码段的运行地址
       (1) board/$(VENDOR)/$(BOARD)/config.mk
           TEXT_BASE = 代码段运行地址
       
       (2) 链接脚本
           不推荐
    
    2.  子目录下的Makefile
       拷贝别的目录下的Makefile,修改下列变量的值:
       LIB                 最终生成的*.a
       SOBJS               *.S源代码编译出来的*.o
       COBJS               *.c源代码编译出来的*.o
       START               启动代码生成的*.o
       
    3. 顶层目录的Makefile
       OBJS                启动代码的目标文件(也可以在链接脚本中实现)
       LIBS                非启动代码的目标文件
       
[7] 如何添加模块到工程中?(必须掌握)
    1. 添加源代码
       例:
       drivers/uart/uart.c(记得在start_armboot函数中调用这个文件中的函数,否则uart.c中的函数不会被链接到最终的可执行代码中)
       
    2. 子目录下的Makefile
       拷贝别的目录下的Makefile,修改下列变量的值:
       LIB                 最终生成的*.a
       SOBJS               *.S源代码编译出来的*.o
       COBJS               *.c源代码编译出来的*.o
       START               启动代码生成的*.o
       
       例:
       LIB := libuart.a
       SOBJS := 
       COBJS := uart.o(非启动代码的*.c编译出来的目标文件)
       START := uart.o(启动代码编译出来的目标文件)
       
     3. 修改顶层目录下的Makefile,添加新目录生成的目标文件到工程
        (1) 如果添加的模块是非启动代码, 添加模块的Makefile生成的目标文件到工程
            (将目标文件放入顶层目录下的Makfile的LIBS变量中)
            例:
            LIBS += drivers/uart/libuart.a
            
        (2) 如果添加的模块是启动代码,方法如下:
            1. 添加模块的Makefile生成的目标文件到工程
               (将目标文件放入顶层目录下的Makfile的OBJS变量中)
               例:
               OBJS += drivers/uart/uart.o
            
               $(OBJS):...                 (应该是标志的u-boot做好的)
                  $(MAKE) -c $(dir $@) $(notdir $@)
               
             2. 添加模块的Makefile生成的目标文件到工程
                (1) 将目标文件放入顶层目录下的Makfile的OBJS(LIBS)变量中
                    例:
                    LIBS += drivers/uart/libuart.a
                    
                (2) 在链接脚本中修改链接顺序
                    例:
                    .text :                      (不推荐)
                    {
                       cpu/arm_cortexa8/start.o (.text)
                       drivers/uart/uart.o      (.text)
                       *.o                      (.text)
                    }    


[8] 源代码编辑软件
    vi + ctags                     查看源代码
    1. 进入顶层目录,然后打开任意的文件,然后按F9新建tags索引文件
    2. 查找定义: 把光标移动要查找的标识符,然后按ctrl+]
    3. 回退: ctrl + o
    
    sourceinside
    建立工程,添加所有文件到工程
    
[9] 源代码分析
    (1) 启动操作系统
        第一阶段: 自启动
                  硬件初始化
                     CPU初始化
                        关闭中断进入SVC模式
                        关闭MMU和Cache
                     
                     SOC初始化
                         如果有,需要关闭看门狗
                         如果需要加快代码的执行速度, 初始化PLL(clock control)
                         UART初始化
                         如果需要用到DMA,就初始化
                         Nandflash控制器初始化(为读驱动做准备)
                         
                     主板初始化(驱动)
                         初始化SDRAM
                         驱动Nandflash读操作
                  
                  软件环境建立
                     自拷贝到SDRAM
                     预留malloc动态分配区
                     给全局数据预留内存
                     栈内存分配(每种模式下都需要分配自己的栈)
                     .BSS段清0
                     跳转到SDRAM中运行
                 主板初始化(所有软硬件模块的初始化都需要在这里完成)
                  
        第二阶段: 命令执行阶段
                  接收命令输入
                  解析命令
                  执行命令
                     协议栈
                     文件系统
                     软件驱动
                     硬件驱动
                     
        第三阶段: 启动操作系统(bootm)
                  建立操作系统运行环境
                    linux内核启动前提条件:
                      1. 关闭中断,进入SVC
                      2. 关闭MMU和Cache
                      3. R0     0                             不是强制
                      4. R1     arch number(machine type id)  强制
                      5. R2     内核参数指针(atags list)      不必须
                      
                     传递参数给内核
                        atags list
                        struct传递方式(内存地址 + 0x100 BSP确定)
                      
                  跳转到内核代码运行
                  
        
            cpu/arm_cortexa8/start.S(reset --->cpu_init_crit)
         -->board/samsung/fsc100/lowlevel_init.S(lowlevel_init-->system_clock_init)
                                                      -->uart_asm_init
                                                      -->dma_init
                                                      -->nand_pin_mux
                                                      ?-->board/samsung/fsc100/mem_setup.S(mem_ctrl_asm_init)
                                                      -->wakeup_reset
         ?-->board/samsung/fsc100/nand_cp.c(stack_setup)
         -->copy_uboot_to_ram
         -->board/samsung/fsc100/nand_cp.c(stack_setup)
         -->lib_arm/board.c(start_armboot)
                            -->common/main.c(main_loop)
   
    
      reset                            复位异常启动的正常代码
      cpu_init_crit                    关闭MMU和cache
      lowlevel_init                    关闭看门狗、初始化SRAM接口、初始化中断控制器、初始化PLL
      system_clock_init                初始化PLL
      dma_init                         初始化dma
      nand_pin_mux                     初始化nandflash的多功能管脚
      nand_asm_init                    nandflash初始化
      mem_ctrl_asm_init                DDR控制器初始化
      wakeup_reset                     唤醒复位
      stack_setup                      栈初始化
      start_armboot                    软硬件初始化
      main_loop                        初始化及命令循环
             
        
    (2) 关键代码理解
         /*******************************************/
           // 判断u-boot当前运行位置(iRAM SDRAM)
           ldr r0, =0xff0fffff
           r0: 0xff0fffff         (20-23bit)

        @清除除20-23bit以外的所有位
        @u-boot只有可能运行在iRAM和SDRAM
        @如果运行在iRAM中, pc的值为(0x20000 - 0x38000)
        @     r1:0x00020000
        @BIC  r0:0xff0fffff 
        -------------------
        @        0x00000000
        
        @如果运行在SDRAM中, pc的值为(0x2ff80000-...)
        @     r1:0x2ff80000
        @BIC  r0:0xff0fffff 
        -------------------
        @        0x00f00000
        
        bic r1, pc, r0

        @ 读取代码段的起始地址
        @ 0x2ff80000-->r2
        ldr r2, _TEXT_BASE(0x2ff80000)
        
        @     r2:0x2ff80000
        @BIC  r0:0xff0fffff 
        -------------------
        @        0x00f00000
   
        bic r2, r2, r0

        @ 判断代码是否运行在SDRAM中
        @ 如果R1==R2, 在SDRAM中运行, 说明当前从USB启动
        @ 如果R1!=R2, 在iRAM中运行, 说明当前从Nandflash启动
        cmp r1, r2
           beq 1f
           
        /*******************************************/
           代码一:   
           val = test;
           // test-->R0  ldr R0, test
           // R0-->val   str r0, val
           tmp = test;
           // R0-->tmp   str r0, tmp
           
           代码二:
           val = test;
           // test-->R0  ldr R0, test
           // R0-->val   str r0, val
           __asm__ __volatile__("":::"memory");
           tmp = test;   
           // test-->R0  ldr R0, test
           // R0-->tmp   str r0, val
           
          /*******************************************/
           #用法
           定义:
           #define TOSTR(name) #name
           使用:
           TOSTR(go)     <----> "go"
           
           ##用法
           定义:
           #define U_BOOT_CMD(name) __u_boot_cmd_##name
           使用:
           U_BOOT_CMD(go) <----> __u_boot_cmd_go
           cmd_tbl_t __u_boot_cmd_go __attribute__ ((unused, section(".u_boot_cmd"))) = {"go", ...}
           
[10] 移植
     1. 工程配置
        // $ make fsc100(主板名)_config
        (1) 添加配置
            在参考板(芯片厂家提供的开发板)的配置命令:
            smdkc100_config: unconfig(删除以前配置)
               @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 smdkc100 samsung s5pc1xx
               @mkconfig smdkc100(主板名) arm(架构) arm_cortexa8(cpu名) smdkc100 samsung(芯片厂家) s5pc1xx(SOC名)
               
            下面添加相同命令,并且修改主板名:
            fsc100_config: unconfig(删除以前配置)
               @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 fsc100 samsung s5pc1xx
            
       2. 添加主板目录(board/samsung/smdkc100)
          拷贝“芯片厂家开发板的主板目录”到 “新的主板目录”
          $ cp board/VENDOR/BOARD/ board/VENDOR/新主板名/ -a
          $ mv board/VENDOR/BOARD/BOARD.c board/VENDOR/新主板名/新主板名.c
          修改Makefile,内容如下:
          COBJS := BOARD.o ...
          改为
          COBJS := 新主板名.o ...
          
       3. 添加主板配置文件
          拷贝“芯片厂家开发板的主板配置”到“新的主板配置”
          $cp include/configs/BOARD.h include/configs/新主板名.h
          
       4. 修改改交叉编译器(顶层目录Makefile)
          CROSS_COMPILE ?= arm-cortex_a8-linux-gnueabi-
       
     2. 代码移植
        (1) 启动代码移植
            1. 硬件初始化
               CPU初始化(新主板和芯片厂家开发板芯片相同,cpu相同,所以做移植仅仅是更改cpu/CPU/start.S)
                  关闭中断进入SVC模式(确认)
                  关闭MMU和Cache(确认)
                  
               SOC初始化 (cpu/CPU/start.S 或 cpu/CPU/芯片名/...(确认) 或 board/VENDOR/新主板名/...)
                  如果需要,关闭看门狗(确认)
                  如果需要加快代码执行速度,初始化PLL(clk control)(确认/添加)
                  UART初始化(确认)
                  如果需要,初始化DMA(概率很小)(添加)
                  Nandflash初始化(为读驱动做准备)(确认)
                  
               主板初始化(驱动)(board/VENDOR/新主板名/...)
                  初始化SDRAM(内存类型发生变化,需要修改内存的初始化参数)
                  驱动Nand的读操作(Nand类型发生变化,需要修改)
               
            2. 软件环境建立
                  自拷贝到SDRAM(flash类型或代码段位置变化,需要修改)
                  预留malloc动态分配区(确认,修改大小)
                  给全局数据预留内存(确认)
                  栈分配(预留)内存(每种模式下都需要分配自己的栈)(栈位置变化,需要修改)
                  BSS段清0(确认)
                  跳转到SDRAM中运行(确认)
                  主板初始化(u-boot中需要使用的软件模块和硬件驱动模块的初始化都在这里完成)(需要根据硬件信息修改)
             效果:可以运行到命令提示符
             注意:可能需要添加模块,方法见《[7] 如何添加模块到工程中?》
             
        (2) 命令移植(实现命令执行代码)
            接收命令输入
            解析命令
            执行命令
               协议栈(根据命令需要添加)
               文件系统(根据命令需要添加)
               软件驱动(根据命令需要添加)
               硬件驱动(根据命令需要添加)
               
            include/config_cmd_default.h                   编译到u-boot可执行程序的命令的配置项
            include/config_cmd_all.h                       u-boot源码中支持的所有命令的配置项
            注意:可能需要添加模块,方法见《[7] 如何添加模块到工程中?》
            
        (3) 启动操作系统(bootm)
            修改board/VENDOR/新主板名/新主板名.c文件中的代码,如下:
            int board_init(void)
            {
               ....
               gd->bd->bi_arch_number = 主板ID(来自于内核);
            gd->bd->bi_boot_params = 内核参数存放位置(物理内存的首地址+0x100);
            ....
         }


            int dram_init(void)
            {
            gd->bd->bi_dram[0].start = 物理内存的首地址;
            gd->bd->bi_dram[0].size = 物理内存大小;
  ...
            }


           

bootloader