rk3506的uboot源码分析(三)

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函数数组,他包含了调用的函数。他定义在当前的文件中。

这里的分析由于会很多,放到下一章节中。

相关推荐
2023自学中2 小时前
imx6ull 开发板 贪吃蛇, C++11 SDL2 无硬件GPU优化版
linux·c++
我头发多我先学3 小时前
Linux入门:简要认识Linux和基础指令
linux·运维·服务器
逍遥德3 小时前
运维技术栈Linux+docker+Kubernetes+Jenkins/GitLab CI 知识点详细列表
linux·运维·docker
冰封之寂3 小时前
Docker 部署与基础命令详解:从安装到容器管理全流程指南
linux·运维·docker·容器
cellurw3 小时前
20260723 六组件全流程编译打通与音频项目启动
linux·服务器·音视频
Brilliantwxx4 小时前
【Linux】 软件包管理器(yum)+ Vim使用
linux·运维·服务器·开发语言·编辑器·vim
skywalk81634 小时前
在FreeBSD的Uubntu兼容环境下安装Reasonix
linux·运维·freebsd
^yi4 小时前
【Linux系统编程】对操作系统的理解
linux·运维·服务器·操作系统·系统调用·os
Kina_C4 小时前
LVS-NAT 负载均衡实验从环境搭建到规则持久化
linux·运维·服务器·负载均衡·lvs