u-boot 订阅
Das U-Boot 是一个主要用于嵌入式系统的引导加载程序,可以支持多种不同的计算机系统结构,包括PPC、ARM、AVR32、MIPS、x86、68k、Nios与MicroBlaze。这也是一套在GNU通用公共许可证之下发布的自由软件。Das U-Boot可以在x86计算机上建构,但这部x86计算机必须安装有可支持特定平台结构的交互发展GNU工具链,例如crosstool、Embedded Linux Development Kit (ELDK)或OSELAS.Toolchain。 展开全文
Das U-Boot 是一个主要用于嵌入式系统的引导加载程序,可以支持多种不同的计算机系统结构,包括PPC、ARM、AVR32、MIPS、x86、68k、Nios与MicroBlaze。这也是一套在GNU通用公共许可证之下发布的自由软件。Das U-Boot可以在x86计算机上建构,但这部x86计算机必须安装有可支持特定平台结构的交互发展GNU工具链,例如crosstool、Embedded Linux Development Kit (ELDK)或OSELAS.Toolchain。
信息
类    型
开放源码项目
特    点
遵循GPL条款
中文名
U-Boot
全    称
Universal Boot Loader
U-Boot简介
U-Boot不仅仅支持嵌入式Linux系统的引导,它还支持NetBS D, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS, android。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、 x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11 月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心Wolfgang Denk[以下简称W.D]本人精湛专业水平和执着不懈的努力。当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOT LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。选择U-Boot的理由:① 开放源码;② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android;③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS;④ 较高的可靠性和稳定性;⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;⑦ 较为丰富的开发调试文档与强大的网络技术支持;
收起全文
精华内容
参与话题
问答
  • Boot downloading started. Boot 100 % Downloaded. Boot Downloading completed!...U-Boot 2010.06 (Sep 27 2014 - 13:55:05) NAND: Check nand flash controller v610. found Special NAND id table Ver
    Boot downloading started.
    Boot 100 % Downloaded.
    Boot Downloading completed!

    U-Boot 2010.06 (Sep 27 2014 - 13:55:05)


    NAND:  Check nand flash controller v610. found
    Special NAND id table Version 1.36
    Nand ID: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    No NAND device found!!!
    0 MiB
    Check spi flash controller v350... Found
    Spi(cs1) ID: 0xC2 0x20 0x18 0xC2 0x20 0x18
    Spi(cs1): Block:64KB Chip:16MB Name:"MX25L128XX"


    *** Warning - bad CRC, using default environment


    In:    serial
    Out:   serial
    Err:   serial
    start download process.


    Boot Started successfully!
    version: 3.0.3
    (OK)
    "getinfo version" command sent successfully!
    spi
    (OK)
    "getinfo bootmode" command sent successfully!
    16384 KiB hi_sfc at 0:0 is now current device
    (OK)
    "sf probe 0" command sent successfully!
    Erasing at 0x10000 --   6% complete.
    Erasing at 0x20000 --  12% complete.
    Erasing at 0x30000 --  18% complete.
    Erasing at 0x40000 --  25% complete.
    #
    Erasing at 0x50000 --  31% complete.
    Erasing at 0x60000 --  37% complete.
    Erasing at 0x70000 --  43% complete.
    #
    Erasing at 0x80000 --  50% complete.
    Erasing at 0x90000 --  56% complete.
    Erasing at 0xa0000 --  62% complete.
    Erasing at 0xb0000 --  68% complete.
    #
    Erasing at 0xc0000 --  75% complete.
    Erasing at 0xd0000 --  81% complete.
    Erasing at 0xe0000 --  87% complete.
    Erasing at 0xf0000 --  93% complete.
    #
    Erasing at 0x100000 -- 100% complete.
    (OK)
    "sf erase 0 0x00100000" command sent successfully!
    Writing at 0x10000 --   6% complete.
    Writing at 0x20000 --  12% complete.
    Writing at 0x30000 --  18% complete.
    #
    Writing at 0x40000 --  25% complete.
    Writing at 0x50000 --  31% complete.
    Writing at 0x60000 --  37% complete.
    Writing at 0x70000 --  43% complete.
    Writing at 0x80000 --  50% complete.
    Writing at 0x90000 --  56% complete.
    Writing at 0xa0000 --  62% complete.
    Writing at 0xb0000 --  68% complete.
    #
    Writing at 0xc0000 --  75% complete.
    Writing at 0xd0000 --  81% complete.
    Writing at 0xe0000 --  87% complete.
    Writing at 0xf0000 --  93% complete.
    Writing at 0x100000 -- 100% complete.
    (OK)
    "sf write 0x81000000 0 0x00100000" command sent successfully!

    展开全文
  • U-Boot -- uboot代码深度解析

    千次阅读 2018-06-05 10:19:37
    1.1 uboot源码 本文是基于hdu5开发板对应的uboot源码进行分析。 1.2 代码阅读软件source insight 可以想象uboot源码包有10000多个文件,每个文件都有几百行甚至上千行代码。需要专业的代码阅读器查找函数原型,...

    1. 准备工作

     

        1.1 uboot源码

    本文是基于hdu5开发板对应的uboot源码进行分析。

     1.2 代码阅读软件source insight

    可以想象uboot源码包有10000多个文件,每个文件都有几百行甚至上千行代码。需要专业的代码阅读器查找函数原型,根据需求去查找阅读,完全没必要重头读到尾。

        1.3 hdu5开发板和对应的数据手册

    在uboot源码中常常直接对一块地址进行操作,看的人云里雾里,通过查阅数据手册可以协助我们理解那些语句的作用。

        1.4 开发环境

    我们还需要一个linux环境去编译uboot代码,还需要在linux下对uboot镜像进行反汇编。

    注意:编译uboot镜像前需要配置交叉编译工具链。

    2. U-Boot源码分析

     

        2.1 uboot的配置过

            在拿到uboot代码后的第一步,我们需要做什么?执行make hdu5_config。

            那为什么要执行make hdu5_config? 我们可以通过查看源代码去理解执行make hdu5_config的深层次原因。

     

            通过顶层Makefile可以看到,在执行make hdu5_config的时候,实质上调用了如下部分:

    #### u-boot-2010.06/Makefile ####
    hdu5_config: unconfig
            @$(MKCONFIG) $(@:_config=) arm hi3536 hdu5 NULL hi3536
    #### 注意:$(@:_config=) 就是将hdu5_config中的_config替换为空!得到hdu5; ####
    #### 注意:每段代码段的第一行指明了代码存在的目录 ####
    

            首先,确定下变量的值,这里以hdu5板为例:

    #### 在顶层Makefile中会涉及到如下变量 ####
    $1 = hdu5 
        $2 = arm  
        $3 = hi3536 
        $4 = hdu5 
        $5 = NULL
        $6 = hi3536  
          
        CURDIR  = ./  
        SRCTREE = ./  
        TOPDIR  = ./  
        MKCONFIG= $(SRCTREE)/mkconfig = ./mkconfig  
          
        BOARD_NAME = "$1" = hdu5  
        ARCH= arm  
        OBJTREE= $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))  = ./  
        LNPREFIX = 空  
        BOARDDIR = $4 = hdu5
    

            通过上面的代码可以推导出:@$(MKCONFIG) $(@:_config=) arm hi3536 hdu5 NULL hi3536 等于 ./mkconfig hdu5 arm hi3536 hdu5NULL hi3536

            推导出:”makehdu5_config” 实际执行 “./mkconfig hdu5 arm hi3536 hdu5 NULLhi3536”

            上面那这段代码具体干了什么事情呢?咱们继续向下分析。

            mkconfig实际上就是顶层目录下的一个文件。那么,就来研究下顶层目录下的mkconfig文件:

    #### u-boot-2010.06/mkconfig ####
    注: mkconfig文件注释符改为/* 注释内容 */ 
        /* Default: Create new config file */  
        APPEND=no  
        /* Name to print in make output */  
        BOARD_NAME=""     
        TARGETS=""  
        
        /* $#: ./mkconfig hdu5 arm hi3536 hdu5 NULL hi3536命令行参数的个数 
         *     $0         $1   $2  $3     $4   $5   $6 
         * $符号总结: 
         * $#: 代表后接的参数个数,以上为例这里为[6] 
         * $@: 代表["$1" "$2" "$3" "$4"]之意,每个变量是独立的(用双引号括起来) 
         * $*: 代表["$1c$2c$3c$4"],其中c为分割字符,默认为空格键,所以本例中代表["$1 $2 $3 $4"] 
         * 
         * -gt: great than; -lt: less than 
         */  
        while [ $# -gt 0 ] ; do  
            case "$1" in  
            /* shift命令:  
             * 变量号码偏移功能,简单来说就是移动变量,即转到下一个变量$2,$3... 
             * 见[鸟哥的linux私房菜] 
             */  
            --) shift ; break ;;  
            -a) shift ; APPEND=yes ;;  
            -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;  
            -t) shift ; TARGETS="`echo $1 | sed 's:_: :g'` ${TARGETS}" ; shift ;;  
            *)  break ;;  
            /* case条件不满足,故本循环中不做任何事 */  
            esac  
        done  
          
        /* BOARD_NAME = hdu5 */  
        [ "${BOARD_NAME}" ] || BOARD_NAME="$1"  
          
        /* 参数检查,不满足直接退出 */  
        [ $# -lt 4 ] && exit 1  
        [ $# -gt 6 ] && exit 1  
          
        if [ "${ARCH}" -a "${ARCH}" != "$2" ]; then  
            echo "Failed: \$ARCH=${ARCH}, should be '$2' for ${BOARD_NAME}" 1>&2  
            exit 1  
        fi  
          
        echo "Configuring for ${BOARD_NAME} board..."  
          
        /* Create link to architecture specific headers */  
        if [ "$SRCTREE" != "$OBJTREE" ] ; then  
            /* 在指定的${OBJTREE}目录下编译,可以保持源代码目录的干净,不执行该分支 */  
            mkdir -p ${OBJTREE}/include  
            mkdir -p ${OBJTREE}/include2  
            cd ${OBJTREE}/include2  
            rm -f asm  
            ln -s ${SRCTREE}/arch/$2/include/asm asm  
            LNPREFIX=${SRCTREE}/arch/$2/include/asm/  
            cd ../include  
            rm -f asm  
            ln -s ${SRCTREE}/arch/$2/include/asm asm  
        else  
            cd ./include  
            /* -f: 删除是不显示提示信息,对于不存在的文件,会忽略掉  
             * asm: 上次配置过程中建立的连接文件 
             */  
            rm -f asm  
            /* -s: make symbolic links instead of hard links 
             * asm -> /arch/arm/include/asm 
             */  
            ln -s ../arch/$2/include/asm asm  
        fi  
          
        /* 即/arch/$2/include/asm/arch-$6,为执行make hdu5_config产生的连接文件, arch/arm/include/asm/arch-hi3536 */  
        rm -f asm/arch  
          
        /* -z STRING: 判断字符串STRING是否为0,若STRING为空字符串,则为true 
         * -o: or或的意思 
         */  
        if [ -z "$6" -o "$6" = "NULL" ] ; then  
            ln -s ${LNPREFIX}arch-$3 asm/arch  
        else  
            /* arch->arch/arm/include/asm/arch-hi3536 */  
            ln -s ${LNPREFIX}arch-$6 asm/arch  
        fi  
          
        if [ "$2" = "arm" ] ; then  
            /* proc->arch/arm/include/asm/proc-armv */  
            rm -f asm/proc  
            ln -s ${LNPREFIX}proc-armv asm/proc  
        fi  
          
        /* Create include file for Make 
         * >: 定向输出到文件,若文件不存在创建空文件 
         * >>: 追加内容到指定的文件末尾 
         */  
        echo "ARCH   = $2" >  config.mk  
        echo "CPU    = $3" >> config.mk  
        echo "BOARD  = $4" >> config.mk  
          
        [ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk  
          
        [ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk  
          
        /* Assign board directory to BOARDIR variable */  
        if [ -z "$5" -o "$5" = "NULL" ] ; then  
    
       /* BOARDDIR = hdu5 */ 
          BOARDDIR=$4  
        else   
            BOARDDIR=$5/$4  
        fi  
          
        /* Create board specific header file 
         */  
        if [ "$APPEND" = "yes" ] /* Append to existing config file */  
        then  
            echo >> config.h  
        else  
            > config.h       /* Create new config file */  
        fi  
        echo "/* Automatically generated - do not edit */" >>config.h  
          
        for i in ${TARGETS} ; do  
            echo "#define CONFIG_MK_${i} 1" >>config.h ;  
        done  
        cat << EOF >> config.h  
        #define CONFIG_BOARDDIR board/$BOARDDIR  
        #include <config_defaults.h>  
        #include <configs/$1.h>  
        #include <asm/config.h>  
        EOF  
        exit 0  

            ./include/config.h文件内容:

    #### u-boot-2010.06/include/config.h ####
        /* Automatically generated - do not edit */    
        #define CONFIG_BOARDDIR board/hdu5   
        #include <config_defaults.h>    
        #include <configs/hdu5.h>    
        #include <asm/config.h>  

            ./include/config.mk文件内容:

    #### u-boot-2010.06/include/config.mk ####
        ARCH    = arm  
        CPU     = hi3536
        BOARD   = hdu5
        VENDOR  = 空 
        SOC     = hdu5

            综上,总结下mkconfig文件(或者叫make hdu5_config)的作用:

                l  确定ARCHCPUBOARD等变量的值,并存到./include/config.mk文件中

                l  建立板级相关的 ./include/config.h文件

                l  建立指向其他文件的软链接

        2.2 uboot的编译与链接过程

     

            说完配置我们再回到Makefile中来看看编译与链接,面对Makefile的时候首先我们就会想到最后的目标文件u-boot.bin是怎样产生的:

     

    #### u-boot-2010.06/Makefile ####
    /*  CROSS_COMPILE =                             #指定编译器种类   */
    /*  OBJCOPY        = $(CROSS_COMPILE)objcopy   #转换目标文件格式  */
    /*  OBJCFLAGS     += --gap-fill=0xff                #段之间的空隙用0xff填充  */ 
    $(obj)u-boot.bin:	$(obj)u-boot
    $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
    #### 注意:/* */这些是注释 ####
    

            从上段代码可以看到u-boot.bin 是用$(OBJCOPY) 从u-boot生成的,u-boot是elf格式的文件,不能直接在裸机上运行,所以需要用$(OBJCOPY) 把u-boot转换成二进制u-boot.bin文件。

    #### u-boot-2010.06/Makefile ####
    $(obj)u-boot:	ddr_training depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
    		$(GEN_UBOOT)
    ifeq ($(CONFIG_KALLSYMS),y)
    		smap=`$(call SYSTEM_MAP,u-boot) | \
    			awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
    		$(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
    			-c common/system_map.c -o $(obj)common/system_map.o
    		$(GEN_UBOOT) $(obj)common/system_map.o
    endif

            通过上面代码可以分析出:u-boot的产生依赖于depend, $(SUBDIRS), $(OBJS), $(LIBBOARD), $(LIBS), $(LDSCRIPT) 。这里介绍一下这几个依赖目标(其中涉及到很多变量,均在顶层config.mk中):

                l  $(SUBDIRS):进入各个子目录中执行make

    SUBDIRS	= tools \
    	  examples/standalone \
    	  examples/api
    .PHONY : $(SUBDIRS)
    ...
    $(SUBDIRS):	depend
    		$(MAKE) -C $@ all

                l  $(OBJS):OBJS  = $(CPUDIR)/start.o, 即为'arch/arm/cpu/hi3536/start.o',而要产生start.o需要进入$(CPUDIR)进行编译。

    CPUDIR=arch/$(ARCH)/cpu/$(CPU)  #### u-boot-2010.06\config.mk ####
    OBJS  = $(CPUDIR)/start.o         
    …
    $(OBJS):	depend
    		$(MAKE) -C $(CPUDIR) $(if $(REMOTE_BUILD),$@,$(notdir $@))

                l  $(LIBBOARD):这个也很好理解就是产生board/$(BOARDDIR)/lib$(BOARD).a,对hdu5来说,LIBBOARD =board /hdu5/libhdu5.a

    BOARD    = hdu5          #### u-boot-2010.06\include\config.mk ####
    BOARDDIR = $(BOARD)    #### u-boot-2010.06\config.mk ####
    LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).a
    LIBBOARD := $(addprefix $(obj),$(LIBBOARD))
    $(LIBBOARD):	depend $(LIBS)
    		$(MAKE) -C $(dir $(subst $(obj),,$@))

                l  $(LIBS):LIBS包括的目标非常多,都是将子目录的源码编成*.a库文件,通过执行每个目录的Makefile来实现。

    LIBS  = lib/libgeneric.a
    LIBS += lib/lzma/liblzma.a
    …
    $(LIBS):	depend $(SUBDIRS)
    		$(MAKE) -C $(dir $(subst $(obj),,$@))

                l  $(LDSCRIPT):这里其实就是执行链接所需要的链接脚本,这里我需要特别强调链接脚本,链接脚本是程序链接的依据,它规定了可执行文件中的程序的输出格式是大端还是小端,程序如何来布局(第一条指令是那一条,各个依赖文件是如何组成最后的目标文件的),程序的入口是那里(只对elf文件有用)。

    CURDIR       = ./
    SRCTREE		:= $(CURDIR)
    TOPDIR		:= $(SRCTREE)
    LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
    $(LDSCRIPT):	depend
    		$(MAKE) -C $(dir $@) $(notdir $@)

            总结:u-boot的产生其实简单来说就进入各个目录下执行make,将指定目录下的.c文件编译生成.o文件,将指定目录下源码编成*.a库,最后再将这些文件按照链接脚本组合成最后的目标文件。

            还有一点,通常放到板子上运行的镜像为u-boot.bin而不是u-boot,是因为u-boot虽然是一个可执行镜像,但里面包含了大量的调试信息,文件也非常的大。而u-boot.bin是将u-boot镜像通过objcopy转换为二进制,去掉了其中调试信息,代码非常紧凑,文件小很多,适合作为镜像放板子上运行。

        2.3 uboot第一阶段解析

     

            接下来正式开始uboot源码之旅,分析代码当然要从上电后执行的第一条指令开始看起咯,那第一条指令在哪呢?还是以hdu5为例,首先我们来看一下它的链接脚本,通过它我们可以知道它整个程序的各个段是怎么存放的uboot运行的第一段代码在arch/arm/cpu/hi3536/start.S文件中)

     

    #### u-boot-2010.06\arch\arm\cpu\hi3536\ u-boot.lds ####
    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")    /*指定输出可执行文件是elf格式,*/
    /* 32位ARM指令,小端 */
    OUTPUT_ARCH(arm)    /*指定输出可执行文件的平台为ARM*/
    ENTRY(_start)          /*指定输出可执行文件的起始代码段为_start*/
    SECTIONS             
    {
    /*指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置。*/
    /*必须使编译器知道这个地址,通常都是修改此处来完成*/
    	. = 0x00000000;    /*;从0x0位置开始运行*/
    	. = ALIGN(4);     /*代码以4字节对齐*/
    	.text	:
    	{
    		__text_start = .;
    		arch/arm/cpu/hi3536/start.o	(.text)    /* 代码段的起始部分就是最开始运行代码的地方, */
                                             /* 因此uboot运行的第一条指令在arch/arm/cpu/hi3536/start.S文件 */
    		drivers/ddr/ddr_training_impl.o (.text)
    		drivers/ddr/ddr_training_ctl.o (.text)
    		drivers/ddr/ddr_training_boot.o (.text)
    		drivers/ddr/ddr_training_custom.o (.text)
    		__init_end = .;
    		ASSERT(((__init_end - __text_start) < 0x16000), "init sections too big!");
    		*(.text)      /*下面依次为各个text段函数*/
    	}
    	. = ALIGN(4);    /*代码以4字节对齐*/
    	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }     /*指定只读数据段*/
    	. = ALIGN(4);
    	.data : { *(.data) }
    	. = ALIGN(4);
    	.got : { *(.got) }                    /*指定got段, got段是uboot自定义的一个段, 非标准段*/
    	__u_boot_cmd_start = .;             /*把__u_boot_cmd_start赋值为当前位置, 即起始位置*/
    	.u_boot_cmd : { *(.u_boot_cmd) }     /*指定u_boot_cmd段, uboot把所有的uboot命令放在该段.*/
    	__u_boot_cmd_end = .;             /*把__u_boot_cmd_end赋值为当前位置,即结束位置*/
    	. = ALIGN(4);
    	__bss_start = .;                    /*把__bss_start赋值为当前位置,即bss段的开始位置*/
    	.bss : { *(.bss) }                   /*指定bss段 */
    	_end = .;                         /*把_end赋值为当前位置,即bss段的结束位置*/
    }

            现在知道uboot的第一行代码在哪里运行了吗?(在arch/arm/cpu/hi3536/start.S中运行)下面我们来分析start.S汇编代码。

    .globl _start
    _start: b	reset
    	ldr	pc, _undefined_instruction
    	ldr	pc, _software_interrupt
    	ldr	pc, _prefetch_abort
    	ldr	pc, _data_abort
    	ldr	pc, _not_used
    	ldr	pc, _irq
    	ldr	pc, _fiq

            在这里我们终于看到了第一条运行指令是_start:b reset,呵呵!看到这段代码的时候许多人都认为_start的值是0x00000000,为什么是这个地址呢? 因为连接脚本上指定了。真的是这样吗?我们来看看我们编译好之后,在u-boot目录下有个System.map,这里面有各个变量的值。

    #### u-boot-2010.06\System.map ####
    40c00000 T __text_start
    40c00000 T _start
    40c00020 t _undefined_instruction
    40c00024 t _software_interrupt
    40c00028 t _prefetch_abort
    40c0002c t _data_abort
    40c00030 t _not_used
    40c00034 t _irq
    40c00038 t _fiq

            哈哈,_start的值怎么会是40c00000?这是因为在顶层的Makefile里面我们指定了它的连接地址。

    #### u-boot-2010.06\Makefile ####
    GEN_UBOOT = \
    		UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
    		sed  -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
    		cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
    			--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
    			-Map u-boot.map -o u-boot
    $(obj)u-boot:	ddr_training depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
    		$(GEN_UBOOT)

            看到那个LDFLAGS变量了吗?它是什么呢,我们继续往下面看:

    #### u-boot-2010.06\config.mk ####
    LDFLAGS += -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)
    ifneq ($(TEXT_BASE),)
    LDFLAGS += -Ttext $(TEXT_BASE)    /* 如果有TEXT_BASE变量,那LDFLAGS重新赋值 */
    endif

            看到了没有,LDFLAGS先等于链接脚本中的地址,再判断TEXT_BASE是否等于空,如果TEXT_BASE不为空,LDFLAGS会被重新赋值。TEXT_BASE的值是多少呢?我们可以在u-boot-2010.06\board\hdu5\config.mk里面找到定义,它的值为0x40c00000。这样我就可以知道为什么System.map的起始地址0x40c00000。

    #### u-boot-2010.06\board\hdu5\config.mk ####
    TEXT_BASE = 0x40c00000

            下面我们继续来看第一条汇编指令b reset,初始化相关硬件操作。

            Reset处代码有点不按照常理出牌,和网上通用的汇编起始代码有点不一样,它先判断部分寄存器中的值,再跳转到不同标志处运行。其中,“after_ziju”标志处代码执行初始化PLL/DDRC/pin mux/…等命令;

    #### u-boot-2010.06\arch\arm\cpu\hi3536\start.S ####
    reset:                                 /* uboot刚进来就进行的初始化操作 */
    …
    	beq	after_ziju                   /* 若REG_SC_GEN2寄存器值 == 魔数,跳转到after_ziju标志处运行 */
    …
    	bne	normal_start_flow           /* 若REG_SC_GEN20寄存器值 !=魔数,跳转到 normal_start_flow 标志处运行 */
    …
    after_ziju:    /* 初始化 PLL/DDRC/pin mux/... */
         …
    	beq	pcie_slave_addr    /* 跳转到 pcie_slave_addr 处执行(PCIE相关初始化操作) */
    	…
    	b	ziju_ddr_init    /* 跳转到 ziju_ddr_init 处运行(初始化PLL/DDRC/pin mux/...) */
    pcie_slave_addr:    /* PCIE相关初始化操作 */
        …
    ziju_ddr_init:    /*初始化PLL/DDRC/pin mux/... */
        …
    	bl	init_registers  /*跳转到 init_registers函数处运行,初始化PLL/DDRC/... */
        …
    	bl	start_ddr_training  /*跳转到 start_ddr_training函数处运行,DDR training */
        …
    	beq	pcie_slave_hold  /* 跳转到 pcie_slave_hold标志处运行,通常代码不会跑到这里 */
    	…
    	mov	pc, r1  /* 将pc指针返回到 bootrom */
    pcie_slave_hold:  /* pcie 出错保持,通常代码不对跑到这里 */
        …
    	b	.                        /* bug here */

            若满足“bne   normal_start_flow”条件,运行“normal_start_flow”标志处的代码,这部分代码是普通uboot最开始启动时执行的命令。重要部分看注释。

            这段汇编代码很好理解,就是设置CPU为管理模式、将cache置无效、关闭MMU和cache。这边抛出一个问题:

    在汇编代码中,Invalidate cache、disable cache、flash cache分别表示什么含义?

            Invalidate cache表示当前cache内的数据无效,所有cpu获取数据只能重新读取;flash cache表示清空cache中的数据;disable cache表示关闭cache。

    #### u-boot-2010.06\arch\arm\cpu\hi3536\start.S ####
    normal_start_flow:
    mrs	r0, cpsr   /* set the cpu to SVC32 mode 设置管理模式 */
    …
    mov	r0, #0   /* Invalidate L1 I/D   -- 置无效 I/D cache */
    …
    mrc	p15, 0, r0, c1, c0, 0   /* disable MMU stuff and caches    --关闭MMU和cache */
    …

            在normal_start_flow标志处代码执行到尾部,都没有跳转这一类指令,因此pc指针继续向下执行main_core标志处代码。

    此处代码内容为找到对应的存储介质,将其中的代码拷贝到DDR中运行。

    #### u-boot-2010.06\arch\arm\cpu\hi3536\start.S ####
    main_core:
        …
    	bne    check_bootrom_type  /*检测是否需要跳转,PC的高八位如果不为0(已经在ram中运行了)则跳转,不等于则跳转*/
    #ifndef CONFIG_HI3536_A7  /* 找到对应的存储介质 */
    …
    cmp    r6, #0
        ldreq   pc, _clr_remap_spi_entry  /* SPI存储 */
        cmp    r6, #1
        ldreq   pc, _clr_remap_spi_nand_entry  /* SPI_NAND 存储 */
    	cmp   r6, #2
        ldreq   pc, _clr_remap_nand_entry  /* NAND 存储 */
    	cmp     r6, #3
    	ldreq   pc, _clr_remap_ddr_entry  /* DDR 存储 */
    	ldr     pc, _clr_remap_nand_entry  /* 所有其他情况,默认采用 NAND 存储 */
    #endif
    check_bootrom_type:  /* 将bootrom中的u-boot.bin 拷贝到RAM(0x4010c00) */
        …
        	ldreq  pc, _clr_remap_ram_entry  /* 根据不同的存储介质,传不同参数 */
    do_clr_remap:  /*清除remap */
    #ifndef CONFIG_HI3536_A7
    …
    /*清除remap */
    #endif
    #ifdef CONFIG_ARCH_HI3536
    …
    /* 如果使用Hi3536板卡,那就需要使能I/D cache */
    #endif
       …
    /* 使能 Cache 操作 */
    …
    bne	ddr_init  /* DDR初始化 */
        …
    	b      	copy_to_ddr  /* 将u-boot.bin 拷贝到DDR */
    ddr_init:  /* DDR初始化相关 */
    #ifndef CONFIG_HI3536_A7
        …
    	bl      init_registers  /* 初始化寄存器 */
    #endif
    #ifdef CONFIG_DDR_TRAINING_V2
        ….
    	bl	start_ddr_training  /* DDR training */
    #endif
    #ifndef CONFIG_HI3536_A7
        …
    	bne       copy_flash_to_ddr  /* 拷贝镜像到DDR */
    #ifdef CONFIG_EMMC_SUPPORT
    emmc_boot:  /* 初始化emmc,跳转到 jump_to_ddr  */
    	bl      emmc_boot_read  /* 拷贝镜像 */
    	b       jump_to_ddr  /*跳转到 jump_to_ddr */
    #endif
    copy_flash_to_ddr:  /* 从NAND中拷贝镜像,跳转到 copy_to_ddr */
        ..
    	bne	spi_nor_copy  /* 拷贝镜像 */
        …
    	b	copy_to_ddr  /* 跳转到copy_to_ddr */
    spi_nor_copy:  /* 从SPI_NOR中拷贝镜像,跳转到 copy_to_ddr */
        …
    	bne	spi_nand_copy  /* 拷贝镜像 */
        …
    	b	copy_to_ddr  /* 跳转到copy_to_ddr */
    spi_nand_copy:   /* 从SPI_NAND中拷贝镜像,跳转到 copy_to_ddr */
        …
    	b	copy_to_ddr  /* 跳转到copy_to_ddr */
    #endif
    copy_to_ddr:  /* 将指定存储内的数据拷贝到DDR */
        …
    	beq     copy_abort_code  /* 拷贝操作 */
        …
    	bl      memcpy  /* 拷贝操作 */
    jump_to_ddr:
        …
    	ldr     pc, _copy_abort_code  /* 拷贝操作 */
    copy_abort_code:
        …
    	bl      memcpy  /* 拷贝操作 */

            又到了熟悉的部分,如果要在C语言环境下执行代码,必须先初始化堆栈。

            这段代码的意思是设置一些堆栈。

    #### u-boot-2010.06\arch\arm\cpu\hi3536\start.S ####
    stack_setup:  /* 设置栈指针 */
    	ldr	r0, _TEXT_BASE		@ upper 128 KiB: relocated uboot    
    	sub	r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area            
    	sub	r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo              
    	sub	sp, r0, #12		@ leave 3 words for abort-stack        
    	and	sp, sp, #~7		@ 8 byte alinged for (ldr/str)d

            只要将sp指针指向一段没有被使用的内存就完成栈的设置了。根据上面的代码可以知道U-Boot内存使用情况了,如下图所示:

     

     

            这段代码的意思是清bss段。

     

    #### u-boot-2010.06\arch\arm\cpu\hi3536\start.S ####
    clear_bss:  /* 清除bss段 */
    	ldr	r0, _bss_start  /* r0 = bss段的起始位置 */
    	ldr	r1, _bss_end		@ stop here                        /* r1 = bss段结束位置 */
    	mov	r2, #0x0		@ clear value                           /* r2 = 0 */
    clbss_l:
    	str	r2, [r0]		@ clear BSS location                    /* 先将r2,即0x0,存到地址为r0的内存中去 */
    	cmp	r0, r1			@ are we at the end yet                  /* 比较r0地址和r1地址,即比较当前地址是否到了bss段的结束位置 */
    	add	r0, r0, #4		@ increment clear index pointer           /* 然后r0地址加上4 */
    	bne	clbss_l			@ keep clearing till at end           /* 如果不等于,那么就跳到clbss_l,即接着这几个步骤,直到地址超过了bss的_end位置,即实现了将整个bss段,都清零。*/

            这个时候,pc指针开始跳到RAM里面执行代码,这也就到了第二阶段(C语言阶段),后面的代码都是用C语言写的。

    #### u-boot-2010.06\arch\arm\cpu\hi3536\start.S ####
    	ldr	pc, _start_armboot	          /* start_armboot,赋值给PC,即调用start_armboot函数 */
    _start_armboot: .word start_armboot        /* start_armboot函数,在C文件中,即跳转执行c代码 */

            总结:汇编第一阶段的代码主要可以分为以下部分:

                l  设置异常向量表

                设置特权管理模式

                初始化PLLDDRMUX…

                MMU,关CACHE

                判断代码在RAM还是FLASH,将FLASH代码复制至RAM

                设置堆栈、清空bss

                跳转至C语言处,进入第二阶段

        3.4 uboot第二阶段解析

     

            在uboot第一阶段启动完成后将会调用start_armboot开始第二阶段的启动流程,这个阶段的代码由c语言编写,代码位于u-boot-2010.06\arch\arm\lib\board.c。

     

            基础数据结构

            第二阶段主要用到了两个数据结构即 gd_t 和 bd_t,其定义如下:

    这两个类型变量记录了刚启动时的信息,还将记录作为引导内核和文件系统的参数,如 bootargs 等,并且将来还会在启动内核时,由 uboot 交由 kernel 时会有所用。

    #### u-boot-2010.06\arch\arm\include\asm\global_data.h ####
    /*  U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址,这个指针存放在指定的寄存器r8中 */
    typedef	struct	global_data {  /* 全局数据结构 */  
    	bd_t		*bd;  /* 指向板级信息结构 */  
    	unsigned long	flags;   /* 标记位 */  
    	unsigned long	baudrate;  /* 串口波特率 */  
    	unsigned long	have_console;  /* serial_init() was called */
    	unsigned long	env_addr;  /* 环境参数地址 */ 
    	unsigned long	env_valid;  /* 环境参数 CRC 校验有效标志 */
    	unsigned long	fb_base;  /* fb 起始地址 */
    #ifdef CONFIG_VFD
    	unsigned char	vfd_type;  /* 显示器类型(VFD代指真空荧光屏) */
    #endif
    #ifdef CONFIG_FSL_ESDHC  /* 宏未定义 */
    	unsigned long	sdhc_clk;  
    #endif
    #if 0  /* 未定义 */
    	unsigned long	cpu_clk;  /* cpu 频率*/
    	unsigned long	bus_clk;  /* bus 频率 */  
    	phys_size_t	ram_size;  /* ram 大小 */
    	unsigned long	reset_status;	/* reset status register at boot */
    #endif
    	void		**jt;  /* 跳转函数表 */
    } gd_t;
    
    typedef struct bd_info {  /* 板级信息结构 */ 
        int			bi_baudrate;  /* 波特率 */
        unsigned long	bi_ip_addr;  /* IP地址 */
        struct environment_s	       *bi_env;  /* 板子的环境变量 */  
        ulong	        bi_arch_number;  /* 板子的 id */ 
        ulong	        bi_boot_params;  /* 板子的启动参数 */ 
        struct	  /* RAM 配置 */ 
        {
    	ulong start;
    	ulong size;
        }			bi_dram[CONFIG_NR_DRAM_BANKS];
    } bd_t;

            启动流程

            start_armboot 首先为全局数据结构和板级信息结构分配内存,代码如下:

            可以看到 bd_t 、gd_t 以及 MALLOC 区域是紧挨着的。

    #### u-boot-2010.06\arch\arm\lib\board.c ####
    	gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
    	/* compiler optimization barrier needed for GCC >= 3.4 */
    	__asm__ __volatile__("": : :"memory");  /* 内存屏障,防止编译器优化 */
    	memset ((void*)gd, 0, sizeof (gd_t));  /* 将指定的内存地址清零( 将全局数据清零 ) */
    	gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));  /* gd->bd指向一块地址( 取得板级信息数据结构的起始地址 )  */
    	memset (gd->bd, 0, sizeof (bd_t));  /* gd->db指向地址中的内容清零( 将板级信息清零 ) */
    	gd->flags |= GD_FLG_RELOC;  /* 标记为代码已经转移到 RAM */

            然后依次调用 init_sequence数组中的函数指针完成各部分的初始化,代码如下:

    #### u-boot-2010.06\arch\arm\lib\board.c ####
    init_fnc_t *init_sequence[] = {
    #if defined(CONFIG_ARCH_CPU_INIT)
    	arch_cpu_init,  /* 基本的处理器相关配置 -- basic arch cpu dependent setup */
    #endif
    	timer_init,	 /* 初始化定时器 -- initialize timer before usb init */
    	board_init,  /* 板级特殊设备初始化(很重要) -- basic board dependent setup */
    #if defined(CONFIG_USE_IRQ)
    	interrupt_init,  /* 中断初始化 -- set up exceptions */
    #endif
    //	timer_init,	/* 初始化定时器 */
    #ifdef CONFIG_FSL_ESDHC
    	get_clocks,
    #endif
    	env_init,  /* 初始化环境变量(默认的环境变量) -- initialize environment */
    	init_baudrate,  /* 初始化波特率设置 -- initialze baudrate settings */
    	serial_init,  /* 初始化串口 */
    	console_init_f,  /* 控制台初始化 -- stage 1 init of console */
    	display_banner,	 /* 打印uboot版本信息 -- say that we are here */
    #if defined(CONFIG_DISPLAY_CPUINFO)
    	print_cpuinfo,  /* 显示cpu信息 -- display cpu info (and speed) */
    #endif
    #if defined(CONFIG_DISPLAY_BOARDINFO)
    	checkboard,  /* 显示板级信息 -- display board info */
    #endif
    #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    	init_func_i2c,  /* 初始化IIC,hard:真正iic,soft:gpio模拟iic */
    #endif
    	dram_init,	/* 配置可用RAM -- configure available RAM banks */
    #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
    	arm_pci_init,  /* 初始化pci */
    #endif
    	NULL,
    };
    /* 函数指针,执行指针数组中的内容(实际内容为函数指针),初始化cpu、总线、设备等等*/
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
    if ((*init_fnc_ptr)() != 0) {
    hang ();
    }
    }
    void hang (void) {
    	puts ("### ERROR ### Please RESET the board ###\n");
    	for (;;);
    }

            在hdu5平台比较重要的初始化函数有 board_init 以及 env_init,代码如下:

    #### u-boot-2010.06\board\hdu5\board.c ####
    int board_init(void)
    {
    	unsigned long reg;
    	/* set uart clk from apb bus */
    	reg = readl(CRG_REG_BASE + PERI_CRG57);  /* 设置串口时钟 */
    	reg &= ~UART_CKSEL_APB;
    	writel(reg, CRG_REG_BASE + PERI_CRG57);
    	DECLARE_GLOBAL_DATA_PTR;
    	gd->bd->bi_arch_number = MACH_TYPE_HI3536;
    	gd->bd->bi_boot_params = CFG_BOOT_PARAMS;
    	gd->flags = 0;
    	boot_flag_init();
    	add_board_partition(&pri_board_part, FLASH_TYPE_EMMC);
    	return 0;
    }
    #### u-boot-2010.06\common\env_common_func.c ####
    /* 初始化环境变量 */  
    int env_init(void)
    {
    #ifdef CONFIG_HI3536_A7
    	env_cmn_func = &nw_env_cmn_func;
    #else
    	switch (get_boot_media()) {
    	default:
    		env_cmn_func = NULL;
    		break;
    	case BOOT_MEDIA_NAND:
    		env_cmn_func = &nand_env_cmn_func;
    		break;
    	case BOOT_MEDIA_SPIFLASH:
    		env_cmn_func = &sf_env_cmn_func;
    		break;
    	case BOOT_MEDIA_EMMC:
    		env_cmn_func = &emmc_env_cmn_func;
    		break;
    	case BOOT_MEDIA_DDR:
    		env_cmn_func = &nw_env_cmn_func;
    		break;
    	}
    #endif
    	if (env_cmn_func && !env_cmn_func->env_name_spec)
    		env_cmn_func = NULL;
    	/* unknow start media */
    	if (!env_cmn_func)
    		return -1;
    	env_cmn_func->env_init();
    	env_name_spec = env_cmn_func->env_name_spec;
    
    	return 0;
    }

            在环境变量 default_environment 中我们设置了很多参数,列表如下:

            我们可以在 uboot 命令行模式下输入 printenv 命令查看当前的环境变量值。

    #### u-boot-2010.06\tools\env\fw_env.c ####
    static char default_environment[] = {
    #if defined(CONFIG_BOOTARGS)
    	"bootargs=" CONFIG_BOOTARGS "\0"
    #endif
    #if defined(CONFIG_BOOTCOMMAND)
    	"bootcmd=" CONFIG_BOOTCOMMAND "\0"
    #endif
        …
    };

            start_armboot 在接下来的流程中还做了如下操作:

    #### u-boot-2010.06\arch\arm\lib\board.c ####
    void start_armboot (void)
    {
        …
    	nand_init();  /* 初始化 NAND */  
        …
    	mmc_initialize(0);  /* 初始化MMC */
    	mmc_flash_init(0);
    	env_relocate ()  /* 重定位环境变量,将其从 NAND 拷贝到内存中 */  
        …
    	gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");  /* 设置IP地址 */
    	stdio_init ();  /* 初始化外设 */  
    	jumptable_init ();  /* 初始化跳转函数表 */
        …
    	console_init_r ();  /* 控制台初始化第二阶段 */
        …
    	misc_init_r ();  /* 杂项设备初始化, eg:battery */  
        …
    	enable_interrupts ();  /* 使能中断 */
    #ifdef CONFIG_KEDACOM_E2PROM
    	extern int kd_set_ethaddr();
    	kd_set_ethaddr();
    #endif
        …
    	/* 如果存在则从环境变量中读取装载地址,其默认为 ulong load_addr = CONFIG_SYS_LOAD_ADDR; */
    	if ((s = getenv ("loadaddr")) != NULL) {
    		load_addr = simple_strtoul (s, NULL, 16);
    	}
    #if defined(CONFIG_CMD_NET)
    	if ((s = getenv ("bootfile")) != NULL) {
    		copy_filename (BootFile, s, sizeof (BootFile));
    	}
    #endif
        …
    #if defined(CONFIG_CMD_NET)
        …
    	eth_initialize(gd->bd);  /* 网络初始化 */
        …
    #endif
    #if defined(CONFIG_BOOTROM_SUPPORT)
    	extern void download_boot(const int (*handle)(void));
    	download_boot(NULL);
    #endif
    	product_control();
        …
    #ifdef CONFIG_PARTTAB_ON_FLASH
    	partition_check_update_flags();
    #endif
    	/* main_loop() can return to retry autoboot, if so just run it again. */
    	for (;;) {
    		main_loop ();  /* 进入主循环 common/main.c */ 
    	}
    }

            start_armboot 最终进入 main_loop 函数,首先判断用户选择的启动模式,如果是命令模式则等待输入命令然后执行,代码如下:

    #### u-boot-2010.06\arch\arm\lib\board.c ####
    void main_loop (void)
    {
        …
    setenv ("ver", version_string);  /* 设置版本信息 */
        …
    	update_tftp ();
        ….
    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    	s = getenv ("bootdelay");    /* 获取bootdelay环境变量的值 */
    	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;    /* 将字符串转换为long类型变量 */
    	debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
    	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
        /* 倒数读秒,如果delay时间内没有操作,执行run_command命令 */
    	if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
    		run_command (s, 0);
    	}
    #endif	/* CONFIG_BOOTDELAY */
        …
    	for (;;) {
            …
    		len = readline (CONFIG_SYS_PROMPT); /* 读取输入 */
    		flag = 0;	/* assume no special flags for now */
    		if (len > 0)
    			strcpy (lastcommand, console_buffer);  /* 将输入保存到历史记录中 */
    		else if (len == 0)
    			flag |= CMD_FLAG_REPEAT;  /* 如果没有输入则重复上次 */  
            …
    		if (len == -1)
    			puts ("<INTERRUPT>\n");
    		else
    			rc = run_command(lastcommand, flag);  /* 执行命令 */
    			lastcommand[0] = 0;  /* 将命令置无效命令令其不可重复 */
    	}
    }

            总结,C语言第二阶段代码可以分为以下部分:

                l  gdbd数据结构分配地址,并清零

                执行 init_fnc_ptr 函数指针数组中的各个初始化函数

                板级特殊设备初始化(board_init)、时钟初始化(timer_init)、初始化环境变量(env_init)、串口控制台初始化(init_baudrateconsole_init_f)、打印U-Boot信息(display_bannerprint_cpuinfocheckboard)、配置可用RAM大小(dram_init

                gd, bd 数据结构赋值初始化

                各种设备初始化

             NAND Flash初始化 (nand_init) 、MMC初始化 (mmc_initializemmc_flash_init) 、网络初始化          (eth_initialize)、初始化串口(serial_initconsole_init_r) 、初始化其他外设(stdio_init)、杂项设备初始化(misc_init_r

                环境变量代码重定位(env_relocate

                使能中断(enable_interrupts

                进入主循环(main_loop

    4. 总结

     

     

     

        u-boot的配置过程,可以简单描述为:

            l  创建到目标板相关文件的链接

           l  创建include/config.mk文件,内容如下:

           l  创建与目标板相关的头文件include/config.h

           l  后续执行编译的时候,到哪些路径下面找文件都是在配置时确定的。

        uboot的编译和链接过程,可以简单描述为:

           l  将所有需要的.c文件编译生成.o文件,将需要的部分文件编成.a库,最后再将这些文件按照链接脚本组合成最后的目标文件。

        第一阶段代码,可以简单描述为:

           l  初始化本阶段要使用到的硬件设备

           l  为加载Bootloader的第二阶段代码准备RAM空间

           l  复制Bootloader的第二阶段到RAM空间

           l  设置好堆栈

           l  跳转到第二阶段的C入口点

        第二阶段代码,可以简单描述为:

           l 初始化本阶段要使用到的硬件设备

           l  配置系统内存映射,

     

           l  将内核映像和根文件系统映像从Flash上读到RAM空间中

           l  为内核设置启动参数

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • U-Boot -- uboot裁剪详细步骤

    千次阅读 2017-12-27 09:29:31
    在进行uboot裁剪前,我们需要准备对应板卡的uboot源码,uboot交叉编译工具,uboot编译说明文档和uboot镜像烧录文档。 建议在进行uboot裁剪前,先确认原先的uboot编译生成的镜像能在板卡上正常运行。 2.uboot裁剪 ...

     

    1.uboot裁剪前准备

    在进行uboot裁剪前,我们需要准备对应板卡的uboot源码,uboot交叉编译工具,uboot编译说明文档和uboot镜像烧录文档。

    建议在进行uboot裁剪前,先确认原先的uboot编译生成的镜像能在板卡上正常运行。

     

    2.uboot裁剪

    2.1获取对应板卡的配置文件

    uboot主要是通过对应板卡配置文件中宏的添加和删除来实现裁剪。所以首先需要找到对应板卡的配置文件。可以通过uboot根目录下的CFG文件,找到对应的板卡配置文件。

    以AM4378为例,在uboot-2013.10根目录下找到CFG文件,如图 2.1所示。

    图 2.1 CFG文件

    在CFG文件中,通过对应板卡型号,获取到配置文件名称,以AM43XX为例,在CFG文件中如图 2.2所示。

    图 2.2 配置文件名

    若板卡型号对应多个配置文件,需要参考uboot编译说明文档,选择编译选项对应的配置文件,如图 2.3所示。

    图 2.3 配置选项

    获取到配置文件名称后,进入uboot/include/configs/目录下找到配置文件。以AM4378为例,根据编译选项myir_ricoboard找到对应的配置文件rico_board如图 2.4所示。

    图 2.4 配置文件

    2.2修改板卡配置文件

    在配置文件中通过增加和删除宏定义来增加和裁剪uboot功能。以AM4378为例,我们去掉PING功能并验证。

    在rico_board配置文件中找到PING功能宏并注释,如图 2.5所示。

    图 2.5 配置宏

    保存配置文件,将修改后的uboot源码编译,再将生成的镜像文件通过指定的烧录工具烧录至板卡运行。

    展开全文
  • u-boot编译学习--uboot编译链接过程

    千次阅读 2016-01-16 11:12:16
    参考博客:... ... U-BOOT是一个LINUX下的工程,在编译之前必须已经安装对应体系结构的交叉编译环境, 这里只针对ARM,编译器系列软件为arm-linux-*。   U-BOOT的下载地址: ...

    参考博客:http://blog.chinaunix.net/uid-18921523-id-165078.html

    基于这篇文章设计的产品可参见杭州扩维科技有限公司的网站www.kuoweitech.com

    U-BOOT是一个LINUX下的工程,在编译之前必须已经安装对应体系结构的交叉编译环境,

    这里只针对ARM,编译器系列软件为arm-linux-*。

     

    U-BOOT的下载地址: http://sourceforge.net/projects/u-boot
    下载的是1.1.6版本。

     

    u-boot 源码结构:

    解压就可以得到全部u-boot源程序。在顶层目录下有18个子目录,分别存放和管理不同的源程序。

     

    这些目录中所要存放的文件有其规则,可以分为3类。

        第1类目录: 与处理器体系结构或者开发板硬件直接相关;
        第2类目录: 是一些通用的函数或者驱动程序;
        第3类目录: 是u-boot的应用程序、工具或者文档。
     

    u-boot 的源码顶层目录说明:

    [cpp] view plaincopyprint?

    1. 目    录              特    性                解 释 说 明  
    2. board                平台依赖            存放电路板相关的目录文件,  
    3.                                          例如:RPXlite(mpc8xx)、  
    4.                                          smdk2410(arm920t)、  
    5.                                          sc520_cdp(x86) 等目录;  
    6. cpu                  平台依赖            存放CPU相关的目录文件  
    7.                                          例如:mpc8xx、ppc4xx、  
    8.                                          arm720t、arm920t、 xscale、i386等目录;  
    9. lib_ppc              平台依赖            存放对PowerPC体系结构通用的文件,  
    10.                                          主要用于实现PowerPC平台通用的函数;  
    11. lib_arm              平台依赖            存放对ARM体系结构通用的文件,  
    12.                                          主要用于实现ARM平台通用的函数;  
    13. lib_i386             平台依赖            存放对X86体系结构通用的文件,  
    14.                                          主要用于实现X86平台通用的函数;  
    15. include              通用                头文件和开发板配置文件,  
    16.                                          所有开发板的配置文件都在configs目录下;  
    17. common               通用                通用的多功能函数实现  
    18. lib_generic          通用                通用库函数的实现  
    19. net                  通用                存放网络的程序  
    20. fs                   通用                存放文件系统的程序  
    21. post                 通用                存放上电自检程序  
    22. drivers              通用                通用的设备驱动程序,主要有以太网接口的驱动  
    23. disk                 通用                硬盘接口程序  
    24. rtc                  通用                RTC的驱动程序  
    25. dtt                  通用                数字温度测量器或者传感器的驱动  
    26. examples             应用例程            一些独立运行的应用程序的例子,例如helloworld  
    27. tools                工具                存放制作S-Record或者u-boot格式的映像等工具,  
    28.                                          例如mkimage  
    29. doc                  文档                开发使用文档  
    目    录              特    性                解 释 说 明
    board                平台依赖            存放电路板相关的目录文件,
                                             例如:RPXlite(mpc8xx)、
                                             smdk2410(arm920t)、
                                             sc520_cdp(x86) 等目录;
    cpu                  平台依赖            存放CPU相关的目录文件
                                             例如:mpc8xx、ppc4xx、
                                             arm720t、arm920t、 xscale、i386等目录;
    lib_ppc              平台依赖            存放对PowerPC体系结构通用的文件,
                                             主要用于实现PowerPC平台通用的函数;
    lib_arm              平台依赖            存放对ARM体系结构通用的文件,
                                             主要用于实现ARM平台通用的函数;
    lib_i386             平台依赖            存放对X86体系结构通用的文件,
                                             主要用于实现X86平台通用的函数;
    include              通用                头文件和开发板配置文件,
                                             所有开发板的配置文件都在configs目录下;
    common               通用                通用的多功能函数实现
    lib_generic          通用                通用库函数的实现
    net                  通用                存放网络的程序
    fs                   通用                存放文件系统的程序
    post                 通用                存放上电自检程序
    drivers              通用                通用的设备驱动程序,主要有以太网接口的驱动
    disk                 通用                硬盘接口程序
    rtc                  通用                RTC的驱动程序
    dtt                  通用                数字温度测量器或者传感器的驱动
    examples             应用例程            一些独立运行的应用程序的例子,例如helloworld
    tools                工具                存放制作S-Record或者u-boot格式的映像等工具,
                                             例如mkimage
    doc                  文档                开发使用文档


    u-boot的源代码包含对几十种处理器、数百种开发板的支持。

     

    可是对于特定的开发板,配置编译过程只需要其中部分程序。

    这里具体以S3C2410 & arm920t处理器为例,

    具体分析S3C2410处理器和开发板所依赖的程序,以及u-boot的通用函数和工具。

    编译:

     

    以smdk_2410板为例,编译的过程分两部:

    # make smdk2410_config
    # make

     

    要了解一个LINUX工程的结构必须看懂Makefile,

    尤其是顶层的,没办法,UNIX世界就是这么无奈,什么东西都用文档去管理、配置。

    首先在这方面我是个新手,时间所限只粗浅地看了一些Makefile规则。

    以smdk_2410为例,顺序分析Makefile大致的流程及结构如下:

    1) 定义了源码及生成的目标文件存放的目录:

    目标文件存放目录BUILD_DIR可以通过make O=dir 指定。

    如果没有指定,则设定为源码顶层目录。一般编译的时候不指定输出目录,则BUILD_DIR为空。

    其它目录变量定义如下:

    #OBJTREE和LNDIR为存放生成文件的目录,TOPDIR与SRCTREE为源码所在目录:

    [cpp] view plaincopyprint?

    1. OBJTREE  := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))  
    2. SRCTREE  := $(CURDIR)  
    3. TOPDIR  := $(SRCTREE)  
    4. LNDIR  := $(OBJTREE)  
    5. export TOPDIR SRCTREE OBJTREE  
    OBJTREE  := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
    SRCTREE  := $(CURDIR)
    TOPDIR  := $(SRCTREE)
    LNDIR  := $(OBJTREE)
    export TOPDIR SRCTREE OBJTREE

    2)定义变量 MKCONFIG:

    这个变量指向一个脚本,即顶层目录的mkconfig。

     

    [cpp] view plaincopyprint?

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

    在编译U-BOOT之前,先要执行

    [cpp] view plaincopyprint?

    1. # make smdk2410_config  
    # make smdk2410_config

    smdk2410_config是Makefile的一个目标,定义如下:

     

    [cpp] view plaincopyprint?

    1. smdk2410_config : unconfig  
    2.  @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0  
    3.   
    4.  unconfig::  
    5.  @rm -f $(obj)include/config.h $(obj)include/config.mk \  
    6.   $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp  
    smdk2410_config : unconfig
     @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
    
     unconfig::
     @rm -f $(obj)include/config.h $(obj)include/config.mk \
      $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

    显然,执行# make smdk2410_config时,先执行unconfig目标,

    注意不指定输出目标时,obj,src变量均为空,

    unconfig下面的命令清理上一次执行make *_config时生成的头文件和makefile的包含文件。

    主要是 include/config.h 和 include/config.mk 文件。

    然后才执行命令

     

    [cpp] view plaincopyprint?

    1. @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0  
    @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0


    MKCONFIG 是顶层目录下的mkcofig脚本文件,后面五个是传入的参数。

     

    对于smdk2410_config而言,mkconfig主要做三件事:

    在include文件夹下建立相应的文件(夹)软连接

    #如果是ARM体系将执行以下操作:

    [cpp] view plaincopyprint?

    1. #ln -s     asm-arm        asm     
    2.   
    3. #ln -s  arch-s3c24x0    asm-arm/arch    
    4. #ln -s   proc-armv       asm-arm/proc  
    #ln -s     asm-arm        asm  
    
    #ln -s  arch-s3c24x0    asm-arm/arch 
    #ln -s   proc-armv       asm-arm/proc

     

    生成 Makefile 包含文件 include/config.mk,内容很简单,定义了四个变量:

     

    [cpp] view plaincopyprint?

    1. ARCH   = arm  
    2. CPU    = arm920t  
    3. BOARD  = smdk2410  
    4. SOC    = s3c24x0  
    ARCH   = arm
    CPU    = arm920t
    BOARD  = smdk2410
    SOC    = s3c24x0

    生成 include/config.h 头文件,只有一行:

    [cpp] view plaincopyprint?

    1. /* Automatically generated - do not edit */  
    2. #include "config/smdk2410.h"  
    /* Automatically generated - do not edit */
    #include "config/smdk2410.h"

    mkconfig脚本文件的执行至此结束,继续分析Makefile剩下部分。

    3)包含 include / config . mk:

    其实也就相当于在Makefile里定义了上面四个变量而已。

    4) 指定交叉编译器前缀 arm - linux - 

     

    [cpp] view plaincopyprint?

    1. ifeq ($(ARCH),arm)#这里根据ARCH变量,指定编译器前缀。  
    2. CROSS_COMPILE = arm-linux-  
    3. endif  
    ifeq ($(ARCH),arm)#这里根据ARCH变量,指定编译器前缀。
    CROSS_COMPILE = arm-linux-
    endif

    5) 包含顶层 config . mk:

    包含顶层目录下的 config.mk ,这个文件里面主要定义了 交叉编译器 及 选项 和 编译规则

     

    [cpp] view plaincopyprint?

    1. # load other configuration   
    2. include $(TOPDIR)/config.mk  
    # load other configuration
    include $(TOPDIR)/config.mk

    分析 config.mk 的内容:

    包含体系,开发板,CPU特定的规则文件:

    1,指定预编译体系结构选项:

     

    [cpp] view plaincopyprint?

    1. ifdef ARCH   
    2. sinclude $(TOPDIR)/$(ARCH)_config.mk # include architecture dependend rules  
    3. endif  
    ifdef ARCH 
    sinclude $(TOPDIR)/$(ARCH)_config.mk # include architecture dependend rules
    endif

    PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__

     

    2,定义编译时对齐,浮点等选项:

    [cpp] view plaincopyprint?

    1. ifdef CPU   
    2. sinclude $(TOPDIR)/cpu/$(CPU)/config.mk # include  CPU specific rules  
    3. endif  
    ifdef CPU 
    sinclude $(TOPDIR)/cpu/$(CPU)/config.mk # include  CPU specific rules
    endif

     

    [cpp] view plaincopyprint?

    1. ifdef SOC #没有这个文件  
    2. sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk # include  SoC specific rules  
    3. endif  
    ifdef SOC #没有这个文件
    sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk # include  SoC specific rules
    endif

    3,指定开发板代码所在目录

     

    [cpp] view plaincopyprint?

    1. ifdef VENDOR  
    2. BOARDDIR = $(VENDOR)/$(BOARD)  
    3. eles  
    4. BOARDDIR = $(BOARD)  
    ifdef VENDOR
    BOARDDIR = $(VENDOR)/$(BOARD)
    eles
    BOARDDIR = $(BOARD)

     

     

    4,指定特定板子的镜像连接时的内存基地址,重要!:TEXT_BASE = 0x33D00000

    [cpp] view plaincopyprint?

    1. ifdef BOARD   
    2. sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules  
    3. endif  
    ifdef BOARD 
    sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
    endif


    5,定义交叉编译链工具:

     

     

    [cpp] view plaincopyprint?

    1. # Include the make variables (CC, etc...)   
    2. #   
    3. AS = $(CROSS_COMPILE)as  
    4. LD = $(CROSS_COMPILE)ld  
    5. CC = $(CROSS_COMPILE)gcc  
    6. CPP = $(CC) -E  
    7. AR = $(CROSS_COMPILE)ar  
    8. NM = $(CROSS_COMPILE)nm  
    9. STRIP = $(CROSS_COMPILE)strip  
    10. OBJCOPY = $(CROSS_COMPILE)objcopy  
    11. OBJDUMP = $(CROSS_COMPILE)objdump  
    12. RANLIB = $(CROSS_COMPILE)RANLIB  
    # Include the make variables (CC, etc...)
    #
    AS = $(CROSS_COMPILE)as
    LD = $(CROSS_COMPILE)ld
    CC = $(CROSS_COMPILE)gcc
    CPP = $(CC) -E
    AR = $(CROSS_COMPILE)ar
    NM = $(CROSS_COMPILE)nm
    STRIP = $(CROSS_COMPILE)strip
    OBJCOPY = $(CROSS_COMPILE)objcopy
    OBJDUMP = $(CROSS_COMPILE)objdump
    RANLIB = $(CROSS_COMPILE)RANLIB

    6,定义AR选项ARFLAGS

    调试选项DBGFLAGS,优化选项OPTFLAGS:

     

    [cpp] view plaincopyprint?

    1. AFLAGS := $(AFLAGS_DEBUG) -D__ASSEMBLY__ $(CPPFLAGS)  
    AFLAGS := $(AFLAGS_DEBUG) -D__ASSEMBLY__ $(CPPFLAGS)

     

    7,预处理选项CPPFLAGS

    C编译器选项CFLAGS,连接选项LDFLAGS:

     

    [cpp] view plaincopyprint?

    1. LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)  
    LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)

     

    其中  LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds   再  BOARDDIR = $(VENDOR)/$(BOARD)  即是

    u-boot.lds 为连接脚本文件.

    8,指定编译规则:

     

    [cpp] view plaincopyprint?

    1. $(obj)%.s: %.S  
    2.  $(CPP) $(AFLAGS) -o $@ $<  
    3. $(obj)%.o: %.S  
    4.  $(CC) $(AFLAGS) -c -o $@ $<  
    5. $(obj)%.o: %.c  
    6.  $(CC) $(CFLAGS) -c -o $@ $<  
    $(obj)%.s: %.S
     $(CPP) $(AFLAGS) -o $@ $<
    $(obj)%.o: %.S
     $(CC) $(AFLAGS) -c -o $@ $<
    $(obj)%.o: %.c
     $(CC) $(CFLAGS) -c -o $@ $<

     

    回到顶层makefile文件:

    6)U-boot 需要的目标文件 start.o:

     

    [cpp] view plaincopyprint?

    1. OBJS  = cpu/$(CPU)/start.o # 顺序很重要,start.o必须放第一位  
    OBJS  = cpu/$(CPU)/start.o # 顺序很重要,start.o必须放第一位

    7)需要的库文件:

     

     

    [cpp] view plaincopyprint?

    1. LIBS  = lib_generic/libgeneric.a  
    2. LIBS += board/$(BOARDDIR)/lib$(BOARD).a  
    3. LIBS += cpu/$(CPU)/lib$(CPU).a  
    4. ifdef SOC  
    5. LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a  
    6. endif  
    7. LIBS += lib_$(ARCH)/lib$(ARCH).a  
    8. LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \  
    9.  fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a  
    10. LIBS += net/libnet.a  
    11. LIBS += disk/libdisk.a  
    12. LIBS += rtc/librtc.a  
    13. LIBS += dtt/libdtt.a  
    14. LIBS += drivers/libdrivers.a  
    15. LIBS += drivers/nand/libnand.a  
    16. LIBS += drivers/nand_legacy/libnand_legacy.a  
    17. LIBS += drivers/sk98lin/libsk98lin.a  
    18. LIBS += post/libpost.a post/cpu/libcpu.a  
    19. LIBS += common/libcommon.a  
    20. LIBS += $(BOARDLIBS)  
    21.   
    22. LIBS := $(addprefix $(obj),$(LIBS))  
    23. .PHONY : $(LIBS)  
    LIBS  = lib_generic/libgeneric.a
    LIBS += board/$(BOARDDIR)/lib$(BOARD).a
    LIBS += cpu/$(CPU)/lib$(CPU).a
    ifdef SOC
    LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
    endif
    LIBS += lib_$(ARCH)/lib$(ARCH).a
    LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
     fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
    LIBS += net/libnet.a
    LIBS += disk/libdisk.a
    LIBS += rtc/librtc.a
    LIBS += dtt/libdtt.a
    LIBS += drivers/libdrivers.a
    LIBS += drivers/nand/libnand.a
    LIBS += drivers/nand_legacy/libnand_legacy.a
    LIBS += drivers/sk98lin/libsk98lin.a
    LIBS += post/libpost.a post/cpu/libcpu.a
    LIBS += common/libcommon.a
    LIBS += $(BOARDLIBS)
    
    LIBS := $(addprefix $(obj),$(LIBS))
    .PHONY : $(LIBS)

     

    根据上面的 include/config.mk 文件定义的 ARCH、CPU、BOARD、SOC 这些变量。

    硬件平台依赖的目录文件可以根据这些定义来确定。

    SMDK2410平台相关目录及对应生成的库文件如下:

     

    [cpp] view plaincopyprint?

    1. board/smdk2410/        :库文件board/smdk2410/libsmdk2410.a  
    2. cpu/arm920t/           :库文件cpu/arm920t/libarm920t.a  
    3. cpu/arm920t/s3c24x0/   :库文件cpu/arm920t/s3c24x0/libs3c24x0.a  
    4. lib_arm/               :库文件lib_arm/libarm.a  
    5. include/asm-arm/       :下面两个是头文件。  
    6. include/configs/smdk2410.h  
        board/smdk2410/        :库文件board/smdk2410/libsmdk2410.a
        cpu/arm920t/           :库文件cpu/arm920t/libarm920t.a
        cpu/arm920t/s3c24x0/   :库文件cpu/arm920t/s3c24x0/libs3c24x0.a
        lib_arm/               :库文件lib_arm/libarm.a
        include/asm-arm/       :下面两个是头文件。
        include/configs/smdk2410.h

     

    8)最终生成的各种镜像文件 ALL:

     

     

    [cpp] view plaincopyprint?

    1. ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)  
    2.   
    3. all:  $(ALL)  
    4.   
    5. $(obj)u-boot.hex: $(obj)u-boot  
    6.   $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@  
    7.   
    8. $(obj)u-boot.srec: $(obj)u-boot  
    9.   $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@  
    10.   
    11. $(obj)u-boot.bin: $(obj)u-boot  
    12.   $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@  
    13.  #这里生成的是U-boot 的ELF文件镜像   
    14. $(obj)u-boot:  depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)  
    15.   UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e '''''''''''''''''''''''''''''''  
    16.   cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \  
    17.    --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \  
    18.    -Map u-boot.map -o u-boot  
    ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
    
    all:  $(ALL)
    
    $(obj)u-boot.hex: $(obj)u-boot
      $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
    
    $(obj)u-boot.srec: $(obj)u-boot
      $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
    
    $(obj)u-boot.bin: $(obj)u-boot
      $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
     #这里生成的是U-boot 的ELF文件镜像
    $(obj)u-boot:  depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
      UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e '''''''''''''''''''''''''''''''
      cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
       --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
       -Map u-boot.map -o u-boot

     

     

    9) 对于各子目录的makefile文件:

     

    主要是生成 *.o 文件然后执行 AR 生成对应的库文件。如 lib_generic 文件夹 Makefile:

     

    [cpp] view plaincopyprint?

    1. LIB = $(obj)libgeneric.a  
    2.   
    3. COBJS = bzlib.o bzlib_crctable.o bzlib_decompress.o \  
    4.    bzlib_randtable.o bzlib_huffman.o \  
    5.    crc32.o ctype.o display_options.o ldiv.o \  
    6.    string.o vsprintf.o zlib.o  
    7.   
    8. SRCS  := $(COBJS:.o=.c)  
    9. OBJS := $(addprefix $(obj),$(COBJS))  
    10.   
    11. $(LIB): $(obj).depend $(OBJS) #项层Makefile执行make libgeneric.a  
    12.  $(AR) $(ARFLAGS) $@ $(OBJS)  
    LIB = $(obj)libgeneric.a
    
    COBJS = bzlib.o bzlib_crctable.o bzlib_decompress.o \
       bzlib_randtable.o bzlib_huffman.o \
       crc32.o ctype.o display_options.o ldiv.o \
       string.o vsprintf.o zlib.o
    
    SRCS  := $(COBJS:.o=.c)
    OBJS := $(addprefix $(obj),$(COBJS))
    
    $(LIB): $(obj).depend $(OBJS) #项层Makefile执行make libgeneric.a
     $(AR) $(ARFLAGS) $@ $(OBJS)

     

    u-boot ELF 文件镜像的生成:

    分析一下最关键的 u-boot ELF 文件镜像的生成:

    依赖目标 depend:

    生成各个子目录的.depend文件,.depend 列出每个目标文件的依赖文件。

    生成方法,调用每个子目录的 make _depend 。

     

    [cpp] view plaincopyprint?

    1. depend dep:  
    2.   for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done  
    depend dep:
      for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

    依赖目标 version:

    生成版本信息到版本文件VERSION_FILE中。

    [cpp] view plaincopyprint?

    1. version:  
    2.   @echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \  
    3.   echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \  
    4.   echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \  
    5.     $(TOPDIR)) >> $(VERSION_FILE); \  
    6.   echo "\"" >> $(VERSION_FILE)  
    version:
      @echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
      echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
      echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
        $(TOPDIR)) >> $(VERSION_FILE); \
      echo "\"" >> $(VERSION_FILE)

    伪目标 SUBDIRS:

    执行tools ,examples ,post,post\cpu 子目录下面的make文件。

     

    [cpp] view plaincopyprint?

    1. SUBDIRS = tools \  
    2.    examples \  
    3.    post \  
    4.    post/cpu  
    5. .PHONY : $(SUBDIRS)  
    6.   
    7. $(SUBDIRS):  
    8.   $(MAKE) -C $@ all  
    SUBDIRS = tools \
       examples \
       post \
       post/cpu
    .PHONY : $(SUBDIRS)
    
    $(SUBDIRS):
      $(MAKE) -C $@ all

     

    依赖目标 $(OBJS):

    即cpu/start.o

    [cpp] view plaincopyprint?

    1. $(OBJS):  
    2.   $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))  
    $(OBJS):
      $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

    依赖目标 $(LIBS):

    这个目标太多,都是每个子目录的库文件*.a ,通过执行相应子目录下的make来完成:

    [cpp] view plaincopyprint?

    1. $(LIBS):  
    2.   $(MAKE) -C $(dir $(subst $(obj),,$@))   
    $(LIBS):
      $(MAKE) -C $(dir $(subst $(obj),,$@)) 

    依赖目标 $(LDSCRIPT):

     

    [cpp] view plaincopyprint?

    1. LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds  
    2. LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)  
    LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
    LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)

    连接脚本文件 /u-boot.lds:

    对于smdk2410,LDSCRIPT即连接脚本文件是board/smdk2410/u-boot.lds,定义了连接时各个目标文件是如何组织的。

    内容如下:

     

    [cpp] view plaincopyprint?

    1. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")  
    2. /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/  
    3. OUTPUT_ARCH(arm)  
    4. ENTRY(_start)  
    5. SECTIONS  
    6. {  
    7.  . = 0x00000000;  
    8.   
    9.  . = ALIGN(4);  
    10.  .text    :/*.text的基地址由LDFLAGS中-Ttext $(TEXT_BASE)指定*/  
    11.  {                      /*smdk2410指定的基地址为0x33f80000*/  
    12.    cpu/arm920t/start.o (.text)         /*start.o为首*/  
    13.    *(.text)  
    14.  }  
    15.   
    16.  . = ALIGN(4);  
    17.  .rodata : { *(.rodata) }  
    18.   
    19.  . = ALIGN(4);  
    20.  .data : { *(.data) }  
    21.   
    22.  . = ALIGN(4);  
    23.  .got : { *(.got) }  
    24.   
    25.  . = .;  
    26.  __u_boot_cmd_start = .;  
    27.  .u_boot_cmd : { *(.u_boot_cmd) }  
    28.  __u_boot_cmd_end = .;  
    29.   
    30.  . = ALIGN(4);  
    31.  __bss_start = .;  
    32.  .bss : { *(.bss) }  
    33.  _end = .;  
    34. }  
    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
    OUTPUT_ARCH(arm)
    ENTRY(_start)
    SECTIONS
    {
     . = 0x00000000;
    
     . = ALIGN(4);
     .text    :/*.text的基地址由LDFLAGS中-Ttext $(TEXT_BASE)指定*/
     {                      /*smdk2410指定的基地址为0x33f80000*/
       cpu/arm920t/start.o (.text)         /*start.o为首*/
       *(.text)
     }
    
     . = ALIGN(4);
     .rodata : { *(.rodata) }
    
     . = ALIGN(4);
     .data : { *(.data) }
    
     . = ALIGN(4);
     .got : { *(.got) }
    
     . = .;
     __u_boot_cmd_start = .;
     .u_boot_cmd : { *(.u_boot_cmd) }
     __u_boot_cmd_end = .;
    
     . = ALIGN(4);
     __bss_start = .;
     .bss : { *(.bss) }
     _end = .;
    }

     

    执行连接命令:

     

    [cpp] view plaincopyprint?

    1. cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \  
    2.    --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \  
    3.    -Map u-boot.map -o u-boot  
    cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
       --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
       -Map u-boot.map -o u-boot

    其实就是把 start.o 和各个子目录 makefile 生成的库文件按照   LDFLAGS   连接在一起,

    生成ELF文件 u-boot  和连接时内存分配图文件 u-boot.map。

    u-boot.map 表示的是地址标号到该标号表示的地址的一个映射关系。

    整个 Makefile 剩下的内容全部是各种不同的开发板的 *_config: 目标的定义了。

    概括总结起来:

    工程的编译流程也就是通过执行执行一个 make *_config 传入ARCH,CPU,BOARD,SOC参数,

    mkconfig 根据参数将 include 头文件夹相应的头文件夹连接好,生成 config.h 。

    然后执行 make 分别调用各子目录的 makefile  生成所有的obj文件和obj库文件*.a.  最后连接所有目标文件,生成镜像。

    不同格式的镜像都是调用相应工具由 elf 镜像直接或者间接生成的。

    剩下的工作就是分析U-Boot源代码了。

     

    总结 uboot 的编译流程:

     

    1、   首先编译cpu/$(CPU)start.S

    2、   然后,对于平台相关的每个目录,每个通用的目录都使用他们各自得Makefile生成相应得库

    3、   将1、2步骤生成的.0.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代码段起始地址、board/$(BOARDDIR)/u-boot.lds链接脚本进行连接。

    4、   第3步得到的是ELF格式的uboot,后面的Makefile还会将它转换为二进制格式(bin格式)、s-record格式。

     

     

     

     

     

    展开全文
  • 手上有块以前买的Forlinx OK6410开发板,上面的Nandflash坏块太多也只能使用外部MMC卡来做启动设备,但uboot比较旧,...1. 从uboot官网下载源码:http://ftp.denx.de/pub/u-boot/ 。我选了u-boot-2018.09.tar.bz2...
  • 1.开发板简介  X210CV3 是九鼎创展继 X210CV01 和 X210CV02 推出的又一款低功耗,高性能,可扩展性强的核心板,它由深圳市九鼎创展科技设计,生产并发行销售。它采用三星Cortex-A8 架构的 S5PV210 作为主处理器,运行...
  • 欢迎加入朱老师物联网大课堂qq群 一起学习进步  群号:397164505
  • U-Boot -- Hi3536 uboot代码完全解析

    千次阅读 2018-04-16 18:49:13
    1.u-boot代码下载地址:http://ftp.denx.de/pub/u-boot/2.uboot可以分为两部分:处理器和外围设备。其中,处理器驱动相关内容在uboot/arch/arm/cpu/目录下,是由arm厂商编写的;而外围设备驱动相关在uboot/board/...
  • 编译uboot会生成很多image: u-boot.img uboot.bin uboot_crc.bin 及 uboot_crc.bin.crc 。 这些image文件有哪些区别和联系, 我们将做以下详细分析: 1. u-boot.bin 是u-boot 编译生成的原始的image 文件, 很多...
  • u-boot分析_uboot启动内核.WMV (1)启动内核主要命令: s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); if (bootdelay >= 0 && s && !abortboot (bootdelay)) { # i
  • u-boot的主要目的是启动内核,在启动内核之前,我们一般使用u-boot的命令来
  • u-boot分析 七(添加u-boot命令,学习u-boot命令实现原理)本文目标:理解u-boot命令的实现原理。上一篇文章分析了u-boot是如何启动kernel的,其中就涉及到bootm命令,考虑到文章主题需要,当时并没有对bootm命令做...
  •  对于此版本之前的工程结构,网上文章很多,最多的就是 u-boot 1.1.6 u-boot 1.3.4 以及 u-boot 2009.03。 从u-boot 2010.09开始,工程结构发和了一些变化。变化,使得结构更清晰,更简洁,修改起来也更容易。 ...
  • 为了以后能很快掌握uboot的新版本,推荐大家拿到uboot的第一步就是阅读README文档。... uboot官网: ftp://ftp.denx.de/pub/u-boot/ 2.解压u-boot-2012.10.tar.bz2  root@crazyrain:/home/share/uboot# tar xjvf
  • 转载地址:... 下面分析$(obj)u-boot的各个依赖:  1依赖目标depend  # Explicitly make _depend in subdirscontaining multiple targets to prevent    # parallel sub-makes creating ...
  • 继ok6410 u-boot-2012.04.01移植六后,开发板已支持MLC NAND、DM9000等。但还需要完善比如环境变量、mtdpart分区、裁剪、制作补丁等。下面的工作就是完善移植的u-boot。 开发环境: 系统:ubuntu 10.04.4 单板:ok...
  • 硬件平台:tq2440开发环境:Ubuntu-3.11u-boot下载地址:http://ftp.denx.de/pub/u-boot/下载最新版本:u-boot-2014.10.tar.bz2在用户根目录下创建目录结构tq2440/{src/bootloader/u-boo
  • 推荐阅读:http://www.ibm.com/developerworks/cn/linux/l-btloader/阅读并实际操作mini2440之U-boot移植详细手册.pdf文档中的内容。
  • u-boot移植随笔:u-boot启动流程简图

    千次阅读 2010-12-01 09:16:00
    一个u-boot启动流程简略图示,自己画的。

空空如也

1 2 3 4 5 ... 20
收藏数 36,560
精华内容 14,624
关键字:

u-boot