.globl _start _start: b reset //uboot启动从_start开始,跳转到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
系统启动后直接跳转到reset
reset: mrs r0, cpsr //程序状态寄存器cpsr bic r0, r0, #0x1f //cpsr的低五位被清零 orr r0, r0, #0xd3 //关中断,并进入SVC32模式 msr cpsr,r0 bl cpu_init_crit //跳转到cpu_init_crit,初始化内存,时钟等关键寄存器reset设置为管理模式后进入cpu_init_crit
cpu_init_crit: bl cache_init //初始化cache /* * Invalidate L1 I/D */ mov r0, #0 @ set up for MCR mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs //ARM最多可支持16个协处理器p0-p15 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, #0x00001000 @ set bit 12 (---I) Icache 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 /* * 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. */ mov ip, lr //cpu_init_crit被reset调用,lr保存用于返回reset函数的地址, bl lowlevel_init //此处子函数cpu_init_crit发生二次调用,故将用于返回reset的lr保存, mov lr, ip //调用lowlevel_init后lr更新为返回cpu_init_crit的地址 mov pc, lr
关闭cache和MMU,在arm中cache是cpu的内部缓存,MMU实现虚拟地址与物理地址的转换。
cache在cpu中用来存放常用的数据和指令,cache开启的情况下,cpu首先在cache中寻找需要的指令或数据,cache中没有再从RAM获取,uboot启动的时候不管cache是否初始化都不会有cpu需要的数据,uboot用来实现简单的初始化和引导操作,关闭cache优化uboot性能。
C语言不能控制cache的开关,但关键字volatile能够避免优化,所谓避免优化实际上就是编译器告诉cpu在获取这个变量时不要从cache中读取,因为这个变量是随时可变的,cache不能实时的反应这些变量的值,cpu每次读取都直接从变量实际地址读取。同样关闭MMU,在uboot刚启动的时候避免虚拟地址无物理地址的转换。
lowlevel_init是lowlevel_init.S下定义的全局函数,配置启动必须的硬件
.globl lowlevel_init lowlevel_init: /* use iROM stack in bl2 */ ldr sp, =0x02060000 //上面提到关于函数调用返回地址的保存,cpu_init_crit将lr保存在ip, push {lr} //lowlevel_init肯定会调用更多函数,所以将回调地址保存到iROM的堆栈中 #ifdef CONFIG_EXYNOS4412 bl set_ema_value //如果4412版本号大于2.0,设置apll,我们是1.1版本,不需要 #endif /* initialization for CMU_SYSCLK_ISP function */ mov r1, #0 ldr r0, =0x10021174 /* CMU_RESET_ISP_SYS_PWR_REG */ str r1, [r0] ldr r0, =0x100213B8 /* CMU_SYSCLK_ISP_SYS_PWR_REG */ str r1, [r0] /* check reset status */ ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET) ldr r1, [r0] //查看cpu是怎么复位的,如果从睡眠唤醒进入复位则发生跳转,否则继续执行 /* Sleep wakeup reset */ ldr r2, =S5P_CHECK_SLEEP cmp r1, r2 beq wakeup_reset /* PS-Hold high */ //拉高PSHOLD管脚,唤醒电源管理芯片 ldr r0, =0x1002330c ldr r1, [r0] orr r1, r1, #0x300 str r1, [r0] /* set CP reset to low */ ldr r0, =0x11000C60 ldr r1, [r0] ldr r2, =0xFFFFFF0F and r1, r1, r2 orr r1, r1, #0x10 str r1, [r0] ldr r0, =0x11000C68 ldr r1, [r0] ldr r2, =0xFFFFFFF3 and r1, r1, r2 orr r1, r1, #0x4 str r1, [r0] ldr r0, =0x11000C64 ldr r1, [r0] ldr r2, =0xFFFFFFFD and r1, r1, r2 str r1, [r0] /* During sleep/wakeup or AFTR mode, pmic_init function is not available * and it causes delays. So except for sleep/wakeup and AFTR mode, * the below function is needed */ bl pmic_init //上电启动,需要初始化电源,如果是从睡眠状态恢复,则没到这一步 bl read_om //确定启动设备 /* when we already run in ram, we don't need to relocate U-Boot. * and actually, memory controller must be configured before U-Boot * is running in ram. */ ldr r0, =0xff000fff bic r1, pc, r0 /* r0 <- current base addr of code */ ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */ bic r2, r2, r0 /* r0 <- current base addr of code */ cmp r1, r2 /* compare r0, r1 */ beq after_copy /* r0 == r1 then skip sdram init and u-boot.bin loading */ ldr r0, =CHIP_ID_BASE ldr r1, [r0] lsr r1, r1, #8 and r1, r1, #3 cmp r1, #2 bne v310_1 /* Memory initialize */ bl mem_ctrl_asm_init /* init system clock */ bl system_clock_init b 1f /*********/ 1: b load_uboot //加载uboot镜像系统复位或上电重启后进入reset,在reset中将cpu设为管理模式后进入cpu_init_crit,在这里初始化cache并且关闭cache和MMU,跳转到lowlevel_init,在lowlevel_init中,根据不同的外部条件进行不同操作,首先判断系统是否从睡眠状态中复位,如果是则跳转到wakeup_reset,我们分析启动过程,显然不是不发生跳转,接下来就启动开发板上的电源,并且确定启动设备,同时通过pc判断执行环境的位置,如果已经在RAM中则证明RMA初始化uboot的拷贝都已经完成了,可以跳过,否则继续执行,初始化内存和时钟,跳转到load_uboot加载uboot镜像