U-BOOT-2016.07移植 (第一篇) 初步分析
U-BOOT-2016.07移植 (第二篇) 添加单板
U-BOOT-2016.07移植 (第三篇) 代码重定位
U-BOOT-2016.07移植 (第四篇) 修改代码,从NOR启动
目录
1. 编译和移植环境
编译环境:Ubuntu9.10
交叉编译工具:arm-linux-gcc 4.4.3
u-boot版本号:2016.07
移植目标单板信息: JZ2440v2
CPU: S3C2440
NAND: K9F2G08U0C
NOR: MX29LV160DBTI
网卡:DM9000A
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
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/
- 1
- 2
- 3
- 4
-
(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 //保存退出
- 1
- 2
-
(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:"%"=%)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
可见执行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)
824:
825:u-boot.bin: u-boot-dtb.bin FORCE
826: $(call if_changed,copy)
827:else //由于没有定义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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
展开后: (若无目标或依赖文件更新)
u-boot.bin: u-boot-nodtb.bin FORCE
echo COPY $@; cp $< $@ //输出 COPY u-boot.bin, 然后执行cp u-boot-nodtb.bin u-boot.bin
- 1
- 2
- (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)
- 1
- 2
- 3
- 4
- 5
- 6
- (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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- (5)u-boot-init,u-boot-main
-> 679:u-boot-init := $(head-y)
-> 680:u-boot-main := $(libs-y)
- 1
- 2
-
(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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
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 添加进来了
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
%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
115:
116:# Added for U-Boot (backward compatibility)
-> 117:%_config: %_defconfig
118: @:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
分析到这里,我们可以看出配置单板相关的参数是在 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))) )
239:else
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)))
- 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
这里简单分析一下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 ...
- 1
- 2
4. 初次编译u-boot
4.1 配置:
首先大家需要配置交叉编译选项,编辑顶层目录Makefile: vi Makefile
查找一下CROSS_COMPILE
这个参数, 然后直接在下面添加:
ARCH = arm
CROSS_COMPILE = arm-linux-
- 1
- 2
博主使用的开发板是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: }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
很明显,程序的第一条指令在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 <[email protected]>
* Copyright (c) 2002 Alex Züpke <[email protected]>
* Copyright (c) 2002 Gary Jennejohn <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm-offsets.h>
#include <common.h>
#include <config.h>
/*
*************************************************************************
*
* 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 */
- 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
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
5.2 分析crt0.S
arch/arm/lib/crt0.S:(对部分注释进行了翻译)
/*
* crt0 - C-runtime startup Code for ARM U-Boot
*
* Copyright (c) 2012 Albert ARIBAUD <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <asm-offsets.h>
#include <linux/linkage.h>
#ifdef CONFIG_CPU_V7M
#include <asm/armv7m.h>
#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)
- 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
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
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()进行其他初始化(不返回)
至此,我们的初步分析就到这里结束,下面将开始分析如何添加单板,以及如何修改启动代码使串口有输出
U-BOOT-2016.07移植 (第一篇) 初步分析
U-BOOT-2016.07移植 (第二篇) 添加单板
U-BOOT-2016.07移植 (第三篇) 代码重定位
U-BOOT-2016.07移植 (第四篇) 修改代码,从NOR启动
目录
1. 编译和移植环境
编译环境:Ubuntu9.10
交叉编译工具:arm-linux-gcc 4.4.3
u-boot版本号:2016.07
移植目标单板信息: JZ2440v2
CPU: S3C2440
NAND: K9F2G08U0C
NOR: MX29LV160DBTI
网卡:DM9000A
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
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/
- 1
- 2
- 3
- 4
-
(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 //保存退出
- 1
- 2
-
(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:"%"=%)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
可见执行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)
824:
825:u-boot.bin: u-boot-dtb.bin FORCE
826: $(call if_changed,copy)
827:else //由于没有定义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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
展开后: (若无目标或依赖文件更新)
u-boot.bin: u-boot-nodtb.bin FORCE
echo COPY $@; cp $< $@ //输出 COPY u-boot.bin, 然后执行cp u-boot-nodtb.bin u-boot.bin
- 1
- 2
- (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)
- 1
- 2
- 3
- 4
- 5
- 6
- (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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- (5)u-boot-init,u-boot-main
-> 679:u-boot-init := $(head-y)
-> 680:u-boot-main := $(libs-y)
- 1
- 2
-
(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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
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 添加进来了
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
%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
115:
116:# Added for U-Boot (backward compatibility)
-> 117:%_config: %_defconfig
118: @:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
分析到这里,我们可以看出配置单板相关的参数是在 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))) )
239:else
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)))
- 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
这里简单分析一下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 ...
- 1
- 2
4. 初次编译u-boot
4.1 配置:
首先大家需要配置交叉编译选项,编辑顶层目录Makefile: vi Makefile
查找一下CROSS_COMPILE
这个参数, 然后直接在下面添加:
ARCH = arm
CROSS_COMPILE = arm-linux-
- 1
- 2
博主使用的开发板是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: }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
很明显,程序的第一条指令在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 <[email protected]>
* Copyright (c) 2002 Alex Züpke <[email protected]>
* Copyright (c) 2002 Gary Jennejohn <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm-offsets.h>
#include <common.h>
#include <config.h>
/*
*************************************************************************
*
* 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 */
- 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
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
5.2 分析crt0.S
arch/arm/lib/crt0.S:(对部分注释进行了翻译)
/*
* crt0 - C-runtime startup Code for ARM U-Boot
*
* Copyright (c) 2012 Albert ARIBAUD <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <asm-offsets.h>
#include <linux/linkage.h>
#ifdef CONFIG_CPU_V7M
#include <asm/armv7m.h>
#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)
- 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
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
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()进行其他初始化(不返回)
至此,我们的初步分析就到这里结束,下面将开始分析如何添加单板,以及如何修改启动代码使串口有输出