2014.4版本uboot启动至命令行几个重要函数为:_start,_main,board_init_f,relocate_code,board_init_r。
_start在arch/arm/cpu/armv7/start.S中,一段一段的分析,如下:
- .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
- #ifdef CONFIG_SPL_BUILD
- _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
- _pad: .word 0x12345678
- #else
- .globl _undefined_instruction
- _undefined_instruction: .word undefined_instruction
- .globl _software_interrupt
- _software_interrupt: .word software_interrupt
- .globl _prefetch_abort
- _prefetch_abort: .word prefetch_abort
- .globl _data_abort
- _data_abort: .word data_abort
- .globl _not_used
- _not_used: .word not_used
- .globl _irq
- _irq: .word irq
- .globl _fiq
- _fiq: .word fiq
- _pad: .word 0x12345678
-
#endif
-
- .global _end_vect
- _end_vect:
-
- .balignl 16,0xdeadbeef
.global声明_start为全局符号,_start就会被连接器链接到,也就是链接脚本中的入口地址了。
以上代码是设置arm的异常向量表,arm异常向量表如下:
地址 |
异常 |
进入模式 |
描述 |
0x00000000 |
复位 |
管理模式 |
复位电平有效时,产生复位异常,程序跳转到复位处理程序处执行 |
0x00000004 |
未定义指令 |
未定义模式 |
遇到不能处理的指令时,产生未定义指令异常 |
0x00000008 |
软件中断 |
管理模式 |
执行SWI指令产生,用于用户模式下的程序调用特权操作指令 |
0x0000000c |
预存指令 |
中止模式 |
处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常 |
0x00000010 |
数据操作 |
中止模式 |
处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常 |
0x00000014 |
未使用 |
未使用 |
未使用 |
0x00000018 |
IRQ |
IRQ |
外部中断请求有效,且CPSR中的I位为0时,产生IRQ异常 |
0x0000001c |
FIQ |
FIQ |
快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常
|
8种异常分别占用4个字节,因此每种异常入口处都填写一条跳转指令,直接跳转到相应的异常处理函数中,reset异常是直接跳转到reset函数,其他7种异常是用ldr将处理函数入口地址加载到pc中。
后面汇编是定义了7种异常的入口函数,这里没有定义CONFIG_SPL_BUILD,所以走后面一个。
接下来定义的_end_vect中用.balignl来指定接下来的代码要16字节对齐,空缺的用0xdeadbeef,方便更加高效的访问内存。接着分析下面一段代码
- #ifdef CONFIG_USE_IRQ
-
- .globl IRQ_STACK_START
- IRQ_STACK_START:
- .word 0x0badc0de
-
-
- .globl FIQ_STACK_START
- FIQ_STACK_START:
- .word 0x0badc0de
- #endif
-
-
- .globl IRQ_STACK_START_IN
- IRQ_STACK_START_IN:
- .word 0x0badc0de
如果uboot中使用中断,这里声明中断处理函数栈起始地址,这里给出的值是0x0badc0de,是一个非法值,注释也说明了,这个值会在运行时重新计算,我查找了一下代码是在interrupt_init中。
- reset:
- bl save_boot_params
-
-
-
-
- mrs r0, cpsr
- and r1, r0, #0x1f @ mask mode bits
- teq r1, #0x1a @ test for HYP mode
- bicne r0, r0, #0x1f @ clear all mode bits
- orrne r0, r0, #0x13 @ set SVC mode
- orr r0, r0, #0xc0 @ disable FIQ and IRQ
- msr cpsr,r0
在上电或者重启后,处理器取得第一条指令就是b reset,所以会直接跳转到reset函数处。reset首先是跳转到save_boot_params中,如下:
-
-
-
-
-
-
-
-
-
- ENTRY(save_boot_params)
- bx lr @ back to my caller
- ENDPROC(save_boot_params)
- .weak save_boot_params
这里save_boot_params函数中没做什么直接跳回,注释也说明了,栈没有初始化,最好不要再函数中做操作。
这里值得注意的是.weak关键字,在网上找了到的解释,我的理解是.weak相当于声明一个函数,如果该函数在其他地方没有定义,则为空函数,有定义则调用该定义的函数。
具体解释可以看这位大神的详解:
http://blog.csdn.net/norains/article/details/5954459
接下来reset执行7条指令,修改cpsr寄存器,设置处理器进入svc模式,并且关掉irq和fiq。
save_boot_params_ret:
/*
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
-
-
-
-
-
- #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
-
- mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register
- bic r0, #CR_V @ V = 0
- mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register
-
-
- ldr r0, =_start
- mcr p15, 0, r0, c12, c0, 0 @Set VBAR
- #endif
-
-
- #ifndef CONFIG_SKIP_LOWLEVEL_INIT
- bl cpu_init_cp15
- bl cpu_init_crit
- #endif
-
- bl _main
前面6条汇编指令是对协处理器cp15进行操作,设置了处理器的异常向量入口地址为_start,
这里需要注意,ARM默认的异常向量表入口在0x0地址,uboot的运行介质(norflash nandflash sram等)映射地址可能不在0x0起始的地址,所以需要修改异常向量表入口。
但是我在网上没有找到cp15协处理器的c12寄存器的说明,可能是armv7新添加的
协处理器cp15的说明可以看我转的一篇文章:
http://blog.csdn.net/skyflying2012/article/details/25823967
接下来如果没有定义宏CONFIG_SKIP_LOWLEVEL_INIT,则会分别跳转执行cpu_init_cp15以及cpu_init_crit。
在分析这2个函数之前先总结一下上面分析的这一段_start中汇编的作用:
1 初始化异常向量表 2 设置cpu svc模式,关中断 3 配置cp15,设置异常向量入口
都是跟异常有关的部分。
接下来先分析cpu_init_cp15
-
-
-
-
-
-
-
-
- ENTRY(cpu_init_cp15)
-
-
-
- mov r0, #0 @ set up for MCR
- mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
- mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
- mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
- mcr p15, 0, r0, c7, c10, 4 @ DSB
- mcr p15, 0, r0, c7, c5, 4 @ ISB
-
-
-
-
- mrc p15, 0, r0, c1, c0, 0
- bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
- bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
- orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
- orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
- #ifdef CONFIG_SYS_ICACHE_OFF
- bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
- #else
- orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
- #endif
- mcr p15, 0, r0, c1, c0, 0
- #ifdef CONFIG_ARM_ERRATA_716044
- mrc p15, 0, r0, c1, c0, 0 @ read system control register
- orr r0, r0, #1 << 11 @ set bit #11
- mcr p15, 0, r0, c1, c0, 0 @ write system control register
- #endif
-
- #if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072))
- mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
- orr r0, r0, #1 << 4 @ set bit #4
- mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
- #endif
-
- #ifdef CONFIG_ARM_ERRATA_743622
- mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
- orr r0, r0, #1 << 6 @ set bit #6
- mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
- #endif
-
- #ifdef CONFIG_ARM_ERRATA_751472
- mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
- orr r0, r0, #1 << 11 @ set bit #11
- mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
- #endif
- #ifdef CONFIG_ARM_ERRATA_761320
- mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
- orr r0, r0, #1 << 21 @ set bit #21
- mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
- #endif
-
- mov pc, lr @ back to my caller
- ENDPROC(cpu_init_cp15)
cpu_init_cp15函数是配置cp15协处理器相关寄存器来设置处理器的MMU,cache以及tlb。如果没有定义CONFIG_SYS_ICACHE_OFF则会打开icache。关掉mmu以及tlb。
具体配置过程可以对照cp15寄存器来看,这里不详细说了
接下来看cpu_init_crit
-
-
-
-
-
-
-
-
- ENTRY(cpu_init_crit)
-
-
-
-
-
-
- b lowlevel_init @ go setup pll,mux,memory
- ENDPROC(cpu_init_crit)
看注释可以明白,cpu_init_crit调用的lowlevel_init函数是与特定开发板相关的初始化函数,在这个函数里会做一些pll初始化,如果不是从mem启动,则会做memory初始化,方便后续拷贝到mem中运行。
lowlevel_init函数则是需要移植来实现,做clk初始化以及ddr初始化
从cpu_init_crit返回后,_start的工作就完成了,接下来就要调用_main,总结一下_start工作:
1 前面总结过的部分,初始化异常向量表,设置svc模式,关中断
2 配置cp15,初始化mmu cache tlb
3 板级初始化,pll memory初始化
原文地址:
http://blog.csdn.net/skyflying2012/article/details/25804209