s_init
rk3506的s_init函数和正点原子的imx6ull视频里讲的不一样,这里我并没有找到定义,只有一个在u-boot/arch/arm/cpu/armv7/lowlevel_init.S的弱定义
.pushsection .text.s_init, "ax"
WEAK(s_init)
bx lr
ENDPROC(s_init)
.popsection
这里什么都没有做,直接返回,返回到lowlevel_init
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)
这里也运行完毕,返回到save_boot_params_ret,也就是u-boot/arch/arm/cpu/armv7/start.S里。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
bl cpu_init_crit
#endif
#endif
bl _main
下一步就是_main。
_main
这里在u-boot/arch/arm/lib/crt0.S里。这里枚举一些代码,具体查看自己的仓库
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK)
ldr r0, =(CONFIG_TPL_STACK)
#elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr r0, =(CONFIG_SPL_STACK)
#else
ldr r0, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
bic r0, r0, #7 /* 8-byte alignment for ABI compliance */
mov sp, r0
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
bl board_init_f_boot_flags
bl board_init_f
#if ! defined(CONFIG_SPL_BUILD)
那么整个流程如下图
_main:
│
├─ ldr r0, =0x03f00000 @ #define CONFIG_SPL_STACK 0x03f00000
├─ bic r0, r0, #7 @ 8 字节对齐
├─ mov sp, r0 @ sp = 0x03f00000 ← 临时栈就位
│
├─ bl board_init_f_alloc_reserve
│ │ @ 在栈上"挖"出 GD_SIZE 大小的空间
│ │ @ 返回新 sp,r0 = sp - GD_SIZE
│ │
├─ mov sp, r0 @ sp 指向 gd 空间下方
├─ mov r9, r0 @ r9 = gd 指针(正式就位!)
│ @ 注意:之前在 lowlevel_init 里
│ @ r9=0(DM模式),这里才真正赋值
│
├─ bl board_init_f_init_reserve @ 初始化 gd 结构体,清零
├─ bl board_init_f_boot_flags @ 设置启动标记
│
└─ bl board_init_f @ 进入 SPL 核心初始化!
│ @ DDR 初始化、时钟树、外设...
│ @ 返回后继续执行下面
▼
(SPL 不重定位,跳过 relocate_code)
│
├─ bl spl_relocate_stack_gd @ 可选:把栈和 gd 搬到 DDR
├─ 清零 BSS 段
└─ b board_init_r @ 进入 SPL 第二阶段
这里我们先进入到board_init_f_alloc_reserve分析
board_init_f_alloc_reserve
函数位置在:u-boot/common/init/board_init.c
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
top -= CONFIG_VAL(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;
}
这里有个参数top,在_main里,top的值是r0寄存器的值,r0寄存器的值就是sp。sp的值在lowlevel_init函数中指定了,ldr sp, =CONFIG_SPL_STACK。
#define CONFIG_SPL_STACK 0x03f00000
所以,top就是这个值。接下来定义SYS_MALLOC_F_LEN这个值定义在u-boot/include/generated/autoconf.h下。很明显是存在的,所以留出1MB的大小给早期malloc使用。
#define CONFIG_SYS_MALLOC_F_LEN 0x100000
接下来就是再留出一部分内存,用来给global_data结构体用。实际上这里在上一章节里也出现了,lowlevel_init函数中:老版本通过sub sp, sp, #GD_SIZE来存留空间,这里则是通过这样的方式来指定。
| lowlevel_init.S(老式路径) | board_init_f_alloc_reserve(新式路径) | |
|---|---|---|
| 操作 | sub sp, sp, #GD_SIZE |
top = top - sizeof(struct global_data) |
| 大小 | 256 (= GD_SIZE) | 256 (= sizeof) |
| 对齐 | bic sp, sp, #7(8 字节) |
rounddown(..., 16)(16 字节) |
| 谁走 | 没开 DM 的老板子 | 所有板子(包括 RK3506) |
核心操作一模一样:都是把栈指针往下减 256 字节,腾出 global_data 的空间。
函数执行完后,栈顶区域的布局如下:
高地址
0x03f00000 ┌─────────────────────────┐ ← 原始 sp (CONFIG_SPL_STACK)
│ │
│ SPL 栈帧 │ ← push/pop 在这片区域增长
│ (函数调用使用) │
│ │
0x03e00000 ├─────────────────────────┤ ← top - 1MB
│ │
│ Early Malloc Arena │ ← 1MB,供 SPL 阶段 malloc 使用
│ (1MB = 0x100000) │ board_init_f 里可以动态分配
│ │
≈0x03dfff ├─────────────────────────┤ ← rounddown(top - sizeof(gd), 16)
│ │
│ global_data (gd) │ ← r9 指向这里!
│ (256 字节) │ board_init_f_init_reserve
│ │ 会把它清零初始化
0x03df00 └─────────────────────────┘
低地址
回到_main
mov sp, r0
/* set up gd here, outside any C code */
mov r9, r0
上一个最终返回top,也就是移动后的值r0,然后分别将r0的值给sp,r9,r9就是之前说的,专门记录gd的寄存器。在lowleve_init里边没有走记录分支,最终在这里记录了gddate的位置。
接下来是函数board_init_f_init_reserve
board_init_f_init_reserve
这个函数也在u-boot/common/init/board_init.c内
void board_init_f_init_reserve(ulong base)
{
struct global_data *gd_ptr;
/*
* 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 */
memset(gd_ptr, '\0', sizeof(*gd));
/* 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 CONFIG_VAL(SYS_MALLOC_F_LEN)
/* go down one 'early malloc arena' */
gd->malloc_base = base;
/* next alloc will be higher by one 'early malloc arena' size */
base += CONFIG_VAL(SYS_MALLOC_F_LEN);
#endif
}
同样的,参数是r0寄存器,r0存储的是上一次移动后的值。
gd_ptr = (struct global_data *)base;
/* zero the area */
memset(gd_ptr, '\0', sizeof(*gd));
这gd区域全部清零。
#if !defined(CONFIG_ARM)
arch_setup_gd(gd_ptr);
#endif
这些不执行。我们是arm架构,所以不走这里。
/* next alloc will be higher by one GD plus 16-byte alignment */
base += roundup(sizeof(struct global_data), 16);
这里base是+号,所以是跨过之前申请的gd内存区域,到达之前申请的early_malloc区域
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
/* go down one 'early malloc arena' */
gd->malloc_base = base;
/* next alloc will be higher by one 'early malloc arena' size */
base += CONFIG_VAL(SYS_MALLOC_F_LEN);
#endif
这里就是把这个早期的malloc开始地址,记录到gd这个结构体里边去。这里的gd他是全局变量。紧接着又增加1M的长度,也就是申请的malloc内存区域。
回到_main
bl board_init_f_boot_flags
紧接着又进入这个函数
board_init_f_boot_flags
同样的在u-boot/common/init/board_init.c下。
__weak int board_init_f_boot_flags(void)
{
gd->baudrate = CONFIG_BAUDRATE;
gd->serial.baudrate = CONFIG_BAUDRATE;
gd->serial.addr = CONFIG_DEBUG_UART_BASE;
gd->serial.using_pre_serial = 0;
return 0;
}
这个函数实际上并不做flag的工作,而是给gd做初始化。比如:
1.串口的波特率115200(定义在u-boot/include/generated/autoconf.h)
2.一样的,只不过存到serial的子结构里
3.调试串口的寄存器基地址 0xff0a0000(定义在u-boot/include/generated/autoconf.h)
4.最后一个是标记还没有开始使用串口
5.return 0;返回0,此时r0 = 0;
回到_main,进入board_init_f
这个函数定义在u-boot/common/board_f.c
①、初始化一系列外设,比如串口、定时器,或者打印一些消息等。
②、初始化 gd 的各个成员变量,uboot 会将自己重定位到 DRAM 最后面的地址区域,也就
是将自己拷贝到 DRAM 最后面的内存区域中。
void board_init_f(ulong boot_flags)
{
gd->flags = boot_flags; // ① 把 board_init_f_boot_flags 返回的 0 存进 gd
gd->have_console = 0; // ② 标记:还没控制台
if (initcall_run_list(init_sequence_f)) // ③ 跑初始化序列(核心!)
hang(); // ④ 任何一步失败 → 死机
// ⑤ 非 ARM 架构到这里就是出 bug 了,hang
#if !defined(CONFIG_ARM) && ...
hang();
#endif
}
重点在initcall_run_list这里,这个函数会轮流调用其他的函数,依次初始化。那么我们看一下会调用哪些。就是这个init_sequence_f函数数组,他包含了调用的函数。他定义在当前的文件中。
这里的分析由于会很多,放到下一章节中。