物理基址定义

1
2
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE

start.s 中使用 _TEXT_PHY_BASE 存放物理基址。这个变量很重要,因为我们在 u-boot 中使用 MMU ,在 MMU 没有开启之前,需要这个变量来保证程序能在正确的地址运行

通过在 u-boot 源码中全局搜索可以发现, CFG_PHY_UBOOT_BASE 定义在 uboot/include/configs/x210_sd.h

1
2
3
#define MEMORY_BASE_ADDRESS	0x30000000
...
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0x3e00000

CFG_PHY_UBOOT_BASE 这个宏是在 MEMORY_BASE_ADDRESS 的位置上偏移了 0x3e00000 的空间

链接脚本需要的变量

1
2
3
4
5
6
7
.globl _bss_start
_bss_start:
.word __bss_start

.globl _bss_end
_bss_end:
.word _end

_bss_start _bss_end 这两个变量之前也在链接脚本中见过

BSS 段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域

BSS 段使用前需要清0,通过在这里提供 BSS 段的地址,方便链接时清0

复位

1
2
3
4
5
6
7
8
9
reset:
/*
* set the cpu to SVC32 mode and IRQ & FIQ disable
*/
@;mrs r0,cpsr
@;bic r0,r0,#0x1f
@;orr r0,r0,#0xd3
@;msr cpsr,r0
msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC

到这里就是 u-boot 真正的复位代码了

MSR 指令用亍将操作数的内容传送到程序状态寄存器的特定域中

cpsr 是 ARM 架构的当前程序状态寄存器,而 cpsr_c 是程序状态寄存器的后8位,也就是控制位

cpsr 寄存器的描述如下

I 和 F 位对应的是 IRQ 和 FIQ 中断的标志位,置1为关闭

因为模式位是前5位控制的,所以 0xd3 相当于 0x13,对应的就是 SVC(管理)模式

再加上代码中的注释,我们就可以知道这段代码的作用就是让处理器进入 SVC 模式并关闭中断

cpu_init_crit

这里是为了初始化一些重要的寄存器和内存的时钟

cpu_init_crit 只会在重启的时候运行,当 u-boot 在 ram 中的时候不会运行

这部分做了这些事 1. 重新初始化开启 L2 cache 2. 刷新 L1 的数据和指令 cache 3. 关闭 MMU 4. 读取启动介质选择

## 读取启动信息

1
2
3
ldr	r0, =PRO_ID_BASE        @ PRO_ID_BASE=E000 0000
ldr r1, [r0,#OMR_OFFSET] @ OMR_OFFSET=0000 0004
bic r2, r1, #0xffffffc1

这段代码目的是从 E000 0004 这个寄存器读取电平信息,这个寄存器是 OM 引脚的地址。通过设置 OM 引脚的电平,就可以设置 u-boot 的启动介质

bic 的作用是为了清除无关的位,方便后面进行启动介质的判断

1
2
3
4
5
6
7
8
9
10
11
12
13
/* NAND BOOT */
cmp r2, #0x0 @ 512B 4-cycle
moveq r3, #BOOT_NAND

...

/* SD/MMC BOOT */
cmp r2, #0xc
moveq r3, #BOOT_MMCSD

/* NOR BOOT */
cmp r2, #0x14
moveq r3, #BOOT_NOR

通过判断前面存入 r2 的值,得到不同的启动介质的信息

lowlevel_init

1
2
3
4
5
ldr	sp, =0xd0036000 /* end of sram dedicated to u-boot */
sub sp, sp, #12 /* set stack */
mov fp, #0

bl lowlevel_init /* go setup pll,mux,memory */

从图中可以看出 0xd0036000 是 sram 的地址空间,此时 ddr 还没有初始化完成,只能使用不需要初始化的 sram

通过 sub 创建了一个 stack,再让 fp(栈帧指针)指向 stack 的开头(fp 用作栈的开头,sp 作为栈的当前位置,fp 和 sp 一起组成了一个栈帧)

设置 stack 是为了用来存储 LR 的值,因为当前是被调用的子函数中, LR 中存储着当前子函数的范围地址,如果直接使用 bl 调用子函数,就会丢失当前子函数的返回地址

所以在子函数中调用子函数时,需要先将当前的 LR 压栈

后记

关于 SP FP PC LR 寄存器有空会说说的,我也是在学习汇编才接触到这些寄存器,也是在看了些资料才稍微了解了些