流程:通过u-boot.lds(各种段,刚开始有个链接地址)找到start.S的入口
(ENTRY(__start))
第一阶段:start.S(初始化soc内部的一些部件)
__start: b reset(异常向量表)->
通过cpsr设置SVC模式->
关闭MMU->读取启动介质->
设置sp(ldr #0xd0036000,这时还在sram中运行) 调用函数 bl lowlevelinit(如果只是一层调用就用bl,如果子函数里面还有调用就要使用栈)->lowlevel_init.S->push {lr}(压栈,因为后面还要调用)->
检查复位状态(冷热启动,睡眠启动(低功耗))来判断是否还要初始化DDR->
关看门狗->
判断当前代码是在SRAM还是DDR中运行(BL1(uboot前8K)在DDR和SRAM中各有一份)(adr ro, _start(运行地址或者PC) ldr r1, =_start(链接地址,自己指定) cmp r0 r1)->来判定是否时钟和DDR初始化->pop {pc}->DDR已经初始化,把栈挪到DDR中,重新设置栈(ldr sp, _TEXT_PHY_BASE sub sp, sp, ##12 mov fp, #0) 把第二阶段加载到33e00000->
重定位,将bl2从sd卡或者mmc搬运到ddr中(在sd卡中的开始扇区,扇区数,还有CFG_PHY_UBOOT_BASE:33e00000)->
串口->
使能mmu(TTB,cp15的c1的bit0置1即可开启mmu)->
ldr pc, _start_armboot
CFG_PHY_UBOOT_BASE 33e00000 uboot在DDR中的物理地址
TEXT_BASE c3e00000 uboot的虚拟地址
第二阶段:lib_arm/board.c start_armboot(初始化soc外部的一些硬件,uboot本身的一些东西(命令行,环境变量)) 最终bootcmd进入启动内核阶段
typedef int (int_func_t) (void);//这是一个函数类型,不是函数指针类型
init_func_t **init_func_ptr; //二重指针(一个用来指向一重指针,一个用来指向一个函数指针数组)
全局变量gd register volatile gd_t *gd asm("r8")
register 修饰,这个变量尽量放到寄存器中,volatie 修饰
gd的内存分配不能用malloc
gd_base
gd = (gd_t*)gd_base;
gd->bd = (bd_t*)((char*)gd-sizeof(bd_t));
函数指针数组去遍历,可以在函数指针数组末尾添加一个null来作为结束标志,这样做的好处的是不用事先去指定数组大小。
挂起
void hang(void)
{
printf("error XXXXX");
for(;;);
init_sequence
}
bi_arch_number 机器码 uboot和kernel需要匹配(向uboot官方申请,国内的板子基本都不申请)
bi_boot_params uboot将cmd_line(bootarg),meminfo,mtdpartition,放在地址bi_boot_number处,uboot通过r0,r1,r2(bi_boot_params)kernel的传参
控制台(console)最终也是操作硬件(serial)操作寄存器来实现,优点是优化通信,比如说缓冲机制。
重定位:把代码从(有可能是运行地址)copy一份到链接地址
bl 短跳转
ldr pc,=0x33e00000
adr r0,=_start adr短加载,加载运行地址 (adr的反汇编是sub)
ldr r1,=_start ldr长加载,加载链接地址(ldr反汇编后还是ldr)
ldr r2, =bss_start
cmp r0 r1
beq clean_bss
copy_loop:
ldr r3, [r0], #4 //源
str r3, [r1], #4 //目的
cmp r1, r2
bne copy_loop
clean_bss:
.word 相当于int(4字节)
__TEXT_BASE:
.word TEXT_BASE
这里标号__TEXT_BASE相当于指针,可以使用ldr __TEXT_BASE相当于加载TEXT_BASE