U-Boot 启动过程笔记

简介: [1] 规则    目标: 依赖    [TAB]命令(命令名 参数 依赖 目标)   [2] 难点    1. 自动变量       作用域在当前规则中, Make给它自动赋值       $@   目标            注意: 每一条规则只有一个目标            test test.
[1] 规则
    目标: 依赖
    [TAB]命令(命令名 参数 依赖 目标)
   
[2] 难点
    1. 自动变量
       作用域在当前规则中, Make给它自动赋值
       $@   目标
            注意: 每一条规则只有一个目标
            test test.o: test.c
            [TAB]$(CC) $(CFLAGS) $< -o $@
            等价于
            test: test.c
            [TAB]$(CC) $(CFLAGS) $< -o $@
           
            test.o: test.c
            [TAB]$(CC) $(CFLAGS) $< -o $@
           
       $<   第一个依赖
       $^   所有依赖
       
     2. 模式规则
        目标和依赖使用%匹配符的规则
        %.o: %.c
        [TAB]$(CC) $(CFLAGS) $< -o $@
       
     3. 自动推倒(自动找规则生成目标)
        (1) 寻找生成目标的显示规则(自己写的非模式规则)
        (2) 寻找生成目标的模式规则
        (3) 寻找生成目标的隐式规则(make程序默认的模式规则)
       
[3] ARM工程
    1. 目录
       board           跟主板相关的配置文件和源代码(主板的初始化代码)
       common          主程序
       cpu             cpu的启动代码
       drivers         通用设备驱动
       include         头文件
       lib             库(通用算法)的源代码
       
    2. 源文件
       *.S             汇编源码
       *.c             C源码
       *.h             头文件
       
       config.mk       编译命令和编译参数
       Makefile        make脚本
       map.lds         链接脚本
       
    3. 编译
       $ make
       
    4. 目标文件
       fs4412.bin      binary格式的可执行文件
       fs4412          ELF格式的可执行文件
       fs4412.dis      反汇编文件
       fs4412.map      详细符合表文件(链接过程文件)
       System.map      符合表文件
       
       
    5. 编译原理
       1. 主Makefile
          OBJS         启动代码编译出来的目标文件(*.o)
          LIBS         非启动代码编译出来的静态库(lib*.a)(多个目标文件的打包)
         
       2. 子Makefile
          LIB          子目录下的源代码编译出来的目标文件,打包成的静态库文件
          START        启动代码编译出来的目标文件
          SOBJS        汇编代码编译出来的目标文件
          COBJS        C代码编译出来的目标文件
       
    6. 如何添加模块到工程?
       1. 添加源代码
       2. 添加子目录
       3. 添加源代码到子目录
       4. 给子目录添加Makefile
          拷贝别的子目录下的Makefile,修改如下变量的值:
          LIB          子目录下的源代码编译出来的目标文件,打包成的静态库文件
          START        启动代码编译出来的目标文件
          SOBJS        汇编代码编译出来的目标文件
          COBJS        C代码编译出来的目标文件
       5. 修改顶层目录下的Makefile, 添加新的子目录生成的目标到工程:
          OBJS         启动代码编译出来的目标文件(*.o)
          LIBS         非启动代码编译出来的静态库(lib*.a)(多个目标文件的打包)
         
[4] u-boot工程
    1. 目录
       cpu(cpu/cpu名称(arm_cortexa8)/...)                CPU的启动代码
       board(board/芯片厂家(samsung)/主板名(fsc100)/...  启动代码中需要使用的核心硬件的驱动(内存初始化、flash的读驱动、soc中部分硬件的初始化)
       
       common                                            主程序和命令实现代码
       lib_arm(lib_generic)                              某个架构的CPU需要使用的公共代码(校验算法CRC 压缩算法 标准C库)
       net                                               网络协议代码
       drivers                                           通用设备驱动
       fs                                                文件系统实现代码
       disk                                              磁盘分区驱动
       
       tools                                             工具
       doc                                               文档(作者写的)
       
    2. 源文件
       Makefile                                          make脚本文件
       config.mk                                         编译命令和编译参数
       README                                            整个工程的说明
       mkconfig                                          工程配置的shell脚本
       rules.mk                                          产生Makefile规则(hello.o: hello.c hello.h stdio.h)
   
    3. 特点
       1. 支持多个CPU
       2. 支持多个主板
       3. 支持非常多的设备驱动
       
       4. 工程管理和代码比较混乱
       任何一款产品,都不可能用到u-boot提供的所有代码, 所以编译过程应该分成:
       1. 选择需要的源代码--配置
       2. 编译需要的源代码--编译
       
    4. 编译
       1. 配置
          $ make fsc100(主板名)_config
         
       2. 编译
          $ make
         
       3. 清除编译
          $ make clean
         
       4. 清除配置
          $ make clobber
       
    5. 目标文件
       u-boot                  ELF格式的可执行程序
       u-boot.bin              binary格式的可执行程序
       u-boot.map              详细符号表(有链接过程)
       System.map              符号表文件
     
    6. 配置原理
       1. 过程
          《u-boot配置流程图.bmp》 注意:2010版本的u-boot没有boards.cfg
         
       2. 结果
          include/config.mk                          选择源代码目录
          include/config.h                           选取目录下的源代码文件和从选取的源代码文件中选取一部分源代码            
             #include <configs/fsc100.h>             主板的配置文件(通过宏定义配置)
                #include <config_cmd_default.h>      通过里面的宏定义控制命令是否编译到最终的u-boot可执行文件
                (config_cmd_all.h)                   u-boot支持的所有命令的配置项(宏定义)
       
       3. 原理  
          1. $ make fsc100_config命令的实现原理
             根据配置命令($ make fsc100_config),来看主Makefile,即在主Makefile中寻找目标为fsc100_config规则:
             SRCTREE     := $(CURDIR)
             ...
             MKCONFIG    := $(SRCTREE)/mkconfig
             ...
             fsc100_config:  unconfig
             [tab]@$(MKCONFIG) $(@:_config=) arm arm_cortexa8 fsc100 samsung s5pc1xx
                   mkconfig  fsc100 arm arm_cortexa8 fsc100 samsung s5pc1xx
                   
          2. 选择目录
             include include/config.mk
             (....
              CPU = arm-cortex_a8(目录名)
              )
              ....
             LIBS += cpu/$(CPU)/lib$(CPU).a            选择arm-cortex_a8 cpu目录


          3. 选择目录中的源代码文件
             例: 在include/configs/fsc100.h定义:
                 #define   CONFIG_DRIVER_DM9000 1      如果宏定义不存在, 就不会被转换成相应的Makefile变量

                 在make时, 它会被转换成Makefile变量,存放在include/autoconf.mk中:
                 CONFIG_DRIVER_DM9000 = y

                 在主Makefile中,会包含include/autoconf.mk,因此在子Makefile中也可以见到CONFIG_DRIVER_DM9000变量

                 在driver/net/Makefile中,会按照如下方式使用:
                 LIB = libnet.a
                 ...
                 ...
                 COBJS-$(CONFIG_DRIVER_DM9000) += dm9000x.o
                 ...
                 ...
                 COBJS = $(COBJS-y)
                 ...
                 OBJS = $(COBJS)
                 ...
                 $(LIB): .depend $(OBJS)
                 [TAB]$(AR) $(ARFLAGS) $@ $(OBJS)


           4. 选取文件中的部分源码(条件编译)
              例: 在include/configs/fsc100.h头文件中,存在如下宏定义:
                  #define CONFIG_CMD_PING 1        (注意: 命令配置项最好放入include/config_cmd_default.h)

                  在common/cmd_net.c文件中,存在如下代码:
                  #if defined(CONFIG_CMD_PING)
                  ...(ping命令实现代码)
                  #endif


           5. 给源代码提供数据(宏替换)
              例: 在include/configs/fsc100.h头文件中,存在如下宏定义:
                  #define CONFIG_DM9000_BASE 0X88000000

                  在driver/net/dm9000x.c文件中会使用这个宏定义:
                  ...
                  dm9000_probe(void)
                  {
                     ...
                     printf("dm9000 i/o: 0x%x, id: 0x%x \n", CONFIG_DM9000_BASE, id_val);
                     ...
                  }
                  ...


 7. 编译原理
       1. 总体编译过程
          见《u-boot编译过程图2013.01.bmp》
         
       2. 顶层目录下的Makefile
          OBJS            启动代码编译出来的目标文件
          LIBS            非启动代码编译出来的目标文件
         
       3. 子目录下的Makefile
          LIB             最终生成的lib*.a文件(只有非启动代码会被打包到这个文件)
          COBJS           *.C编译出来的目标文件
          SOBJS           *.S编译出来的目标文件
       
8. 如何添加模块到工程?
# 包含编译命令和编译参数
include $(TOPDIR)/config.mk

# 总目标(非启动代码)
LIB := $(obj)libuart.a

# COBJS-$(CONFIG_USB_TTY) += usbtty.o

# C语言源代码编译出来的目标文件
COBJS := uart.o  #$(sort $(COBJS-y))

# 汇编代码编译出来的目标文件
#SOBJS := ...

# 源代码文件,用在rules.mk中
SRCS := $(COBJS:.o=.c)

# 等价于COBJS
OBJS := $(addprefix $(obj),$(COBJS)) # $(addprefix $(obj),$(SOBJS))

# 防止LIB变量中有多个目标
all: $(LIB)

# 将目标文件打包成静态库
$(LIB): $(obj).depend $(OBJS)
 $(AR) $(ARFLAGS) $@ $(OBJS)

#########################################################################

# 产生.depend文件
# defines $(obj).depend target
include $(SRCTREE)/rules.mk

# 包含.depend文件(*.o : *.c *.h 规则)
sinclude $(obj).depend

#########################################################################
      1. 添加源代码
       2. 添加子目录
       3. 添加源代码到子目录
       4. 给子目录添加Makefile
          拷贝别的子目录下的Makefile,修改如下变量的值:
          LIB          子目录下的源代码编译出来的目标文件,打包成的静态库文件
          START        启动代码编译出来的目标文件
          SOBJS        汇编代码编译出来的目标文件
          COBJS        C代码编译出来的目标文件
         
       5. 修改顶层目录下的Makefile, 添加新的子目录生成的目标到工程:
          OBJS         启动代码编译出来的目标文件(*.o)
                       注意: 有些版本,不支持将启动代码放置的cpu以外目录,这时需要修改如下规则:
                       $(OBJS): depend
                       # $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
                          $(MAKE) -C $(dir $(subst $(obj),,$@)) $(if $(REMOTE_BUILD),$@,$(notdir $@))
                         
                          # $(dir $(subst $(obj),,$@))
                            $(dir A)                    求A的路径,如: A是"drivers/uart/uart.o" 路径为"drivers/uart"
                            $(subst $(obj),,$@)         obj指定目标代码的路径(当源代码和目标代码不放在同一目录时)
                                                        将@中的每个值的$(obj)部分,替换成空
                                                       
                          # $(if $(REMOTE_BUILD),$@,$(notdir $@)) 当REMOTE_BUILD变量值非空时,整个函数的值为@变量的值
                                                                   ....                 空时,               $(notdir $@)的结果
         
          LIBS         非启动代码编译出来的静态库(lib*.a)(多个目标文件的打包)
         
       注意:
          1. 如何控制源代码文件是否编译到u-boot?
          2. 如何空源代码文件的一部分编译到u-boot?
          3. 如何为源代码文件提供数据?
          开关或数据都必须在include/configs/主板名.h
         
    9. 源代码阅读软件
       vi(编辑或查看) + ctags(搜索定义)
       1. 建立ctags索引文件
          $ ctags -R     (在工程的顶层目录下执行)
         
       2. 查找定义
          把光标移动到要查找定义的标识符,然后按ctrl + ]
         
       3. 回退
          ctrl + O
         
       sourceinside
       先建立工程, 添加所有文件到工程
         
    10. 源代码分析
       1. 自启动
          1.1 核心硬件(初始化或驱动)
              1.1.1 CPU(A8)
                    异常中断向量表(包含配置异常中断向量表的位置)
                    cpu/arm-cortex_A8 35 - 56行
                   
                    (ALU + 控制器)传统的CPU     关闭中断, 进入SVC模式
                    cpu/arm-cortex_A8 106 - 109行
                   
                    MMU 和 cache                关闭                  
cpu/arm_cortexa8/start.S
227     /*
228      * 关闭tlb和icache
229      */
230     mov r0, #0          @ set up for MCR
231     mcr p15, 0, r0, c8, c7, 0   @ invalidate TLBs
232     mcr p15, 0, r0, c7, c5, 0   @ invalidate icache
233
234     /*
235      * 关闭mmu和cache
236      */
237     mrc p15, 0, r0, c1, c0, 0
238     bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
239     bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
240     orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
241     orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
242     mcr p15, 0, r0, c1, c0, 0
                   
              1.1.2 SOC
                    如果有看门狗                关闭或喂狗
 board/samsung/fsc100/lowlevel_init.S
 57     /* 关闭看门狗 */
 58     ldr r0, =S5PC100_WATCHDOG_BASE      @0xEA200000
 59     orr r0, r0, #0x0
 60     str r5, [r0]


                    如果需要加快代码的执行速度  初始化PLL
board/samsung/fsc100/lowlevel_init.S
96     bl  system_clock_init

                    UART                        初始化(驱动)发送即可
board/samsung/fsc100/lowlevel_init.S
101     bl  uart_asm_init

                    如果需要用到DMA             初始化(驱动)DMA
board/samsung/fsc100/lowlevel_init.S
104     bl  dma_init

                    如果从Nandflash启动         初始化nandflash控制器管脚
board/samsung/fsc100/lowlevel_init.S  
106     bl  nand_pin_mux
108     bl  nand_asm_init

              1.1.3 BOARD
                    初始化DDR
board/samsung/fsc100/lowlevel_init.S  
118     bl  mem_ctrl_asm_init

                    如果从Nandflash启动         驱动nandflash的读
cpu/arm_cortexa8/start.S
158 ldr sp, =(0x22000000)
159 bl copy_uboot_to_ram

          1.2 软件(创建C语言执行环境)
              1.2.1 自拷贝到DDR
cpu/arm_cortexa8/start.S
158 ldr sp, =(0x22000000)
159 bl copy_uboot_to_ram

              1.2.2 栈内存分配(需要给SVC、abort、undefined instruction 三个模式分配栈空间)
              1.2.3 .BSS段清空
              1.2.4 跳转到内存运行
              1.2.5 预留内存给malloc函数
              1.2.6 预留内存给全局数据结构
         
       2. 命令执行
          2.1 接收命令输入
          2.2 解析命令
          2.3 执行命令
              协议栈(网络协议栈(net)/USB协议栈(drivers/usb)/...)
              文件系统(fs)
              分区驱动(disk)
              硬件驱动(drivers)

       3. OS启动(bootm)
          建立linux系统的启动环境:
          3.1 关闭中断, 进入SVC
          3.2 关闭MMU和cache
          3.3 R0           0
              R1           arch number(mach type id) 主板id
              R2           内核参数指针(atags list)
             
          3.4 传参给内核
          3.5 跳转到内核的运行地址去启动
相关文章
|
7月前
|
XML Java 数据格式
SpringBoot的启动过程
启动详解 SpringBoot的启动分为两个部分: 构造SpringApplication 执行run方法 构造SpringApplication 我们先来整体看看: 加入我们当前启动类如下: 可以发现大致做了以下几件事: 设置BeanDefinition的主源 推断应用类型 设置ApplicationContext 初始化器 设置监听器 推断著主启动类 接下来我们详细的看看每一个步骤: 第一步:记录 BeanDefinition 源 大家知道我们的Spring容器刚开始内部的BeanFactory是空的,它要从各个源头去寻找BeanDefinition, 这些源有可能来自
67 1
|
4月前
|
缓存 Linux 编译器
Linux系统中u-boot启动流程分析(详解)
Linux系统中u-boot启动流程分析(详解)
61 1
|
5月前
|
Java Spring 容器
Spring Boot启动命令参数详解及源码分析
Spring Boot启动命令参数详解及源码分析
160 1
|
7月前
|
缓存 Java Go
解决Spring Boot启动错误的技术指南
解决Spring Boot启动错误的技术指南
226 0
|
9月前
|
Java 容器 Spring
|
9月前
|
Java 应用服务中间件 测试技术
Spring Boot配置秘籍:快速掌握启动核心配置
能够说出SpringBoot工程的三种配置文件格式 能够书写格式正确的yml配置文件 能够配置多种环境并在需要时选择使用何种环境运行
114 0
|
10月前
|
SQL 缓存 安全
【企业项目实战】Spring Boot 启动时加载指定方法
【企业项目实战】Spring Boot 启动时加载指定方法
421 0
【企业项目实战】Spring Boot 启动时加载指定方法
|
11月前
|
Java Spring
三分钟了解Spring Boot启动原理
大家通常只需要给一个类添加一个@SpringBootApplication 注解,然后再加一个main 方法里面固定的写法 SpringApplication.run(Application.class, args); 那么spring boot 到底是如何启动服务的呢。 接下来咱们通过源码解析。 Spring Boot 的启动原理可以概括为以下几个步骤: 加载 Spring Boot 应用程序的启动类 根据启动类所在的包路径扫描相关的类 根据扫描到的类自动配置 Spring 应用程序 启动嵌入式的 Web 服务器 运行 Spring 应用程序
161 0
|
11月前
|
Java 应用服务中间件 Spring
源码解析Spring Boot 的启动流程
最近有位开发同学说面试被问到Spring Boot 的启动流程,以及被问到Spring Boot 的嵌入式Web容器是什么时候加载的。如何加载的。是怎么无缝切换的。这些问题,其实回答起来也是比较复杂的。我们今天就从 `SpringApplication.run(Application.class, args);`入口,逐渐向下看下执行流程。来试着回答一下前面这两个问题。后面关于SpringBoot 的web容器可以无缝随意切换为`jetty`,`undertow.`.这个问题的回答涉及到Spring Boot是如何设计WebServer的。我们后续专门讲解一下。
11157 1
源码解析Spring Boot 的启动流程
|
存储 XML Java
Spring启动过程源码分析
本文是通过 AnnotationConfigApplicationContext读取配置类来一步一步去了解Spring的启动过程。 在看源码之前,我们要知道某些类的作用,这样更方便后续的了解。
133 0
Spring启动过程源码分析