本文以OpenSBI 0.9版本为参考进行讲解
现在分析kernel是如何调用sbi里面的函数的
-
在sbi的初始化过程中,在fw_base.S里面会设置M mode的异常处理函数:
la a4, _trap_handler
…
csrw CSR_MTVEC, a4
设置完成后,只要发生M mode的异常就会进入到_trap_handler里面处理,
_trap_handler这个函数在fw_base.S里面有定义,是汇编代码,实现了异常/中断调用的各个流程,
_trap_handler:
TRAP_SAVE_AND_SETUP_SP_T0TRAP_SAVE_MEPC_MSTATUS 0 TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 TRAP_CALL_C_ROUTINE --------------------------这里进入异常处理的c函数,也就是sbi_trap_handler TRAP_RESTORE_GENERAL_REGS_EXCEPT_SP_T0 TRAP_RESTORE_MEPC_MSTATUS 0 TRAP_RESTORE_SP_T0 mretsbi_trap_handler函数实现在/lib/sbi/sbi_trap.c里面。
发生异常/中断的时候会进入到sbi_trap_handler函数,根据 mcause = csr_read(CSR_MCAUSE);
的值,进入不同的处理分支,kernel下发的ecall会进入到:
case CAUSE_SUPERVISOR_ECALL:
case CAUSE_MACHINE_ECALL:
rc = sbi_ecall_handler(regs);
这里面,接着调用 sbi_ecall_find_extension(extension_id);
struct sbi_ecall_extension *sbi_ecall_find_extension(unsigned long extid)
{
struct sbi_ecall_extension *t, *ret = NULL;sbi_list_for_each_entry(t, &ecall_exts_list, head) { if (t->extid_start <= extid && extid <= t->extid_end) { ret = t; break; } } return ret; }通过extid获取到具体extension的handler,所有的异常处理在sbi初始化阶段,调用sbi_ecall_init
的时候,全部注册在了ecall_exts_list里面。这个时候会从ecall_exts_list里面找到
对应的handler。 -
以sbi_ecall_init()为例,介绍异常处理handler的处理过程
2.1 注册了很多的异常回调handler
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; }2.2
sbi_ecall_register_extension(&ecall_hsm);
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; } 会把ecall_hsm放到ecall_exts_list链表里面,ecall_hsm里面有extid和具体的handler。 struct sbi_ecall_extension ecall_hsm = { .extid_start = SBI_EXT_HSM, .extid_end = SBI_EXT_HSM, .handle = sbi_ecall_hsm_handler, };
到这里,sbi中M mode的异常处理已经注册完成,这时候只要出发M mode的异常就会进入M mode,进入对应的handler。
-
kernel是如何出发M mode的异常的:
3.1
在arch/riscv/kernel/sbi.c里面有sbi_ecall()调用函数,kernel可以通过sbi_ecall来出发M mode的异常处理。
struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5)
{
struct sbiret ret;register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4); register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5); register uintptr_t a6 asm ("a6") = (uintptr_t)(fid); register uintptr_t a7 asm ("a7") = (uintptr_t)(ext); asm volatile ("ecall" : "+r" (a0), "+r" (a1) : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7) : "memory"); ret.error = a0; ret.value = a1; return ret; } 各种异常都是通过sbi_ecall来实现的,在sbi.c以及cpu_ops_sbi.c里面已经封装了各种的实现, 如启动core的函数sbi_hsm_hart_start -> sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, saddr, priv, 0, 0, 0); 最终调用的就是sbi里面注册的ecall_hsm。
1万+



被折叠的 条评论
为什么被折叠?



