U-boot启动流程分析 Start.s 汇编文件

时间:2020-11-30 16:45:49

2014.4版本uboot启动至命令行几个重要函数为:_start,_main,board_init_f,relocate_code,board_init_r

_start在arch/arm/cpu/armv7/start.S中,一段一段的分析,如下:

[cpp]  view plain  copy   U-boot启动流程分析 Start.s 汇编文件 U-boot启动流程分析 Start.s 汇编文件
  1. .globl _start
  2. _start: b   reset  
  3.     ldr pc, _undefined_instruction  
  4.     ldr pc, _software_interrupt  
  5.     ldr pc, _prefetch_abort  
  6.     ldr pc, _data_abort  
  7.     ldr pc, _not_used  
  8.     ldr pc, _irq  
  9.     ldr pc, _fiq  
  10. #ifdef CONFIG_SPL_BUILD  
  11. _undefined_instruction: .word _undefined_instruction  
  12. _software_interrupt:    .word _software_interrupt  
  13. _prefetch_abort:    .word _prefetch_abort  
  14. _data_abort:        .word _data_abort  
  15. _not_used:      .word _not_used  
  16. _irq:           .word _irq  
  17. _fiq:           .word _fiq  
  18. _pad:           .word 0x12345678 /* now 16*4=64 */  
  19. #else  
  20. .globl _undefined_instruction  
  21. _undefined_instruction: .word undefined_instruction  
  22. .globl _software_interrupt  
  23. _software_interrupt:    .word software_interrupt  
  24. .globl _prefetch_abort  
  25. _prefetch_abort:    .word prefetch_abort  
  26. .globl _data_abort  
  27. _data_abort:        .word data_abort  
  28. .globl _not_used  
  29. _not_used:      .word not_used  
  30. .globl _irq  
  31. _irq:           .word irq   
  32. .globl _fiq  
  33. _fiq:           .word fiq   
  34. _pad:           .word 0x12345678 /* now 16*4=64 */
  35. #endif  /* CONFIG_SPL_BUILD */  
  36.   
  37. .global _end_vect  
  38. _end_vect:  
  39.   
  40.     .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,方便更加高效的访问内存。接着分析下面一段代码

[cpp]  view plain  copy   U-boot启动流程分析 Start.s 汇编文件 U-boot启动流程分析 Start.s 汇编文件
  1. #ifdef CONFIG_USE_IRQ  
  2. /* IRQ stack memory (calculated at run-time) */  
  3. .globl IRQ_STACK_START  
  4. IRQ_STACK_START:  
  5.     .word   0x0badc0de  
  6.   
  7. /* IRQ stack memory (calculated at run-time) */  
  8. .globl FIQ_STACK_START  
  9. FIQ_STACK_START:  
  10.     .word 0x0badc0de  
  11. #endif  
  12.   
  13. /* IRQ stack memory (calculated at run-time) + 8 bytes */  
  14. .globl IRQ_STACK_START_IN  
  15. IRQ_STACK_START_IN:  
  16.     .word   0x0badc0de  

如果uboot中使用中断,这里声明中断处理函数栈起始地址,这里给出的值是0x0badc0de,是一个非法值,注释也说明了,这个值会在运行时重新计算,我查找了一下代码是在interrupt_init中。

[cpp]  view plain  copy   U-boot启动流程分析 Start.s 汇编文件 U-boot启动流程分析 Start.s 汇编文件
  1. reset:  
  2.     bl  save_boot_params  
  3.     /* 
  4.      * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, 
  5.      * except if in HYP mode already 
  6.      */  
  7.     mrs r0, cpsr  
  8.     and r1, r0, #0x1f       @ mask mode bits  
  9.     teq r1, #0x1a       @ test for HYP mode  
  10.     bicne   r0, r0, #0x1f       @ clear all mode bits  
  11.     orrne   r0, r0, #0x13       @ set SVC mode  
  12.     orr r0, r0, #0xc0       @ disable FIQ and IRQ  
  13.     msr cpsr,r0  
在上电或者重启后,处理器取得第一条指令就是b reset,所以会直接跳转到reset函数处。reset首先是跳转到save_boot_params中,如下:

[cpp]  view plain  copy   U-boot启动流程分析 Start.s 汇编文件 U-boot启动流程分析 Start.s 汇编文件
  1. /************************************************************************* 
  2.  * 
  3.  * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) 
  4.  *  __attribute__((weak)); 
  5.  * 
  6.  * Stack pointer is not yet initialized at this moment 
  7.  * Don't save anything to stack even if compiled with -O0 
  8.  * 
  9.  *************************************************************************/  
  10. ENTRY(save_boot_params)  
  11.     bx  lr          @ back to my caller  
  12. ENDPROC(save_boot_params)  
  13.     .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


[cpp]  view plain  copy   U-boot启动流程分析 Start.s 汇编文件 U-boot启动流程分析 Start.s 汇编文件
  1. /* 
  2.  * Setup vector: 
  3.  * (OMAP4 spl TEXT_BASE is not 32 byte aligned. 
  4.  * Continue to use ROM code vector only in OMAP4 spl) 
  5.  */  
  6. #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))  
  7.     /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */  
  8.     mrc p15, 0, r0, c1, c0, 0   @ Read CP15 SCTRL Register  
  9.     bic r0, #CR_V       @ V = 0  
  10.     mcr p15, 0, r0, c1, c0, 0   @ Write CP15 SCTRL Register  
  11.   
  12.     /* Set vector address in CP15 VBAR register */  
  13.     ldr r0, =_start  
  14.     mcr p15, 0, r0, c12, c0, 0  @Set VBAR  
  15. #endif  
  16.   
  17.     /* the mask ROM code should have PLL and others stable */  
  18. #ifndef CONFIG_SKIP_LOWLEVEL_INIT  
  19.     bl  cpu_init_cp15  
  20.     bl  cpu_init_crit  
  21. #endif  
  22.   
  23.     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

[cpp]  view plain  copy   U-boot启动流程分析 Start.s 汇编文件 U-boot启动流程分析 Start.s 汇编文件
  1. /************************************************************************* 
  2.  * 
  3.  * cpu_init_cp15 
  4.  * 
  5.  * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless 
  6.  * CONFIG_SYS_ICACHE_OFF is defined. 
  7.  * 
  8.  *************************************************************************/  
  9. ENTRY(cpu_init_cp15)  
  10.     /* 
  11.      * Invalidate L1 I/D 
  12.      */  
  13.     mov r0, #0          @ set up for MCR  
  14.     mcr p15, 0, r0, c8, c7, 0   @ invalidate TLBs  
  15.     mcr p15, 0, r0, c7, c5, 0   @ invalidate icache  
  16.     mcr p15, 0, r0, c7, c5, 6   @ invalidate BP array  
  17.     mcr     p15, 0, r0, c7, c10, 4  @ DSB  
  18.     mcr     p15, 0, r0, c7, c5, 4   @ ISB  
  19.   
  20.     /* 
  21.      * disable MMU stuff and caches 
  22.      */  
  23.     mrc p15, 0, r0, c1, c0, 0  
  24.     bic r0, r0, #0x00002000 @ clear bits 13 (--V-)  
  25.     bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)  
  26.     orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align  
  27.     orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB  
  28. #ifdef CONFIG_SYS_ICACHE_OFF  
  29.     bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache  
  30. #else  
  31.     orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache  
  32. #endif  
  33.     mcr p15, 0, r0, c1, c0, 0  
  34. #ifdef CONFIG_ARM_ERRATA_716044  
  35.     mrc p15, 0, r0, c1, c0, 0   @ read system control register  
  36.     orr r0, r0, #1 << 11    @ set bit #11  
  37.     mcr p15, 0, r0, c1, c0, 0   @ write system control register  
  38. #endif  
  39.   
  40. #if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072))  
  41.     mrc p15, 0, r0, c15, c0, 1  @ read diagnostic register  
  42.     orr r0, r0, #1 << 4     @ set bit #4  
  43.     mcr p15, 0, r0, c15, c0, 1  @ write diagnostic register  
  44. #endif  
  45.   
  46. #ifdef CONFIG_ARM_ERRATA_743622  
  47.     mrc p15, 0, r0, c15, c0, 1  @ read diagnostic register  
  48.     orr r0, r0, #1 << 6     @ set bit #6  
  49.     mcr p15, 0, r0, c15, c0, 1  @ write diagnostic register  
  50. #endif  
  51.   
  52. #ifdef CONFIG_ARM_ERRATA_751472  
  53.     mrc p15, 0, r0, c15, c0, 1  @ read diagnostic register  
  54.     orr r0, r0, #1 << 11    @ set bit #11  
  55.     mcr p15, 0, r0, c15, c0, 1  @ write diagnostic register  
  56. #endif  
  57. #ifdef CONFIG_ARM_ERRATA_761320  
  58.     mrc p15, 0, r0, c15, c0, 1  @ read diagnostic register  
  59.     orr r0, r0, #1 << 21    @ set bit #21  
  60.     mcr p15, 0, r0, c15, c0, 1  @ write diagnostic register  
  61. #endif  
  62.   
  63.     mov pc, lr          @ back to my caller  
  64. ENDPROC(cpu_init_cp15)  


cpu_init_cp15函数是配置cp15协处理器相关寄存器来设置处理器的MMU,cache以及tlb。如果没有定义CONFIG_SYS_ICACHE_OFF则会打开icache。关掉mmu以及tlb。
具体配置过程可以对照cp15寄存器来看,这里不详细说了

接下来看cpu_init_crit

[cpp]  view plain  copy   U-boot启动流程分析 Start.s 汇编文件 U-boot启动流程分析 Start.s 汇编文件
  1. /************************************************************************* 
  2.  * 
  3.  * CPU_init_critical registers 
  4.  * 
  5.  * setup important registers 
  6.  * setup memory timing 
  7.  * 
  8.  *************************************************************************/  
  9. ENTRY(cpu_init_crit)  
  10.     /* 
  11.      * Jump to board specific initialization... 
  12.      * The Mask ROM will have already initialized 
  13.      * basic memory. Go here to bump up clock rate and handle 
  14.      * wake up conditions. 
  15.      */  
  16.     b   lowlevel_init       @ go setup pll,mux,memory  
  17. 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