UBOOT学习笔记(六):UBOOT启动--CPU架构及板级初始化阶段

3.1、_main

ENTRY(_main)

#if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK)
    ldr r0, =(CONFIG_TPL_STACK)      /* TPL(三级引导)使用独立栈 */
#elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
    ldr r0, =(CONFIG_SPL_STACK)      /* SPL(二级引导)使用独立栈 */
#else
    ldr r0, =(CONFIG_SYS_INIT_SP_ADDR) /* 主 U-Boot 使用默认栈 */
#endif
    bic r0, r0, #7                 /* 栈地址 8 字节对齐(ARM ABI 要求) */
    mov sp, r0                     /* 设置栈指针 */
    bl board_init_f_alloc_reserve  /* 在栈顶预留 gd 和早期 malloc 空间 */
    mov sp, r0                     /* 调整栈指针到预留区域顶部 */
    mov r9, r0                     /* r9 = gd(全局数据指针) */
    bl board_init_f_init_reserve   /* 初始化 gd 结构 */

#if defined(CONFIG_SPL_EARLY_BSS)
	SPL_CLEAR_BSS
#endif

    mov r0, #0
    bl board_init_f  /* 执行板级早期初始化 */

#if ! defined(CONFIG_SPL_BUILD)

    ldr r0, [r9, #GD_START_ADDR_SP]  /* 获取新栈地址(gd->start_addr_sp) */
    bic r0, r0, #7                   /* 8 字节对齐 */
    mov sp, r0                       /* 切换到新栈 */
    ldr r9, [r9, #GD_BD]             /* r9 = gd->bd(板级信息结构体) */
    sub r9, r9, #GD_SIZE             /* 新 gd 位于 bd 下方 */

    adr lr, here                     /* 保存当前返回地址(here 标签) */
    ldr r0, [r9, #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:
	bl	relocate_vectors

	bl c_runtime_cpu_setup           /* 执行 CPU 相关的后期初始化(如缓存、MMU 配置) */
#endif
#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK)

#if !defined(CONFIG_SPL_EARLY_BSS)
	SPL_CLEAR_BSS                    /* 确保未初始化的全局变量(.bss 段)全部清零 */
#endif

# 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

#if ! defined(CONFIG_SPL_BUILD)
	bl coloured_LED_init
	bl red_led_on
#endif
    mov r0, r9         				 /* 参数1: gd(全局数据指针) */
    ldr r1, [r9, #GD_RELOCADDR] 	 /* 参数2: 重定位地址 */
#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
    ldr lr, =board_init_r 			 /* Thumb 模式跳转 */
    bx lr
#else
    ldr pc, =board_init_r 			 /* ARM 模式跳转 */
#endif

ENDPROC(_main)

由于IMX6ULL没有定义CONFIG_SPL_BUILD和CONFIG_TPL_STACK,即没有定义SPL和TPL两个阶段,因此该段函数有几个关键点:

ldr r0, =(CONFIG_SYS_INIT_SP_ADDR)加载主 U-Boot 的默认栈地址到 r0
bic r0, r0, #7确保栈地址 8 字节对齐(ARM ABI 要求)
mov sp, r0设置栈指针,准备运行 C 代码
bl board_init_f_alloc_reserve在栈顶预留 gd 和早期 malloc 空间
bl board_init_f_init_reserve初始化 gd 结构
bl board_init_f执行板级早期初始化
b relocate_code重定位代码
bl relocate_vectors重定位向量表
ldr pc, =board_init_r进行板级初始化

3.2、board_init_f

void board_init_f(ulong boot_flags)
{
	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) && !CONFIG_IS_ENABLED(X86_64) && \
		!defined(CONFIG_ARC)
	/* NOTREACHED - jump_to_copy() does not return */
	hang();
#endif
}

这是一个关于 U-Boot 启动过程中 init_sequence_f 函数指针数组的详细说明。init_sequence_f 是 U-Boot 早期初始化阶段的核心执行流程,按顺序调用一系列初始化函数,逐步完成硬件和软件环境的搭建。运行在 原始位置(Flash/ROM),尚未重定位到 RAM,主要任务如下:
(1)CPU 和基础外设初始化(初始化 CPU、时钟、串口、定时器等基础硬件)→ arch_cpu_init、timer_init、serial_init
(2)内存初始化 → dram_init、testdram
(3)环境变量和调试信息 → env_init、print_cpuinfo
(4)内存保留U-Boot、malloc、设备树等) → reserve_uboot、reserve_malloc
(5)重定位准备(计算目标地址,保留空间) → setup_dest_addr、setup_reloc


3.3、relocate_code

relocate_code 函数主要用于 将 U-Boot 自身代码从 Flash/ROM 复制到 RAM(重定位),并修复重定位后的符号地址(如全局变量、函数指针)

ENTRY(relocate_code)
    ldr r1, =__image_copy_start  /* r1 = 源代码起始地址(Flash 中的 U-Boot) */
    subs r4, r0, r1             /* r4 = 重定位偏移量(目标地址 - 源地址) */
    beq relocate_done           /* 如果偏移量为0(无需重定位),直接跳过 */
    ldr r2, =__image_copy_end   /* r2 = 源代码结束地址 */

copy_loop:
    ldmia r1!, {r10-r11}  /* 从源地址 [r1] 加载2个寄存器(64位) */
    stmia r0!, {r10-r11}  /* 存储到目标地址 [r0] */
    cmp r1, r2            /* 检查是否到达源结束地址 [r2] */
    blo copy_loop         /* 如果未结束,继续循环 */

    ldr r2, =__rel_dyn_start  /* r2 = 重定位表起始地址 */
    ldr r3, =__rel_dyn_end    /* r3 = 重定位表结束地址 */
fixloop:
    ldmia r2!, {r0-r1}        /* r0 = 需修复的地址,r1 = 修复类型 */
    and r1, r1, #0xff         /* 提取低8位(类型字段) */
    cmp r1, #R_ARM_RELATIVE   /* 检查是否是相对重定位 */
    bne fixnext               /* 如果不是,跳过 */

    /* 相对重定位修复:原值 + 偏移量 */
    add r0, r0, r4           /* r0 = 需修复的地址(RAM 中的新地址) */
    ldr r1, [r0]             /* 读取原值 */
    add r1, r1, r4           /* 新值 = 原值 + 重定位偏移量 */
    str r1, [r0]             /* 写回修复后的值 */
fixnext:
    cmp r2, r3               /* 检查是否处理完所有重定位项 */
    blo fixloop              /* 如果未结束,继续循环 */

relocate_done:
#ifdef __XSCALE__
    /* XScale 架构需无效化指令缓存和排空写缓冲区 */
    mcr p15, 0, r0, c7, c7, 0  /* 无效化指令缓存 */
    mcr p15, 0, r0, c7, c10, 4 /* 排空写缓冲区 */
#endif

    /* 返回调用者 */
#ifdef __ARM_ARCH_4__
    mov pc, lr  /* ARMv4 不支持 bx lr,直接 mov pc, lr */
#else
    bx lr       /* ARMv5+ 使用 bx lr 返回 */
#endif

ENDPROC(relocate_code)

uboot的relocate动作就是指UBOOT的重定向动作,也就是将uboot自身镜像拷贝到DDR上的另外一个位置的动作。(对于IMX6ULL来讲,在BOOTROM阶段就已经把整个UBOOT拷贝到了DDR,因此是从DDR一个位置,拷贝到DDR的另一个位置,有些芯片是BOOTROM,将SPL拷贝到SRAM,SPL进行初级初始化后将UBOOT从SD等外设拷贝到DDR)

3.4、board_init_r

void board_init_r(gd_t *new_gd, ulong dest_addr)
{
    /*
     * Set up the new global data pointer. So far only x86 does this
     * here.
     * TODO(sjg@chromium.org): Consider doing this for all archs, or
     * dropping the new_gd parameter.
     */
#if CONFIG_IS_ENABLED(X86_64)
    arch_setup_gd(new_gd);
#endif

#ifdef CONFIG_NEEDS_MANUAL_RELOC
    int i;
#endif

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
    gd = new_gd;
#endif
    gd->flags &= ~GD_FLG_LOG_READY;

#ifdef CONFIG_NEEDS_MANUAL_RELOC
    for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
        init_sequence_r[i] += gd->reloc_off;
#endif

    if (initcall_run_list(init_sequence_r))
        hang();

    /* NOTREACHED - run_main_loop() does not return */
    hang();
}

board_init_r() 函数是 U-Boot 第二阶段初始化的入口函数,主要职责是:

  • 设置全局数据结构指针
  • 处理需要手动重定位的函数
  • 按顺序执行 init_sequence_r 中的所有初始化函数
  • 最后进入 run_main_loop 主循环(通常不会返回)

主要可以分为以下几类:

(1)基础系统初始化

  • initr_trace: 初始化跟踪系统
  • initr_reloc: 重定位处理
  • initr_caches: 缓存初始化(ARM架构)
  • initr_malloc: 初始化内存分配器
  • log_init: 初始化日志系统
  • initr_console_record: 控制台记录初始化

(2)硬件相关初始化

  • board_init: 板级初始化(设置芯片选择等)
  • initr_serial: 串口初始化
  • initr_watchdog: 看门狗初始化
  • power_init_board: 电源初始化
  • initr_flash: Flash 初始化
  • initr_nand: NAND 初始化
  • initr_mmc: MMC/SD 卡初始化

(3)驱动和总线初始化

  • initr_dm: 设备模型初始化
  • initr_pci: PCI 总线初始化
  • initr_scsi: SCSI 初始化
  • initr_ide: IDE 初始化

(4)网络相关

  • initr_ethaddr: 以太网地址初始化
  • initr_net: 网络初始化

(5)环境变量和配置

  • initr_env: 环境变量初始化
  • mac_read_from_eeprom: 从EEPROM读取MAC地址

(6)后期初始化

  • board_late_init: 板级后期初始化
  • last_stage_init: 最后阶段初始化

(7)最终阶段

  • run_main_loop: 进入主循环(处理命令等)
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
	sandbox_main_loop_init();
#endif
	/* main_loop() can return to retry autoboot, if so just run it again */
	for (;;)
		main_loop();
	return 0;
}

U-Boot 启动后会显示一个 3 秒的等待提示,在此期间如果用户按下回车键,系统将停留在 U-Boot 命令行界面;若倒计时结束仍无操作,则会自动执行预设命令启动 Linux 内核。这一交互逻辑是由 run_main_loop() 函数实现的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值