U-BOOT-2016.07移植 (第一篇) 初步分析

U-BOOT-2016.07移植 (第一篇) 初步分析

U-BOOT-2016.07移植 (第二篇) 添加单板

U-BOOT-2016.07移植 (第三篇) 代码重定位

U-BOOT-2016.07移植 (第四篇) 修改代码,从NOR启动

目录

  • U-BOOT-201607移植 第一篇 初步分析
  • U-BOOT-201607移植 第二篇 添加单板
  • U-BOOT-201607移植 第三篇 代码重定位
  • U-BOOT-201607移植 第四篇 修改代码从NOR启动
    • 目录
    • 编译和移植环境
    • 更新交叉编译工具
      • 1 下载arm-linux-gcc 443
      • 2 安装arm-linux-gcc 443 安装环境Ubuntu 910
    • 下载u-boot-201607并解压
    • 分析顶层Makefile
      • 1 找出目标依赖关系
      • 2 总结
    • 初次编译u-boot
      • 1 配置
      • 2 编译
    • 分析u-boot启动流程
      • 1 分析startS
      • 2 分析crt0S
      • 3 总结

1. 编译和移植环境

编译环境:Ubuntu9.10
交叉编译工具:arm-linux-gcc 4.4.3
u-boot版本号:2016.07

移植目标单板信息: JZ2440v2
                CPU: S3C2440
                NAND: K9F2G08U0C
                NOR:  MX29LV160DBTI
                网卡:DM9000A

2. 更新交叉编译工具

1.1 下载arm-linux-gcc 4.4.3

  • 下载地址:arm-linux-gcc-4.4.3-20100728.tar.gz (这是博主自己上传的)
      

1.2 安装arm-linux-gcc 4.4.3 (安装环境:Ubuntu 9.10)

  • (1) 先创建临时目录tmp

    mkdir tmp

  • (2) 将下载好的压缩包解压到tmp中

    tar xzf arm-linux-gcc-4.4.3-20100728.tar.gz -C tmp/

    解压完成后可以发现交叉编译工具在 tmp/opt/FriendlyARM/toolschain/4.4.3/bin 目录中

  • (3) 安装到/usr/local/中

   cd /usr/local
   mkdir arm
   chmod 777 arm   
   sudo cp -a tmp/opt/FriendlyARM/toolschain/4.4.3/ /usr/local/arm/
  • (4) 修改环境变量

    方法一:立即生效

    echo $PATH

    得到当前PATH值,如:
    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/gcc_old_path

    复制当前PATH环境变量,将老的交叉编译工具路径给删除,然后添加新的路径:
    /usr/local/arm/4.4.3/bin

    然后export
    export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/arm/4.4.3/bin

    方法二:如果不想每次都手动修改PATH,可以编辑/etc/environment 这个方法需要重启后生效
    sudo vi /etc/environment

    将老路径去掉,加上新路径

    PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.4.3/bin"
    :wq     //保存退出
  • (5) 查看版本信息

    arm-linux-gcc -v

    若输出版本号是4.4.3则表示安装成功

3. 下载u-boot-2016.07并解压

  • (1)下载
    u-boot源码下载地址:ftp://ftp.denx.de/pub/u-boot/
    选择u-boot-2016.07下载

  • (2)在虚拟机上解压
    tar xjf u-boot-2016.07.tar.bz2

4. 分析顶层Makefile

4.1 找出目标依赖关系

  • (1)all
Top Makefile:

803: all: $(ALL-y)             //<----------------------

732: ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
765: ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.bin
771: ALL-y += $(CONFIG_BUILD_TARGET:"%"=%)

可见执行make会生成u-boot.bin

  • (2)u-boot.bin
Top Makefile:

821:ifeq ($(CONFIG_OF_SEPARATE),y)
822:u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
823:    $(call if_changed,cat)
824825:u-boot.bin: u-boot-dtb.bin FORCE
826:    $(call if_changed,copy)
827else                        //由于没有定义CONFIG_OF_SEPARATE选项,所以我们关心下面这条依赖关系
828:u-boot.bin: u-boot-nodtb.bin FORCE //<----------------------
829:    $(call if_changed,copy)        //if_changed函数检测依赖文件是否有更新或目标文件是否不存在,
                                        //然后echo打印传入的参数,最后执行传入的命令copy,
                                        //下面贴出 scripts/Kbuild.include文件中的部分内容,大家可以自行分析
830:endif

展开后: (若无目标或依赖文件更新)

u-boot.bin: u-boot-nodtb.bin FORCE
    echo COPY    $@; cp $<  $@      //输出 COPY   u-boot.bin, 然后执行cp u-boot-nodtb.bin u-boot.bin
  • (3)u-boot-nodtb.bin
Top Makefile 

-> 862:u-boot-nodtb.bin: u-boot FORCE
   863$(call if_changed,objcopy)      //objcopy
   864$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
   865$(BOARD_SIZE_CHECK)
  • (4)u-boot
   1173:quiet_cmd_u-boot__ ?= LD      $@
   1174:      cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
   1175-T u-boot.lds $(u-boot-init)                             \
   1176--start-group $(u-boot-main) --end-group                 \
   1177:      $(PLATFORM_LIBS) -Map u-boot.map

-> 1186:u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
   1187:    $(call if_changed,u-boot__)            //将u-boot-init、u-boot-main按照u-boot.lds进行链接
   1188:ifeq ($(CONFIG_KALLSYMS),y)
   1189:    $(call cmd,smap)
   1190:    $(call cmd,u-boot__) common/system_map.o
   1191:endif
  • (5)u-boot-init,u-boot-main
-> 679:u-boot-init := $(head-y)
-> 680:u-boot-main := $(libs-y)
  • (6)head-y
    在linux上搜索head-y这个目标:
    grep "head-y" * -nR

    得到下面这个结果:
    arch/arm/Makefile:74:head-y := arch/arm/cpu/$(CPU)/start.o

    所以head-y指的是start.S

  • (7)libs-y
    在顶层目录Makefile中搜索libs-y可以发现其包含许多目录
    比如:
    631:libs-y += drivers/mtd/

    另:
    667:libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
    这条规则使libs-y中每个条目的最后一个斜杠替换成 /built-in.o,比如 drivers/mtd/ 会变为 drivers/mtd/built-in.o

    可见libs-y最后指向各个文件夹的built-in.o
    而这些built-in.o则由 Kbuild Makefile 将obj-y所包含的各个文件编译而成,
    具体可研究 scripts/Kbuild.include 和 scripts/Makefile.build

  • (8)%config
    一般我们在执行make之前都需要配置一下开发板参数,比如make smdk2410_config,在顶层目录Makefile有:

   396:scripts_basic:
   397$(Q)$(MAKE) $(build)=scripts/basic  //build 在 scripts/Kbuild.include:181:
                                                //build := -f $(srctree)/scripts/Makefile.build obj, 
                                    //展开来就是:@make -f scripts/Makefile.build obj=scripts/basic 
   398$(Q)rm -f .tmp_quiet_recordmcount

-> 476%config: scripts_basic outputmakefile FORCE     
   477$(Q)$(MAKE) $(build)=scripts/kconfig $@         //展开来就是:
                                        //@make -f scripts/Makefile.build obj=scripts/kconfig %config 
scripts/Makefile.build中:

12: prefix := spl
13: src := $(patsubst $(prefix)/%,%,$(obj))         //obj=scripts/kconfig, src := scripts/kconfig

57: kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))                    
  //kbuild-dir := $(srctree)/scripts/kconfig

58: kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)   
//由于没有scripts/kconfig/Kbuild这个文件,所以 kbuild-file := $(srctree)/scripts/kconfig/Makefile

59: include $(kbuild-file)  //在这里将scripts/kconfig/Makefile 添加进来了
%config这个目标的生成规则在scripts/kconfig/Makefile中:

    15: SRCARCH := ..    

   113%_defconfig: $(obj)/conf
   114$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)         
   //执行:@scripts/kconfig/conf --defconfig=arch/../configs/%_defconfig Kconfig      (没有进入arch目录)
     //即:  @scripts/kconfig/conf --defconfig=configs/%_defconfig Kconfig 
   115116:# Added for U-Boot (backward compatibility)
-> 117%_config: %_defconfig
   118@:

分析到这里,我们可以看出配置单板相关的参数是在 scripts/kconfig/conf 中进行的,
传入的参数是 --defconfig=configs/%_defconfig Kconfig

可以推测conf会从configs目录下读取对应的defconfig文件进行解析然后保存参数,但我看了一下scripts/kconfig/conf.c 的代码,发现实在是过于复杂,恕博主不继续深入分析,如果以后有时间会再继续研究。

  • (*)分析if_changed规则:
   scripts/Kbuild.include:
     7:squote  := '

    30:escsq = $(subst $(squote),'\$(squote)',$1)

   217:echo-cmd = $(if $($(quiet)cmd_$(1)),\
   218:echo '  $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)

   234:ifneq ($(KBUILD_NOCMDDEP),1)
   235:# Check if both arguments has same arguments. Result is empty string if equal.
   236:# User may override this check using make KBUILD_NOCMDDEP=1
   237:arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \
   238:                    $(filter-out $(cmd_$@),   $(cmd_$(1))) )
   239else
   240:arg-check = $(if $(strip $(cmd_$@)),,1)
   241:endif

   249:make-cmd = $(call escsq,$(subst \#,\\\#,$(subst $$,$$$$,$(cmd_$(1)))))

   253:any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)

-> 257:if_changed = $(if $(strip $(any-prereq) $(arg-check)),              \
   258:    @set -e;                                                            \
   259:    $(echo-cmd) $(cmd_$(1));                                            \
   260:    printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)

   314:echo-why = $(call escsq, $(strip $(why)))

这里简单分析一下if_changed规则:

首先any-prereq, arg-check目标是检测目标和依赖是否存在或有更新

set -e 发生错误后立即停止,此处是为了不让错误像滚雪球一样越来越多。

echo-cmd 由于是”安静模式”下进行编译的,所以这个参数会被展开成: echo ' quiet_cmd_cmd(1) ';此处忽略why, 编译的时候也没出现why信息, 估计是被屏蔽了(KBUILD_VERBOSE不等于2)

所以259行可以展开成:
echo ' quiet_cmd_cmd(1) '; $(cmd_$(1)); 即先打印信息,然后执行命令
大家分析命令的时候可以搜索 quiet_cmd_xxx, 还有cmd_xxx, “xxx”是调用call函数时传入的命令参数

3.2 总结:

依赖关系如下:

all -> u-boot.bin -> u-boot-nodtb.bin -> u-boot |-> u-boot-init -> head-y (start.o)    -> start.S
                                                |-> u-boot-main -> libs-y (built-in.o) -> obj-y ...

4. 初次编译u-boot

4.1 配置:

首先大家需要配置交叉编译选项,编辑顶层目录Makefile:
vi Makefile

查找一下CROSS_COMPILE这个参数, 然后直接在下面添加:

ARCH = arm
CROSS_COMPILE = arm-linux-

博主使用的开发板是s3c2440,但是configs目录下没有smdk2440_defconfig这个文件,只有smdk2410_defconfig:
执行:make smdk2410_config

如果大家更新了交叉编译工具,应该能配置成功,当然现在u-boot也支持menuconfig了,所以大家可以直接执行:
make menuconfig

然后在menuconfig中进行配置

4.2 编译:

配置完之后就可以进行编译了,执行:
make

编译成功后会生成一个u-boot.bin,可以烧写到开发板上,不过一般是用不起来的,需要进一步修改

5. 分析u-boot启动流程

想要分析启动流程,第一步就是分析链接文件,弄清楚程序入口是哪里。在第三步分析Makefile中,u-boot的依赖中可以看到链接脚本文件是u-boot.lds,这个文件是通过编译生成的,为了方便,我们就不分析生成规则了,编译成功后在顶层目录就可以看到u-boot.lds
打开u-boot.lds:

 6:  . = 0x00000000;
 7:  . = ALIGN(4);
 8:  .text :
 9:  {
10:   *(.__image_copy_start)
11:   *(.vectors)
12:   arch/arm/cpu/arm920t/start.o (.text*)
13:   *(.text*)
14:  }

很明显,程序的第一条指令在arch/arm/cpu/arm920t/start.S中,当然这是我配置smdk2410过后得到的u-boot.lds文件,不能一概而论

5.1 分析start.S

arch/arm/cpu/arm920t/start.S:

/*
 *  armboot - Startup Code for ARM920 CPU-core
 *
 *  Copyright (c) 2001  Marius Gröger 
 *  Copyright (c) 2002  Alex Züpke 
 *  Copyright (c) 2002  Gary Jennejohn 
 *
 * SPDX-License-Identifier: GPL-2.0+
 */

#include 
#include 
#include 

/*
 *************************************************************************
 *
 * Startup Code (called from the ARM reset exception vector)
 *
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage
 *
 *************************************************************************
 */

    .globl  reset

reset:
    /*
     * set the cpu to SVC32 mode    1. 设置为SVC模式
     */
    mrs r0, cpsr
    bic r0, r0, #0x1f
    orr r0, r0, #0xd3
    msr cpsr, r0    

#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
    /*
     * relocate exception table
     */
    ldr r0, =_start
    ldr r1, =0x0
    mov r2, #16
copyex:
    subs    r2, r2, #1
    ldr r3, [r0], #4
    str r3, [r1], #4
    bne copyex
#endif

#ifdef CONFIG_S3C24X0
    /* turn off the watchdog */

# if defined(CONFIG_S3C2400)
#  define pWTCON    0x15300000
#  define INTMSK    0x14400008  /* Interrupt-Controller base addresses */
#  define CLKDIVN   0x14800014  /* clock divisor register */
#else
#  define pWTCON    0x53000000  /* 看门狗控制寄存器地址 */
#  define INTMSK    0x4A000008  /* Interrupt-Controller base addresses */
#  define INTSUBMSK 0x4A00001C
#  define CLKDIVN   0x4C000014  /* clock divisor register */
# endif

    ldr r0, =pWTCON        //2. 关看门狗
    mov r1, #0x0
    str r1, [r0]

    /*
     * mask all IRQs by setting all bits in the INTMR - default
     */
    mov r1, #0xffffffff    //3. 屏蔽中断
    ldr r0, =INTMSK
    str r1, [r0]
# if defined(CONFIG_S3C2410)
    ldr r1, =0x3ff
    ldr r0, =INTSUBMSK
    str r1, [r0]
# endif

    /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */
    ldr r0, =CLKDIVN      //4. 设置分频系数,未设置时钟频率
    mov r1, #3
    str r1, [r0]
#endif  /* CONFIG_S3C24X0 */

    /*
     * we do sys-critical inits only at reboot,
     * not when booting from ram!
     */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl  cpu_init_crit    //5. 跳到cpu_init_crit函数
#endif

    bl  _main           //6. 进入arch/arm/lib/crt0.S的_main函数,进行其他初始化

/*------------------------------------------------------------------------------*/

    .globl  c_runtime_cpu_setup
c_runtime_cpu_setup:

    mov pc, lr

/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */


#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
    /*
     * flush v4 I/D caches      [1]. 清除cache
     */
    mov r0, #0
    mcr p15, 0, r0, c7, c7, 0   /* flush v3/v4 cache */
    mcr p15, 0, r0, c8, c7, 0   /* flush v4 TLB */

    /*
     * disable MMU stuff and caches  [2]. 禁止MMU
     */
    mrc p15, 0, r0, c1, c0, 0
    bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
    bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
    orr r0, r0, #0x00000002 @ set bit 1 (A) Align
    orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
    mcr p15, 0, r0, c1, c0, 0

#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
    /*
     * before relocating, we have to setup RAM timing
     * because memory timing is board-dependend, you will
     * find a lowlevel_init.S in your board directory.
     */
    mov ip, lr

    bl  lowlevel_init          //[3]. 进入board/samsung/smdk2410/lowlevel_init.S执行,
                               //     设置内存控制寄存器时序参数
    mov lr, ip
#endif
    mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

5.2 分析crt0.S

arch/arm/lib/crt0.S:(对部分注释进行了翻译)

/*
 *  crt0 - C-runtime startup Code for ARM U-Boot
 *
 *  Copyright (c) 2012  Albert ARIBAUD 
 *
 * SPDX-License-Identifier: GPL-2.0+
 */

#include 
#include 
#include 
#ifdef CONFIG_CPU_V7M
#include 
#endif

/*
 * This file handles the target-independent stages of the U-Boot
 * start-up where a C runtime environment is needed. Its entry point
 * is _main and is branched into from the target's start.S file.
 *
 * _main execution sequence is:
 *
 * 1. Set up initial environment for calling board_init_f().
 *    This environment only provides a stack and a place to store
 *    the GD ('global data') structure, both located in some readily
 *    available RAM (SRAM, locked cache...). In this context, VARIABLE
 *    global data, initialized or not (BSS), are UNAVAILABLE; only
 *    CONSTANT initialized data are available. GD should be zeroed
 *    before board_init_f() is called.
 * 
 * 1. _main函数首先设置栈,然后预留一定的内存空间给gd_t结构体并清零,用来存放全局参数,
 *    然后调用board_init_f();
 *
 * 2. Call board_init_f(). This function prepares the hardware for
 *    execution from system RAM (DRAM, DDR...) As system RAM may not
 *    be available yet, , board_init_f() must use the current GD to
 *    store any data which must be passed on to later stages. These
 *    data include the relocation destination, the future stack, and
 *    the future GD location.
 *
 * 2. board_init_f()这个函数调用一系列函数来初始化硬件以使程序能够在SDRAM中执行,
 *    且这个函数需要使用1.中设置好的gd_t结构体来存放初始化中的各个参数以备后用
 *    比如:重定位地址,重定位后的栈地址、gd_t的地址
 * 
 * 3. Set up intermediate environment where the stack and GD are the
 *    ones allocated by board_init_f() in system RAM, but BSS and
 *    initialized non-const data are still not available.
 *
 * 3. 由于BSS段还没初始化,board_init_f()设置gd_t时不能使用全局变量、静态变量等,
 *    所以设置的环境参数不是最终的
 * 
 * 4a.For U-Boot proper (not SPL), call relocate_code(). This function
 *    relocates U-Boot from its current location into the relocation
 *    destination computed by board_init_f().
 *
 * 4b.For SPL, board_init_f() just returns (to crt0). There is no
 *    code relocation in SPL.
 * 
 * 4. smdk_2410没有定义SPL,所以会在_main()函数中根据board_init_f设置的重定位参数进行代码重定位。
 * 
 * 5. Set up final environment for calling board_init_r(). This
 *    environment has BSS (initialized to 0), initialized non-const
 *    data (initialized to their intended value), and stack in system
 *    RAM (for SPL moving the stack and GD into RAM is optional - see
 *    CONFIG_SPL_STACK_R). GD has retained values set by board_init_f().
 * 
 * 5. 重定位后,BSS和non-const data都被初始化了,在进入board_init_r函数之前应先设置最终的环境参数
 *
 * 6. For U-Boot proper (not SPL), some CPUs have some work left to do
 *    at this point regarding memory, so call c_runtime_cpu_setup.
 *
 * 7. Branch to board_init_r().
 *
 * For more information see 'Board Initialisation Flow in README.
 */

/*
 * entry point of crt0 sequence
 */

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
    ldr sp, =(CONFIG_SPL_STACK)
#else
    ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)    //1. 设置栈
#endif
#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
    mov r3, sp
    bic r3, r3, #7
    mov sp, r3
#else
    bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
#endif
    mov r0, sp
    bl  board_init_f_alloc_reserve    //2. 为gd_t结构体保留空间
    mov sp, r0
    /* set up gd here, outside any C code */
    mov r9, r0
    bl  board_init_f_init_reserve    //3. 初始化gd_t(清零)
                    //gd_t的地址存在r9寄存器中,结构体中存放的是全局参数

    mov r0, #0
    bl  board_init_f           //4. 进入board_init_f进行各种初始化,分配SDRAM内存空间,填充进gd_t结构体中

#if ! defined(CONFIG_SPL_BUILD)

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_moni). Trick here is that we'll return
 * 'here' but relocated.
 */

    ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
    mov r3, sp
    bic r3, r3, #7
    mov sp, r3
#else
    bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
#endif
    ldr r9, [r9, #GD_BD]        /* r9 = gd->bd */
    sub r9, r9, #GD_SIZE        /* new GD is below bd */
                                //5. 将重定位后的GD地址放入r9中
    adr lr, here
    ldr r0, [r9, #GD_RELOC_OFF]     /* r0 = gd->reloc_off */
    add lr, lr, r0
#if defined(CONFIG_CPU_V7M)
    orr lr, #1              /* As required by Thumb-only */
#endif
    ldr r0, [r9, #GD_RELOCADDR]     /* r0 = gd->relocaddr */
    b   relocate_code       //6. 重定位代码,地址是gd->relocaddr
here:                       //7. 在SDRAM中运行
/*
 * now relocate vectors
 */

    bl  relocate_vectors    //重定位中断向量表

/* Set up final (full) environment */

    bl  c_runtime_cpu_setup /* we still call old routine here */
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD
    /* Use a DRAM stack for the rest of SPL, if requested */
    bl  spl_relocate_stack_gd
    cmp r0, #0
    movne   sp, r0
    movne   r9, r0
# endif
    ldr r0, =__bss_start    /* this is auto-relocated! */
                            //8. 清BSS
#ifdef CONFIG_USE_ARCH_MEMSET
    ldr r3, =__bss_end      /* this is auto-relocated! */
    mov r1, #0x00000000     /* prepare zero to clear BSS */

    subs    r2, r3, r0      /* r2 = memset len */
    bl  memset
#else
    ldr r1, =__bss_end      /* this is auto-relocated! */
    mov r2, #0x00000000     /* prepare zero to clear BSS */

clbss_l:cmp r0, r1          /* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
    itt lo
#endif
    strlo   r2, [r0]        /* clear 32-bit BSS word */
    addlo   r0, r0, #4      /* move to next */
    blo clbss_l
#endif

#if ! defined(CONFIG_SPL_BUILD)
    bl coloured_LED_init
    bl red_led_on
#endif
    /* call board_init_r(gd_t *id, ulong dest_addr) */
    mov     r0, r9                  /* gd_t */
    ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
    /* call board_init_r */
#if defined(CONFIG_SYS_THUMB_BUILD)
    ldr lr, =board_init_r   /* this is auto-relocated! */      
    bx  lr
#else
    ldr pc, =board_init_r   /* this is auto-relocated! */     //9. 调用board_init_r
#endif
    /* we should not return here. */
#endif

ENDPROC(_main)

5.3 总结

  • 5.3.1 start.S中:
    (1) 将CPU设为SVC模式
    (2) 关看门狗
    (3) 屏蔽中断
    (4) 设置分频系数,未设置时钟频率
    (5) bl cpu_init_crit
      [1]清除caches
      [2] 禁止 MMU cache
      [3] bl lowlevel_init  在重定位之前,需要设置好内存管理器的各时序参数,这个函数的源文件在对应的单板目录中。对于smdk2410, 在board/samsung/smdk2410/lowlever_init.S 中定义了这个函数

    (6) bl _main 在arch/arm/lib/crt0.S中定义

  • 5.3.2 crt0.S中:
    (1) 设置栈,使程序可以在运行C代码
    (2) bl board_init_f_alloc_reserve 给gd_t预留空间,gd_t是global_data结构体,里面存放了各种硬件相关参数,一般将gd_t的地址存放在r9中
    (3) bl board_init_f_init_reserve 初始化gd_t(清零)
    (4) bl board_init_f 在这里面进行各种初始化,分配内存空间,填充gd_t结构体
    (5) 在代码重定位前,将重定位后的GD地址放入r9中
    (6) 重定位代码,地址是gd->relocaddr
    (7) 进入SDRAM中运行
    (8) 清bss段
    (9) 进入board_init_r()进行其他初始化(不返回)

至此,我们的初步分析就到这里结束,下面将开始分析如何添加单板,以及如何修改启动代码使串口有输出

你可能感兴趣的:(技术文章)