OpenSBI初始化分析
OpenSBI初始化
启动流程:主要涉及三个阶段
- .S汇编阶段,主要是Opensbi的底层初始化阶段,为C准备执行环境;
- 设备初始化阶段,此阶段正式进入C环境;
- 会跳转到二级 Bootloader,正式进入S模式;
OpenSBI底层初始化
确定启动的hart id
非启动hart转入_wait_relocate_copy_done等待启动hart初始化完成。
在fw_boot_hart中:fw_jump和fw_payload模式将a0返回为-1;fw_dynamic将根据a2中的dynamic_info去决定a0的值(-1或者为一个固定值,应该是设置的),a1值为2;
_start:
/* Find preferred boot HART id */
MOV_3R s0, a0, s1, a1, s2, a2
call fw_boot_hart
add a6, a0, zero
MOV_3R a0, s0, a1, s1, a2, s2
li a7, -1
beq a6, a7, _try_lottery
/* Jump to relocation wait loop if we are not boot hart */
bne a0, a6, _wait_relocate_copy_done
根据hart id执行重定位(lottery算法确定主hart)
_try_lottery:
/* Jump to relocation wait loop if we don't get relocation lottery */
la a6, _relocate_lottery
li a7, 1
amoadd.w a6, a7, (a6)
/*_relocate_lottery不等于0,即跳到boot hart做重定位的地方。若多核一起启动执行,只有最先执行源自指令的a6是0,后续执行到这里的从核直接跳到重定位完成地址*/
bnez a6, _wait_relocate_copy_done /*这里没抢到锁的核将到_wait_relocate_copy_done处循环*/
/* Save load address */
la t0, _load_start
la t1, _start
REG_S t1, 0(t0) //将_start指向_load_start
当加载地址和链接地址不相同时,需要进行重定位
/* Relocate if load address != link address
*
* 地址分布:
* _link_start <-----t0
*
* _link_end <-----t1
*
* _load_start <-----t2
*
* _load_end <-----t3
*
*/
_relocate:
la t0, _link_start
REG_L t0, 0(t0)
la t1, _link_end
REG_L t1, 0(t1)
la t2, _load_start
REG_L t2, 0(t2)
sub t3, t1, t0
add t3, t3, t2
beq t0, t2, _relocate_done
la t4, _relocate_done
sub t4, t4, t2
add t4, t4, t0
blt t2, t0, _relocate_copy_to_upper
地址拷贝:首先要避免地址重叠,实际copy时需要区分copy到高地址或低地址,因此,copy始于地址开头或结尾的效果不同。若_load_start < _link_start,则需要往高地址拷贝数据。
_relocate_copy_to_upper:
// load_end > link_start将存在地址重叠,将进入异常
ble t3, t0, _relocate_copy_to_upper_loop
la t2, _relocate_lottery
BRANGE t0, t3, t2, _start_hang
la t2, _boot_status
BRANGE t0, t3, t2, _start_hang
la t2, _relocate
la t5, _relocate_done
BRANGE t0, t3, t2, _start_hang
BRANGE t0, t3, t5, _start_hang
BRANGE t2, t5, t0, _start_hang
重定位的地址拷贝发生在_relocate_copy_to_upper_loop中,保证不发生重叠,所以从
_relocate_copy_to_upper_loop:
add t3, t3, -__SIZEOF_POINTER__
add t1, t1, -__SIZEOF_POINTER__
REG_L t2, 0(t3)
REG_S t2, 0(t1)
blt t0, t1, _relocate_copy_to_upper_loop
jr t4
低地址拷贝同理,方向与高地址相反
_relocate_copy_to_lower:
ble t1, t2, _relocate_copy_to_lower_loop
la t3, _relocate_lottery
BRANGE t2, t1, t3, _start_hang
la t3, _boot_status
BRANGE t2, t1, t3, _start_hang
la t3, _relocate
la t5, _relocate_done
BRANGE t2, t1, t3, _start_hang
BRANGE t2, t1, t5, _start_hang
BRANGE t3, t5, t2, _start_hang
_relocate_copy_to_lower_loop:
REG_L t3, 0(t2)
REG_S t3, 0(t0)
add t0, t0, __SIZEOF_POINTER__
add t2, t2, __SIZEOF_POINTER__
blt t0, t1, _relocate_copy_to_lower_loop
jr t4
再来看下非启动hart的动作:一直在等待启动hart的link_start和load_start相同,也就是_boot_status变为BOOT_STATUS_RELOCATE_DONE。接下来可以看到在几个nop指令后,非启动hart会跳转到_wait_for_boot_hart的链接地址,将继续等待启动hart完成其他的初始化工作。
_wait_relocate_copy_done:
la t0, _start
la t1, _link_start
REG_L t1, 0(t1)
//若load adress与link address不同,表示重定位未完成
beq t0, t1, _wait_for_boot_hart
la t2, _boot_status
//记录_wait_for_boot_hart的链接地址
la t3, _wait_for_boot_hart
sub t3, t3, t0
add t3, t3, t1
1:
/* waitting for relocate copy done (_boot_status == 1) */
li t4, BOOT_STATUS_RELOCATE_DONE
REG_L t5, 0(t2)
/* Reduce the bus traffic so that boot hart may proceed faster */
nop
nop
nop
bgt t4, t5, 1b
jr t3
重定位结束:设置_boot_status为BOOT_STATUS_RELOCATE_DONE
_relocate_done:
/*
* Mark relocate copy done
* Use _boot_status copy relative to the load address
*/
la t0, _boot_status
la t1, _link_start
REG_L t1, 0(t1)
la t2, _load_start
REG_L t2, 0(t2)
sub t0, t0, t1
add t0, t0, t2
/* 重定位完成,设置标志*/
li t1, BOOT_STATUS_RELOCATE_DONE
REG_S t1, 0(t0)
fence rw, rw
/* At this point we are running from link address */
/* Reset all registers for boot HART */
li ra, 0
call _reset_regs
/* Zero-out BSS */
la s4, _bss_start
la s5, _bss_end
清除bss段,设置sp指针,8k的临时栈空间,以及scratch空间的构造
写状态寄存器CSR_MTVEC,保存发生异常时处理器需要跳转到的地址
设置sp寄存器,栈指针:栈空间为8k,向下生长
保存:a0~a4寄存器的值,用于fw_save_info,只有fw_dynamic的模式才会用到这些寄存器(因为暂时只有dynamic模式才会将这个结构体传向内核)
_bss_zero:
REG_S zero, (s4)
add s4, s4, __SIZEOF_POINTER__
blt s4, s5, _bss_zero
/* Setup temporary trap handler */
la s4, _start_hang
csrw CSR_MTVEC, s4
/* Setup temporary stack */
la s4, _fw_end
li s5, (SBI_SCRATCH_SIZE * 2)
add sp, s4, s5
/* Allow main firmware