Opensbi初始化分析:设备初始化
-
- 设备初始化
-
- sbi_init函数
-
- coldinit,冷启动初始化
- sbi_scratch_init函数
- sbi_domain_init函数
- sbi_hsm_init
- sbi_platform_early_init
- sbi_hart_init
- sbi_console_init
- sbi_platform_irqchip_init中断控制器的初始化
- sbi_ipi_init函数
- sbi_tlb_init函数
- sbi_ecall_init函数
- sbi_domain_finalize
- sbi_hart_pmp_configure函数
- sbi_platform_final_init
- wake_coldboot_harts函数
- sbi_hsm_prepare_next_jump函数
- sbi_hart_switch_mode
- 参考
设备初始化
紧接_start_warm,接下来将正式进入C阶段,调用到sbi_init,这里也会在终端出经典的Opensbi界面。
sbi_init函数
这个函数传入的参数为sbi_scratch结构体,也就是在前面的汇编阶段初始化的scratch空间,CSR_MSCRATCH存储着起始地址。
- 首先需要判断启动模式
- 随机选择满足条件的hart执行clodboot,其余的hart执行warmboot
注意:Opensbi中的coolboot和warmboot不是传统意义上的冷启动和热启动,而是完全初始化和部分初始化的意思
/**
* Initialize OpenSBI library for current HART and jump to next
* booting stage.
*
* The function expects following:
* 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART
* 2. Stack pointer (SP) is setup for current HART
* 3. Interrupts are disabled in MSTATUS CSR
* 4. All interrupts are disabled in MIE CSR
*
* @param scratch pointer to sbi_scratch of current HART
*/
void __noreturn sbi_init(struct sbi_scratch *scratch)
{
bool next_mode_supported = FALSE;
bool coldboot = FALSE;
u32 hartid = current_hartid();
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
if ((SBI_HARTMASK_MAX_BITS <= hartid) ||
sbi_platform_hart_invalid(plat, hartid))
sbi_hart_hang();
switch (scratch->next_mode) {
case PRV_M:
next_mode_supported = TRUE;
break;
case PRV_S:
if (misa_extension('S'))
next_mode_supported = TRUE;
break;
case PRV_U:
if (misa_extension('U'))
next_mode_supported = TRUE;
break;
default:
sbi_hart_hang();
}
/*
* Only the HART supporting privilege mode specified in the
* scratch->next_mode should be allowed to become the coldboot
* HART because the coldboot HART will be directly jumping to
* the next booting stage.
*
* We use a lottery mechanism to select coldboot HART among
* HARTs which satisfy above condition.
*/
if (next_mode_supported && atomic_xchg(&coldboot_lottery, 1) == 0)
coldboot = TRUE;
if (coldboot)
init_coldboot(scratch, hartid);
else
init_warmboot(scratch, hartid);
}
coldinit,冷启动初始化
这个函数执行的是冷启动初始化:传入的参数为scratch结构体和hartid,主要做了下面几件事:
- 初始化scratch空间
- 初始化domain加载的镜像模块
- hsm\paltfrom早期\hart\console\中断控制器\核间中断\初始化
- MMU的tlb初始化
- timer初始化
- ecall初始化:注册系统调用
- …
- 发出软中断,唤醒其他warmboot的核
- 准备下一级的bootloader
static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
{
int rc;
unsigned long *init_count;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
/* Note: This has to be first thing in coldboot init sequence */
rc = sbi_scratch_init(scratch);
if (rc)
sbi_hart_hang();
/* Note: This has to be second thing in coldboot init sequence */
rc = sbi_domain_init(scratch, hartid);
if (rc)
sbi_hart_hang();
init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__,
"INIT_COUNT");
if (!init_count_offset)
sbi_hart_hang();
rc = sbi_hsm_init(scratch, hartid, TRUE);
if (rc)
sbi_hart_hang();
rc = sbi_platform_early_init(plat, TRUE);
if (rc)
sbi_hart_hang();
rc = sbi_hart_init(scratch, TRUE);
if (rc)
sbi_hart_hang();
rc = sbi_console_init(scratch);
if (rc)
sbi_hart_hang();
sbi_boot_print_banner(scratch);
rc = sbi_platform_irqchip_init(plat, TRUE);
if (rc) {
sbi_printf("%s: platform irqchip init failed (error %d)\n",
__func__, rc);
sbi_hart_hang();
}
rc = sbi_ipi_init(scratch, TRUE);
if (rc) {
sbi_printf("%s: ipi init failed (error %d)\n", __func__, rc);
sbi_hart_hang();
}
rc = sbi_tlb_init(scratch, TRUE);
if (rc) {
sbi_printf("%s: tlb init failed (error %d)\n", __func__, rc);
sbi_hart_hang();
}
rc = sbi_timer_init(scratch, TRUE);
if (rc) {
sbi_printf("%s: timer init failed (error %d)\n", __func__, rc);
sbi_hart_hang();
}
rc = sbi_ecall_init();
if (rc) {
sbi_printf("%s: ecall init failed (error %d)\n", __func__, rc);
sbi_hart_hang();
}
sbi_boot_print_general(scratch);
/*
* Note: Finalize domains after HSM initialization so that we
* can startup non-root domains.
* Note: Finalize domains before HART PMP configuration so
* that we use correct domain for configuring PMP.
*/
rc = sbi_domain_finalize(scratch, hartid);
if (rc) {
sbi_printf("%s: domain finalize failed (error %d)\n",
__func__, rc);
sbi_hart_hang();
}
sbi_boot_print_domains(scratch);
rc = sbi_hart_pmp_configure(scratch);
if (rc) {
sbi_printf("%s: PMP configure failed (error %d)\n",
__func__, rc);
sbi_hart_hang();
}
/*
* Note: Platform final initialization should be last so that
* it sees correct domain assignment and PMP configuration.
*/
rc = sbi_platform_final_init(plat, TRUE);
if (rc) {
sbi_printf("%s: platform final init failed (error %d)\n",
__func__, rc);
sbi_hart_hang();
}
sbi_boot_print_hart(scratch, hartid);
wake_coldboot_harts(scratch, hartid);
init_count = sbi_scratch_offset_ptr(scratch, init_count_offset);
(*init_count)++;
sbi_hsm_prepare_next_jump(scratch, hartid);
sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr,
scratch->next_mode, FALSE);
}
sbi_scratch_init函数
这个函数的主要作用:根据hart id和scratch结构的映射关系,初始化hartid_to_scratch_table这个数组。值得注意的是它记住了最后一个hart。
int sbi_scratch_init(struct sbi_scratch *scratch)
{
u32 i;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) {
if (sbi_platform_hart_invalid(plat, i))
continue;
hartid_to_scratch_table[i] =
((hartid2scratch)scratch->hartid_to_scratch)(i,
sbi_platform_hart_index(plat, i));
if (hartid_to_scratch_table[i])
last_hartid_having_scratch = i;
}
return 0;
}
sbi_domain_init函数
这个函数本质是初始化动态加载的镜像模块,首先会初始化一个ROOT的内存域,最后会将root域注册到opensbi的西欧统中
int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid)
{
u32 i;
struct sbi_domain_memregion *memregs;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
/* Root domain firmware memory region */
root_memregs[ROOT_FW_REGION].order = log2roundup(scratch->fw_size);
root_memregs[ROOT_FW_REGION].base = scratch->fw_start &
~((1UL << root_memregs[0].order) - 1UL);
root_memregs[ROOT_FW_REGION].flags = 0;
/* Root domain allow everything memory region */
root_memregs[ROOT_ALL_REGION].order = __riscv_xlen;
root_memregs[ROOT_ALL_REGION].base = 0