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_timer_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;
root_memregs[ROOT_ALL_REGION].flags = (SBI_DOMAIN_MEMREGION_READABLE |
SBI_DOMAIN_MEMREGION_WRITEABLE |
SBI_DOMAIN_MEMREGION_EXECUTABLE);
/* Root domain memory region end */
root_memregs[ROOT_END_REGION].order = 0;
/* Use platform specific root memory regions when available */
memregs = sbi_platform_domains_root_regions(plat);
if (memregs)
root.regions = memregs;
/* Root domain boot HART id is same as coldboot HART id */
root.boot_hartid = cold_hartid;
/* Root domain next booting stage details */
root.next_arg1 = scratch->next_arg1;
root.next_addr = scratch->next_addr;
root.next_mode = scratch->next_mode;
/* Root domain possible and assigned HARTs */
for (i = 0; i < SBI_HARTMASK_MAX_BITS; i++) {
if (sbi_platform_hart_invalid(plat, i))
continue;
sbi_hartmask_set_hart(i, &root_hmask);
}
return sbi_domain_register(&root, &root_hmask);
}
sbi_hsm_init
这个函数主要作用就是初始化hsm状态,以处理安全性相关问题(加密、解密、签名、认证),在opensbi中hsm的状态要对所有的hart共享,所以需要初始化每个hart。
因为HSM启动可能需要一段时间,当hart需要等待其他hart都启动,才能开始安全启动想相关操作。
int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
{
u32 i;
struct sbi_scratch *rscratch;
struct sbi_hsm_data *hdata;
if (cold_boot) {
hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata),
"HART_DATA");
if (!hart_data_offset)
return SBI_ENOMEM;
/* Initialize hart state data for every hart */
for (i = 0; i <= sbi_scratch_last_hartid(); i++) {
rscratch = sbi_hartid_to_scratch(i);
if (!rscratch)
continue;
hdata = sbi_scratch_offset_ptr(rscratch,
hart_data_offset);
ATOMIC_INIT(&hdata->state,
(i == hartid) ? SBI_HART_STARTING : SBI_HART_STOPPED);
}
} else {
sbi_hsm_hart_wait(scratch, hartid);
}
return 0;
}
sbi_platform_early_init
这个是平台的一些早期初始化操作,这里是一贯的面向的对象的思想,根据不用的platfrom执行其对应的early_init函数,当平台为generic时,会调用ops中的.early_init指向的eneric_early_init。
/**
* Early initialization for current HART
*
* @param plat pointer to struct sbi_platform
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
*
* @return 0 on success and negative error code on failure
*/
static inline int sbi_platform_early_init(const struct sbi_platform *plat,
bool cold_boot)
{
if (plat && sbi_platform_ops(plat)->early_init)
return sbi_platform_ops(plat)->early_init(cold_boot);
return 0;
}
sbi_hart_init
主要作用是初始化当前的hart:
- 检测当前hart的feature
- 初始化状态寄存器
- 初始化浮点寄存器,需要根据isa
- 将中断、异常委托给S模式进行处理
int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
{
int rc;
if (cold_boot) {
if (misa_extension('H'))
sbi_hart_expected_trap = &__sbi_expected_trap_hext;
hart_features_offset = sbi_scratch_alloc_offset(
sizeof(struct hart_features),
"HART_FEATURES");
if (!hart_features_offset)
return SBI_ENOMEM;
}
hart_detect_features(scratch);
mstatus_init(scratch);
rc = fp_init(scratch);
if (rc)
return rc;
rc = delegate_traps(scratch);
if (rc)
return rc;
return 0;
}
在上述代码中,可以看到冷启动hart初始化了hart_featuress结构体,定义如下:
struct hart_features {
unsigned long features;
unsigned int pmp_count;
unsigned int pmp_addr_bits;
unsigned long pmp_gran;
unsigned int mhpm_count;
};
这里分析下delegate_traps函数:
正如字面意思委托,它会将M模式的中断和异常委托给S模式,并写入两个状态寄存器CSR_MIDELEG和CSR_MEDELEG。
static int delegate_traps(struct sbi_scratch *scratch)
{
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
unsigned long interrupts, exceptions;
if (!misa_extension('S'))
/* No delegation possible as mideleg does not exist */
return 0;
/* Send M-mode interrupts and most exceptions to S-mode */
interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
exceptions = (1U << CAUSE_MISALIGNED_FETCH) | (1U << CAUSE_BREAKPOINT) |
(1U << CAUSE_USER_ECALL);
if (sbi_platform_has_mfaults_delegation(plat))
exceptions |= (1U << CAUSE_FETCH_PAGE_FAULT) |
(1U << CAUSE_LOAD_PAGE_FAULT) |
(1U << CAUSE_STORE_PAGE_FAULT);
/*
* If hypervisor extension available then we only handle hypervisor
* calls (i.e. ecalls from HS-mode) in M-mode.
*
* The HS-mode will additionally handle supervisor calls (i.e. ecalls
* from VS-mode), Guest page faults and Virtual interrupts.
*/
if (misa_extension('H')) {
exceptions |= (1U << CAUSE_VIRTUAL_SUPERVISOR_ECALL);
exceptions |= (1U << CAUSE_FETCH_GUEST_PAGE_FAULT);
exceptions |= (1U << CAUSE_LOAD_GUEST_PAGE_FAULT);
exceptions |= (1U << CAUSE_VIRTUAL_INST_FAULT);
exceptions |= (1U << CAUSE_STORE_GUEST_PAGE_FAULT);
}
csr_write(CSR_MIDELEG, interrupts);
csr_write(CSR_MEDELEG, exceptions);
return 0;
}
sbi_console_init
这个函数主要做的就是console的初始化,可以看出这部分是跟platfrom相关的。
int sbi_console_init(struct sbi_scratch *scratch)
{
console_plat = sbi_platform_ptr(scratch);
return sbi_platform_console_init(console_plat);
}
/**
* Initialize the platform console
*
* @param plat pointer to struct sbi_platform
*
* @return 0 on success and negative error code on failure
*/
static inline int sbi_platform_console_init(const struct sbi_platform *plat)
{
if (plat && sbi_platform_ops(plat)->console_init)
return sbi_platform_ops(plat)->console_init();
return 0;
}
console初始化完成后,会调用sbi_boot_print_banner函数,这样可以在控制台上看到相应的logo。
#define BANNER \
" ____ _____ ____ _____\n" \
" / __ \\ / ____| _ \\_ _|\n" \
" | | | |_ __ ___ _ __ | (___ | |_) || |\n" \
" | | | | '_ \\ / _ \\ '_ \\ \\___ \\| _ < | |\n" \
" | |__| | |_) | __/ | | |____) | |_) || |_\n" \
" \\____/| .__/ \\___|_| |_|_____/|____/_____|\n" \
" | |\n" \
" |_|\n\n"
static void sbi_boot_print_banner(struct sbi_scratch *scratch)
{
if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS)
return;
#ifdef OPENSBI_VERSION_GIT
sbi_printf("\nOpenSBI %s\n", OPENSBI_VERSION_GIT);
#else
sbi_printf("\nOpenSBI v%d.%d\n", OPENSBI_VERSION_MAJOR,
OPENSBI_VERSION_MINOR);
#endif
sbi_printf(BANNER);
}
sbi_platform_irqchip_init中断控制器的初始化
这部分同样跟platfrom相关,generic平台调用fdt_irqchip_init函数,作用为初始化中断控制器,这里一般会区分coldboot和warmboot。
/**
* Initialize the platform interrupt controller for current HART
*
* @param plat pointer to struct sbi_platform
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
*
* @return 0 on success and negative error code on failure
*/
static inline int sbi_platform_irqchip_init(const struct sbi_platform *plat,
bool cold_boot)
{
if (plat && sbi_platform_ops(plat)->irqchip_init)
return sbi_platform_ops(plat)->irqchip_init(cold_boot);
return 0;
}
sbi_ipi_init函数
这个函数的主要核间中断的初始化,主要做的事情如下:
- 分配IPI的时间空间,保存偏移地址
- 创建IPI事件,将事件的句柄保存在ipi_smode_event和ipi_halt_event
- 初始化平台特有的IPI初始化操作
- 启用软中断,将状态控制寄存器CDR_MIE的MIP_MSIP位置置一
int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot)
{
int ret;
struct sbi_ipi_data *ipi_data;
if (cold_boot) {
ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data),
"IPI_DATA");
if (!ipi_data_off)
return SBI_ENOMEM;
ret = sbi_ipi_event_create(&ipi_smode_ops);
if (ret < 0)
return ret;
ipi_smode_event = ret;
ret = sbi_ipi_event_create(&ipi_halt_ops);
if (ret < 0)
return ret;
ipi_halt_event = ret;
} else {
if (!ipi_data_off)
return SBI_ENOMEM;
if (SBI_IPI_EVENT_MAX <= ipi_smode_event ||
SBI_IPI_EVENT_MAX <= ipi_halt_event)
return SBI_ENOSPC;
}
ipi_data = sbi_scratch_offset_ptr(scratch, ipi_data_off);
ipi_data->ipi_type = 0x00;
/* Platform init */
ret = sbi_platform_ipi_init(sbi_platform_ptr(scratch), cold_boot);
if (ret)
return ret;
/* Enable software interrupts */
csr_set(CSR_MIE, MIP_MSIP);
return 0;
}
sbi_platform_ipi_init函数:
/**
* Initialize the platform IPI support for current HART
*
* @param plat pointer to struct sbi_platform
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
*
* @return 0 on success and negative error code on failure
*/
static inline int sbi_platform_ipi_init(const struct sbi_platform *plat,
bool cold_boot)
{
if (plat && sbi_platform_ops(plat)->ipi_init)
return sbi_platform_ops(plat)->ipi_init(cold_boot);
return 0;
}
sbi_tlb_init函数
主要对TLB表进行初始化:一个sync变量、一个fifo队列还有一个TLB的刷新时间
int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot)
{
int ret;
void *tlb_mem;
unsigned long *tlb_sync;
struct sbi_fifo *tlb_q;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
if (cold_boot) {
tlb_sync_off = sbi_scratch_alloc_offset(sizeof(*tlb_sync),
"IPI_TLB_SYNC");
if (!tlb_sync_off)
return SBI_ENOMEM;
tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q),
"IPI_TLB_FIFO");
if (!tlb_fifo_off) {
sbi_scratch_free_offset(tlb_sync_off);
return SBI_ENOMEM;
}
tlb_fifo_mem_off = sbi_scratch_alloc_offset(
SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE,
"IPI_TLB_FIFO_MEM");
if (!tlb_fifo_mem_off) {
sbi_scratch_free_offset(tlb_fifo_off);
sbi_scratch_free_offset(tlb_sync_off);
return SBI_ENOMEM;
}
ret = sbi_ipi_event_create(&tlb_ops);
if (ret < 0) {
sbi_scratch_free_offset(tlb_fifo_mem_off);
sbi_scratch_free_offset(tlb_fifo_off);
sbi_scratch_free_offset(tlb_sync_off);
return ret;
}
tlb_event = ret;
tlb_range_flush_limit = sbi_platform_tlbr_flush_limit(plat);
} else {
if (!tlb_sync_off ||
!tlb_fifo_off ||
!tlb_fifo_mem_off)
return SBI_ENOMEM;
if (SBI_IPI_EVENT_MAX <= tlb_event)
return SBI_ENOSPC;
}
tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off);
tlb_q = sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
tlb_mem = sbi_scratch_offset_ptr(scratch, tlb_fifo_mem_off);
*tlb_sync = 0;
sbi_fifo_init(tlb_q, tlb_mem,
SBI_TLB_FIFO_NUM_ENTRIES, SBI_TLB_INFO_SIZE);
return 0;
}
** 这个函数的工作机理:为TLB刷新创建一个tlb_event,当每次刷新TLB时,将刷新信息写入队列FIFO中,然后通过tlb_event去通知其他的hart刷新TLB。**
创建TLB刷新事件的句柄具体的数据结构和事件创建的函数如下:
static struct sbi_ipi_event_ops tlb_ops = {
.name = "IPI_TLB",
.update = sbi_tlb_update,
.sync = sbi_tlb_sync,
.process = sbi_tlb_process,
};
//sbi_tlb_init函数
----------------------------------------------
ret = sbi_ipi_event_create(&tlb_ops);
if (ret < 0) {
sbi_scratch_free_offset(tlb_fifo_mem_off);
sbi_scratch_free_offset(tlb_fifo_off);
sbi_scratch_free_offset(tlb_sync_off);
return ret;
}
tlb_event = ret;
------------------------------------------------------
int sbi_ipi_event_create(const struct sbi_ipi_event_ops *ops)
{
int i, ret = SBI_ENOSPC;
if (!ops || !ops->process)
return SBI_EINVAL;
for (i = 0; i < SBI_IPI_EVENT_MAX; i++) {
if (!ipi_ops_array[i]) {
ret = i;
ipi_ops_array[i] = ops;
break;
}
}
return ret;
}
下面需要注意的是,TLB的刷新是需要被限制的,过多的刷新操作会导致额外的开销和负面影响,这个限制可能会跟platfrom有较大关系(有默认阈值)。
/**
* Get platform specific tlb range flush maximum value. Any request with size
* higher than this is upgraded to a full flush.
*
* @param plat pointer to struct sbi_platform
*
* @return tlb range flush limit value. Returns a default (page size) if not
* defined by platform.
*/
static inline u64 sbi_platform_tlbr_flush_limit(const struct sbi_platform *plat)
{
if (plat && sbi_platform_ops(plat)->get_tlbr_flush_limit)
return sbi_platform_ops(plat)->get_tlbr_flush_limit();
return SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT;
}
#define SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT (1UL << 12)
紧接着是获取sync、fifo和fifo内存空间的指针,并且初始化fifo(每个元素条目个数和队列的最大长度等信息)
tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off);
tlb_q = sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
tlb_mem = sbi_scratch_offset_ptr(scratch, tlb_fifo_mem_off);
*tlb_sync = 0;
sbi_fifo_init(tlb_q, tlb_mem,
SBI_TLB_FIFO_NUM_ENTRIES, SBI_TLB_INFO_SIZE);
void sbi_fifo_init(struct sbi_fifo *fifo, void *queue_mem, u16 entries,
u16 entry_size)
{
fifo->queue = queue_mem;
fifo->num_entries = entries;
fifo->entry_size = entry_size;
SPIN_LOCK_INIT(&fifo->qlock);
fifo->avail = fifo->tail = 0;
sbi_memset(fifo->queue, 0, (size_t)entries * entry_size);
}
sbi_timer_init函数
这个函数主要是针对timer的初始化:
- 刷新time_delta_off变量,用于追踪当前haer上次根本更新定时器的时间戳
- 初始化platfrom的始终中断
- 获取时钟相关feature(目前看来无具体作用)
int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
{
u64 *time_delta;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
int ret;
if (cold_boot) {
time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta),
"TIME_DELTA");
if (!time_delta_off)
return SBI_ENOMEM;
} else {
if (!time_delta_off)
return SBI_ENOMEM;
}
time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off);
*time_delta = 0;
ret = sbi_platform_timer_init(plat, cold_boot);
if (ret)
return ret;
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_TIME))
get_time_val = get_ticks;
else if (sbi_platform_has_timer_value(plat))
get_time_val = sbi_platform_timer_value;
else
/* There is no method to provide timer value */
return SBI_ENODEV;
return 0;
}
sbi_ecall_init函数
系统调用的初始化函数,OpsenSBI的ecall机制会提供一种从S模式向M模式请求的方式;从下面的函数可以看出其支持一种扩展机制,OpenSBI的ecall机制还包括了一系列的扩展,通过扩展同样可以向应用程序提供不同的服务。
在V0.9中实现是写死的,后面的版本会提供一个数组便利,更利于维护。这里主要添加的ecall包括(time,rfence,ipi,base,hsm,srst,legacy,vendor)
int sbi_ecall_init(void)
{
int ret;
/* The order of below registrations is performance optimized */
ret = sbi_ecall_register_extension(&ecall_time);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_rfence);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_ipi);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_base);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_hsm);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_srst);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_legacy);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_vendor);
if (ret)
return ret;
return 0;
}
sbi_ecall_register_extension函数则具体实现了将sbi_ecall_extension注册到OpenSBI的ecall机制中,也就是插入ecall_exts_list这个全局链表中尾部
int sbi_ecall_register_extension(struct sbi_ecall_extension *ext)
{
struct sbi_ecall_extension *t;
if (!ext || (ext->extid_end < ext->extid_start) || !ext->handle)
return SBI_EINVAL;
sbi_list_for_each_entry(t, &ecall_exts_list, head) {
unsigned long start = t->extid_start;
unsigned long end = t->extid_end;
if (end < ext->extid_start || ext->extid_end < start)
/* no overlap */;
else
return SBI_EINVAL;
}
SBI_INIT_LIST_HEAD(&ext->head);
sbi_list_add_tail(&ext->head, &ecall_exts_list);
return 0;
}
sbi_domain_finalize
这个函数主要完成domian域的最终初始化,具体的函数分析就不展开了。
需要注意两点:
*在HSM初始化之后完成域的最终配置,这样可以启动non-root domains
*在hart pmp之前需要完成域的最终配置,这样我们才能保证用正确的域去配置pmp
int sbi_domain_finalize(struct sbi_scratch *scratch, u32 cold_hartid)
{
int rc;
u32 i, dhart;
struct sbi_domain *dom;
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
/* Initialize and populate domains for the platform */
rc = sbi_platform_domains_init(plat);
if (rc) {
sbi_printf("%s: platform domains_init() failed (error %d)\n",
__func__, rc);
return rc;
}
/* Startup boot HART of domains */
sbi_domain_for_each(i, dom) {
/* Domain boot HART */
dhart = dom->boot_hartid;
/* Ignore of boot HART is off limits */
if (SBI_HARTMASK_MAX_BITS <= dhart)
continue;
/* Ignore if boot HART not possible for this domain */
if (!sbi_hartmask_test_hart(dhart, dom->possible_harts))
continue;
/* Ignore if boot HART assigned different domain */
if (sbi_hartid_to_domain(dhart) != dom ||
!sbi_hartmask_test_hart(dhart, &dom->assigned_harts))
continue;
/* Startup boot HART of domain */
if (dhart == cold_hartid) {
scratch->next_addr = dom->next_addr;
scratch->next_mode = dom->next_mode;
scratch->next_arg1 = dom->next_arg1;
} else {
rc = sbi_hsm_hart_start(scratch, NULL, dhart,
dom->next_addr,
dom->next_mode,
dom->next_arg1);
if (rc) {
sbi_printf("%s: failed to start boot HART %d"
" for %s (error %d)\n", __func__,
dhart, dom->name, rc);
return rc;
}
}
}
return 0;
}
domain阶段是有打印提示的:
sbi_boot_print_domains(scratch);
static void sbi_boot_print_domains(struct sbi_scratch *scratch)
{
if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS)
return;
/* Domain details */
sbi_domain_dump_all(" ");
}
sbi_hart_pmp_configure函数
对PMP配置进行初始化:
对当前hart的PMP配置进行初始化。PMP是RISC-V体系结构中提供的硬件机制,用于限制物理内存的访问。而在RISC-V系统中,一个hart可以占有多个物理内存保护区域,而PMP机制就是通过配置这些保护区域来限制内存的访问。
int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
{
/*
* dom指向sbi_domain结构体
* pmp_idx 代表PMP寄存器的索引
* pmp_flags PMP寄存器的标志位
* pmp_bits PMP地址位数
* pmp_gran_log2 PMP寄存器数量
* pmp_addr PMP寄存器地址
* pmp_addr_max PMP地址最大值
*/
struct sbi_domain_memregion *reg;
struct sbi_domain *dom = sbi_domain_thishart_ptr();
unsigned int pmp_idx = 0, pmp_flags, pmp_bits, pmp_gran_log2;
unsigned int pmp_count = sbi_hart_pmp_count(scratch);
unsigned long pmp_addr = 0, pmp_addr_max = 0;
if (!pmp_count)
return 0;
pmp_gran_log2 = log2roundup(sbi_hart_pmp_granularity(scratch));
pmp_bits = sbi_hart_pmp_addrbits(scratch) - 1;
pmp_addr_max = (1UL << pmp_bits) | ((1UL << pmp_bits) - 1);
sbi_domain_for_each_memregion(dom, reg) {
if (pmp_count <= pmp_idx)
break;
pmp_flags = 0;
if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE)
pmp_flags |= PMP_R;
if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE)
pmp_flags |= PMP_W;
if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE)
pmp_flags |= PMP_X;
if (reg->flags & SBI_DOMAIN_MEMREGION_MMODE)
pmp_flags |= PMP_L;
pmp_addr = reg->base >> PMP_SHIFT;
if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max)
pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
else {
sbi_printf("Can not configure pmp for domain %s", dom->name);
sbi_printf("because memory region address %lx or size %lx is not in range\n",
reg->base, reg->order);
}
}
return 0;
}
需要注意的是:在V1.12版本中,加入了对缓存的刷新,因为CPU可能会在实际访问之前执行虚拟地址翻译,而PMP规则或导致访问失败,因此需要确保翻译信息与PMP规则保持一致,保证在特权模式下对内存访问的正确性。
if (misa_extension('S')) {
__asm__ __volatile__("sfence.vma");
/*
* If hypervisor mode is supported, flush caching
* structures in guest mode too.
*/
if (misa_extension('H'))
__sbi_hfence_gvma_all();
}
return 0;
sbi_platform_final_init
这个函数将对platfrom的初始化进行最后的工作,可以看到在generic平台中,主要是进行fdt的修正。
/**
* Final initialization for current HART
*
* @param plat pointer to struct sbi_platform
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
*
* @return 0 on success and negative error code on failure
*/
static inline int sbi_platform_final_init(const struct sbi_platform *plat,
bool cold_boot)
{
if (plat && sbi_platform_ops(plat)->final_init)
return sbi_platform_ops(plat)->final_init(cold_boot);
return 0;
}
static int generic_final_init(bool cold_boot)
{
void *fdt;
int rc;
if (generic_plat && generic_plat->final_init) {
rc = generic_plat->final_init(cold_boot, generic_plat_match);
if (rc)
return rc;
}
if (!cold_boot)
return 0;
fdt = sbi_scratch_thishart_arg1_ptr();
fdt_cpu_fixup(fdt);
fdt_fixups(fdt);
fdt_domain_fixup(fdt);
if (generic_plat && generic_plat->fdt_fixup) {
rc = generic_plat->fdt_fixup(fdt, generic_plat_match);
if (rc)
return rc;
}
return 0;
}
wake_coldboot_harts函数
这个函数主要为了标志cold_boot的完成,向其他hart发送核间中断进行通知
static void wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid)
{
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
/* Mark coldboot done */
__smp_store_release(&coldboot_done, 1);
/* Acquire coldboot lock */
spin_lock(&coldboot_lock);
/* Send an IPI to all HARTs waiting for coldboot */
for (int i = 0; i <= sbi_scratch_last_hartid(); i++) {
if ((i != hartid) &&
sbi_hartmask_test_hart(i, &coldboot_wait_hmask))
sbi_platform_ipi_send(plat, i);
}
/* Release coldboot lock */
spin_unlock(&coldboot_lock);
}
sbi_hsm_prepare_next_jump函数
准备进行下一级的BOOT阶段,通过管理sbi_hsm_data中原子变量state,将hart的状态从SBI_HSM_HART_STATUS_START_PENDING更改为SBI_HSM_HART_STATUS_STARTED,若无法更改状态则进入wfi
struct sbi_hsm_data {
atomic_t state;
};
int sbi_hsm_hart_state_to_status(int state)
{
int ret;
switch (state) {
case SBI_HART_STOPPED:
ret = SBI_HSM_HART_STATUS_STOPPED;
break;
case SBI_HART_STOPPING:
ret = SBI_HSM_HART_STATUS_STOP_PENDING;
break;
case SBI_HART_STARTING:
ret = SBI_HSM_HART_STATUS_START_PENDING;
break;
case SBI_HART_STARTED:
ret = SBI_HSM_HART_STATUS_STARTED;
break;
default:
ret = SBI_EINVAL;
}
return ret;
}
void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid)
{
u32 oldstate;
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
hart_data_offset);
oldstate = atomic_cmpxchg(&hdata->state, SBI_HART_STARTING,
SBI_HART_STARTED);
if (oldstate != SBI_HART_STARTING)
sbi_hart_hang();
}
sbi_hart_switch_mode
最后进入上下文切换的阶段,将hart执行的上下文切换到下一个指定的特权模式(S或U),并进行了一系列的传参(填写很多CSR寄存器)
void __attribute__((noreturn))
sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
unsigned long next_addr, unsigned long next_mode,
bool next_virt)
{
#if __riscv_xlen == 32
unsigned long val, valH;
#else
unsigned long val;
#endif
switch (next_mode) {
case PRV_M:
break;
case PRV_S:
if (!misa_extension('S'))
sbi_hart_hang();
break;
case PRV_U:
if (!misa_extension('U'))
sbi_hart_hang();
break;
default:
sbi_hart_hang();
}
val = csr_read(CSR_MSTATUS);
val = INSERT_FIELD(val, MSTATUS_MPP, next_mode);
val = INSERT_FIELD(val, MSTATUS_MPIE, 0);
#if __riscv_xlen == 32
if (misa_extension('H')) {
valH = csr_read(CSR_MSTATUSH);
if (next_virt)
valH = INSERT_FIELD(valH, MSTATUSH_MPV, 1);
else
valH = INSERT_FIELD(valH, MSTATUSH_MPV, 0);
csr_write(CSR_MSTATUSH, valH);
}
#else
if (misa_extension('H')) {
if (next_virt)
val = INSERT_FIELD(val, MSTATUS_MPV, 1);
else
val = INSERT_FIELD(val, MSTATUS_MPV, 0);
}
#endif
csr_write(CSR_MSTATUS, val);
csr_write(CSR_MEPC, next_addr);
if (next_mode == PRV_S) {
csr_write(CSR_STVEC, next_addr);
csr_write(CSR_SSCRATCH, 0);
csr_write(CSR_SIE, 0);
csr_write(CSR_SATP, 0);
} else if (next_mode == PRV_U) {
if (misa_extension('N')) {
csr_write(CSR_UTVEC, next_addr);
csr_write(CSR_USCRATCH, 0);
csr_write(CSR_UIE, 0);
}
}
register unsigned long a0 asm("a0") = arg0;
register unsigned long a1 asm("a1") = arg1;
__asm__ __volatile__("mret" : : "r"(a0), "r"(a1));
__builtin_unreachable();
}
** 下面将执行U-boot或者Linux阶段的代码了,Opensbi将常驻于内存中 **
参考
https://zhuanlan.zhihu.com/p/630065971