1. u-boot简介1.1. 概述U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目,从FADSROM、8xxROM、PPCBOOT逐步发展演化而来,其源码目录、编译形式与Linux内核很相似。事实上,不少U-Boot源码就是相应Linux内核源程序的简化,尤其是一些设备的驱动程序,从UBoot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS嵌入式操作系统。其目前要支持的目标操作系统包括OpenBSD、NetBSD、FreeBSD、4.4BSD、Linux、SVR4、Esix、Solaris、Irix、SCO、Dell、NCR、VxWorks、LynxOS、pSOS、QNX、RTEMS和ARTOS。这是U-Boot中Universal的一层含义。另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、Nios、xScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人,德国DENX软件工程中心的Wolfgang Denk(以下简称W.D)本人精湛的专业水平和持着不懈的努力。当前,U-Boot项目在他的领军下,众多有志于开放源码Bootloader移植工作的嵌入式开发人员,正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多嵌入式操作系统的装载与引导。 1.2. U-Boot主要目录结构 board——目标板相关文件,主要包含SDRAM、Flash驱动; common——独立于处理器体系结构的通用代码,如 内存大小探测与故障检测; cpu——与处理器相关的文件,如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件; driver——通用设备驱动,如CFI Flash驱动(目前对Intel Flash支持较好) doc——U-Boot的说明文档; examples——可在U-Boot下运行的示例程序;如 helloworld.c,timer.c; include——U-Boot头文件,configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件; lib_xxx——处理器体系相关的文件,如lib—ppc,lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件; net——与网络功能相关的文件目录,如bootp、nfs、tftp; post——上电自检文件目录,尚有待于进一步完善; rtc——RTC驱动程序; tools——用于创建U-Boot S-RECORD和BIN镜像文件的工具。 1.3. U-Boot支持的主要功能U-Boot可支持的主要功能如表1所列。 表格 1 系统引导 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统 支持NTS挂载,从Flash中引导压缩或非压缩系统内核 基本辅助功能 强大的操作系统接口功能,可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤其对Linux支持最为强劲 支持目标板环境参数的多种存储方式,如Flash、NVRAIvl、EEPROM CRC32校验,可校验Flash中内核、RAMDISK镜像文件是否完好 设备驱动 串口、SDRAM、Flash、以太网、LCD、NVRA/vl、EEPROM、键盘、JSB、PCMCIA、PCI、RTC等驱动支持 上电自检功能 SDRAM、Flash大小自动检测;SDRAM故障检测:CPLy型号 特殊功能 xⅢ内核引导
2. u-boot启动过程2.1. 启动模式Boot loader启动模式通常有两种:启动加载模式和下载模式。u-boot作为boot loader的一种也不例外。 启动加载模式:整个系统启动过程是自主的,完全没有人的干预。 下载模式:linux内核在加载时有开发人员控制,通过串口或者网络进行加载的方式。 对于普通用户来说,一般都用在启动加载模式,但是对于开发人员来数,则需要运行在下载模式下,因为开发过程中需要经常更新镜像。为了达到两者兼顾的目的,通常,采用两种模式相结合的方式。复位后,boot loader 首先进行初始化工作,结束后不是立刻加载linux镜像,而是等待一段时间,如果用户有键盘输入,就进入下载模式,否则进入启动加载模式。 2.2. 启动的阶段u-boot启动分为两个阶段。第一阶段主要针对与CPU体系结构和存储设备密切相关部分作一些必要的初始化工作,是一些依赖于CPU结构的代码,为了增加效率以及涉及到处理器的设置,只能采用汇编语言编写。第二阶段主要针对目标板一级的一些初始化工作并且提供一些驱动的支持,用c语言实现。
2.3. 启动代码分析首先我们整体上浏览下u-boot启动代码的流程图。
relocate
Stack_setup
Start_armboot()
Init_sequence[]
…… Getenv()
Cpu_init_crit
Main_loop()
Lowelevel_init.S
Reset
下面详细分析各个阶段中代码的具体工作。 1. Start.S 源码位置:cpu/arm920t/start.S 运行位置:Flash 描述:一个可执行的映像有且只有一个全局入口。这个入口点为处理器复位取第一条指令的位置。U-boot映像的入口就是start.S。这个可以通过 board/smdk2410/u-boot.lds连接脚本文件决定。
.globl _start _start: b reset //复位向量
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction _software_interrupt: .word software_interrupt _prefetch_abort: .word prefetch_abort _data_abort: .word data_abort _not_used:
.word not_used _irq: .word irq //中断向量 _fiq: .word fiq
//快速中断向量
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start _armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/ .globl _bss_start _bss_start:
.word __bss_start
.globl _bss_end _bss_end:
.word _end
#ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */ .globl FIQ_STACK_START FIQ_STACK_START:
.word 0x0badc0de #endif
/*
* the actual reset code
*/
reset: //复位启动子程序
/* 设置CPU 为SVC32模式 */
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/* 关闭看门狗 */ #if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */ #elif defined(CONFIG_S3C2410) # define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ #endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0] # if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0] # endif
/* FCLK:HCLKCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0] #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/* 关键的初始化代码在系统重启是执行,而在热复位也就是直接从RAM中启动不执行
*/ #ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit #endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* 把u-boot重新定位到RAM
*/
adr r0, _start
/* 把代码的当前位置也就是需要复制代码的起始地址放到r0 */
ldr r1, _TEXT_BASE /* 测试启动从Flash还是RAM */
cmp r0, r1 /* 比较r0和r1,调试的时候也就是都是从RAM启动*/ beq stack_setup /*的情况下,不需要重定位,跳过重定位代码 */
ldr r2, _armboot_start /*准备重新定位代码,得到_armboot_start的位置*/
ldr r3, _bss_start /*得到_bss_start的位置*/
sub r2, r3, r2
/*r2为arm_boot的大小 */
add r2, r0, r2
/* r2得到需要复制代码的末尾地址 */
copy_loop: /* 重新定位代码,也就是执行从代码从flash到RAM的拷贝工作*/
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop #endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* 初始化堆栈 */ stack_setup:
ldr r0, _TEXT_BASE /* 上面128KiB是重定位后的u-boot */
sub r0, r0, #CFG_MALLOC_LEN
/*向下是内存分配空间 */
sub r0, r0, #CFG_GBL_DATA_SIZE /*然后是bdinfo结构体地址空间 */ #ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif
sub sp, r0, #12 /* 为abort-stack预留3个字 */
clear_bss:
ldr r0, _bss_start /* 找到bss 段起始地址 */
ldr r1, _bss_end /* bss段的末尾地址 */
mov r2, #0x00000000 /* 清零 */
clbss_l:str r2, [r0] /* bss段地址空间循环清零 */
add r0, r0, #4
cmp r0, r1
ble clbss_l
/* 跳转到start_armboot函数入口, _start_armboot字保存函数的入口地址 */
ldr pc, _start_armboot
_start_armboot: .word start_armboot
#ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit: /*关键的初始化程序 */
/*
初始化CACHE
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* 关闭MMU 和 CACHE
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr #endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
*************************************************************************
*
* 中断向量
*
*************************************************************************
*/
@ @ IRQ stack frame. @ #define S_FRAME_SIZE 72
#define S_OLD_R0 68 #define S_PSR 64 #define S_PC 60 #define S_LR 56 #define S_SP 52
#define S_IP 48 #define S_FP 44 #define S_R10 40 #define S_R9 36 #define S_R8 32 #define S_R7 28 #define S_R6 24 #define S_R5 20 #define S_R4 16 #define S_R3 12 #define S_R2 8 #define S_R1 4 #define S_R0 0
#define MODE_SVC 0x13 #define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/*
*异常向量
*/
.align 5 undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5 software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5 prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5 data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5 not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5 irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5 fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5 irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5 fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif
|