可以看出,不管是固化在irom里的代码还是三星加密代码都是不需要我们系统/驱动工程师所深究的,所以我们弄清楚了启动流程,接下来来看具体代码。
u-boot上电一启动首先运行的是start.S文件,这是u-boot的第一阶段的代码
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
/*_start是一个函数的起始地址,也是编译、链接后程序的起始地址。(此为bootloader第一阶段的代码)U_boot运行的第一/条命令是b Reset 异常向量表(位置固定,不可更改)*/
_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 /* now 16*4=64 */
/*用 . word 修饰 函数入口地址是让pc程序计数器能够跳到更远的地址(异常向量表异常函数入口地址)*/
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE (0x43E00000)
/*
* These are defined in the board-specificlinker script.
*/
_bss_start_ofs:
.word __bss_start - _start// (去掉bss段uboot大小)
_image_copy_end_ofs:
.word __image_copy_end- _start(.txt + .rodata + .data + .u_boot_list)
_bss_end_ofs:
.word __bss_end__ - _start //(整个uboot大小)
_end_ofs:
.word _end - _start (.txt + .rodata +.data + .u_boot_list + .rel.dyn + .dynsym)
/* the actual reset code*/
/*程序一开始,跳到Reset这运行函数 save_boot_params 是官方指定函数名称*/
reset:
bl save_boot_params
/*
*set the cpu to SVC32 mode 把ARM核的工作模式设置为超级用户模式 */
mrs r0,cpsr
bic r0,r0, #0x1f
orr r0,r0, #0xd3
msr cpsr,r0
/*告诉ARM核异常向量表所在的地址为低地址
低地址 0x0~0xffff0000 大于就是高地址*//* Set V=0 in CP15 SCTRL register - forVBAR to point to vector */
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 SCTRLRegister
/*获得异常向量表所在的地址,将异常向量表的基地址写入P15协处理器的C12寄存器中------------即告诉ARM核异常向量表所在的地方。 Set vector address in CP15 VBARregister */
ldr r0,=_start
mcr p15,0, r0, c12, c0, 0 @Set VBAR
/* 关闭MMu与cache的原因是它们必须在内存初始化之后才能使用,需要用时再开启*/
bl cpu_init_cp15//(关MMU和cache)
bl cpu_init_crit//(初始化系统时钟和内存)
/*跳转到main函数执行bootloader第二阶段的代码。 arch/arm/lib/crt0.S*/
bl _main
/*以上是start.S文件的主体部分跳转到main函数执行bootloader第二阶段的代码,以下是一些跳转的具体实现*/
save_boot_params:
bx lr @ back to my caller
.weak save_boot_params
/*Reset 跳到这之后又跳回去
. weak 的作用是弱化这个函数
(原因是该函数可能会被传参,便于别人覆盖,但设计师并不能确定,所以以弱化的方式先规范死函数名。)*/cpu_init_cp15:
/*
*Invalidate L1 I/D 开启cache作为ARM核从内存中取数据的缓冲,提高ARM核提取数据的速度
*/
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/*MMU是虚拟地址到物理地址的一个映射,现阶段没有操作系统,不需要使用到MMU*/
/ * 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
orr r0,r0, #0x00001000 @ set bit 12 (I) I-cache
mcr p15,0, r0, c1, c0, 0
mov pc,lr @ back to my caller
cpu_init_crit:
/*
*Jump to board specific initialization...
*The Mask ROM will have already initialized
*basic memory. Go here to bump up clock rate and handle
*wake up conditions.
不带返回的跳转,需要在下面保存lr寄存器的值*/
b lowlevel_init @ go setup pll,mux,memory
/* 把lr寄存器的值保存起来(lr中保存的实际是bl main这句程序的地址*/
board/samsung/origen/lowlevel_init.S
lowlevel_init:
push {lr}
/*
*If U-boot is already running in ram, no need to relocate U-Boot.
*Memory controller must be configured before relocating U-Boot
*in ram.
判断现在程序是否在内存中运行,如果是的话,就不需要执行下面的初始化时钟与初始化内存的程序了。
具体做法:取出当前 pc 程序计数器指针的值,后 24 位清除(在 IRAM 中则是 0x02000000 ,在内存中则是 0x40000000)
把__TEXT_BASE(0x43e00000)低24位清除进行比较,相同则程序已运行在内存中*/
ldr r0,=0x0ffffff /* r0 <- Mask Bits*/
bic r1,pc, r0 /* pc <- currentaddr of code */
/* r1<- unmasked bits of pc */
ldr r2,_TEXT_BASE /* r2 <- originalbase addr in ram */
bic r2,r2, r0 /* r2 <- unmaskedbits of r2*/
cmp r1,r2 /* compare r1, r2 */
beq 1f /* r0 == r1 then skip sdraminit */
/* 初始化系统时钟,让cpu核运行的更快,提高主频*/
/* init system clock */
bl system_clock_init
/*初始化内存,让内存可以用,为后面在内存里的操作做准*/
/* Memoryinitialize */
bl mem_ctrl_asm_init
/*初始化串口的一些gpio管脚设为UART模式*/
1:
/* for UART */
bl uart_as`-/``m_init
pop {pc}
至此,start.S文件与exynos4412u-boot第一阶段的代码至此完结
第一阶段u_boot 实现功能:1、将第二阶段的U_boot代码搬移到内存中指定位置。 2、将pc指针指向内存中的第二阶段代码的起始位置开始执行第二阶段的代码。
(本人经验技术有限,如有错误请在留言中指正,谢谢大家,共同进步!)