Linux驱动开发:uboot启动流程详解_uboot是怎么启动的(1)

.dynstr : { (.dynstr) }
.dynamic : { (.dynamic) }
.plt : { (.plt) }
.interp : { (.interp) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { (.gnu) }
.ARM.exidx : { (.ARM.exidx) }
.gnu.linkonce.armexidx : { (.gnu.linkonce.armexidx.) }
}


通过查看链接脚本u-boot.lds知道入口点是 arch/arm/lib/vectors.S 文件中的\_start函数。


## 一、uboot总体启动流程


**★**作者将uboot的总体启动流程分为2部分:**arch级初始化(架构)**和**板级初始化**。


![](https://img-blog.csdnimg.cn/376af54ad14b49c79fbe7add6ea46ee7.png)


## 二、arch级初始化


如下图所示,**uboot** 程序的入口函数**\_start**位于**arch/arm/lib/vector.S**文件中,**\_start 开始的是中断向量表**,随后通过 **b reset** 函数跳转到 **reset** 函数, **reset** 函数在 **arch/arm/cpu/armv7/start.S** 里面。 **reset** 函数跳转到了 **save\_boot\_params** 函数,**save\_boot\_params** 函数又跳转到 **save\_boot\_params\_ret** 函数,该函数主要分为五个功能:


①、设置CPU处于SVC特权模式,并且关闭FIQ和IRQ两个中断;  
 ②、设置向量表重定位;  
 ③、设置cp15寄存器(禁止Cache,MMU,TLBs);  
 ④、配置关键寄存器和初始化  
 ⑤、跳转\_main,进入板级初始化



> 
> **补充说明:\_start** 开始是**中断向量表**,这段话语部分读者可能存在疑惑,因为自己看 **uboot** 代码流程发现: **\_start** 进入之后就紧跟着 **b reset**,并未出现定义中断向量表。其实,大部分开发板的中断向量表是固定定义在 **0x0000000** 开始的地址上的,比如 **reset中断** 的地址是 **0x00000000**
> 
> 
> 


![](https://img-blog.csdnimg.cn/6c3944f1acf547d79beb853882b33912.png)


### 2.1 u-boot.lds链接代码


 打开 **arch/arm/cpu/armv7/u-boot.lds**这个文件, **ENTRY** 进入 **\_start** ,代码具体如下:



#include <config.h>
OUTPUT_FORMAT(“elf32-littlearm”, “elf32-littlearm”, “elf32-littlearm”)
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
#if defined(CONFIG_ARMV7_SECURE_BASE) && defined(CONFIG_ARMV7_NONSEC)
/DISCARD/ : { (.rel._secure) }
#endif
. = 0x00000000;

. = ALIGN(4);
.text :
{
	*(.__image_copy_start)
	*(.vectors)
	CPUDIR/start.o (.text*)
	*(.text*)
}

在 **arch/arm/lib/vector.S**中寻找到 **\_start** 定义,并且发现跳转到了 **reset** :



#include <config.h>
.globl _start
.section “.vectors”, “ax”
_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif
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
,


### 2.2 reset


**reset** 函数存在于 **arch/arm/cpu/armv7/start.s** 文件中,而 **reset** 函数跳转到 **save\_boot\_params** 函数,**save\_boot\_params** 函数又跳转到 **save\_boot\_params\_ret**中,具体如下:



//第一段:reset跳转到save_boot_params
.globl reset
.globl save_boot_params_ret

reset:
/* Allow the board to save important registers /
b save_boot_params
save_boot_params_ret:
/

* 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

//第二段:save_boot_params跳转到save_boot_params_ret
ENTRY(save_boot_params)
b save_boot_params_ret @ back to my caller
ENDPROC(save_boot_params)
.weak save_boot_params


### 2.3 cpsr和cp15 STCLR


 **reset** 函数跳转到 **save\_boot\_params\_ret**函数后,具体如下:



save_boot_params_ret:
/*
* 关闭 FIQ 和 IRQ 中断,设置 CPU 处于 SVC 特权模式
* except if in HYP mode already(除非已经处于HYP特权模式,比SVC低一点的特权)
*/
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

/*
 *★SCTLR寄存器bit13清零(bit13控制中断向量表地址,0:可以重定位;1:默认为0xFFFF0000)将_start 
 *的数值写入该寄存器,也就是中断向量表的起始地址为0x87800000
 */

#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

/* the mask ROM code should have PLL and others stable */

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
bl cpu_init_crit
#endif

bl	_main

从上述代码能看出,代码包含5个部分:**cpsr,cp15,cpu\_init\_cp15,cpu\_init\_crit**和**\_main**。


#### 2.3.1 cpsr



/*

  • 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


> 
> **cpsr:**给目标寄存器写入对应的数值(可以参考芯片手册),关闭 FIQ 和 IRQ,并且讲CPU设置为 SVC 特权模式。
> 
> 
> **为什么这样操作的原因:**
> 
> 
> **(1) 禁止 FIQ 和 IRQ:**uboot 作为芯片上电的第一道程序是至关重要的,而中断是很危险的,它的等级太高了,可以轻松打断程序的正常运行。所以,为了保证uboot的正常运行,需要关闭部分中断。
> 
> 
> **(2) 设置 SVC**:uboot的运行避免不了对各类寄存器等操作,为了保证操作的正常,需要给CPU设置高特权模式。
> 
> 
> 


#### 2.3.2 **cp15**



/*

  • 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



> 
> **cp15 SCTLR:**SCTLR寄存器bit13清零(bit13控制中断向量表地址,0:可以重定位;1:默认为0xFFFF0000)将\_start的数值写入该寄存器,也就是中断向量表的起始地址为0x87800000
> 
> 
> 


### 2.4 cpu\_init\_cp15


**cpu\_init\_cp15** 存在于 **arch/arm/cpu/armv7/start.s** 文件中:



/*************************************************************************
*

  • cpu_init_cp15
  • Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
  • CONFIG_SYS_ICACHE_OFF is defined.

************************************************************************/
ENTRY(cpu_init_cp15)
/

* Invalidate L1 I/D
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs /禁止从TLB中取地址描述符,也就是禁止虚拟地址到物理地址的转换,因为刚开始操作的都是物理寄存器!/
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache /关闭指令cache/
mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array /关闭分支预测/
mcr p15, 0, r0, c7, c10, 4 @ DSB /多核cpu之间进行数据同步/
mcr p15, 0, r0, c7, c5, 4 @ ISB /进行指令同步,放弃流水线中已经取到的指令,重新取指令/

/*
 * disable MMU stuff and caches
 */
mrc	p15, 0, r0, c1, c0, 0
bic	r0, r0, #0x00002000	@ clear bits 13 (--V-)
bic	r0, r0, #0x00000007	@ clear bits 2:0 (-CAM)
orr	r0, r0, #0x00000002	@ set bit 1 (--A-) Align
orr	r0, r0, #0x00000800	@ set bit 11 (Z---) BTB

#ifdef CONFIG_SYS_ICACHE_OFF
bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
#endif
mcr p15, 0, r0, c1, c0, 0

#ifdef CONFIG_ARM_ERRATA_716044
mrc p15, 0, r0, c1, c0, 0 @ read system control register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c1, c0, 0 @ write system control register
#endif

#if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072))
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 4 @ set bit #4
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif

#ifdef CONFIG_ARM_ERRATA_743622
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 6 @ set bit #6
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif

#ifdef CONFIG_ARM_ERRATA_751472
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_761320
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 21 @ set bit #21
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_845369
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 22 @ set bit #22
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif

mov	r5, lr			@ Store my Caller
mrc	p15, 0, r1, c0, c0, 0	@ r1 has Read Main ID Register (MIDR)
mov	r3, r1, lsr #20		@ get variant field
and	r3, r3, #0xf		@ r3 has CPU variant
and	r4, r1, #0xf		@ r4 has CPU revision
mov	r2, r3, lsl #4		@ shift variant field for combined value
orr	r2, r4, r2		@ r2 has combined CPU variant + revision

#ifdef CONFIG_ARM_ERRATA_798870
cmp r2, #0x30 @ Applies to lower than R3p0
bge skip_errata_798870 @ skip if not affected rev
cmp r2, #0x20 @ Applies to including and above R2p0
blt skip_errata_798870 @ skip if not affected rev

mrc	p15, 1, r0, c15, c0, 0  @ read l2 aux ctrl reg
orr	r0, r0, #1 << 7         @ Enable hazard-detect timeout
push	{r1-r5}			@ Save the cpu info registers
bl	v7_arch_cp15_set_l2aux_ctrl
isb				@ Recommended ISB after l2actlr update
pop	{r1-r5}			@ Restore the cpu info - fall through

skip_errata_798870:
#endif

#ifdef CONFIG_ARM_ERRATA_801819
cmp r2, #0x24 @ Applies to lt including R2p4
bgt skip_errata_801819 @ skip if not affected rev
cmp r2, #0x20 @ Applies to including and above R2p0
blt skip_errata_801819 @ skip if not affected rev
mrc p15, 0, r0, c0, c0, 6 @ pick up REVIDR reg
and r0, r0, #1 << 3 @ check REVIDR[3]
cmp r0, #1 << 3
beq skip_errata_801819 @ skip erratum if REVIDR[3] is set

mrc	p15, 0, r0, c1, c0, 1	@ read auxilary control register
orr	r0, r0, #3 << 27	@ Disables streaming. All write-allocate
				@ lines allocate in the L1 or L2 cache.
orr	r0, r0, #3 << 25	@ Disables streaming. All write-allocate
				@ lines allocate in the L1 cache.
push	{r1-r5}			@ Save the cpu info registers
bl	v7_arch_cp15_set_acr
pop	{r1-r5}			@ Restore the cpu info - fall through

skip_errata_801819:
#endif

#ifdef CONFIG_ARM_ERRATA_454179
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_454179

mrc	p15, 0, r0, c1, c0, 1	@ Read ACR
orr	r0, r0, #(0x3 << 6)	@ Set DBSM(BIT7) and IBE(BIT6) bits
push	{r1-r5}			@ Save the cpu info registers
bl	v7_arch_cp15_set_acr
pop	{r1-r5}			@ Restore the cpu info - fall through

skip_errata_454179:
#endif

#ifdef CONFIG_ARM_ERRATA_430973
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_430973

mrc	p15, 0, r0, c1, c0, 1	@ Read ACR
orr	r0, r0, #(0x1 << 6)	@ Set IBE bit
push	{r1-r5}			@ Save the cpu info registers
bl	v7_arch_cp15_set_acr
pop	{r1-r5}			@ Restore the cpu info - fall through

skip_errata_430973:
#endif

#ifdef CONFIG_ARM_ERRATA_621766
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_621766

mrc	p15, 0, r0, c1, c0, 1	@ Read ACR
orr	r0, r0, #(0x1 << 5)	@ Set L1NEON bit
push	{r1-r5}			@ Save the cpu info registers
bl	v7_arch_cp15_set_acr
pop	{r1-r5}			@ Restore the cpu info - fall through

skip_errata_621766:
#endif

mov	pc, r5			@ back to my caller

ENDPROC(cpu_init_cp15)



> 
> **cpu\_init\_cp15:**关闭MMU和Cache
> 
> 
> **为什么这样操作的原因:**
> 
> 
> **(1) 关闭MMU:**因为MMU是把虚拟地址转化为物理地址得作用而我们现在是要设置控制寄存器,而控制寄存器本来就是实地址(物理地址),再使能MMU,不就是多此一举了吗?
> 
> 
> **(2) 关闭cache:**cache和MMU是通过CP15管理的,刚上电的时候,CPU还不能管理他们。所以上电的时候MMU必须关闭,指令cache可关闭,可不关闭,但数据cache一定要关闭否则可能导致刚开始的代码里面,去取数据的时候,从cache里面取,而这时候RAM中数据还没有cache过来,导致数据预取异常。
> 
> 
> 


### 2.5 cpu\_init\_crit


**cpu\_init\_crit** 存在于 **arch/arm/cpu/armv7/start.s** 文件中,**cpu\_init\_crit** 代码比较简单:**b    lowlevel\_init**。所以,直接去研究 **lowlevel\_init 函数**,**lowlevel\_init 函数**存在 arch/arm/cpu/armv7/lowlevel\_init.S 文件中:



ENTRY(lowlevel_init)
/*
* Setup a temporary stack. Global data is not available yet.
/
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 /
8-byte alignment for ABI compliance /
#ifdef CONFIG_SPL_DM
mov r9, #0
#else
/

* Set up global data for boards that still need it. This will be
* removed soon.
/
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata
#else
sub sp, sp, #GD_SIZE
bic sp, sp, #7
mov r9, sp
#endif
#endif
/

* Save the old lr(passed in ip) and the current lr to stack
/
push {ip, lr}
/

* Call the very early init function. This should do only the
* absolute bare minimum to get started. It should not:
*
* - set up DRAM
* - use global_data
* - clear BSS
* - try to start a console
*
* For boards with SPL this should be empty since SPL can do all of
* this init in the SPL board_init_f() function which is called
* immediately after this.
*/
bl s_init
pop {ip, pc}
ENDPROC(lowlevel_init)


考虑到 **cpu\_init\_crit** 函数是 **arch** 级初始化中重要的函数,所以,作者这里详解给大家分析一下。


![](https://img-blog.csdnimg.cn/d51d204418a64636843279eccd721044.png)


**①** 设置SP指向CONFIG\_SYS\_INIT\_SP\_ADDR=0X0091FF00,流程见上图。可结合下图自己分析


![](https://img-blog.csdnimg.cn/161e676218fd44f9aa1246301b3b5021.png)


**②** 设置SP 8字节对齐  
**③** 设置gd(global data)大小为248B  
**④** sp地址(0x0091FE08)保存在r9寄存器中


![](https://img-blog.csdnimg.cn/6e17e601a2a44056b8ef5c5274c5a0fe.png)


**⑤** 将ip和lr入栈  
**⑥** 跳转s\_init //对于IMX6ULL而言,什么都没有做(空函数)  
**⑦** 将入栈的ip和lr出栈,并将lr赋给pc


## 三、板级初始化


![](https://img-blog.csdnimg.cn/955affe733b441c3b79ae4c53d0d429f.png)


**\_main 函数**存在于 **arch/arm/lib/crt0.S** 中,**\_main** 包含六个部分,**简单来说:**主要是把ROM中的程序拷贝至DDR(**加快uboot的运行**),然后载重定向,最后初始化各种外设。**\_mian** 函数具体如下:



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)
#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
mov sp, r0
/
set up gd here, outside any C code */
mov r9, r0
bl board_init_f_init_reserve

mov	r0, #0
bl	board_init_f

#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 */

    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
    here:
    /

  • 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! */

#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! /
#endif
/
we should not return here. */
#endif

ENDPROC(_main)


### 3.1 board\_init\_f\_alloc\_reserve


**board\_init\_f\_alloc\_reserve** 函数位于 **common/init/board\_init.c** 文件中,具体如下:



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;

}


**board\_init\_f\_alloc\_reserve** 函数目的:**留出早期的 malloc 内存区域和 gd 内存区域**,其中CONFIG\_SYS\_MALLOC\_F\_LEN=0X400( 在文件 **include/generated/autoconf.h** 中定义 ) ,sizeof(struct global\_data)=248(GD\_SIZE 值),完成以后的内存分布如图所示(top返回地址为:0x0091FA00):


![](https://img-blog.csdnimg.cn/cc81ae1d5e554c95a46e962d29ec10f0.png)


### 3.2 board\_init\_f\_init\_reserve


**board\_init\_f\_init\_reserve** 函数同样位于**common/init/board\_init.c** 文件中,具体如下:



void board_init_f_init_reserve(ulong base)
{
struct global_data *gd_ptr;
#ifndef _USE_MEMCPY
int *ptr;
#endif

/*
 * clear GD entirely and set it up.
 * Use gd_ptr, as gd may not be properly set yet.
 */

gd_ptr = (struct global_data *)base;
/* zero the area */

#ifdef _USE_MEMCPY
memset(gd_ptr, ‘\0’, sizeof(*gd));
#else
for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
ptr++ = 0;
#endif
/
set GD unless architecture did it already /
#if !defined(CONFIG_ARM)
arch_setup_gd(gd_ptr);
#endif
/
next alloc will be higher by one GD plus 16-byte alignment */
base += roundup(sizeof(struct global_data), 16);

/*
 * record early malloc arena start.
 * Use gd as it is now properly set for all architectures.
 */

#if defined(CONFIG_SYS_MALLOC_F)
/* go down one ‘early malloc arena’ /
gd->malloc_base = base;
/
next alloc will be higher by one ‘early malloc arena’ size */
base += CONFIG_SYS_MALLOC_F_LEN;
#endif
}


**board\_init\_f\_init\_reserve** 函数目的:**初始化gd(全局变量)**。


### 3.3 board\_init\_f


**board\_init\_f** 函数定义在文件 **common/board\_f.c** 中,具体代码如下:



void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
/*
* For some archtectures, global data is initialized and used before
* calling this function. The data should be preserved. For others,
* CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
* here to host global data until relocation.
*/
gd_t data;

gd = &data;

/*
 * Clear global data before it is accessed at debug print
 * in initcall_run_list. Otherwise the debug print probably
 * get the wrong vaule of gd->have_console.
 */
zero_global_data();

#endif

gd->flags = boot_flags;
gd->have_console = 0;

if (initcall_run_list(init_sequence_f))
	hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) &&
!defined(CONFIG_EFI_APP)
/* NOTREACHED - jump_to_copy() does not return */
hang();
#endif

/* Light up LED1 */
imx6_light_up_led1();

}



> 
> 
> **board\_init\_f 函数主要有两个工作:**
> 
> 
>  1、 
>  初始化一系列外设,比如串口、定时器,或者打印一些消息等。 
> 
> 
>  2、初始化 gd 的各个成员变量,uboot 会将自己重定位到 DRAM 最后面的地址区域,也就 
>  
> 是将自己拷贝到 
> DRAM 
> 最后面的内存区域中。
> 
> 
> 


**board\_init\_f** 函数中的 **initcall\_run\_list** 函数主要用于调用一系列函数,值保存在 **init\_sequence\_f** 函数中。 


![](https://img-blog.csdnimg.cn/35be2073253343d3a087a0684f7d8a8d.png)


**initcall\_run\_list** 函数的具体主要功能如下(可结合上图分析):



1、gd->num_len=_bss_end-_start//uboot image大小,即代码长度,0X878A8E74-0x87800000=0XA8EF4
2、initf_malloc()//gd->malloc=CONFIG_SYS_MALLOC_F_LEN=0X400,内存池大小为0x400
3、arch_cpu_init()//初始化架构相关的内容,CPU级别操作
4、 initf_dm()//驱动模型一些初始化
5、 board_early_init_f()//初始化串口的IO配置(I.MX6ULL)
6、 timer_init()//初始化定时器(Cortex-A7内核)

14、 init_baud_rate()//根据环境变量baudrate设置gd->baudrate=115200

24、dram_init()//设置gd->ram_size=512MB 0X2000 0000B

44、set_up_addr()//设置地址gd->ram_zise=0X2000 0000;gd->ram_top=0XA0000000
(0X80000000+0X2000 0000);gd->relocadder=0XA0000000(重定位后最高地址)…

48、reserve_uboot//gd->mon_len=0X8EF4;gd->start_addr_sp=
0X9FF47000;gd->relocadder=0X9FF47000//uboot重定位后的起始地址
49、reserve_malloc//TOTAL_MALLOC_LEN=CONFIG_SYS_MALLOC_LEN(0X10000000B=16MB)+
CONFIG_ENV_SIZE(0X2000=8K)
50、reserve_board()//留出板子bd所占的内存区
52、reserve_global_data()//留出gd所占的内存区

55、resreve_stacks//留出栈空间,gd->start_addr_sp-16,然后16字节对齐

最终sp=gd->start_addr_sp=0X9EF44E90

61、setup_reloc//设置gd其他一些成员变量,供后面定位使用,并且将以前的gd拷贝到gd->new_gd处

最终uboot重定位后偏移为0X18747000(0X9FF47000-0X87800000),新的gd首地址0X9EF44EB8,新的sp首地址0X9EF44E90


### 3.4 relocate\_code


**relocate\_code** 函数主要用于**代码拷贝**,在 **relocate\_code** 函数之前还有语句 **ldr r0,[r9,#GD\_RELOCADDR]**,即**r0=gd-> relocaddr= 0X9FF47000**, uboot 重定位后的首地址。  
**relocate\_code** 函数在 **arch/arm/lib/relocate.S** 中,下面结合代码分析该函数

## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/1b070ac9378acb6dcdeda0273db2be72.png)

![img](https://img-blog.csdnimg.cn/img_convert/67ea6392539d1f424cdf957c7385d268.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/5e6f1b329da2a81bd9cc99521078bec9.png)

 ![img](https://img-blog.csdnimg.cn/img_convert/b17f5630ffa8df31679e6bf28a57a4eb.png)

![img](https://img-blog.csdnimg.cn/img_convert/80b3415e119e7c5515fd3ffcf0e2141b.png)

![img](https://img-blog.csdnimg.cn/img_convert/659d85c40062d03f884d5aae18dbc0b1.png)

![](https://img-blog.csdnimg.cn/img_convert/c9424b39e317f1713d1244c5393a94d1.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


uboot 重定位后的首地址。  
**relocate\_code** 函数在 **arch/arm/lib/relocate.S** 中,下面结合代码分析该函数

## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中...(img-G8drDHYy-1715587231515)]

[外链图片转存中...(img-xfmjHuD3-1715587231516)]

[外链图片转存中...(img-WkICfh7R-1715587231517)]

 [外链图片转存中...(img-gemgXrFV-1715587231518)]

[外链图片转存中...(img-K99ZqHmN-1715587231519)]

[外链图片转存中...(img-r8kIQfGR-1715587231519)]

[外链图片转存中...(img-DiZtKdMq-1715587231520)]

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值