一、启动流程的分析
-
4412芯片启动流程
TBL0: ARM的起始地址都是0地址, 三星的芯片一般将0地址映射到iROM中, BL0就是指iROM中固化的启动代码, 主要负责加载BL1
BL1: 三星对于bootloader的加密代码bl1.bin, 要放在外设中uboot.bin的头上, 和一部分uboot.bin一起加载到iRAM中运行.
BL2: 从(nand/sd/usb)中拷贝的uboot.bin头最大14K到iRAM中代码中除去bl1.bin后剩余的部分, 负责设置CPU为SVC模式, 关闭MMU, 关闭中断, 关闭iCache, 关闭看门狗, 初始化DRAM,初始化时钟, 初始化串口, 设置栈, 校验BL2并将其搬移到DRAM高位地址, 重定位到DRAM中执行BL3
BL3:是指在代码重定向后在内存中执行的uboot的完整代码, 负责初始化外设,更新向量表, 清BSS, 准备内核启动参数, 加载并运行OS内核
我们常说的uboot分为两个过程,主要指的事BL2和BL3.
BL2主要是汇编实现的
BL3是c语言实现的。 -
cpu 模式的理解
cpu 有七种模式,每种模式对应的权限是不同的,可以这样理解这个权限,首次按arm有31个通用寄存器和6个状态寄存器,某些寄存器在特定模式下才能被访问。Svc代表管理者模式,权限可以理解为最高。 -
mmu代表内存管理单元
mmu的工作只有一个,就是把虚拟地址映射到物理地址。为什么需要mmu,考虑一个问题,32位的cpu,可访问的地址空间是4G,但是cpu的ram是远远没有4g的,比如说一个开发板的ddr是512m。但是怎么实现4g的访问空间,但实际只有512m的ram那?这就需要MMU了。具体实现原理就不过多叙述了。 -
cache
什么是cache,cache是一个高速缓存单元,它介于cpu和内存之间。为什么需要cache?因为cpu读写内存的速率比较慢,影响性能,cache的读写速率接近cpu的处理速率。为什么一开始要关闭cache和mmu那,我们知道内存使用前是需要初始化的,内存还没有初始化,就是用介于内存和cpu之间的cache和mmu,肯定会出问题,所以一开始就要禁用。 -
uboot运行流程

二结合实例,分析uboot
- 填充16字节的校验位
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
- uboot.bin镜像在开头需要加16字节的检验头(详见 irom application
note文件)。这一部分主要功能是在代码段最开始处放置16字节的填充位,即通过伪.word指令定义4个word(32位)的空间来在代码段最开始处占16字节。 - 在此处只是占据16字节的空间,校验头的正确值在编译阶段通过mkv210image.c中计算并填充的。
- 初始化异常向量表
板子上电后首先执行的时设置异常向量表,arm公司一共规定了七种异常向量,其中复位也是异常的一种。复位的入口地址为0.b和ldr都是跳转指令。PC是程序计数器,cpu总是按照pc的指向进行命令抓去,所以pc决定了程序运行的流向。所以第一条程序就执行了reset指向的函数。
_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
- 设置全局变量
_TEXT_BASE:
.word TEXT_BASE
/*
* Below variable is very important because we use MMU in U-Boot.
* Without it, we cannot run code correctly before MMU is ON.
* by scsuh.
*/
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
- TEXT_BASE是U-Boot代码的链接地址,为 0xc3e00000.
- CFG_PHY_UBOOT_BASE设置U-Boot在DDR中的物理地址,即运行地址,U-Boot重定位将整个U-Boot拷贝至DDR中的_TEXT_PHY_BASE,CFG_PHY_UBOOT_BASE定义在\include\configs\itop_4412_ubuntu.h文件中。为什么是0x40000000,这个要看具体芯片的的内存分配,4412为ddr分配的起始地址为0x40000000.为什么是0x 0xc3e00000.
- _armboot_start为uboot的入口地址,应为uboot有一次重定位的过程,所以这个值应该是会变得。
- _bss_start为bss段的起始地址。
- _bss_end为bss段的结束地址。
- IRQ_STACK_START和FIQ_STACK_START设置中断栈和异常栈起始地址。

#define MEMORY_BASE_ADDRESS 0x40000000
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0x3e00000

- 禁止中断和异常,设置管理者模式
/*
* set the cpu to SVC32 mode and IRQ & FIQ disable
*/
mrs r0, cpsr
bic r0, r0, #0x3f
orr r0, r0, #0xd3
msr cpsr, r0
- mrs
mrs指令用于将程序状态寄存器的内容传入到普通寄存器中。当需要修改程序状态寄存器时,首先把程序状态寄存器的值读入到普通寄存器,修改后在传入到程序状态寄存器。 - cpsr
cpsr是程序状态寄存器,用于管理中断、异常和模式,反应运行状态。
程序状态寄存器的读写只能用msr和mrs指令。

- MSR指令
对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。 - BIC
BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器、或一个立即数。操作数2为32位的掩码,如果在掩码中置了某一位1,则清除这一位。未设置的掩码位保持不变
BIC{条件}{S} 目的寄存器,操作数1,操作数2
- 关闭mmu和cache
由前面分析知,最后跳转到了这个函数。这个函数的作用就是关闭mmu和cache,为什么关闭这两个,前面已经介绍过。这里牵涉到一个cp15知识点的扫盲。这个是协处理器。协处理器的主要是管理mmu和cache、保护系统、配置时钟模式等。Cp15只能被MRC和MCR指令访问。具体内容现在这里挖个坑,有空下看,这里主要移植捋下思路。
#if 1 //*****ly
cache_init:
mrc p15, 0, r0, c0, c0, 0 @ read main ID register读芯片的id
and r1, r0, #0x00f00000 @ variant
and r2, r0, #0x0000000f @ revision
orr r2, r2, r1, lsr #20-4 @ combine variant and revision
cmp r2, #0x30
mrceq p15, 0, r0, c1, c0, 1 @ read ACTLR
orreq r0, r0, #0x6 @ Enable DP1(2), DP2(1)
mcreq p15, 0, r0, c1, c0, 1 @ write ACTLR
/*
* Invalidate L1 I/D
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
/*
* 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, #0x00001000 @ set bit 12 (---I) Icache
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
#endif
- 为什么要关闭mmu?
因为MMU是把虚拟地址转化为物理地址得作用,而我们现在是要设置控制寄存器,而控制寄存器本来就是实地址(物理地址),再使能MMU,不就是多此一举了吗? - 为什么要关闭cache?
catch和MMU是通过CP15管理的,刚上电的时候,CPU还不能管理他们。所以上电的时候MMU必须关闭,指令cache可关闭,可不关闭,但数据cache一定要关闭,否则可能导致刚开始的代码里面,去取数据的时候,从catch里面取,而这时候RAM中数据还没有cache过来,导致数据预取异常 - cp15协处理器
CP15 —系统控制协处理器 (the system control coprocessor),通过协处理器指令 MCR和 MRC 提供具体的寄存器来配置和控制 caches、MMU、保护系统、配置时钟模式(在 bootloader时钟初始化用到)。CP15包含16个32位的寄存器,其编号为0~15。具体请查看这篇博客:https://blog.csdn.net/deep_l_zh/article/details/84852260
- 判断启动介质
/* Read booting information */
ldr r0, =POWER_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
/* NAND BOOT */
@ cmp r2, #0x0 @ 512B 4-cycle
@ moveq r3, #BOOT_NAND
@ cmp r2, #0x2 @ 2KB 5-cycle
@ moveq r3, #BOOT_NAND
@ cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC
@ moveq r3, #BOOT_NAND
cmp r2, #0xA
moveq r3, #BOOT_ONENAND
@ cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC
@ moveq r3, #BOOT_NAND
/* SD/MMC BOOT */
cmp r2, #0x4
moveq r3, #BOOT_MMCSD
/* eMMC4.3 BOOT */
cmp r2, #0x6
moveq r3, #BOOT_EMMC43
/* eMMC441 BOOT */
cmp r2, #0x28
moveq r3, #BOOT_EMMC441
/* NOR BOOT */
@ cmp r2, #0x14
@ moveq r3, #BOOT_NOR
/* For second device booting */
/* OneNAND BOOTONG failed */
@ cmp r2, #0x8
@ moveq r3, #BOOT_SEC_DEV
/* Uart BOOTONG failed */
@ cmp r2, #(0x1<<4)
@ moveq r3, #BOOT_SEC_DEV
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET]
- POWER_BASE定义如下,搜索芯片手册,这个寄存器定义为电源管理寄存器基地址。[r0,#OMR_OFFSET]表示OM_STAT寄存器,这个寄存器保存的是启动模式。关于启动模式的介绍,在芯片手册的uboot章节有介绍,拨码开关拨到不同位置代表不同的启动方式。后面通过比较值,选择不同的启动值。把启动方式保存在r3寄存器中。
- 最后两句,INF_REG_BASE这个值为 0x10020800,基地址为依然为 0x10020000,偏移地址为0x00000800,我检索数据手册,结果如下,这是一类寄存器,用于保存用户的数据的。str指令的意思就是把r3寄存器值保存到,后面地址空间,就是把启动方式保存到用于保存用户信息的寄存器3中。
#define POWER_BASE 0x10020000
#define OMR_OFFSET 0x0







- lowlevel_init
/*
* Go setup Memory and board specific bits prior to relocation.
*/
//b .
bl lowlevel_init /* go setup pll,mux,memory */
跳转到lowlevel_init函数,首先初始化了栈,为后面的c准备环境,然后判断了启动模式,如果是ram启动就会跳过时钟和内存初始化。然后是串口初始化。
8. 初始化栈
/* use iROM stack in bl2 */
ldr sp, =0x02060000
push {lr}
这个函数首先是初始化了栈,0x02060000是一个地址,这个地址是什么哪?为什么是这个地址哪?我们要看一下datasheet的memmorymap部分,如图所示,这个地址代表的是iRAM的地址的首地址,目前程序运行在iram中,所以就使用了这个地址。 sp是堆栈指针,push是压栈的意思,lr保存函数调用的返回指针。那么为什么需要这段程序?lr寄存器只有一个,lr寄存器只能保存一次cp指针的值,当出现嵌套掉调用函数时,需要保存多个cp指针的当前指向地址,一个不够用,所以需要初始化堆栈。

- 判断复位类型
/* check reset status */
ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET)
ldr r1, [r0]
/* AFTR wakeup reset */
ldr r2, =S5P_CHECK_DIDLE
cmp r1, r2
beq exit_wakeup
/* Sleep wakeup reset */
ldr r2, =S5P_CHECK_SLEEP
cmp r1, r2
beq wakeup_reset
这段代码的意义是什么?复位分为很多种复位,比如我们的手机,可能是关 机后重新开机,需要初始化一边所有的硬件,这是冷启动;还有一种就是手 机 长时间不 自动进入休眠模式,当再次使用时,重新唤醒也可以引发复位,这种情况下是不需要初 始化一边硬件 。4412这款芯片有五种复位,分别为冷启动复位、看门狗复位、热启动复位、软件复位和休眠唤醒复位。 不同的复位引发的效果不一样,比如冷启动复位会初始化ddr,休眠唤醒不会初始化DDR,具体如图所示。NF_REG_BASE 和INF_REG1_OFFSET 这两个宏是什么意思,我们搜索下文件,这两个宏定义在include的目录下,如下。这是什么意思哪,我们通过0x10020800这个关键字检索下手册,如图,读取这个寄存器的值,INF_REGx_REG是一系列寄存器,这寄存器我们上面提到了,用于保存一些信息,这些寄存器是当不同复位时保存不同的值,如图,冷启动复位会重新复位这一系列寄存器的值,休眠唤醒复位就不会重新复位这些值,通过这一点就可以区分是否是冷启动复位,还是休眠唤醒复位。S5P_CHECK_DIDLE和S5P_CHECK_SLEEP这两个宏进行比较,如果一样则进入相应的处理程序。这部分的复位要看电源管理部分。



#define INF_REG_BASE 0x10020800
#define INF_REG0_REG __REG(INF_REG_BASE+INF_REG0_OFFSET)
#define INF_REG1_REG __REG(INF_REG_BASE+INF_REG1_OFFSET)
#define INF_REG2_REG __REG(INF_REG_BASE+INF_REG2_OFFSET)
#define INF_REG3_REG __REG(INF_REG_BASE+INF_REG3_OFFSET)
#define INF_REG4_REG __REG(INF_REG_BASE+INF_REG4_OFFSET)
#define INF_REG5_REG __REG(INF_REG_BASE+INF_REG5_OFFSET)
#define INF_REG6_REG __REG(INF_REG_BASE+INF_REG6_OFFSET)
#define INF_REG7_REG __REG(INF_REG_BASE+INF_REG7_OFFSET)

10. 供电锁存
- 0x1002330c这个地址就是PS_HOLD_CONTROL寄存器地址,这个寄存器的描述如图,大概意思就是当冷复位时就要复位此处,所以就要8、9位置1,所以就有“orr r1, r1, #0x300”,orr是位或的意思。
- 地址0x11000c08表示控制gpio口x组的上下拉的,如图,置0表示禁止上下拉模式。STR指令用亍从源寄存器中将一个32位的字数据传送到存储器中。这里禁止上下拉不太理解什么意思。不太理解这里的供电锁存是什么意思。这个地方移植时可能需要修改。
/* PS-Hold high */
ldr r0, =0x1002330c
ldr r1, [r0]
orr r1, r1, #0x300
str r1, [r0]
ldr r0, =0x11000c08
ldr r1, =0x0
str r1, [r0]


11. 禁止看门狗
/* Clear MASK_WDT_RESET_REQUEST */
ldr r0, =0x1002040c
ldr r1, =0x00
str r1, [r0]
为什么禁止看门狗,因为现在还没有喂狗的程序,如果现在使能看门狗,那么程序会不停的复位。0x1002040c为看门狗寄器MASK_WDT_RESET_REQUEST,如图。

12. 检测程序当前的执行位置(SRAM或DDR)
#ifdef check_mem /*liyang 20110822*/
/* 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 1f /* r0 == r1 then skip sdram init */
#endif
- 本段的功能是检测当前代码的执行位置。判断是在SRAM中还是DDR中,即CPU是冷启动还是休眠唤醒复位,从而来决定是否要跳过后面的时钟、DDR的初始化代码。
- bic是位清除指令,其功能是:将pc中的某些位清零(r0中为1的位清零),剩下一些特殊的bit位赋值给r1。
- 比较链接地址和当前地址的特定位,即比较r1和r2。若比较链接地址和当前地址的特定位相等,说明当前代码处于DDR中(休眠唤醒),则跳转到标号1处执行后面的代码(即跳过时钟、DDR的初始化代码);否则,继续执行后续的时钟、DDR的初始化代码。_TEXT_BASE表示uboot被重定位时要加载到的起始地址。
- 判断代码执行的位置的原理,就是根据sram和DDR在内存空间地址不同,通过比较当前程序指针指向的地址和DDR地址空间
13. 初始化时钟、DDR、串口
/* init system clock */
bl system_clock_init
/* Memory initialize */
bl mem_ctrl_asm_init
1:
/* for UART */
bl uart_asm_init //初始化完成打印'O'
bl tzpc_init //基本没用
#if defined(CONFIG_ONENAND)
bl onenandcon_init
#endif
#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
跳转执行时钟初始化函数system_clock_init,然后返回。时钟的相关参数可以在开发板配置文件(/uboot/include/configs/x210_sd.h)中修改,移植过程基本不需要需改该部分代码。跳转执行DDR初始化函数mem_ctrl_asm_init,然后返回。其中,DMC0,DMC1等设置很重要,直接和板子上内存的大小和分布有关,需要注意。DDR的相关参数可以在开发板配置文件(/uboot/include/configs/x210_sd.h)中修改。跳转执行串口初始化函数uart_asm_init,成功执行后打印‘O’,然后返回。‘O’可作为调试的帮助。若定义了ONFIG_ONENAND,则初始化ONENAND;若定义CONFIG_NAND,则初始化NAND。在返回start.S前打印了‘K’,与之前的‘O’组成“OK”,这是uboot的第一条打印信息,可以用来判断lowlevel_init是否正常运行。
把之前保存在栈中的lr值弹出到pc中,来返回到start.S 。这部分可能需要针对不同的开发板进行修改。
- 再次进行供电锁存
ldr r0, =0x1002330C /* PS_HOLD_CONTROL register */
ldr r1, =0x00005300 /* PS_HOLD output high */
str r1, [r0]
- 初始化栈(DDR中)
/* get ready to call C functions */
ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
sub sp, sp, #12
mov fp, #0 /* no previous frame, so fp=0 */
为了即将执行的c程序做准备,这里开始第二次设置栈,设置于DDR中。这里将栈设置在_TEXT_PHY_BASE,即U-Boot在DDR中的真正物理地址(U-Boot运行地址)。由于栈是满减栈,所以紧挨着uboot放置也不会冲突。注意这两次的不同,第一次设置栈是在sram中,目的是为跳转嵌套做铺垫。
- 再次检测当前程序执行地址(SRAM或DDR)
/* 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 flash copy */
在lowlevel_init中,检测当前代码的执行位置。判断是在SRAM中还是DDR中,即cpu是冷启动还是休眠唤醒复位,从而来决定是否要跳过后面的时钟、DDR和串口的初始化代码。在此处再次检测当前代码的执行位置,从而来决定是否要跳过重定位代码。
- 通过引脚检测启动介质
ldr r0, =INF_REG_BASE
ldr r1, [r0, #INF_REG3_OFFSET]
cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */
beq nand_boot
cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */
beq onenand_boot
cmp r1, #BOOT_EMMC441
beq emmc441_boot
cmp r1, #BOOT_EMMC43
beq emmc_boot
cmp r1, #BOOT_MMCSD
beq mmcsd_boot
cmp r1, #BOOT_NOR
beq nor_boot
cmp r1, #BOOT_SEC_DEV
beq mmcsd_boot
nand_boot:
mov r0, #0x1000
bl copy_from_nand
b after_copy
onenand_boot:
bl onenand_bl2_copy /*goto 0x1010*/
b after_copy
//ly
second_mmcsd_boot:
ldr r3, =BOOT_MMCSD
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET]
mmcsd_boot:
#ifdef CONFIG_CLK_1000_400_200
ldr r0, =CMU_BASE
ldr r2, =CLK_DIV_FSYS2_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0xf
str r1, [r0, r2]
#endif
bl
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, =CMU_BASE
ldr r2, =CLK_DIV_FSYS1_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0x3
str r1, [r0, r2]
#endif
bl emmc_uboot_copy
b after_copy
emmc441_boot:
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
ldr r0, =CMU_BASE
ldr r2, =CLK_DIV_FSYS3_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0x3
str r1, [r0, r2]
#endif
bl emmc441_uboot_copy
//ly 20110824
ldr r0, =0x43e00000
ldr r1, [r0]
ldr r2, =0x2000
cmp r1, r2
bne second_mmcsd_boot
b after_copy
nor_boot:
@ bl read_hword
b after_copy
启动介质不同,代码重定位的方式肯定也不一样。emmc441_uboot_copy代码就是进行代码重定位的。
- 建立虚拟地址映射表并开启MMU
#if defined(CONFIG_ENABLE_MMU)
enable_mmu:
/* enable domain access */
ldr r5, =0x0000ffff
mcr p15, 0, r5, c3, c0, 0 @load domain access register
/* Set the TTB register */
ldr r0, _mmu_table_base
ldr r1, =CFG_PHY_UBOOT_BASE
ldr r2, =0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15, 0, r1, c2, c0, 0
/* Enable the MMU */
mmu_on:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0
nop
nop
nop
nop
#endif
从代码重定位函数返回之后,将跳转至start.S->after_copy,建立虚拟地址映射表并开启MMU。
- 第三次初始化栈(DDR中)
#ifdef CONFIG_EVT1
/* store DMC density information in u-boot C level variable */
ldr r0, = CFG_UBOOT_BASE
sub r0, r0, #4
ldr r1, [r0]
ldr r0, _dmc_density
str r1, [r0]
#endif
skip_hw_init:
/* Set up the stack */
stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
#else
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#if defined(CONFIG_USE_IRQ)
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
#endif
第三次设置栈,仍然设在DDR中。虽然上一次已经在DDR中设置过,但是是紧挨着uboot存放的,位置不合理。所以本次将栈设置uboot链接地址上方2MB处,这个位置合理、紧凑、安全。
- 清bss段
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
_bss_start和_bss_end是链接脚本中定义的。
利用循环清零。
- 远跳转至start_armboot(U-Boot第二阶段)
ldr pc, _start_armboot
至此,第一阶段已经运行完成,进入第二阶段。汇编部分也结束了。
694

被折叠的 条评论
为什么被折叠?



