分析 u-boot 的第一阶段代码(cpu/arm920t/start.S)

uboot中start.S详解(zt)

 

#include 
#include 
/* 这段代码的主要作用是初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境 */
#if defined(CONFIG_OMAP1610)
#include <./configs/omap1510.h>
#elif defined(CONFIG_OMAP730)
#include <./configs/omap730.h>
#endif
/*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************/
.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
.balignl 16,0xdeadbeef
/************************************************分析****************************************************
* 从中我们可以看出,ARM支持7种异常。问题是发生了异常后ARM是如何响应的呢?
* 第一个复位异常很好理解,它放在0x0的位置,一上电就执行它,而且我们的程序总是从
* 复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。那么什么时候会执行
* 到后面几个异常处理函数呢?步骤是这样的:
*
* 当一个异常出现以后,ARM会自动执行以下几个步骤:
* (1) 把下一条指令的地址放到连接寄存器LR(通常是R14),这样就能够在处理异常返回时从正确的位置继续执行。
* (2) 将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中。从异常退出的时候,就可以由SPSR来恢复CPSR。
* (3) 根据异常类型,强制设置CPSR的运行模式位。
* (4) PC(程序计数器)被强制成相关异常向量处理函数地址,从而跳转到相应的异常处理程序中。
*
* 当异常处理完毕后,ARM会执行以下几步操作从异常返回:
* (1) 将连接寄存器LR的值减去相应的偏移量后送到PC中
* (2) 将SPSR复制回CPSR中
* (3) 若在进入异常处理时设置了中断禁止位,要在此清除
*
* ARM规定了异常向量的地址:
* b reset ; 复位 0x0
* ldr pc, _undefined_instruction ; 未定义的指令异常 0x4
* ldr pc, _software_interrupt ; 软件中断异常 0x8
* ldr pc, _prefetch_abort ; 预取指令 0xc
* ldr pc, _data_abort ; 数据 0x10
* ldr pc, _not_used ; 未使用 0x14
* ldr pc, _irq ; 慢速中断异常 0x18
* ldr pc, _fiq ; 快速中断异常 0x1c
* 这样理解这段代码就非常简单了。碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到
* 相应的处理程序,然后再返回到主程序继续执行。
*
* .balignl 16,0xdeadbeef, 将地址对齐到16的倍数,如果地址寄存器的值(PC)跳过4个字节才是16的倍数,
* 则使用0xdeadbeef填充这4个字节,如果它跳过1、2、3个字节,则填充值不确定。如果地址寄存器的值(PC)
* 是16的倍数,则无需移动。
********************************************************************************************************/
/*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram
* setup stack
*
*************************************************************************/
/* 保存变量的数据区 */
_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
/*************************************************分析**********************************************************
* 上面这段代码,主要保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者其它的使用。
* 还有一些变量的值是通过连接脚本得到的,比如TEXT_BASE位于/u-boot-1.1.6/board/xxx(开发板目录名称)/config.mk
* 文件里。__bss_start、_end位于/u-boot-1.1.6/board/xxx(开发板目录名称)/u-boot.lds文件里,具体值是由编译器算出来的。
***************************************************************************************************************/
/* the actual reset code*/
/* 系统的复位代码。系统一上电,就跳到这里运行 */
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr /* 取得当前程序状态寄存器cpsr到r0 */
bic r0,r0,#0x1f /* 这里使用位清除指令,把中断全部清除,只置位模式控制位 为中断提供服务通常是 OS
*设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。
*/
orr r0,r0,#0xd3 /* 计算为超级保护模式 */
msr cpsr,r0 /* 设置cpsr为超级保护模式 */
/***********************************************分析*************************************************************
* 设置cpu运行在SVC32模式。ARM共有7种模式:
* 用户模式(usr): arm处理器正常的程序执行状态
* 快速中断模式(fiq): 用于高速数据传输或通道处理
* 外部中断模式(irq): 用于通用的中断处理
* 超级保护模式(svc): 操作系统使用的保护模式
* 数据访问终止模式(abt): 当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护
* 系统模式(sys): 运行具有特权的操作系统任务
* 未定义指令中止模式(und): 当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真
*
* 通过设置ARM的CPSR寄存器,让CPU运行在操作系统保护模式,为后面进行其它操作作好准备了。
****************************************************************************************************************/
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
/******************************************************************************
* BL为相对寻址,以程序计数器PC 的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址
* ARM 指令集中的4条跳转指令可以完成从当前指令向前或向后的32MB 的地址空间的跳转,
* 用的是相对寻址,它们是:B、BL、BLX、BX
*******************************************************************************/
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
/* 重定位Boot代码到RAM内存,将Boot代码从FLASH移到RAM中 */
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
/**************************************************************************
* 把_start的相对地址移到r0, 相对寻址以程序计数器PC 的当前值为基地址,
* 指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。
* 它是与位置无关的,主要看Boot在哪里运行,也就是PC指针在哪里 (假设_start偏移量为0),
* 例如这段代码在 0x02000000 (FLASH起始地址)运行,即此时PC=0x02000000,那么 adr r0, _start 得到 r0 = 0x02000000;
* 如果在地址 0x81008000(Boot在RAM中加载地址)运行,即此时PC=0x81008000,那么r0就是 0x81008000 了。
*
* 此处要注意ldr与adr的区别,看下面的代码片段:
* ldr r0, _start
* adr r0, _start
* ldr r0, =_start
* nop
* mov pc, lr
* _start:
* nop
* 下面是反汇编的结果:
* 0c008000 <_start-0x14>:
* c008000: e59f000c ldr r0, [pc, #12] ; c008014 <_start>
* c008004: e28f0008 add r0, pc, #8 ; 0x8
* c008008: e59f0008 ldr r0, [pc, #8] ; c008018 <_start+0x4>
* c00800c: e1a00000 nop (mov r0,r0)
* c008010: e1a0f00e mov pc, lr
*
* 0c008014 <_start>:
* c008014: e1a00000 nop (mov r0,r0)
*
* 分析:
* ldr r0, _start
* 从内存地址 _start 的地方把值读入。执行这个后,r0 = 0xe1a00000
*
* adr r0, _start
* 取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的是相对的位置。例如这段代码在 0x0c008000 运行,
* 那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。即当前PC值加上_start的偏移量。
*
* ldr r0, =_start
* 这个取得标号 _start 的绝对地址。这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,
* 一条是指令,另一条是 _start 的数据(因为在编译的时候不能确定 _start 的值,所以不能直接用 mov 指令来给 r0 赋一个 32bit 的常量,
* 所以需要多出一个空间存放 _start 的真正数据,这个数据是在 link 的时候确定的,在这里就是 0x0c008014)。
* 因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x0c008014
**************************************************************************/
ldr r1, _TEXT_BASE/* test if we run from flash or RAM */ /* 把_TEXT_BASE地址处的值TEXT_BASE,也就是BOOT在RAM中运行地址移到r1 */
cmp r0, r1 /* don't reloc during debug */ /* 比较两个地址是否相同,如果相同,就已经在RAM运行,否则就是FLASH中运行 */
beq stack_setup
/* 如果是在FLASH中运行, 则把FLASH中的Boot代码移到RAM中,然后再运行 */
ldr r2, _armboot_start /* 把_armboot_start地址处的值也就是_start绝对地址(也即在内存中的地址,这个绝对
* 地址是在 link 的时候确定的,如0x81008000)移到r2 */
ldr r3, _bss_start /* 把_bss_start地址处的值也就是__bss_start绝对地址(也即在内存中的地址,这个绝对
* 地址是在 link 的时候确定的)移到r3 */
sub r2, r3, r2 /* r2 <- size of armboot */ /* 计算引导代码大小并存到r2 */
add r2, r0, r2 /* r2 <- source end address */ /* 计算引导代码最后相对地址并存入r2 */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */ /* 从源地址[r0]读取32个字节到寄存器,并更新r0 */
stmia r1!, {r3-r10} /* copy to target address [r1] */ /* 拷贝寄存器r3-r10的32个字节值保存到 [r1]指明的地址,并更新r1的值 */
cmp r0, r2 /* until source end addreee [r2] */ /* 循环拷贝,直到把所有引导代码都移到内存 */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack */
/* 在内存中建立起堆栈 */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
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 /* leave 3 words for abort-stack */
/* 初始化内存中bss段中数据为0 */
clear_bss:
ldr r0, _bss_start /* find start of bss segment*/ /* 把_bss_start地址处存储的绝对地址移到r0 */
ldr r1, _bss_end /* stop here */ /* 把_bss_end地址处存储的绝对地址移到r1 */
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... STR 指令用于从源寄存器中r2将一个32 位的字数据传送到存储器中[r0]*/
add r0, r0, #4
cmp r0, r1
ble clbss_l /* 小于或等于跳转 */
ldr pc, _start_armboot /***********************************************************
* 已经准备好了堆栈,就可跳到C写的代码里了,也就是
* 跳到内存中的/u-boot-1.1.6/board.c --> start_armboot中运行了
* 把_start_armboot地址处的值也就是start_armboot绝对地址值移到pc
* 神啊!终于跳到C代码了。
***********************************************************/
_start_armboot:
.word start_armboot
/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
/**************************************************************************
* 1、关闭 MMU和CPU 内部指令/数据 (I/D)cache。
* 2、设置 CPU 的速度和时钟频率。
* 3 、RAM 初始化。
****************************************************************************/
cpu_init_crit:
/* flush v4 I/D caches*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
/******************************************************************************************************
* MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,格式为:
* MCR 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
* 其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,
* 源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。
******************************************************************************************************/
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/ * disable MMU stuff and caches*/
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
/ * Go setup Memory and board specific bits prior to relocation.*/
mov ip, lr /* perserve link reg across call */
bl lowlevel_init /* go setup pll,mux,memory */ /* 位于u-boot-1.1.6/board/xxx(开发板目录名称)/lowlevel_init.S */
mov lr, ip /* restore link */
mov pc, lr /* back to my caller */ /* 从cpu_init_crit子函数返回 */
/*************************************************************************
*
* Interrupt handling
*
*************************************************************************/
@ 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
@ carve out a frame on current user stack
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Save user registers (now in svc mode) 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
@ get values for "aborted" pc and cpsr (into parm regs)
ldmia r2, {r2 - r3}
add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp @ save current stack into r0 (param register)
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
@ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
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 in position 0 of saved stack
mrs lr, spsr @ get the spsr
str lr, [r13, #4] @ save spsr in position 1 of saved stack
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13 @ switch modes, make sure moves will execute
mov lr, pc @ capture return pc
movs pc, lr @ jump to next instruction & switch modes.
.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
/*
* exception handlers
*/
.align 5 /*‘.align 5’向后移动位置计数器直至32(2^5)的倍数(计数器的最低的5位为0)。如果地址已经是32倍数,则无需移动。*/
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 


 

来自: http://hi.baidu.com/hannongy/blog/item/85e98d1980d6f50034fa41b6.html

你可能感兴趣的:(arm,Linux)