目录
1.前言
本文主要就Hi3556v200的U-boot+Liteos方案的启动流程做简要介绍, 前面主要分析了Hi3556v200下的start.S文件,在这之后它将解压通用uboot的代码执行,执行的入口位于通用uboot的start.S文件。
U-boot版本:u-boot-2016.11
2.主要流程
2.1 _start
上文分析到从Hi3556v200下的start.S文件执行的最后会通过start_armboot函数解压通用uboot的压缩文件,然后跳转到首地址进行执行,这个首地址就是通用uboot的链接地址0x80800000,通过链接脚本u-boot-2016.11/arch/arm/cpu/u-boot.lds可以知道入口地址为_start.位于u-boot-2016.11/arch/arm/cpu/armv7/start.S
2.2 reset
reset:
#ifndef CONFIG_MINI_BOOT
/* Allow the board to save important registers */
ENTRY(save_boot_params)
b save_boot_params_ret @ back to my caller
ENDPROC(save_boot_params)
.weak save_boot_params
#endif
save_boot_params_ret:
#ifdef CONFIG_ARMV7_LPAE
/*
* check for Hypervisor support
*/
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
and r0, r0, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
cmp r0, #(1 << CPUID_ARM_VIRT_SHIFT)
beq switch_to_hypervisor
switch_to_hypervisor_ret:
#endif
2.3 set SVC32 mode
/*
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
2.4 Setup vector
/*
* Setup vector:
* (OMAP4 spl TEXT_BASE is not 32 byte aligned.
* Continue to use ROM code vector only in OMAP4 spl)
*/
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif
2.5 cpu_init_cp15
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
bl cpu_init_crit
#endif
#endif
2.6 _main
bl _main
_main函数的定义位于./arch/arm/lib/crt0.S
Set up initial C runtime environment and call board_init_f(0)
2.6.1 设置初始运行环境
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
设置栈指针,CONFIG_SYS_INIT_SP_ADDR宏的定义位于
osdrv/opensource/uboot/u-boot-2016.11/include/configs/hi3556v200.h文件中
#define CONFIG_SYS_INIT_SP_ADDR 0x04014000
此位于片内RAM地址空间
0x0401_0000 0x0401_9FFF 片内 RAM 地址空间
#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
8字节对齐
2.6.2 board_init_f
mov r0, sp //r0的值为0x04014000
bl board_init_f_alloc_reserve //返回后r0保存地址变为0x04014000-0x2000
mov sp, r0 //sp栈变为0x04014000-0x2000
/* set up gd here, outside any C code */
mov r9, r0 //r9保存地址0x04014000-0x2000
bl board_init_f_init_reserve
mov r0, #0
bl board_init_f
board_init_f_alloc_reserve :分配一块空间(0x04014000-0x2000~ 0x04014000)作为malloc区域,返回后r0保存地址变为0x04014000-0x2000
board_init_f_init_reserve:对分配的malloc区域(0x04014000-0x2000~ 0x04014000)进行初始化
board_init_f_alloc_reserve
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
top -= CONFIG_SYS_MALLOC_F_LEN;
#endif
/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
top = rounddown(top-sizeof(struct global_data), 16);
return top;
}
CONFIG_SYS_MALLOC_F_LEN位于uboot/u-boot-2016.11/include/generated/autoconf.h
#define CONFIG_SYS_MALLOC_F_LEN 0x2000
通过调用board_init_f_alloc_reserve从CONFIG_SYS_INIT_SP_ADDR 开始来分配一段大小为CONFIG_SYS_MALLOC_F_LEN 的全局空间,用于堆使用
board_init_f_init_reserve
通过调用board_init_f_init_reserve来对分配的malloc区域(0x04014000-0x2000~ 0x04014000)进行初始化,主要是将这段区域通过"\0"填充,同时初始化了gd->malloc_base的地址为
0x04014000 - 0x2000
board_init_f
位于/u-boot-2016.11/common/board_f.c
此时r0被清零,也就是参数boot_flags为0
board_init_f首先通过栈来分配
gd_t data;
gd = &data;
之后再通过重定位到新的地址
之后主要是调用了init_sequence_f函数数组
2.6.3 Set up intermediate environment
osdrv/opensource/uboot/u-boot-2016.11/include/asm-generic/u-boot.h中定义了struct bd_info结构体,它主要包含了一些板级的信息,用于传递给kernel,如:dram的起始地址和大小,sram的起始地址和大小,flash的起始地址和大小等。struct bd_info结构体指针关联于struct global_data结构体。
- 重新设置新的堆栈指针
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
可参考uboot重定位地址空间布局图
- relocate_code
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */
adr lr, here //保存链接地址为here标号地址
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
b relocate_code
分析board_init_f时就会知道r9保存的地址位于sram的0x04014000 -0x2000,它是old gd_t存放的位置,偏移GD_RELOC_OFF就是gd->reloc_off,此时r0存放着gd->reloc_off,也就是uboot加载地址与重定位地址之间的偏移
relocate_code函数定义在/u-boot-2016.11/arch/arm/lib/relocate.S 中
(1)首先通过为编译器指定编译选项-fpic或-fpie产生位置无关码,这样调用函数可以做到位置无关
(2)对于全局变量的访问需要重定位
uboot是通过增加了一个rel.dyn段,这个段保存了所有需要重定位的变量,通过将每个变量的地址加上gd->reloc_off来达到uboot变量重定位的目的
注:通过查看反汇编可以看到,如果函数访问到全局变量,全局变量会被放置到函数的末尾,重定位的思路也就是改变放置到函数末尾的全局变量的地址
关于relocate的原理可以详细参考如下文章:
https://blog.csdn.net/skyflying2012/article/details/37660265
relocate_code函数的最后会执行如下语句,跳转到here,执行relocate_vectors
bx lr
- relocate_vectors
here:
/*
* now relocate vectors
*/
bl relocate_vectors
重定位异常向量表
- Set up final (full) environment
bl c_runtime_cpu_setup /* we still call old routine here */
- bss clear
ldr r0, =__bss_start /* this is auto-relocated! */
#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 */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
#endif
清重定位后的uboot的bss段
- board_init_r
/* 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 */
ldr pc, =board_init_r /* this is auto-relocated! */
board_init_r主要位于common/board_r.c,遍历执行init_sequence_r数组
参考文档
- https://caibiao-lee.blog.csdn.net/article/details/103292583
海思(Hi3521a)uboot详细分析(5)——uboot启动第一阶段start.S文件分析 - https://blog.csdn.net/skyflying2012/article/details/37660265
uboot的relocation原理详细分析