从《链接文件u-boot.lds分析》中我们看到链接脚本的代码段链接的第一个文件是arch/arm/cpu/hi3521a/start.o,也就是说程序运行最开始是从start.S文件开始执行的。 start.S在这里完成uboot的第一阶段的启动,它的内容包括:
设置CPU SVC模式关闭mmu和缓存启动流程判断关闭地址重映射使能指令缓存重定向异常向量表到内部RAM重定向uboot到外部DDR设置栈空间清除bss段跳转到C程序入口
1.设置CPU SVC模式
1.1头文件包含
#include
#include
头文件包含,这里的config.h 是/include/config.h 这个文件不是原来就有的,是uboot配置的的时候生成的它的内容是:#include #include #include 这里面都是一些条件编译的宏,根据不同的设备配置不同而不同。include/version.h中包含了include/version_autogenerated.h,这个头文件就是配置过程中自动生成的。版本号信息来自于Makefile中的配置值在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这来的。
1.2程序入口
.globl _start
_start: b reset
由uboot.lds链接脚本,我们知道整个程序的入口取决于中ENTRY声明的地方。在uboot.lds中有ENTRY(_start),因此_start符号所在的文件就是整个程序的起始文件,_start所在的代码就是整个程序的起始代码。.globl XX 语法:给XX外部连接的属性,一般为了在别的文件中引用这个符号_start后面加上一个冒号' :',表示_start是一个标号_start: b reset 程序开始,跳到reset标号去执行,执行完后不返回
1.3异常向量表的构建
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
LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。以第一个_undefined_instruction为例,就是将地址为_undefined_instruction中的一个word的值,赋值给pc
_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
以_undefined_instruction为例,就是,此处分配了一个word=32bit=4字节的地址空间,里面存放的值是undefined_instruction。在后面的代码,我们可以看到,undefined_instruction也是一个标号,即一个地址值,对应着就是在发生“未定义指令”的时候,系统所要去执行的代码。这里定义的中断分别是:
未定义指令异常,0x04软中断异常,0x08内存操作异常,0x0c数据异常,0x10未适用,0x14慢速中断异常,0x18快速中断异常,0x1c,
1.4设置标号地址
_pad: .word 0x12345678 /* now 16*4=64 */
_pad是一个标号,没有看到有地方使用它
__blank_zone_start:
.fill 1024*4,1,0
__blank_zone_end:
反复拷贝1个字节的0,拷贝1024*4次,也就是填充4KB的0数据通过反汇编我们知道__blank_zone_start的值为0x80800040,__blank_zone_end的值为0x80801040,可以知道它的范围刚好是0x1000=4096=4KB
.globl _blank_zone_start
_blank_zone_start:
.word __blank_zone_start
.globl _blank_zone_end
_blank_zone_end:
.word __blank_zone_end
设置外部引用标号:_blank_zone_start,_blank_zone_end,这样他们就可以在其它汇编和C语言中被调用
.balignl 16,0xdeadbeef
设置16字节对齐,如果没有16字节对齐,则使用0xdeadbeef中的数值来填充。这个数值没有什么特别的意义,只是对应英文单词deadbeef(坏牛肉)
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
TEXT_BASE就是Makefile配置阶段写入的TEXT_BASE值TEXT_BASE = 0x80800000 在/board/hi3521a/config.mk中定义这个是指明链接地址的值,也就是程序应该开始运行的地址,在uboot重定位的时候会使用到。经过链接脚本链接过后_armboot_start的地址与_start的地址都等于0x80800000
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
这里将bss段的开始地址和结束地址设置一个外部标号,bss段的开始位置和结束位置在这里是不确定的,通过u-boot.lds可知,bss的位置与前面的代码段和数据段的长度有关 通过反汇编,我们查看到_bss_start=0x8084abc4,_bss_end=0x8089f700
#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
CONFIG_USE_IRQ 在海思设备上并没有使用中断所以上面这段代码无效
_clr_remap_fmc_entry:
.word FMC_TEXT_ADRS + do_clr_remap - TEXT_BASE
FMC 是flash memory controller FMC_TEXT_ADRS=FMC_MEM_BASE=0x14000000TEXT_BASE = 0x80800000 在/board/hi3521a/config.mk中定义 芯片手册上描述:SFC NAND/NOR MEMORY起始地址为0x14000000,大小为16MBDDR 存储地址空间起始地址为:0x8000_0000,大小为2GB通过反汇编我们查看到do_clr_remap=0x8080113c_clr_remap_fmc_entry=0x1400113c这里是指向了SFC NAND/NOR MEMORY地址空间
1.5.设置cpu SVC模式
/*
* the actual reset code
*/
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
将程序状态寄存器(cpsr)的值存到寄存器r0将r0寄存器的低五位清零将r0与0xd3或之后赋值给r0将寄存器r0里的值赋值程序状态寄存器(cpsr)上面的这四句命令是将CPU设置为禁止FIQ IRQ,ARM状态,SVC模式
2.关闭mmu和缓存
/*
* Invalidate L1 I/D
*/
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
关闭一级缓存的指令和数据缓存功能
/*
* disable MMU stuff and caches
*/
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
mcr p15, 0, r0, c1, c0, 0
关闭MMU功能
3.启动流程判断
3.1正常模式流程
/*
* read system register REG_SC_GEN2
* check if ziju flag
*/
ldr r0, =SYS_CTRL_REG_BASE
ldr r1, [r0, #REG_SC_GEN2]
ldr r2, =0x7a696a75 /* magic for "ziju" */
cmp r1, r2
bne normal_start_flow
将SYS_CTRL_REG_BASE这个宏表示的值赋值给r0寄存器将r0寄存器的值加上REG_SC_GEN2宏地址上的值赋值给r1寄存器将0x7a696a75 赋值给r2将r1与r2寄存器中的值做对比如果不相等 执行函数normal_start_flow,执行完之后再返回这里如果相等,继续往下执行,将sp寄存器的值赋值给r1寄存器将r1寄存器的值存到r0+REG_SC_GEN2值表示的地址中去这里SYS_CTRL_REG_BASE=0x12050000,REG_SC_GEN2=0x0140,这个寄存器的功能海思的官方手册上并没有给出说明,所以这里不好猜测它主要是做什么的,但是基本上重启都是会进入normal_start_flow模式的。
3.2ziju模式流程
mov r1, sp /* save sp */
str r1, [r0, #REG_SC_GEN2] /* clear ziju flag */
/* init PLL/DDRC/pin mux/... */
ldr r0, _blank_zone_start
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =RAM_START_ADRS
add r0, r0, r1
mov r1, #0x0 /* flags: 0->normal 1->pm */
bl init_registers /* init PLL/DDRC/... */
/* after ziju, we need ddr traning */
#ifdef CONFIG_DDR_TRAINING_V2
ldr sp, =STACK_TRAINING
ldr r0, =REG_BASE_SCTL
bl start_ddr_training /* DDR training */
#endif
ldr r0, =SYS_CTRL_REG_BASE
ldr r1, [r0, #REG_SC_GEN2]
mov sp, r1 /* restore sp */
ldr r1, [r0, #REG_SC_GEN3]
mov pc, r1 /* return to bootrom */
nop
nop
nop
nop
nop
nop
nop
nop
b . /* bug here */
海思在\u-boot-2010.06\arch\arm\include\asm\arch-hi3521a\platform.h中有定义:#define REG_SC_GEN0 0x0138#define REG_SC_GEN1 0x013c#define REG_SC_GEN2 0x0140#define REG_SC_GEN3 0x0144#define REG_SC_GEN4 0x0148但是海思的用户手册上并没有给出这几个寄存器的描述,因此从代码上并不能知道面的自举操作实现了些什么功能。从代码上来猜测,RAM_START_ADRS是内部SRAM的开始地址,__blank_zone_start的大小是4K,在hi3520dv400上这个值是5k。综上猜测,这里的ziju模式应该是使用HiBurn进行uboot升级的时候,先将4K或是5k的uboot代码下载到内部SRAM,然后再通过下载的前面一点点uboot代码,初始化ddr,初始化DDR之后,再将所有的uboot程序下载到ddr中,从ddr中使用命令进行uboot烧入flash的操作。
通过这里也可以分析出两个问题:
使用hiburn升级uboot的时候,如果只升级了4~5k程序(通过进度判断)就提示失败,那有可能是ddr初始化失败了。所升级的uboot程序对DDR的操作有问题,导致DDR不能正常被初始化。
3.3.正常启动
normal_start_flow:
@if running not boot from spi/nand/ddr ram,
@we skipping boot_type checking.
mov r0, pc, lsr#24
cmp r0, #0x0
bne do_clr_remap
将PC寄存器的值逻辑右移24位,然后再赋值给r0寄存器将r0寄存器的值与0做对比如果r0寄存器的值不等于零,则执行do_clr_remap函数,执行完后不返回。海思hi3521a的uboot有三种种启动方式:spi nor/nand flash内部RAM外部DDR启动 PC指针地址逻辑右移24位等于0,也就是只有PC指针的地址小于或等于0x8FFFFF时才成立,这种情况应该只有在内部RAM中运行才会出现。正常启动的时候,海思hi3521a芯片这里的PC指针是指向spi flash的地址空间范围0x1400_0000-0x1400_FFFF。
3.4检查启动类型
check_boot_type:
ldr r0, =SYS_CTRL_REG_BASE
ldr r0, [r0, #REG_SYSSTAT]
mov r6, r0, lsr#4
and r6, #0x1
cmp r6, #0 @ [4] = 0 FMC /* spi nor | spi nand */
ldreq pc, _clr_remap_fmc_entry
@otherwise, [31]=1 means boot from bootrom, err
beq bug
通过SYSSTAT寄存器的bootrom_sel控制启动类型。如果是spi nor或是spi nand 启动,程序会进入到这里执行,如果是bootrom启动则不会。正常这里应该是不会进来执行的。
4.关闭地址重映射
do_clr_remap:
/* do clear remap */
ldr r4, =SYS_CTRL_REG_BASE
ldr r0, [r4, #REG_SC_CTRL]
@Set clear remap bit.
orr r0, #(1 |