U-Boot ARM汇编启动代码分析
文件概述
文件路径: arch/arm/cpu/armv7/start.S
目标平台: ARM Cortex-A系列处理器
功能: U-Boot bootloader的启动汇编代码
版本: U-Boot 2012.10
文件结构概览
start.S
├── 头文件包含和宏定义
├── 中断向量表 (_start)
├── 全局符号定义
├── 复位代码 (reset)
├── 代码重定位 (relocate_code)
├── CPU初始化函数
├── 异常处理函数
└── 栈和内存管理
1. 头文件包含部分 (第31-35行)
#include <asm-offsets.h>
#include <config.h>
#include <version.h>
#include <asm/system.h>
#include <linux/linkage.h>
作用:
asm-offsets.h
: 汇编偏移量定义config.h
: 配置选项定义version.h
: 版本信息asm/system.h
: ARM系统相关定义linux/linkage.h
: 函数链接宏定义
2. 中断向量表 (第37-48行)
.globl _start
_start: b reset # 复位向量 - 系统启动入口
ldr pc, _undefined_instruction # 未定义指令异常
ldr pc, _software_interrupt # 软件中断(SWI/SVC)
ldr pc, _prefetch_abort # 指令预取异常
ldr pc, _data_abort # 数据访问异常
ldr pc, _not_used # 保留向量
ldr pc, _irq # 普通中断请求
ldr pc, _fiq # 快速中断请求
重要说明:
- ARM处理器复位后首先执行
_start
标签 - 这是ARM架构标准的8个32位向量表
ldr pc, label
指令将标签地址加载到程序计数器,实现跳转- 向量表必须位于地址0x00000000或0xFFFF0000
3. 向量地址定义 (第49-62行)
根据CONFIG_SPL_BUILD
配置选择不同的向量处理方式:
SPL构建模式:
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
# ... 其他向量指向自身,形成死循环
正常构建模式:
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
# ... 向量指向实际的异常处理函数
4. 系统复位代码 (reset标签, 第125行开始)
4.1 保存启动参数
reset:
bl save_boot_params # 保存bootloader传递的参数
4.2 CPU模式设置
mrs r0, cpsr # 读取当前程序状态寄存器
bic r0, r0, #0x1f # 清除模式位[4:0]
orr r0, r0, #0xd3 # 设置SVC32模式,禁用IRQ和FIQ
msr cpsr,r0 # 写回CPSR
模式说明:
0xd3 = 11010011 (binary)
- Bit[4:0] = 10011 (SVC32模式)
- Bit[7] = 1 (禁用IRQ)
- Bit[6] = 1 (禁用FIQ)
4.3 向量基地址设置
mrc p15, 0, r0, c1, c0, 0 # 读取系统控制寄存器
bic r0, #CR_V # 清除V位,使VBAR有效
mcr p15, 0, r0, c1, c0, 0 # 写回控制寄存器
ldr r0, =_start # 加载向量表基地址
mcr p15, 0, r0, c12, c0, 0 # 设置VBAR寄存器
4.4 低级初始化调用
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15 # 初始化协处理器CP15
bl cpu_init_crit # 关键CPU初始化
#endif
5. CPU协处理器初始化 (cpu_init_cp15)
5.1 缓存和TLB无效化
mov r0, #0
mcr p15, 0, r0, c8, c7, 0 # 无效化所有TLB
mcr p15, 0, r0, c7, c5, 0 # 无效化指令缓存
mcr p15, 0, r0, c7, c5, 6 # 无效化分支预测器
mcr p15, 0, r0, c7, c10, 4 # 数据同步屏障(DSB)
mcr p15, 0, r0, c7, c5, 4 # 指令同步屏障(ISB)
5.2 系统控制寄存器配置
mrc p15, 0, r0, c1, c0, 0 # 读取SCTLR寄存器
bic r0, r0, #0x00002000 # 清除V位(向量表高地址)
bic r0, r0, #0x00000007 # 清除C、A、M位
orr r0, r0, #0x00000002 # 设置A位(对齐检查)
orr r0, r0, #0x00000800 # 设置Z位(分支预测)
orr r0, r0, #0x00001000 # 设置I位(指令缓存)
mcr p15, 0, r0, c1, c0, 0 # 写回SCTLR
控制位说明:
- M位(bit 0): MMU使能 (此时禁用)
- A位(bit 1): 对齐检查使能
- C位(bit 2): 数据缓存使能 (此时禁用)
- I位(bit 12): 指令缓存使能
- V位(bit 13): 向量表位置 (0=低地址,1=高地址)
- Z位(bit 11): 分支预测使能
6. 堆栈设置和板级初始化
call_board_init_f:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) # 设置初始堆栈指针
bic sp, sp, #7 # 8字节对齐
ldr r0,=0x00000000 # 清零参数
bl board_init_f # 调用C语言板级初始化
7. 代码重定位过程 (relocate_code)
7.1 参数保存和堆栈设置
ENTRY(relocate_code)
mov r4, r0 # 保存堆栈指针地址
mov r5, r1 # 保存全局数据指针
mov r6, r2 # 保存目标地址
stack_setup:
mov sp, r4 # 设置堆栈指针
7.2 重定位检查
adr r0, _start # 获取当前运行地址
cmp r0, r6 # 与目标地址比较
moveq r9, #0 # 如果相同,无需重定位
beq clear_bss # 跳转到BSS清零
7.3 代码复制循环
copy_loop:
ldmia r0!, {r9-r10} # 批量加载8字节数据
stmia r1!, {r9-r10} # 批量存储到目标地址
cmp r0, r2 # 检查是否完成
blo copy_loop # 继续循环
7.4 动态重定位修复
fixloop:
ldr r0, [r2] # 读取重定位地址
add r0, r0, r9 # 添加重定位偏移
ldr r1, [r2, #4] # 读取重定位信息
and r7, r1, #0xff # 获取重定位类型
cmp r7, #23 # 相对重定位?
beq fixrel
cmp r7, #2 # 绝对重定位?
beq fixabs
8. 异常处理框架
8.1 寄存器保存宏
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE # 在堆栈上开辟异常帧
stmia sp, {r0 - r12} # 保存通用寄存器
# ... 保存CPSR、PC、LR等状态寄存器
.endm
8.2 异常处理函数
undefined_instruction:
get_bad_stack # 获取异常处理堆栈
bad_save_user_regs # 保存用户寄存器
bl do_undefined_instruction # 调用C处理函数
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort # 处理指令预取异常
9. 关键内存布局
9.1 符号定义
.globl _TEXT_BASE
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE # 代码段基地址
.globl _bss_start_ofs
_bss_start_ofs:
.word __bss_start - _start # BSS段起始偏移
.globl _bss_end_ofs
_bss_end_ofs:
.word __bss_end__ - _start # BSS段结束偏移
9.2 堆栈空间
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de # IRQ堆栈起始地址
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de # FIQ堆栈起始地址
10. 启动流程总结
11. 重要配置宏
宏定义 | 作用 |
---|---|
CONFIG_SPL_BUILD |
SPL(Secondary Program Loader)构建模式 |
CONFIG_SYS_TEXT_BASE |
代码段基地址 |
CONFIG_SYS_INIT_SP_ADDR |
初始堆栈指针地址 |
CONFIG_SKIP_LOWLEVEL_INIT |
跳过低级初始化 |
CONFIG_SYS_ICACHE_OFF |
禁用指令缓存 |
CONFIG_USE_IRQ |
启用中断支持 |
12. 调试要点
- 向量表位置: 确保向量表正确对齐和定位
- 堆栈设置: 检查堆栈指针是否指向有效内存
- 重定位过程: 验证代码是否正确复制到RAM
- 缓存一致性: 确保指令和数据缓存同步
- MMU状态: 初期MMU应该关闭
13. 常见问题
Q1: 系统启动后卡在向量表?
A: 检查向量表地址是否正确设置,VBAR寄存器配置是否有误。
Q2: 重定位后程序崩溃?
A: 检查重定位地址计算、动态链接修复是否正确。
Q3: 异常处理不工作?
A: 确认异常向量表、堆栈设置、处理函数地址是否正确。