从大方面来说,u-boot的启动分成两个阶段,第一个阶段主要的职责是准备初始化的环境,主要有以下几点

①设置异常向量表

②把CPU的工作模式设置为SVC32模式

③关闭中断、MMU和cache

④关闭看门狗

⑤初始化内存、时钟、串口

⑥设置堆栈

⑦代码搬移

⑧清bss段

⑨跳转到c语言中执行(第二阶段)

此时系统还没有进入C语言的运行阶段,并没有堆栈,也就不需要额外的RAM。

第二阶段在上一段建立好C语言运行环境的基础上,进行各种外设的初始化,并循环执行用户命令。主要流程图如下

当我们执行make命令来构建u-boot时,它的构建过程是:首先使用交叉编译工具将各目录下的源文件生成目标文件(*.o),目标文件生成后,会将若干个目标文件组合成静态库文件(*.a),最后通过链接各个静态库文件生成ELF格式的可执行文件。在链接的过程中,需要根据链接脚本(一般是各个以lds为后缀的文本文件),确定目标文件的各个段,链接文件通常是board/<board>/目录中的u-boot.lds文件。一般在链接脚本中通过

ENTRY(_start)

来指定入口为_start标号,通过文本段(.text)的第一个目标来制定u-boot入口文件。所以我们通过这个链接脚本文件可以确定u-boot执行的入口。

Tiny4412 u-boot的链接脚本内容为

//  board/samsung/tiny4412/u-boot.lds
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN();
.text :
{
arch/arm/cpu/armv7/start.o (.text)
board/samsung/tiny4412/libtiny4412.o (.text)
arch/arm/cpu/armv7/exynos/libexynos.o (.text)
*(.text)
}
. = ALIGN();
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN();
.data : {
*(.data)
}
. = ALIGN();
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN();
.rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
}
.dynsym : {
__dynsym_start = .;
*(.dynsym)
}
.bss __rel_dyn_start (OVERLAY) : {
__bss_start = .;
*(.bss)
. = ALIGN();
_end = .;
}
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
}

在本链接脚本文件中,定义了起始地址为0x00000000,每个段使用4字节对齐(.ALIGN(4)),几个段分别为代码段(.text)、只读数据段(.rodata)、数据段(.data)其中,代码段的第一个目标为arch/arm/cpu/armv7/start.o,在其中定义了映像文件的入口_start。

下面来具体分析一下这个start.S。

在文件的一开始定义了映像的入口_start和中断向量表。

.globl _start  //定义u-boot入口
_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
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
_pad: .word 0x12345678 /* now *= */

系统开机进入到u-boot运行时,首先进入到u-boot的入口_start标号处,然后通过 b  reset 跳转到reset标号处,我们就到reset标号一探究竟。

/*
* the actual reset code
*/
reset:
/*
*设置CPU工作模式为SVC32模式
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
//......
//调用 cpu_init_crit
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif

首先会将CPU的工作模式设置为svc32模式,然后便调用 cpu_init_crit ,需要注意的是,这里使用的是 bl 指令,也就是说在运行完 cpu_init_crit 标号处的代码之后,会通过

mov    pc, lr            @ back to my caller

指令回到reset中继续执行

bl    cpu_init_crit

下面的指令(所以这里我们应该使用 调用来描述更为贴切)。下面我们去看一下 cpu_init_crit 指令处做了哪些事

/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
cpu_init_crit: //调用 cache_init
bl cache_init /*
*使 L1 I/D 无效
* Invalidate L1 I/D
*/
mov r0, # @ set up for MCR
mcr p15, , r0, c8, c7, @ invalidate TLBs
mcr p15, , r0, c7, c5, @ invalidate icache
/*
* 关闭 MMU 和 cache
* disable MMU stuff and caches
*/
mrc p15, , r0, c1, c0,
bic r0, r0, #0x00002000 @ clear bits (--V-)
bic r0, r0, #0x00000007 @ clear bits : (-CAM)
orr r0, r0, #0x00000002 @ set bit (--A-) Align
orr r0, r0, #0x00000800 @ set bit (Z---) BTB
mcr p15, , r0, c1, c0,
/*
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rate and handle
* wake up conditions.
*/
mov ip, lr @ persevere link reg across call
//调用 lowlevel_init
bl lowlevel_init @ go setup pll,mux,memory
mov lr, ip @ restore link
//返回到 reset 标号继续执行
mov pc, lr @ back to my caller
/*

首先分析 cache_init ,它被定义在  board/samsung/tiny4412/lowlevel_init.S 文件中

    .globl cache_init
cache_init:
mov pc, lr

可以看出来,这是一个空函数(暂且将它叫做函数-_-!!)。

接下来我们就要去分析 lowlevel_init 了,它也被定义在board/samsung/tiny4412/lowlevel_init.S 文件中

    .globl lowlevel_init
lowlevel_init: //初始化串口
bl uart_asm_init //Read booting information
//读取启动信息
bl read_om
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip sdram init and u-boot.bin loading */
//初始化内存
/* Memory initialize */
bl mem_ctrl_asm_init
//初始化系统时钟
/* init system clock */
bl system_clock_init /*
eg:
1: ;A
cmp r0, #0
beq 1f ; r0==0那么向前跳转到B处执行
bne 1b ; 否则向后跳转到A处执行
:1: ;B
*/ // 向前跳转到1: 标号处执行
b 1f
1:
//初始化 trust zone
bl tzpc_init
b load_uboot
after_copy:
#ifdef CONFIG_ENABLE_MMU
bl enable_mmu
#endif
/* store second boot information in u-boot C level variable */
ldr r0, =CONFIG_PHY_UBOOT_BASE
sub r0, r0, #8
ldr r1, [r0]
ldr r0, _second_boot_info
str r1, [r0]
/* Print 'K' */
ldr r0, =S5PV310_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
//第二阶段入口,调用C语言函数:board_init_f
ldr r0, _board_init_f
mov pc, r0 _board_init_f:
.word board_init_f
load_uboot:
ldr r0, =INF_REG_BASE
ldr r1, [r0, #INF_REG3_OFFSET]
cmp r1, #BOOT_NAND
beq nand_boot
cmp r1, #BOOT_ONENAND
beq onenand_boot
cmp r1, #BOOT_MMCSD
beq mmcsd_boot
cmp r1, #BOOT_EMMC
beq emmc_boot
cmp r1, #BOOT_EMMC_4_4
beq emmc_boot_4_4
cmp r1, #BOOT_NOR
beq nor_boot
cmp r1, #BOOT_SEC_DEV
beq mmcsd_boot
nand_boot:
mov r0, #0x1000
bl copy_uboot_to_ram
b after_copy
onenand_boot:
bl onenand_bl2_copy /*goto 0x1010*/
b after_copy
mmcsd_boot:
#ifdef CONFIG_SMDKC220
//#ifdef CONFIG_CLK_BUS_DMC_200_400
ldr r0, =ELFIN_CLOCK_BASE
ldr r2, =CLK_DIV_FSYS2_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0xf
str r1, [r0, r2]
//#endif
#else
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
ldr r0, =ELFIN_CLOCK_BASE
ldr r2, =CLK_DIV_FSYS2_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0xf
str r1, [r0, r2]
#endif
#endif
bl movi_uboot_copy
b after_copy
emmc_boot:
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
ldr r0, =ELFIN_CLOCK_BASE
ldr r2, =CLK_DIV_FSYS1_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0xf
str r1, [r0, r2]
#endif
bl emmc_uboot_copy
b after_copy
emmc_boot_4_4:
/* read TCBCNT to get Transferred CIU card byte count */
ldr r0, =0x1255005c
ldr r1, [r0]
ldr r2, =0x6000
cmp r1, r2
/* store second boot information in DRAM */
ldr r0, =CONFIG_PHY_UBOOT_BASE
sub r0, r0, #8
mov r3, #0
movlo r3, #1
str r3, [r0]
/* if transferred CIU card byte count >= 0x6000 (24 KB) */
/* BL1 and BL2 are loaded from emmc 4.4 */
/* Otherwise BL1 and BL2 are loaded from sdmmc ch2. */
blo mmcsd_boot
/* mmc ch4 devider value change */
bl mmc_ch4_devider_change
/* u-boot image copy from boot partition to DRAM. */
bl emmc_4_4_uboot_copy
/* Exit Boot mood */
bl emmc_4_4_endbootOp_eMMC
b after_copy

在 lowlevel_init 中,主要做了一些初始化工作,比如系统时钟、内存、串口等的初始化工作,然后初始化堆栈、清bss段,并进行了代码搬移,为第二阶段C语言程序运行提供保障。最后通过

ldr r0, _board_init_f
mov pc, r0

指令跳转到第二阶段C语言函数 board_init_f 函数处。接着我们就去分析一下这个函数。

在分析board_init_f函数之前,先来了解以下gd_t数据结构

// arch/arm/include/asm/global_data.h
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; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#ifdef CONFIG_FSL_ESDHC
unsigned long sdhc_clk;
#endif
#ifdef CONFIG_AT91FAMILY
/* "static data" needed by at91's clock.c */
unsigned long cpu_clk_rate_hz;
unsigned long main_clk_rate_hz;
unsigned long mck_rate_hz;
unsigned long plla_rate_hz;
unsigned long pllb_rate_hz;
unsigned long at91_pllb_usb_init;
#endif
#ifdef CONFIG_ARM
/* "static data" needed by most of timer.c on ARM platforms */
unsigned long timer_rate_hz;
unsigned long tbl;
unsigned long tbu;
unsigned long long timer_reset_value;
unsigned long lastinc;
#endif
unsigned long relocaddr; /* Start address of U-Boot in RAM */
phys_size_t ram_size; /* RAM size */
unsigned long mon_len; /* monitor len */
unsigned long irq_sp; /* irq stack pointer */
unsigned long start_addr_sp; /* start_addr_stackpointer */
unsigned long reloc_off;
#if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
unsigned long tlb_addr;
#endif
void **jt; /* jump table */
char env_buf[]; /* buffer for getenv() before reloc. */
} gd_t;
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

u-boot中使用一个结构体gd_t来存储全局区的数据,使用一个存储在寄存器中的指针gd来记录全局数据区的地址。

DECLARE_GLOBAL_DATA_PTR

在board/samsung/tiny4412/tiny4412.c被声明。

u-boot 中还有一个数据结构 bd_t用来存放板级相关的全局数据,是gd_t中结构体指针成员bd的结构体类型。

//   arch/arm/include/asm/u-boot.h
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;

u-boot启动内核时要给内核传递参数,这时需要使用gd_t、bd_t结构体中的信息来设置标记列表。了解了这两个数据结构我们就去分析一下board_init_f函数

//   arch/arm/lib/board.c
void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp;
//计算全局数据结构的地址,保存在gd指针中
/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory"); memset((void*)gd, , sizeof (gd_t));
gd->mon_len = _bss_end_ofs;
逐个调用init_sequence数组的初始化函数
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != ) {
hang();
}
}
debug ("monitor len: %08lX\n", gd->mon_len);
/*
* Ram is setup, size stored in gd !!
*/
debug ("ramsize: %08lX\n", gd->ram_size);
//填充gd数据结构
gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */
gd->relocaddr = addr;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug ("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof (gd_t));
//调用arch/arm/cpu/armv7/start.S relocate_code
relocate_code(addr_sp, id, addr);
/* NOTREACHED - relocate_code() does not return */
}

u-boot使用一个init_sequence数组来存储大多数开发板都要执行的初始化函数的函数指针

// arch/arm/lib/board.c
typedef int (init_fnc_t)(void);
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
timer_init, /* initialize timer */
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
#if defined(CONFIG_S5P6450) && !defined(CONFIG_S5P6460_IP_TEST)
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
#endif
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* 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,
#endif
dram_init, /* configure available RAM banks */
#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
arm_pci_init,
#endif
NULL,
};

board_init_f函数在调用完初始化函数指针、填充完gd结构之后,调用了arch/arm/cpu/armv7/start.S中的relocate_code去看一下relocate_code做了什么

    .globl    relocate_code
relocate_code:
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */
/* Set up the stack */
stack_setup:
mov sp, r4
adr r0, _start
#if defined(CONFIG_S5PC110) && defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
sub r0, r0, #
#endif
#ifndef CONFIG_PRELOADER
cmp r0, r6
beq clear_bss /* skip relocation */
#endif
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r2, _TEXT_BASE
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
#ifndef CONFIG_PRELOADER
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base */
sub r9, r6, r0 /* r9 <- relocation offset */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
add r10, r10, r0 /* r10 <- sym table in FLASH */
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
add r0, r0, r9 /* r0 <- location to fix up in RAM */
ldr r1, [r2, #]
and r7, r1, #0xff
cmp r7, # /* relative fixup? */
beq fixrel
cmp r7, # /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR # /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
add r1, r1, r9
fixnext:
str r1, [r0]
add r2, r2, # /* each rel.dyn entry is bytes */
cmp r2, r3
blo fixloop
clear_bss:
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r3, _TEXT_BASE /* Text base */
mov r4, r6 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #
cmp r0, r1
bne clbss_l
#endif /* #ifndef CONFIG_PRELOADER */
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/ //调用board_init_r
jump_2_ram:
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
@ add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr
_board_init_r_ofs:
.word board_init_r - _start

可见,最后调用了board_init_r函数

//   arch/arm/lib/board.c
void board_init_r(gd_t *id, ulong dest_addr)
{
char *s;
bd_t *bd;
ulong malloc_start;
gd = id;
bd = gd->bd;
gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */
monitor_flash_len = _bss_start_ofs;
debug ("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */
debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr);
/* The Malloc area is immediately below the monitor copy in DRAM */
malloc_start = dest_addr - TOTAL_MALLOC_LEN;
mem_malloc_init(malloc_start, TOTAL_MALLOC_LEN);
//初始化MMC
#ifdef CONFIG_GENERIC_MMC
mmc_initialize(bd);
#endif
//初始化环境变量
/* initialize environment */
env_relocate();
//将环境变量中的IP填充到gd结构体
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr");
stdio_init(); /* get the devices list going. */
jumptable_init();
//初始化并使能中断
/* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts();
/* Initialize from environment */
if ((s = getenv("loadaddr")) != NULL) {
load_addr = simple_strtoul(s, NULL, );
}
//进入到main_loop
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop();
}
/* NOTREACHED - no way out of command loop except booting */
}

最后调用了main_loop

待完成:

①main_loop分析

②启动内核过程

。。。。。。

最新文章

  1. 如何使用android百度地图离线地图
  2. myeclipse 开发环境下,提示 String cannot be resolved to a type
  3. Haskell List相关操作
  4. java5 CountDownLatch同步工具
  5. [Ubuntu] ubuntu13.04 从php5.4降级到php5.3
  6. Quartz:ERROR threw an unhandled Exception
  7. 【BZOJ 2154】Crash的数字表格 (莫比乌斯+分块)
  8. python学习之day13
  9. 如何保存PDF、Word和Excel文件到数据库中
  10. java 回调函数解读
  11. hbuilder下用plus.barcode.Barcode做二维码扫描,当二维码容器的高度设置过低时,启动扫描会发生闪退
  12. TensorFlow从入门到理解(六):可视化梯度下降
  13. 【UML】-NO.43.EBook.5.UML.1.003-【UML 大战需求分析】- 状态机图(State Machine Diagram)
  14. C++笔记(2018/2/6)
  15. import org.apache.http.xxxxxx 爆红,包不存在之解决办法
  16. CS100.1x Introduction to Big Data with Apache Spark
  17. Java 把异常传递给控制台
  18. Java序列化机制和原理
  19. (转)_declspec(dllexport)
  20. ORM,ORM的原理及测试案例

热门文章

  1. java中集合类详解
  2. DNS安装配置
  3. javascript-DOM操作-留言板制作
  4. 深度学习—BN的理解(一)
  5. selenium与firefox版本不兼容
  6. CentOS7下Tomcat启动特别慢【有效解决】
  7. SpringTask定时任务实例讲解【Java获取微信公众平台accessToken及jsapiTicket】
  8. CentOS X64上64位Oracle 11gR2 静默安装
  9. 1151 LCA in a Binary Tree(30 分)
  10. LeetCode Maximum Length of Pair Chain