• U-BOOT小全(二)


    U-Boot配置和编译

    前面在大概了解U-Boot及其目录结构后,就可以开始配置和编译U-Boot了。

    在U-Boot源码中有一个README文件,它描述了如何配置并编译U-Boot。(其实很多都有,只不过是英文的,哈哈哈)

    正确编译U-Boot

    正确编译U-Boot代码的过程如下:

    1)首先是编译器的问题,如果使用GNU交叉编译工具链,请确保环境变量的设置生效。

    2)为特定的板子建立配置文件。输入make NAME_config就可以,其中“NAME_config”代表一个存在的配置名称,顶层目录下的boards.cfg文件中是已经支持的配置名称。

    3)最后,输入“make all”就可以得到U-Boot映像文件了。其中“u-boot.bin”是二进制文件,“U-Boot”是ELF二进制格式的文件。

    其中第二步举个例子

    第二步:为特定的硬件板pcDuino3建立配置文件,打开boards.cfg文件,添加一行配置信息:

    Active  arm  armv7  sunxi  -  sunxi
    pcDuino3  sun7i:PCDUINO,SPL,SUNXI_EMAC
    
    • 1
    • 2

    然后输入make pcDuino3_config命令,执行第二步的命令,接下来分析这个命令做了什么:

    %_config:: outputmakefile
        @$(MKCONFIG) -A $(@:_config=)
    
    • 1
    • 2

    由于%是个通配符,所以无论是何种配置,make xxx_config都是这个目标。命令中的MKCONFIG在Makefile之前有如下定义:

    MKCONFIG := $(SRCTREE)/mkconfig
    export MKCONFIG
    
    • 1
    • 2

    该定义表明MKCONFIG是顶层目录下的mkconfig脚本文件。

    我们继续来看完整的命令:@ ( M K C O N F I G ) − A (MKCONFIG)-A (MKCONFIG)A(@:_config=),这里$(@:_config=)是变量的替换引用。

    其具体使用方法为:格式为“ ( V A R : A = B ) ”或者“ (VAR:A=B)”或者“ (VAR:A=B)或者{VAR:A=B}”,意思是:**替换变量“VAR”中所有以“A”字符结尾的字为“B”结尾的字。**这将相当于把pcDuino3_config末尾的_config去除了。因此实际执行的是“mkconfig-A pcDuino3”命令。

    执行该脚本将生成两个文件,这两个文件将在后面的步骤中被引用。

    下面就是执行mkconfig脚本了:

    mkconfig -A pcDuino3
    
    • 1

    执行该脚本,生成两个文件,一个是include/config.h,如下:

    /* Automatically generated - do not edit */
    #define CONFIG_PCDUINO        1
    #define CONFIG_SPL        1
    #define CONFIG_SUNXI_EMAC        1
    #define CONFIG_SYS_ARCH  "arm"
    #define CONFIG_SYS_CPU   "armv7"
    #define CONFIG_SYS_BOARD "sunxi"
    #define CONFIG_SYS_TARGET "pcDuino3"
    #define CONFIG_SYS_SOC    "sunxi"
    #define CONFIG_BOARDDIR board/sunxi
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    还有一个文件是include/config.mk,如下:

    ARCH   = arm
    CPU    = armv7
    BOARD  = sunxi
    SOC    = sunxi
    
    • 1
    • 2
    • 3
    • 4

    下一步就是make。

    因为在boards.cfg文件中有配置sun7i:PCDUINO、SPL、SUNXI_EMAC,所以这里我们将使用SPL框架。在spl目录下,有一个Makefile文件。我们简单分析一下这个Makefile文件。

    (移植过uboot的都知道,uboot的启动其实是分为BL0,BL1,BL2三个阶段的,即:ROM->SPL->uboot.img.而这个SPL架构将可以编译产生一个uboot-spl.bin。即BL1的代码。也就是说SPL结构其实做的工作就是uboot的BL1阶段的工作。但是这个SPL不是必须的,后面再讲讲这个东西)

    首先

    CONFIG_SPL_BUILD := y
    export CONFIG_SPL_BUILD
    
    • 1
    • 2

    定义并导出CONFIG_SPL_BUILD,这个定义是SPL框架最重要的定义,在最初的汇编代码中很多代码段都由该定义隔开。

    include include/config.mk
    include $(TOPDIR)/config.mk
    
    • 1
    • 2

    引入刚刚生成的include/config.mk和顶层目录下的config.mk,其中顶层目录下的config.mk是设置了一些编译链接的选项。

    接下来是SPL需要的代码和模块。再往下是指定链接脚本。

    # Linker Script
    ifdef CONFIG_SPL_LDSCRIPT
    # need to strip off double quotes
    LDSCRIPT := $(addprefix $(SRCTREE)/,$(CONFIG_SPL_LDSCRIPT:"%"=%))
    endif
    ifeq ($(wildcard $(LDSCRIPT)),)
        LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-spl.lds
    endif
    ifeq ($(wildcard $(LDSCRIPT)),)
        LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-spl.lds
    endif
    ifeq ($(wildcard $(LDSCRIPT)),)
        LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot-spl.lds
    Endif
    ifeq ($(wildcard $(LDSCRIPT)),)
    $(error could not find linker script)
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    再下面是描述最终的目标:

    ALL-y   += $(obj)/$(SPL_BIN).bin
    ifdef CONFIG_SUNXI
    ifndef CONFIG_SPL_FEL
    ALL-y   += $(obj)/sunxi-spl.bin
    endif
    endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    所以最后在该目录下会生成u-boot-spl.bin和sunxi-spl.bin,其中sunxi-spl.bin是使用mksunxiboot工具生成的:

    ifdef CONFIG_SUNXI
    quiet_cmd_mksunxiboot = MKSUNXI $@
    cmd_mksunxiboot = $(OBJTREE)/tools/mksunxiboot $< $@
    $(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin
        $(call if_changed,mksunxiboot)
    Endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    签名分析的是makefile
    接下来分析一下链接脚本。根据前面关于链接脚本的一系列判断和指定,此处的链接脚本采用的是arch/arm/cpu/armv7/sunxi/u-boot-spl.lds,脚本如下:

    MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\
            LENGTH = CONFIG_SPL_MAX_SIZE }
    MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \
            LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    OUTPUT_ARCH(arm)
    ENTRY(_start)
    SECTIONS
    {
        .text      :
        {
            __start = .;
            arch/arm/cpu/armv7/start.o        (.text)
            *(.text*)
        } > .sram
        . = ALIGN(4);
        .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
        . = ALIGN(4);
        .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
        . = ALIGN(4);
        __image_copy_end = .;
        _end = .;
        .bss :
        {
            . = ALIGN(4);
            __bss_start = .;
            *(.bss*)
            . = ALIGN(4);
            __bss_end = .;
        } > .sdram
    }
    
    • 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

    在脚本最开始处,使用MEMORY命令定义了两个存储区域:一个是sram,起始地址为CONFIG_SPL_TEXT_BASE,长度为CONFIG_SPL_MAX_SIZE;另一个是sdram,起始地址为CONFIG_SPL_BSS_START_ADDR,长度为CONFIG_SPL_BSS_MAX_SIZE。这几个定义都是在include/configs/sunxi-common.h中:

    #define                CONFIG_SPL_TEXT_BASE           0x20
    #define                CONFIG_SPL_TEXT_BASE           0x5fe0
    #define                CONFIG_SPL_BSS_START_ADDR      0x50000000
    #define                CONFIG_SPL_BSS_MAX_SIZE        0x80000
    
    • 1
    • 2
    • 3
    • 4

    这里可以看到TEXT段的基址定为0x20,这是因为mksunxiboot的存在,它会加上大小为0x20的特定的头。

    在SECTIONS命令中定义了.text、.rodata、.data和.bss这四个段。其中.text、.rodata和.data段放在sram内存区域中,而.bss段放在sdram内存区域中。

    在.text段中,我们将arch/arm/cpu/armv7/start.o中的.text段放置在最前面,其他对象文件的.text段随后放置。在.rodata段和.data段中,使用SORT_BY_ALIGNMENT将各个对象文件的相应段对齐排序。

    或许到这里你很困惑,这是因为你和我一样对SPL框架没有认识,下面我们来学一下SPL,在了解了SPL以后,再反过来看看这里对SPL的Makefile和ldscript的讲解。

  • 相关阅读:
    记一次 ThreadLocal 泄漏导致的 shardingsphere-jdbc-core 单元测试偶发失败的排查与修复
    SpringBoot定时任务(@EnableScheduling注解)
    c++day5
    找不到实时聊天软件?给你推荐电商企业都在用的!
    java8 reduce分组去重
    酷开科技智能大屏OS Coolita亮相第134届中国进出口商品交易会
    【科学计算与可视化】1. Numpy 基础
    [激光原理与应用-15]:《激光原理与技术》-1- 什么是激光,激光概述
    RK3588 实现温控风扇之pwm驱动调试(二)
    Pinia 计数器
  • 原文地址:https://blog.csdn.net/weixin_45264425/article/details/127400202