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程序内部,寻找模式规则(隐式规则),生成目标
       
[ARMproject]
[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] project解说(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,加入新文件夹生成的目标文件到project
       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. project管理和代码比較混乱
    何一款嵌入式产品。都不会用到全部的源代码,所以用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] 怎样加入模块到project中?(必须掌握)
    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,加入新文件夹生成的目标文件到project
        (1) 假设加入的模块是非启动代码, 加入模块的Makefile生成的目标文件到project
            (将目标文件放入顶层文件夹下的Makfile的LIBS变量中)
            例:
            LIBS += drivers/uart/libuart.a
            
        (2) 假设加入的模块是启动代码,方法例如以下:
            1. 加入模块的Makefile生成的目标文件到project
               (将目标文件放入顶层文件夹下的Makfile的OBJS变量中)
               例:
               OBJS += drivers/uart/uart.o
            
               $(OBJS):...                 (应该是标志的u-boot做好的)
                  $(MAKE) -c $(dir $@) $(notdir $@)
               
             2. 加入模块的Makefile生成的目标文件到project
                (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
    建立project,加入全部文件到project
    
[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. project配置
        // $ 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] 怎样加入模块到project中?》
             
        (2) 命令移植(实现命令运行代码)
            接收命令输入
            解析命令
            运行命令
               协议栈(依据命令须要加入)
               文件系统(依据命令须要加入)
               软件驱动(依据命令须要加入)
               硬件驱动(依据命令须要加入)
               
            include/config_cmd_default.h                   编译到u-boot可运行程序的命令的配置项
            include/config_cmd_all.h                       u-boot源代码中支持的全部命令的配置项
            注意:可能须要加入模块,方法见《[7] 怎样加入模块到project中?》
            
        (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 = 物理内存大小;
  ...
            }


           

版权声明:本文博客原创文章。博客,未经同意,不得转载。

posted @ 2015-07-17 18:58  blfshiye  阅读(389)  评论(0编辑  收藏  举报