• 【U-Boot笔记整理】U-Boot 完全分析与移植


    1. 大纲

    大概内容如下:

    • u-boot功能概述
      • 目的
      • 功能细分
    • u-boot源码结构
    • u-boot的配置、编译、连接过程
      • Makefile深入练习
      • 分析u-boot的Makefile
      • u-boot可执行程序的组成
    • u-boot源码分析
      • SPL与第1阶段
      • 第2阶段
      • 核心:命令
      • 让u-boot的使用更加便利:env
    • u-boot的设备树
    • u-boot实战
      • 移植其他版本的u-boot
      • 修改设备树支持网卡

    2. u-boot核心功能

    就是启动内核:

    • 读Flash,把内核读入内存
    • 启动内核

    3. 配置u-boot的过程分析

    3.1 源码结构

    在u-boot目录下执行"tree . -d > 1.txt",可以得到目录的结构,精简如下:

    ├── arch    
    │   ├── arm              // 1. 架构相关
    │   │   ├── cpu
    │   │   │   ├── armv7
    │   │   │   │   ├── mx6
    │   │   ├── dts
    │   │   │   └── include
    │   │   │       └── dt-bindings -> ../../../../include/dt-bindings
    │   │   ├── include
    │   │   │   ├── asm
    │   │   │   │   ├── arch-imx
    │   │   │   │   ├── arch-imx8
    │   │   │   │   ├── arch-imx8m
    │   │   │   │   ├── imx-common
    │   │   │   └── debug
    │   │   ├── lib
    │   │   ├── mach-rockchip
    │   │   │   ├── rk3036
    │   │   │   ├── rk3288
    │   │   │   └── rk3399
    │   │   ├── lib
    ├── board             // 单板相关
    │   ├── freescale
    │   │   ├── common
    │   │   │   └── p_corenet
    │   │   ├── corenet_ds
    │   │   ├── mx6ul_14x14_ddr3_arm2
    │   │   ├── mx6ul_14x14_evk
    │   │   ├── mx6ul_14x14_lpddr2_arm2
    │   │   ├── mx6ull_ddr3_arm2
    │   │   ├── mx6ullevk
    ├── cmd                   // 通用的命令
    │   ├── fastboot
    │   └── mvebu
    ├── common                // 通用的
    │   ├── eeprom
    │   ├── init
    │   └── spl
    ├── configs
    ├── disk
    ├── drivers               // 各类驱动
    ├── fs                    // 文件系统
    │   ├── cbfs
    │   ├── cramfs
    │   ├── ext4
    │   ├── fat
    │   ├── jffs2
    │   ├── reiserfs
    │   ├── sandbox
    │   ├── ubifs
    │   ├── yaffs2
    │   └── zfs
    ├── include
    ├── lib                  // 库
    ├── net                  // 网络协议
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    3.2 Makefile分析

    3.2.1 基础知识

    文档:本Git仓库01_all_series_quickstart\04_嵌入式Linux应用开发基础知识\doc_pic\04.2018_Makefile

    3.2.2 make的技巧

    打印Makefile的规则和变量:make -p

    可以把make命令规则和变量存入文件:make -p > 1.txt

    然后执行vi 1.txt,使用vi命令删除注释::g/^#/d

    3.3 u-boot的默认配置

    3.3.1 默认配置的过程

    IMX6ULL: make mx6ull_14x14_evk_defconfig

    STM32MP157: make stm32mp15_trusted_defconfig

    执行过程:

    • 制作工具:scripts/kconfig/conf
    • 把默认配置信息写入文件".config"
      在这里插入图片描述

    分析过程:

    mx6ull_14x14_evk_defconfig: scripts/kconfig/conf
    	$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
    
    • 1
    • 2

    就是:

    UBOOTVERSION=2017.03 scripts/kconfig/conf --defconfig=arch/../configs/mx6ull_14x14_evk_defconfig Kconfig
    
    • 1

    3.3.2 conf命令概述

    总体分析:scripts/kconfig/conf.c

    更详细的配置过程:make mx6ull_14x14_evk_defconfig V=1

    在这里插入图片描述

    defconfig_file = "arch/../configs/mx6ull_14x14_evk_defconfig";
    name = "Kconfig"
    
    conf_parse(name);  // 解析uboot根目录下的Kconfig文件
    
    conf_read(defconfig_file); // 读配置文件
    
    conf_set_all_new_symbols(def_default); // 设置new_symbols为默认值
    
    conf_write(NULL); // 写到.config
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • Kconfig:这是一个通用文件,里面规定了一些依赖,比如:
      • 如果是ARM架构,就默认选中A、B、C配置
      • 如果是RISC-V架构,就默认选中a、b、c配置
    • defconfig_file:这是厂家提供的,里面定义了
      • ARM架构
      • 自己的一些配置项
    • 怎么处理呢?
      • 使用defconfig_file的内容去解析Kconfig,确定各个依赖的配置项
      • 其他未涉及的配置项,给它们指定默认值
      • 写入.config

    3.3.3 conf命令详解

    深入分析:scripts/kconfig/conf.c

    4. Kconfig介绍

    参考文档:

    任一个Linux内核的Documentation\kbuild\kconfig-language.rst
    
    https://www.rt-thread.org/document/site/programming-manual/kconfig/kconfig/
    
    • 1
    • 2
    • 3

    对于各类内核,只要支持menuconfig配置界面,都是使用Kconfig。
    在配置界面中,可以选择、设置选项,这些设置会保存在.config文件里。
    编译脚本会包含.config,根据里面的值决定编译哪些文件、怎么编译文件。
    .config文件也会被转换为头文件,C程序可以从头文件中获得配置信息。

    4.1 配置界面示例

    在这里插入图片描述

    问题:

    • 这个界面里,各个配置项来自哪里
    • 这个界面里,这些配置项是怎么组织的
    • 这个界面里,我们的选择、设置,结果保存在哪里
    • 这些配置结果,怎么使用

    4.2 配置结果的保存

    4.2.1 示例

    在配置界面中操作的结果保存在.config文件中,示例如下:

    #
    # Automatically generated file; DO NOT EDIT.
    # U-Boot  Configuration
    #
    CONFIG_CREATE_ARCH_SYMLINK=y
    # CONFIG_ARC is not set
    CONFIG_ARM=y
    # CONFIG_AVR32 is not set
    # CONFIG_BLACKFIN is not set
    # CONFIG_M68K is not set
    # CONFIG_MICROBLAZE is not set
    # CONFIG_MIPS is not set
    # CONFIG_NDS32 is not set
    # CONFIG_NIOS2 is not set
    # CONFIG_OPENRISC is not set
    # CONFIG_PPC is not set
    # CONFIG_SANDBOX is not set
    # CONFIG_SH is not set
    # CONFIG_SPARC is not set
    # CONFIG_X86 is not set
    # CONFIG_XTENSA is not set
    CONFIG_SYS_ARCH="arm"
    CONFIG_SYS_CPU="armv7"
    CONFIG_SYS_SOC="mx6"
    CONFIG_SYS_VENDOR="freescale"
    CONFIG_SYS_BOARD="mx6ullevk"
    CONFIG_SYS_CONFIG_NAME="mx6ullevk"
    
    #
    # ARM architecture
    #
    CONFIG_HAS_VBAR=y
    CONFIG_HAS_THUMB2=y
    CONFIG_CPU_V7=y
    CONFIG_SYS_ARM_ARCH=7
    CONFIG_SYS_CACHE_SHIFT_6=y
    CONFIG_SYS_CACHELINE_SIZE=64
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    编译脚本会包含.config文件,它会根据里面的变量比如CONFIG_CPU_V7选择u-boot特性。

    4.2.2 配置项的前缀

    在Kconfig文件中,假设配置项的名字是XXX,在.config文件中:

    • 默认情况下,它对应的变量名为CONFIG_XXX
    • 如果设置了环境变量CONFIG_=ABC,则对应的变量名为ABC_XXX

    4.3 描述单个配置项config

    4.3.1 示例

    menuconfig界面,可以看到这个配置项:

    在这里插入图片描述

    在配置界面,使用方向箭头游走到Display information about the CPU during start up后,可以:

    • 输入Y,选择配置项,在.config中对应CONFIG_DISPLAY_CPUINFO=y

    • 输入N,不选择配置项,在.config中对应# CONFIG_DISPLAY_CPUINFO is not set

    上图中的配置项怎么实现的?
    在common/Kconfig文件中,它对应下列代码:

    在这里插入图片描述

    4.3.2 语法

    上面是一个精简的例子,完整的例子可以从Linux中获得,如下:

    config SGI_SNSC
            bool "SGI Altix system controller communication support"
            depends on (IA64_SGI_SN2 || IA64_GENERIC)
            default y
            help
              If you have an SGI Altix and you want to enable system
              controller communication from user space (you want this!),
              say Y.  Otherwise, say N.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    解释如下:

    • config
      表示config option,这是Kconfig的基本entry;其他entry是用来管理config的。
      config 表示一个配置选项的开始,紧跟着的 SGI_SNSC 是配置选项的名称。
      config 下面几行定义了该配置选项的属性。
      属性可以是该配置选项的:类型、输入提示、依赖关系、默认值、帮助信息。
      • bool 表示配置选项的类型,每个 config 菜单项都要有类型定义,变量有5种类型
        • bool 布尔类型
        • tristate 三态类型
        • string 字符串
        • hex 十六进制
        • int 整型
      • “SGI Altix system controller communication support”:提示信息
      • depends on:表示依赖关系,只有(IA64_SGI_SN2 || IA64_GENERIC)被选中,才可以选择SGI_SNSC
      • select XXX:表示反向依赖关系,即当前配置选项被选中后,XXX选项就会被选中。
      • default 表示配置选项的默认值,bool 类型的默认值可以是 y/n。
      • help 帮助信息,在menuconfig界面输入H键时,就会提示帮助信息。

    4.4 实现菜单menu/endmenu

    4.4.1 示例

    示例代码:rt-smart/kernel/src/Kconfig,代码如下:

    menu "Boot media"
    
    config NOR_BOOT
            bool "Support for booting from NOR flash"
            depends on NOR
            help
              Enabling this will make a U-Boot binary that is capable of being
              booted via NOR.  In this case we will enable certain pinmux early
              as the ROM only partially sets up pinmux.  We also default to using
              NOR for environment.
    
    config NAND_BOOT
            bool "Support for booting from NAND flash"
            default n
            help
              Enabling this will make a U-Boot binary that is capable of being
              booted via NAND flash. This is not a must, some SoCs need this,
              some not.
    
    config ONENAND_BOOT
            bool "Support for booting from ONENAND"
            default n
            help
              Enabling this will make a U-Boot binary that is capable of being
              booted via ONENAND. This is not a must, some SoCs need this,
              some not.
    
    config QSPI_BOOT
            bool "Support for booting from QSPI flash"
            default n
            help
              Enabling this will make a U-Boot binary that is capable of being
              booted via QSPI flash. This is not a must, some SoCs need this,
              some not.
    
    config SATA_BOOT
            bool "Support for booting from SATA"
            default n
            help
              Enabling this will make a U-Boot binary that is capable of being
              booted via SATA. This is not a must, some SoCs need this,
              some not.
    
    config SD_BOOT
            bool "Support for booting from SD/EMMC"
            default n
            help
              Enabling this will make a U-Boot binary that is capable of being
              booted via SD/EMMC. This is not a must, some SoCs need this,
              some not.
    
    config SPI_BOOT
            bool "Support for booting from SPI flash"
            default n
            help
              Enabling this will make a U-Boot binary that is capable of being
              booted via SPI flash. This is not a must, some SoCs need this,
              some not.
    
    endmenu
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    界面如下:

    在这里插入图片描述

    4.4.2 语法

    解释如下:

    • menu “xxx"表示一个菜单,菜单名是"xxx”

    • menu和endmenu之间的entry都是"xxx"菜单的选项

    • 在上面的例子中子菜单有6个选项:

      在这里插入图片描述

    4.5 实现单选choice/endchoice

    4.5.1 示例

    示例代码:rt-smart/kernel/src/Kconfig,代码如下:

    config DEBUG_UART
            bool "Enable an early debug UART for debugging"
            help
              The debug UART is intended for use very early in U-Boot to debug
              problems when an ICE or other debug mechanism is not available.
    
    choice
            prompt "Select which UART will provide the debug UART"
            depends on DEBUG_UART
            default DEBUG_UART_NS16550
    
    config DEBUG_UART_ALTERA_JTAGUART
            bool "Altera JTAG UART"
            help
              Select this to enable a debug UART using the altera_jtag_uart driver.
              You will need to provide parameters to make this work. The driver will
              be available until the real driver model serial is running.
    
    config DEBUG_UART_ALTERA_UART
            bool "Altera UART"
            help
              Select this to enable a debug UART using the altera_uart driver.
              You will need to provide parameters to make this work. The driver will
              be available until the real driver model serial is running.
    
    endchoice
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    界面如下:

    在这里插入图片描述

    在上述界面中,设置了DEBUG_UART,才有后续的"选择菜单"。

    4.5.2 语法

    解释如下:

    • choice表示"选择"
    • choice和endchoice之间的entry是可以选择的项目
      • 它们之间,只能有一个被设置为"y":表示编进内核
      • 它们之间,可以设置多个为"m":表示编译为模块
      • 比如一个硬件有多个驱动程序
        • 同一时间只能有一个驱动能编进内核
        • 但是多个驱动都可以单独编译为模块

    4.6 menuconfig

    menuconfig XXXconfig XXX类似,
    唯一不同的是该选项除了能设置y/m/n外,还可以实现菜单效果(能回车进入该项内部)。

    4.6.1 示例

    示例代码:Kconfig

    menuconfig EXPERT
            bool "Configure standard U-Boot features (expert users)"
            default y
            help
              This option allows certain base U-Boot options and settings
              to be disabled or tweaked. This is for specialized
              environments which can tolerate a "non-standard" U-Boot.
              Use this only if you really know what you are doing.
    
    if EXPERT
            config SYS_MALLOC_CLEAR_ON_INIT
            bool "Init with zeros the memory reserved for malloc (slow)"
            default y
            help
              This setting is enabled by default. The reserved malloc
              memory is initialized with zeros, so first malloc calls
              will return the pointer to the zeroed memory. But this
              slows the boot time.
    
              It is recommended to disable it, when CONFIG_SYS_MALLOC_LEN
              value, has more than few MiB, e.g. when uses bzip2 or bmp logo.
              Then the boot time can be significantly reduced.
              Warning:
              When disabling this, please check if malloc calls, maybe
              should be replaced by calloc - if one expects zeroed memory.
    
    config TOOLS_DEBUG
            bool "Enable debug information for tools"
            help
              Enable generation of debug information for tools such as mkimage.
              This can be used for debugging purposes. With debug information
              it is possible to set breakpoints on particular lines, single-step
              debug through the source code, etc.
    
    endif # EXPERT
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    界面如下:

    在这里插入图片描述

    4.6.2 语法

    menuconfig常用格式有2种:

      menuconfig M
      if M
          config C1
          config C2
      endif
    
    • 1
    • 2
    • 3
    • 4
    • 5

    或:

      menuconfig M
      config C1
          depends on M
      config C2
          depends on M
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第1项menuconfig Mconfig M语法是一样的,
    不同之处在于menuocnfig M后面可以跟着好几个依赖于M的config C1config C2等子配置项。

    4.7 if/endif

    4.7.1 语法

    在上面的menuconfig中就有if/endif的使用,它的语法如下:

    "if" <expr>
    <if block>
    "endif"
    
    • 1
    • 2
    • 3

    4.7.2 示例

    示例如下,只有定义了的EXPERT项,SYS_MALLOC_CLEAR_ON_INIT等才会显示出来:

    if EXPERT
            config SYS_MALLOC_CLEAR_ON_INIT
            bool "Init with zeros the memory reserved for malloc (slow)"
            default y
            help
              This setting is enabled by default. The reserved malloc
              memory is initialized with zeros, so first malloc calls
              will return the pointer to the zeroed memory. But this
              slows the boot time.
    
              It is recommended to disable it, when CONFIG_SYS_MALLOC_LEN
              value, has more than few MiB, e.g. when uses bzip2 or bmp logo.
              Then the boot time can be significantly reduced.
              Warning:
              When disabling this, please check if malloc calls, maybe
              should be replaced by calloc - if one expects zeroed memory.
    
    config TOOLS_DEBUG
            bool "Enable debug information for tools"
            help
              Enable generation of debug information for tools such as mkimage.
              This can be used for debugging purposes. With debug information
              it is possible to set breakpoints on particular lines, single-step
              debug through the source code, etc.
    
    endif # EXPERT
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.8 source

    source 语句用于读取另一个文件中的 Kconfig 文件,
    比如Kconfig中就包含了其他Kconfig:

    source "arch/Kconfig"
    
    • 1

    4.9 comment

    comment 语句出现在界面的第一行,用于定义一些提示信息,如cmd/Kconfig

    comment "Commands"
    
    • 1

    界面如下:
    在这里插入图片描述

    4.10 测试代码

    4.10.1 config

    config 100ASK
            bool "test for 100ask"
            default y
            help
              just for Kconfig test
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.10.2 menu(多选)

    menu "100ASK menu"
    
    config IMX6ULL_TEST
            tristate "menu test item 1 IMX6ULL"
            help
              just for test menu.
    
    config STM32MP157_TEST
            tristate "menu test item 2 IMX6ULLh"
            help
              just for test menu.
    endmenu
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.10.3 choice(单选)

    config CHOICE_ON_100ASK
            bool "Enable choice for 100ask"
            help
              Enable choice for 100ask
    
    choice
            prompt "choice test for 100ask"
            depends on CHOICE_ON_100ASK
    
    config CHOICE_ITEM1
            bool "CHOICE_ITEM1"
            help
              CHOICE_ITEM1.
    
    config CHOICE_ITEM2
            bool "CHOICE_ITEM2"
            help
              CHOICE_ITEM2.
    
    endchoice
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4.10.4 menuconfig

    menuconfig 100ASK_MENUCFG
             bool "test for menuconfig of 100ask"
    
    if 100ASK_MENUCFG
    config IMX6ULL_TEST_MENUCFG
            tristate "menuconfig test item 1 IMX6ULL"
            help
              just for test menuconfig.
    
    config STM32MP157_MENUCFG
            tristate "menuconfig test item 2 IMX6ULLh"
            help
              just for test menuconfig.
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    menu "test menu"
    config TEST_A
        bool "TEST A"
        default y
        
    config TEST_B
        bool "TEST B"
        default y
    
    endmenu
    
    choice
            prompt "test choise"
            default TEST_C
    
    config TEST_C
        bool "TEST C"
        default y
        
    config TEST_D
        bool "TEST D"
        default y
    
    endchoice        
    
    menuconfig TEST_MENUCONFIG
        bool "test menuconfig"
        default n
        
    if TEST_MENUCONFIG
    config TEST_E
        bool "TEST E"
        default y
    
    config TEST_F
        bool "TEST F"
        default y
    
    
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    5. 编译uboot的过程分析

    5.1 体验

    IMX6ULL: 
    make mx6ull_14x14_evk_defconfig
    make
    
    STM32MP157: 
    make stm32mp15_trusted_defconfig
    make
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    make过程

    • 检查、更新头文件,比如include/config.h 、u-boot.cfg
    • 制作工具
    • 交叉编译
      • 编译哪些目录、哪些文件?
      • .c文件可能需要使用.config的配置值,它可以引用config.h

    5.2 配置过程详细分析

    参考资料:https://www.cnblogs.com/jianhua1992/archive/2022/11/02/16852774.html

    5.2.1 Makefile的精简代码

    PHONY := _all
    _all:
    
    _all: all
    
    KCONFIG_CONFIG	?= .config
    
    # 定义一些常用变量
    # 比如
    # build := -f $(srctree)/scripts/Makefile.build obj
    include scripts/Kbuild.include
    
    # 定义交叉编译工具链
    CC		= $(CROSS_COMPILE)gcc
    
    -include include/config/auto.conf
    -include include/config/auto.conf.cmd
    $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
    
    include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
    	$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
    	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
    		{ rm -f include/config/auto.conf; false; }
    	$(Q)touch include/config/auto.conf
    
    -include include/autoconf.mk
    -include include/autoconf.mk.dep
    
    # 里面又包含架构相关、CPU相关的文件
    # sinclude $(srctree)/arch/$(ARCH)/config.mk      # include architecture dependend rules
    # sinclude $(srctree)/$(CPUDIR)/config.mk         # include  CPU  specific rules
    include config.mk
    
    # 架构相关参数、相关文件、库
    # head-y := arch/arm/cpu/$(CPU)/start.o
    # libs-y += arch/arm/cpu/$(CPU)/
    include arch/$(ARCH)/Makefile  
    
    # 确定链接脚本
    ifndef LDSCRIPT
    	ifeq ($(wildcard $(LDSCRIPT)),)
    		LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds
    	endif
    	ifeq ($(wildcard $(LDSCRIPT)),)
    		LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds
    	endif
    	ifeq ($(wildcard $(LDSCRIPT)),)
    		LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds
    	endif
    endif
    
    # 默认要处理的库的路径
    libs-y += lib/
    libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
    libs-$(CONFIG_OF_EMBED) += dts/
    libs-y += fs/
    libs-y += net/
    libs-y += disk/
    libs-y += drivers/
    
    # 取决于配置的库的路径
    libs-$(CONFIG_FMAN_ENET) += drivers/net/fm/
    libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
    libs-$(CONFIG_SYS_FSL_MMDC) += drivers/ddr/fsl/
    libs-$(CONFIG_ALTERA_SDRAM) += drivers/ddr/altera/
    
    u-boot-init := $(head-y)
    u-boot-main := $(libs-y)
    
    u-boot-dirs	:= $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
    
    $(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;
    
    $(u-boot-dirs): prepare scripts
    	$(Q)$(MAKE) $(build)=$@
    
    # all目标要生成的依赖
    ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
    
    all:		$(ALL-y)
    
    u-boot.hex u-boot.srec: u-boot FORCE
            $(call if_changed,objcopy)
    
    u-boot:	$(u-boot-init) $(u-boot-main) u-boot.lds FORCE
    	$(call if_changed,u-boot__)
    ifeq ($(CONFIG_KALLSYMS),y)
    	$(call cmd,smap)
    	$(call cmd,u-boot__) common/system_map.o
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    上课时临时精简出来的Makefile:

    ifeq ($(KBUILD_SRC),)
    
    
    # That's our default target when none is given on the command line
    PHONY := _all
    _all:
    
    endif # ifeq ($(KBUILD_SRC),)
    
    # We process the rest of the Makefile if this is the final invocation of make
    ifeq ($(skip-makefile),)
    
    PHONY += all
    _all: all
    
    HOSTCC       = cc
    
    # Decide whether to build built-in, modular, or both.
    # Normally, just do built-in.
    
    KBUILD_MODULES :=
    KBUILD_BUILTIN := 1
    
    # 引入很多变量, 
    # 比如:
    # build := -f $(srctree)/scripts/Makefile.build obj
    include scripts/Kbuild.include
    
    # 定义交叉编译工具链
    AS		= $(CROSS_COMPILE)as
    CC		= $(CROSS_COMPILE)gcc
    CPP		= $(CC) -E
    
    
    version_h := include/generated/version_autogenerated.h
    timestamp_h := include/generated/timestamp_autogenerated.h
    
    no-dot-config-targets := clean clobber mrproper distclean \
    			 help %docs check% coccicheck \
    			 ubootversion backup tests
    
    config-targets := 0
    mixed-targets  := 0
    dot-config     := 1
    
    
    ifeq ($(mixed-targets),1)
    else
    ifeq ($(config-targets),1)
    else
    PHONY += scripts
    scripts: scripts_basic include/config/auto.conf
    	$(Q)$(MAKE) $(build)=$(@)
    
    ifeq ($(dot-config),1)
    -include include/config/auto.conf
    -include include/config/auto.conf.cmd
    # To avoid any implicit rule to kick in, define an empty command
    $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
    
    # If .config is newer than include/config/auto.conf, someone tinkered
    # with it and forgot to run make oldconfig.
    # if auto.conf.cmd is missing then we are probably in a cleaned tree so
    # we execute the config step to be sure to catch updated Kconfig files
    include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
    	$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
    	@# If the following part fails, include/config/auto.conf should be
    	@# deleted so "make silentoldconfig" will be re-run on the next build.
    	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
    		{ rm -f include/config/auto.conf; false; }
    	@# include/config.h has been updated after "make silentoldconfig".
    	@# We need to touch include/config/auto.conf so it gets newer
    	@# than include/config.h.
    	@# Otherwise, 'make silentoldconfig' would be invoked twice.
    	$(Q)touch include/config/auto.conf
    
    -include include/autoconf.mk
    -include include/autoconf.mk.dep
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    5.2.2 配置文件进一步处理

    前面生成了.config,但是它不是最终版本的配置文件。

    顶层Makefile会包含2个配置文件:include/config/auto.conf、include/autoconf.mk。

    u-boot中有非常多的配置文件:

    • .config:来自单板的默认配置、Kconfig

    • include/config/auto.conf:来自.config,去掉了很多注释

    • u-boot.cfg:它的内容跟头文件类似,来自

      • .config

      • 头文件include/common.h,又包含了"#include ",config.h内容如下:

        /* Automatically generated - do not edit */
        #define CONFIG_IMX_CONFIG       board/freescale/mx6ullevk/imximage.cfg
        #define CONFIG_BOARDDIR board/freescale/mx6ullevk
        #include 
        #include 
        #include 
        #include 
        #include 
        #include 
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
    • include/autoconf.mk:来自u-boot.cfg,但是移除include/config/auto.conf的内容以免重复

    需要进一步处理:

    include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
    	$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
    	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
    		{ rm -f include/config/auto.conf; false; }
    	$(Q)touch include/config/auto.conf
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述规则的命令里,有2个操作:

    $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
    $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf
    
    • 1
    • 2

    第1个操作相当于:make silentoldconfig,生成了文件

    • include/config/auto.conf:来自.config,去掉了很多注释
    • include/config/auto.conf.cmd:auto.conf的依赖规则
    • include/generated/autoconf.h:内容跟auto.conf类似,是C语言的头文件
    • include/config/tristate.conf:空文件
    • include/generated/autoksyms.h:空文件
    make silentoldconfig
    
    %config: scripts_basic outputmakefile FORCE
            $(Q)$(MAKE) $(build)=scripts/kconfig $@
    
    进入scripts/kconfig是它的Makefile: 
    silentoldconfig: $(obj)/conf
            $(Q)mkdir -p include/config include/generated
            $(Q)test -e include/generated/autoksyms.h || \
                touch   include/generated/autoksyms.h
            $< $(silent) --$@ $(Kconfig)
    
    scripts/kconfig/conf分析:
    scripts/kconfig/conf  --silentoldconfig Kconfig
    1. sync_kconfig = 1
    2. name = "Kconfig", conf_parse(name);
    3. name = conf_get_configname(); = ".config"
    4. conf_read(NULL);  # 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    第2个操作相当于make -f scripts/Makefile.autoconf,会生成一系列头文件:

    • ln -fsn arch-mx6 arch/arm/include/asm/arch
    • include/autoconf.mk, {spl,tpl}/include/autoconf.mk
    • include/config.h

    scripts/Makefile.autoconf分析:

    __all: include/autoconf.mk include/autoconf.mk.dep
    
    include include/config/auto.conf
    include scripts/Kbuild.include
    
    CC              = $(CROSS_COMPILE)gcc
    CPP             = $(CC) -E
    
    include config.mk
    
    include/autoconf.mk.dep: include/config.h FORCE
            $(call cmd,autoconf_dep)
    
    u-boot.cfg: include/config.h FORCE
            $(call cmd,u_boot_cfg)
    
    include/autoconf.mk: u-boot.cfg
            $(call cmd,autoconf)
    
    include/config.h: scripts/Makefile.autoconf create_symlink FORCE
            $(call filechk,config_h)
    
    create_symlink:
    ifdef CONFIG_CREATE_ARCH_SYMLINK
    ifneq ($(KBUILD_SRC),)
            $(Q)mkdir -p include/asm
            $(Q)if [ -d $(KBUILD_SRC)/arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \
                    dest=arch/$(ARCH)/mach-$(SOC)/include/mach;                     \
            else                                                                    \
                    dest=arch/$(ARCH)/include/asm/arch-$(if $(SOC),$(SOC),$(CPU));  \
            fi;                                                                     \
            ln -fsn $(KBUILD_SRC)/$$dest include/asm/arch
    else
            $(Q)if [ -d arch/$(ARCH)/mach-$(SOC)/include/mach ]; then       \
                    dest=../../mach-$(SOC)/include/mach;                    \
            else                                                            \
                    dest=arch-$(if $(SOC),$(SOC),$(CPU));                   \
            fi;                                                             \
            ln -fsn $$dest arch/$(ARCH)/include/asm/arch
    endif
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    5.2.3 配置过程总结

    在这里插入图片描述

    5.3 编译过程详细分析(IMX6ULL)

    5.3.1 结论

    以IMX6ULL为例,先说结论:

    在这里插入图片描述

    细节如下:

    • 执行make命令时,要编译得到哪些文件由ALL-y决定

      • 规则如下

        include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
        
        _all : all
        
        # Always append ALL so that arch config.mk's can add custom ones
        ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
        
        ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
        
        all:            $(ALL-y)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
      • ALL-y的值为:

        checkarmreloc u-boot-dtb.imx u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check u-boot.dtb
        
        • 1
    • u-boot-dtb.imx依赖于u-boot-dtb.bin,u-boot-dtb.bin又依赖于u-boot-nodtb.bin和dts/dt.dtb

    • 各类u-boot文件都依赖于u-boot,先编译得到u-boot,它由u-boot-initu-boot-main两部分链接而成

      • u-boot-init为:arch/arm/cpu/armv7/start.o

      • u-boot-main为:lib/built-in.o fs/built-in.o net/built-in.o 等等

      • 如何编译各个built-in.o,以lib/built-in.o为例

        • 执行: ``make -f ./scripts/Makefile.build obj=lib`
        • 会包含lib/Makefile,里面定义了obj-y
        • 使用Makefile.build的规则把各个obj-y编译、链接为built-in.o
      • 使用如下的命令链接得到u-boot:

        quiet_cmd_u-boot__ ?= LD      $@
              cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
              -T u-boot.lds $(u-boot-init)                             \
              --start-group $(u-boot-main) --end-group                 \
              $(PLATFORM_LIBS) -Map u-boot.map
        
        • 1
        • 2
        • 3
        • 4
        • 5
    • 再编译得到设备树文件: make -f ./scripts/Makefile.build obj=dts dtbs

      • 顶层Makefile如下:

        dts/dt.dtb: checkdtc u-boot
        ( Q ) (Q) (Q)(MAKE) $(build)=dts dtbs

        
        * scripts/Makefile.build中,会包含 dts/Makefile
        
        ```shell
        DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb
        
        $(DTB): arch-dtbs
        $(obj)/dt.dtb: $(DTB) FORCE
              $(call if_changed,shipped)
        targets += dt.dtb
        
        $(DTB): arch-dtbs
        	  $(Q)test -e $@ || (                                             \
        	  echo >&2;                                                       \
        	  echo >&2 "Device Tree Source is not correctly specified.";      \
        	  echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'";          \
        	  echo >&2 "or build with 'DEVICE_TREE=' argument";  \
        	  echo >&2;                                                       \
        	  /bin/false)
        
        arch-dtbs:
        	  $(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs
        
        .SECONDARY: $(obj)/dt.dtb.S
        
        obj-$(CONFIG_OF_EMBED) := dt.dtb.o
        
        dtbs: $(obj)/dt.dtb
        	  @:
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
      • 最后把u-boot、设备树打包: make -f ./scripts/Makefile.build obj=arch/arm/imx-common u-boot-dtb.imx

        include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
        
        %.imx: %.bin
              $(Q)$(MAKE) $(build)=arch/arm/imx-common $@
        
        • 1
        • 2
        • 3
        • 4

      5.3.2 顶层Makefile

      顶层Makefile里:

      • 包含架构相关的Makefile,里面指定了第1个文件(head-y)
      • 指定了要编译哪些子目录(libs-y)。

      从顶层Makefile开始分析:

      include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
      include arch/$(ARCH)/Makefile  # arch/arm/Makefile, 里面含有head-y libs-y
      
      libs-y += lib/
      libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
      libs-$(CONFIG_OF_EMBED) += dts/
      libs-y += fs/
      libs-y += net/
      libs-y += disk/
      libs-y += drivers/
      libs-y += drivers/dma/
      libs-y += drivers/gpio/
      libs-y += drivers/i2c/
      libs-y += drivers/mmc/
      
      libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)
      
      libs-y := $(sort $(libs-y))
      
      u-boot-dirs     := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
      
      u-boot-alldirs  := $(sort $(u-boot-dirs) $(patsubst %/,%,$(filter %/, $(libs-))))
      
      libs-y          := $(patsubst %/, %/built-in.o, $(libs-y))
      
      u-boot-init := $(head-y)
      u-boot-main := $(libs-y)
      
      ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29

      对于arch/arm/Makefile

      head-y := arch/arm/cpu/$(CPU)/start.o
      
      libs-y += arch/arm/cpu/$(CPU)/
      libs-y += arch/arm/cpu/
      libs-y += arch/arm/lib/
      
      • 1
      • 2
      • 3
      • 4
      • 5

      所以,顶层Makefile包含了arch/arm/Makefile,确定了head-y为arch/arm/cpu/armv7/start.o

      还定义了libs-y变量,它里面含有多个目录。

      5.3.3 编译子目录

      顶层Makefile里定义了libs-y,会进入里面每一目录,使用它的Makefile进行编译:

      u-boot-main := $(libs-y)
      
      $(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;
      
      u-boot-dirs     := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
      
      $(u-boot-dirs): prepare scripts
              # make -f $(srctree)/scripts/Makefile.build obj=arch/arm/cpu/arm7
              $(Q)$(MAKE) $(build)=$@   
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      比如:u-boot-main里面含有一个目录"arch/arm/cpu/armv7/“,在u-boot-dirs中它末尾的”/"被取消,就变成:arch/arm/cpu/armv7。

      然后使用scripts/Makefile.build进行处理: ( Q ) (Q) (Q)(MAKE) ( b u i l d ) = (build)= (build)=@ ,展开就是:

      make -f scripts/Makefile.build obj=arch/arm/cpu/armv7/
      
      • 1

      scripts/Makefile.build是编译u-boot源码的关键:

      src := $(patsubst $(prefix)/%,%,$(obj))  # obj=arch/arm/cpu/arm7/, src=arch/arm/cpu/armv7
      
      # 第1个目标
      __build:
      
      # The filename Kbuild has precedence over Makefile
      kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
      kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
      include $(kbuild-file)  # 包含 arch/arm/cpu/arm7/Makefile
      
      ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
      lib-target := $(obj)/lib.a  # 如果包含的arch/arm/cpu/armv7/Makefile里定义了lib-y
      endif
      
      ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
      builtin-target := $(obj)/built-in.o # 如果包含的arch/arm/cpu/armv7/Makefile里定义了obj-y
      endif
      
      
      __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
               $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
               $(subdir-ym) $(always)
              @:
      
      
      # Built-in and composite module parts
      $(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
              $(call cmd,force_checksrc)
              $(call if_changed_rule,cc_o_c)
              
      
      ifdef builtin-target
      quiet_cmd_link_o_target = LD      $@
      # If the list of objects to link is empty, just create an empty built-in.o
      cmd_link_o_target = $(if $(strip $(obj-y)),\
                            $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
                            $(cmd_secanalysis),\
                            rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)
      
      $(builtin-target): $(obj-y) FORCE
              $(call if_changed,link_o_target)
      
      targets += $(builtin-target)
      endif # builtin-target
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44

      5.3.4 链接得到u-boot

      顶层Makefile里:

      libs-y          := $(patsubst %/, %/built-in.o, $(libs-y))
      
      u-boot-init := $(head-y)
      u-boot-main := $(libs-y)
      
      # Rule to link u-boot
      # May be overridden by arch/$(ARCH)/config.mk
      quiet_cmd_u-boot__ ?= LD      $@
            cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
            -T u-boot.lds $(u-boot-init)                             \
            --start-group $(u-boot-main) --end-group                 \
            $(PLATFORM_LIBS) -Map u-boot.map
      
      quiet_cmd_smap = GEN     common/system_map.o
      cmd_smap = \
              smap=`$(call SYSTEM_MAP,u-boot) | \
                      awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
              $(CC) $(c_flags) -DSYSTEM_MAP="\"$${smap}\"" \
                      -c $(srctree)/common/system_map.c -o common/system_map.o
      
      u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
              $(call if_changed,u-boot__)
      ifeq ($(CONFIG_KALLSYMS),y)
              $(call cmd,smap)
              $(call cmd,u-boot__) common/system_map.o
      endif
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26

      就是把head-y和各个libs-y下的built-in.o链接得到u-boot。

      5.3.5 映像文件的依赖

      对于IMX6ULL,要使用的是u-boot-dtb.imx,它含有u-boot和设备树。

      在顶层Makefile里:

      include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
      
      %.imx: %.bin
              $(Q)$(MAKE) $(build)=arch/arm/imx-common $@
      
      • 1
      • 2
      • 3
      • 4

      u-boot-dtb.imx依赖于u-boot-dtb.bin,u-boot-dtb.bin又依赖于u-boot-nodtb.bin和dts/dt.dtb。

      在顶层Makefile里:

      u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
              $(call if_changed,cat)
      
      u-boot-nodtb.bin: u-boot FORCE
              $(call if_changed,objcopy)
              $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
              $(BOARD_SIZE_CHECK)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      所以,编译出u-boot后,会先制作u-boot-nodtb.bin;然后编译dts/dt.dtb;最后生成u-boot-dtb.bin、imx文件:

      在这里插入图片描述

      5.3.6 编译设备树

      顶层Makefile里:

      dts/dt.dtb: checkdtc u-boot
      	  # make -f $(srctree)/scripts/Makefile.build obj=dts  dtbs
            $(Q)$(MAKE) $(build)=dts dtbs
      
      • 1
      • 2
      • 3

      需要用到scripts/Makefile.build中,它会包含 dts/Makefile, dts/Makefile中含有dtbs目标:

      DEVICE_TREE ?= $(CONFIG_DEFAULT_DEVICE_TREE:"%"=%)
      DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb # 就是 arch/arm/dts/imx6ull-14x14-evk.dtb
      
      $(obj)/dt.dtb: $(DTB) FORCE
              $(call if_changed,shipped)
      
      targets += dt.dtb
      
      $(DTB): arch-dtbs
              $(Q)test -e $@ || (                                             \
              echo >&2;                                                       \
              echo >&2 "Device Tree Source is not correctly specified.";      \
              echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'";          \
              echo >&2 "or build with 'DEVICE_TREE=' argument";  \
              echo >&2;                                                       \
              /bin/false)
      
      arch-dtbs:
              $(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs
      
      .SECONDARY: $(obj)/dt.dtb.S
      
      obj-$(CONFIG_OF_EMBED) := dt.dtb.o
      
      dtbs: $(obj)/dt.dtb
              @:
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26

      最后会使用$(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs来处理,进入:使用arch/arm/dts/Makefile,里面定了dtb-y:

      dtb-$(CONFIG_MX6) += imx6ul-14x14-ddr3-arm2.dtb \
              imx6ul-14x14-ddr3-arm2-emmc.dtb \
              imx6ul-14x14-ddr3-arm2-gpmi-weim.dtb \
              imx6ul-14x14-lpddr2-arm2.dtb \
              imx6ul-14x14-evk.dtb \
              imx6ul-14x14-evk-emmc.dtb \
      
      PHONY += dtbs
      dtbs: $(addprefix $(obj)/, $(dtb-y))
              @:
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      要编译出各类dtb文件,用到scripts/Makefile.lib,规则如下:

      quiet_cmd_dtc = DTC     $@
      # Modified for U-Boot
      # Bring in any U-Boot-specific include after the '/dts-v1/;' header
      cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
              cat $< $(if $(u_boot_dtsi),\
                      | sed '/^\/ {$$/{x;s%$$%\#include \"$(u_boot_dtsi)\"%;G;}') | \
                      $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) - ; \
              $(DTC) -O dtb -o $@ -b 0 \
                      -i $(dir $<) $(DTC_FLAGS) \
                      -d $(depfile).dtc.tmp $(dtc-tmp) ; \
              cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)
      
      $(obj)/%.dtb: $(src)/%.dts FORCE
              $(call if_changed_dep,dtc)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      5.3.7 制作映像文件

      当制作出设备树文件后,在顶层Makefile里有如下规则,进而制作出u-boot-dtb.bin:

      u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
              $(call if_changed,cat)
      
      • 1
      • 2

      回到顶层Makefile,看看如何制作imx映像文件:

      include config.mk              # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
      
      %.imx: %.bin
              $(Q)$(MAKE) $(build)=arch/arm/imx-common $@
      
      • 1
      • 2
      • 3
      • 4

      现在u-boot-dtb.imx的依赖文件u-boot-dtb.bin已经生成了,将会使用arch/arm/imx-common/Makefile来生产imx文件:

      u-boot.imx: u-boot.bin $(IMX_CONFIG) $(PLUGIN).bin FORCE
              $(call if_changed,mkimage)
      
      • 1
      • 2

      最后会使用如下命令生产imx映像文件:

      ./tools/mkimage -n board/freescale/mx6ullevk/imximage.cfg.cfgtmp -T imximage -e 0x87800000 -d u-boot-dtb.bin u-boot-dtb.imx
      
      • 1

      5.4 配置编译过程(STM32MP157)

      使用如下命令配置、编译STM32MP157的u-boot:

      make stm32mp15_trusted_defconfig
      make
      
      • 1
      • 2

      所涉及的配置过程、编译过程,跟前面分析的IMX6ULL几乎一样。

      5.4.1 配置过程总结

      顶层Makefile会包含2个配置文件:include/config/auto.conf、include/autoconf.mk。

      u-boot中有非常多的配置文件:

      • .config:来自单板的默认配置、Kconfig

      • include/config/auto.conf:来自.config,去掉了很多注释

      • u-boot.cfg:它的内容跟头文件类似,来自

        • .config

        • 头文件include/common.h,又包含了"#include ",config.h内容如下:

          /* Automatically generated - do not edit */
          #define CONFIG_BOARDDIR board/st/stm32mp1
          #include 
          #include 
          #include 
          #include 
          #include 
          #include 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
      • include/autoconf.mk:来自u-boot.cfg,但是移除include/config/auto.conf的内容以免重复

      在这里插入图片描述

      5.4.2 编译过程总结

      在这里插入图片描述

      结论:

      • 执行make命令时,要编译得到哪些文件由ALL-y决定

        • 规则如下

          include config.mk              # 里面会包含arch/arm/config.mk
                                         # arch/arm/Makefile又包含$(machdirs)/config.mk
                                         # 就是包含 arch/arm/mach-stm32mp/config.mk
                                         # 里面有 ALL-y += u-boot.stm32
          
          _all : all
          
          # Always append ALL so that arch config.mk's can add custom ones
          ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
          
          ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
          
          all:            $(ALL-y)
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
        • ALL-y的值为:

          checkarmreloc u-boot.stm32 u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check u-boot.dtb
          
          • 1
      • u-boot.stm32依赖于u-boot.bin

        • u-boot.bin依赖于u-boot-dtb.bin
          • u-boot-dtb.bin依赖于u-boot-nodtb.bin和dts/dt.dtb
      • 各类u-boot文件都依赖于u-boot,先编译得到u-boot,它由u-boot-initu-boot-main两部分链接而成

        • u-boot-init为:arch/arm/cpu/armv7/start.o

        • u-boot-main为:lib/built-in.o fs/built-in.o net/built-in.o 等等

        • 如何编译各个built-in.o,以lib/built-in.o为例

          • 执行: ``make -f ./scripts/Makefile.build obj=lib`
          • 会包含lib/Makefile,里面定义了obj-y
          • 使用Makefile.build的规则把各个obj-y编译、链接为built-in.o
        • 使用如下的命令链接得到u-boot:

          quiet_cmd_u-boot__ ?= LD      $@
                cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
                -T u-boot.lds $(u-boot-init)                             \
                --start-group $(u-boot-main) --end-group                 \
                $(PLATFORM_LIBS) -Map u-boot.map
          
          • 1
          • 2
          • 3
          • 4
          • 5
      • 再编译得到设备树文件: make -f ./scripts/Makefile.build obj=dts dtbs

        • 顶层Makefile如下:

          dts/dt.dtb: checkdtc u-boot
                $(Q)$(MAKE) $(build)=dts dtbs
          
          • 1
          • 2
        • scripts/Makefile.build中,会包含 dts/Makefile

          DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb
          
          $(DTB): arch-dtbs
          $(obj)/dt.dtb: $(DTB) FORCE
                  $(call if_changed,shipped)
          
          targets += dt.dtb
          
          $(DTB): arch-dtbs
                  $(Q)test -e $@ || (                                             \
                  echo >&2;                                                       \
                  echo >&2 "Device Tree Source is not correctly specified.";      \
                  echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'";          \
                  echo >&2 "or build with 'DEVICE_TREE=' argument";  \
                  echo >&2;                                                       \
                  /bin/false)
          
          arch-dtbs:
                  $(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs
          
          .SECONDARY: $(obj)/dt.dtb.S
          
          obj-$(CONFIG_OF_EMBED) := dt.dtb.o
          
          dtbs: $(obj)/dt.dtb
                  @:
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
      • 最后制作u-boot.stm32,在arch/arm/mach-stm32mp/config.mk中有如下规则:

        u-boot.stm32: u-boot.bin FORCE
                $(call if_changed,mkimage)
        
        • 1
        • 2
        • if_changed函数在scripts/Kbuild.include中定义:

          if_changed = $(if $(strip $(any-prereq) $(arg-check)),                       \
                  @set -e;                                                             \
                  $(echo-cmd) $(cmd_$(1));                                             \
                  printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)
          
          • 1
          • 2
          • 3
          • 4
        • 展开$(call if_changed,mkimage),就是执行如下命令:

          cmd_mkimage
          
          • 1
        • cmd_mkimage命令在顶层Makefile中定义:

          cmd_mkimage = $(objtree)/tools/mkimage $(MKIMAGEFLAGS_$(@F)) -d $< $@ \
          
          • 1

      6. u-boot代码分析

      6.1 启动过程概述

      参考资料:https://blog.csdn.net/zhoutaopower/article/details/123133291

      BootROM、SPL、u-boot等概念,重定位等概念。

      6.1.1 XIP的概念

      • 一上电,CPU必定从XIP设备得到第1条指令
      • 号称支持Nand启动、支持SD卡启动、支持USB启动、支持UART启动的芯片,里面必定有BootROM
        • BootROM:硬件初始化、把程序从非XIP设备复制进RAM,从RAM里执行

      6.1.2 BootROM的作用

      BootROM:硬件初始化、把程序从非XIP设备复制进RAM,从RAM里执行

      如何支持多种启动方式(SD卡、EMMC、USB、UART启动)

      • 方法1:芯片有boot pin,决定使用哪个外设。bootrom根据引脚决定读取哪个设备的程序
      • 方法2:芯片有boot pin,决定多种外设的尝试顺序
        • 示例顺序1:SD、EMMC、USB
        • 示例顺序2:EMMC、SD、USB
        • 示例顺序3:USB

      6.1.3 完整的u-boot复制进内存

      BootROM被用来启动用户程序,用户程序可能有几百KB、几MB,但是片内的RAM只有几KB:

      • 方法1:
        • BootROM从启动设备读取用户程序的前几KB到SRAM,运行它;
        • 这前几KB的代码负责:初始化DDR、把完整的程序从启动设备复制到DDR、并跳到DDR运行
      • 方法2:
        • BootROM从启动设备读取SPL到SRAM,运行它;
        • SPL负责:初始化DDR、把用户程序从启动设备复制到DDR、并跳到DDR运行

      6.1.4 重定位的2种方法

      相对寻址、绝对寻址

      重定位有2种方法:

      • 程序当前位于地址A,但是它的链接地址是B,把它从A复制到B
      • 程序当前位于地址A,想把它复制到B
        • 把它从A复制到B
        • 修改程序,把里面使用到的地址都重新为基于B的新地址

      6.2 U-Boot的两个阶段

      u-boot的源码大致可以分为2个阶段:

      • board_init_f:f的意思是"running from read-only flash"
        • 作用:初始化硬件(比如DDR、UART),为各个功能预留内存(比如U-boot、Framebuffer、设备树)
      • board_init_r:r的意思是"relocated",意思是重定位过了
        • 作用:初始化各个子系统(各个存储设备、环境变量、网络),进入main_loop

      在这里插入图片描述

      6.2.1 board_init_f

      board_init_f函数的本意是在Flash上执行,程序的数据段并未被复制到内存,也没有清除BSS段。它以及它调用的子函数都不能使用全局变量、静态变量。有些芯片以上电,内置的BROM会把U-Boot复制到内存,对于这种情况,我们也把它当作仍然在Flash上运行,仍然不能使用全局变量、静态变量。

      board_init_f函数是C函数,调用它之前要设置栈。并且board_init_f函数里可能会调用malloc之类的堆函数:

      • 堆内存在哪?可以从栈里划分出来
      • 在哪里记录堆的基地址?继续在栈里划分区域用来保存一个gd_t结构体,在这个结构体里记录堆地址
      • 怎么访问gd_t结构体?它的地址记录在r9寄存器里

      在调用board_init_f之前,先调用board_init_f_alloc_reserve等函数,过程如下:

      在这里插入图片描述

      查看board_init_f_alloc_reserve函数,可以知道第1阶段的内存使用情况如下:

      在这里插入图片描述

      设置好global_data的指针,设置好栈后,_main函数调用board_init_f函数。而board_init_f函数的核心是调用init_sequence_f数组里的各个函数:

      在这里插入图片描述

      init_sequence_f数组内容如下:

      在这里插入图片描述

      board_init_f函数最大的作用就是为各个功能预留了内存,比如重定位后的U-boot放在哪里?Framebuffer、设备树放在哪里?

      它先确定DDR的内存范围,然后从上往下划分内存。

      这些信息记录在global_data里:

      // setup_mon_len
      gd->mon_len = (ulong)&__bss_end - (ulong)_start;  /* u-boot.bin的大小 */
      
      // fdtdec_setup
      gd->fdt_blob = (ulong *)&_end;
      
      // initf_malloc
      gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
      gd->malloc_ptr = 0;
      
      // dram_init
      gd->ram_size = imx_ddr_size();
      
      // setup_dest_addr
      	gd->ram_size = board_reserve_ram_top(gd->ram_size);
      #ifdef CONFIG_SYS_SDRAM_BASE
      	gd->ram_top = CONFIG_SYS_SDRAM_BASE;
      #endif
      	gd->ram_top += get_effective_memsize();
      	gd->ram_top = board_get_usable_ram_top(gd->mon_len);
      	gd->relocaddr = gd->ram_top;
      
      // reserve_logbuffer
      gd->relocaddr -= LOGBUFF_RESERVE;
      
      // reserve_uboot
      	gd->relocaddr -= gd->mon_len;
      	gd->relocaddr &= ~(4096 - 1);
      #ifdef CONFIG_E500
      	/* round down to next 64 kB limit so that IVPR stays aligned */
      	gd->relocaddr &= ~(65536 - 1);
      #endif
      
      	debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10,
      	      gd->relocaddr);
      
      	gd->start_addr_sp = gd->relocaddr;
      
      
      // reserve_malloc
      gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN;
      
      // reserve_board
      gd->start_addr_sp -= sizeof(bd_t);
      gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t));
      memset(gd->bd, '\0', sizeof(bd_t));
      
      // reserve_global_data
      gd->start_addr_sp -= sizeof(gd_t);
      gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t));
      
      // reserve_fdt
      gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
      
      gd->start_addr_sp -= gd->fdt_size;
      gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
      
      // reserve_stacks
      gd->start_addr_sp -= 16;
      gd->start_addr_sp &= ~0xf;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60

      6.2.2 board_init_r

    • 相关阅读:
      figma对比sketch有什么优势和不足?
      面向对象07:简单小结类与对象
      centos上部署Ollama平台,实现语言大模型本地部署
      EF Core :IQueryable的延迟执行
      Vue图片路径问题(动态引入)
      d原位数组扩展优化
      Python数据分析与机器学习2-Numpy
      MQ系列4:NameServer 原理解析
      [源码系列:手写spring] AOP第一节:切点表达式
      Python15题day13
    • 原文地址:https://blog.csdn.net/qq_23174771/article/details/133823481