diff options
-rw-r--r-- | include/sbi/sbi_ecall.h | 22 | ||||
-rw-r--r-- | lib/sbi/sbi_ecall.c | 240 | ||||
-rw-r--r-- | lib/sbi/sbi_init.c | 4 |
3 files changed, 200 insertions, 66 deletions
diff --git a/include/sbi/sbi_ecall.h b/include/sbi/sbi_ecall.h index a5b47a1..e9a9ef6 100644 --- a/include/sbi/sbi_ecall.h +++ b/include/sbi/sbi_ecall.h @@ -11,15 +11,37 @@ #define __SBI_ECALL_H__ #include <sbi/sbi_types.h> +#include <sbi/sbi_list.h> struct sbi_trap_regs; +struct sbi_trap_info; struct sbi_scratch; +struct sbi_ecall_extension { + struct sbi_dlist head; + unsigned long extid_start; + unsigned long extid_end; + int (* probe)(struct sbi_scratch *scratch, + unsigned long extid, unsigned long *out_val); + int (* handle)(struct sbi_scratch *scratch, + unsigned long extid, unsigned long funcid, + unsigned long *args, unsigned long *out_val, + struct sbi_trap_info *out_trap); +}; + u16 sbi_ecall_version_major(void); u16 sbi_ecall_version_minor(void); +struct sbi_ecall_extension *sbi_ecall_find_extension(unsigned long extid); + +int sbi_ecall_register_extension(struct sbi_ecall_extension *ext); + +void sbi_ecall_unregister_extension(struct sbi_ecall_extension *ext); + int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs, struct sbi_scratch *scratch); +int sbi_ecall_init(void); + #endif diff --git a/lib/sbi/sbi_ecall.c b/lib/sbi/sbi_ecall.c index 8d136cc..1c49d79 100644 --- a/lib/sbi/sbi_ecall.c +++ b/lib/sbi/sbi_ecall.c @@ -52,49 +52,29 @@ static int sbi_load_hart_mask_unpriv(struct sbi_scratch *scratch, ulong *pmask, return 0; } -int sbi_check_extension(struct sbi_scratch *scratch, unsigned long extid, - unsigned long *out_val) +static int sbi_ecall_base_probe(struct sbi_scratch *scratch, + unsigned long extid, + unsigned long *out_val) { - /** - * Each extension apart from base & 0.1, will be implemented as - * platform specific feature. Thus, extension probing can be achieved - * by checking the feature bits of the platform. We can create a map - * between extension ID & feature and use a generic function to check - * or just use a switch case for every new extension support added - * TODO: Implement it. - */ - - if ((extid >= SBI_EXT_0_1_SET_TIMER && extid <= SBI_EXT_0_1_SHUTDOWN) || - (extid == SBI_EXT_BASE) || - (extid == SBI_EXT_TIME) || - (extid == SBI_EXT_IPI) || - (extid == SBI_EXT_RFENCE)) { - *out_val = 1; - } else if (extid >= SBI_EXT_VENDOR_START && - extid <= SBI_EXT_VENDOR_END) { - *out_val = sbi_platform_vendor_ext_check( - sbi_platform_ptr(scratch), - extid); - } else + struct sbi_ecall_extension *ext; + + ext = sbi_ecall_find_extension(extid); + if (!ext) { *out_val = 0; + return 0; + } - return 0; -} + if (ext->probe) + return ext->probe(scratch, extid, out_val); -int sbi_ecall_vendor_ext_handler(struct sbi_scratch *scratch, - unsigned long extid, unsigned long funcid, - unsigned long *args, unsigned long *out_val, - struct sbi_trap_info *out_trap) -{ - return sbi_platform_vendor_ext_provider(sbi_platform_ptr(scratch), - extid, funcid, args, - out_val, out_trap); + *out_val = 1; + return 0; } -int sbi_ecall_base_handler(struct sbi_scratch *scratch, - unsigned long extid, unsigned long funcid, - unsigned long *args, unsigned long *out_val, - struct sbi_trap_info *out_trap) +static int sbi_ecall_base_handler(struct sbi_scratch *scratch, + unsigned long extid, unsigned long funcid, + unsigned long *args, unsigned long *out_val, + struct sbi_trap_info *out_trap) { int ret = 0; @@ -122,7 +102,7 @@ int sbi_ecall_base_handler(struct sbi_scratch *scratch, *out_val = csr_read(CSR_MIMPID); break; case SBI_EXT_BASE_PROBE_EXT: - ret = sbi_check_extension(scratch, args[0], out_val); + ret = sbi_ecall_base_probe(scratch, args[0], out_val); break; default: ret = SBI_ENOTSUPP; @@ -131,8 +111,16 @@ int sbi_ecall_base_handler(struct sbi_scratch *scratch, return ret; } -int sbi_ecall_time_handler(struct sbi_scratch *scratch, unsigned long funcid, - unsigned long *args) +static struct sbi_ecall_extension ecall_base = { + .extid_start = SBI_EXT_BASE, + .extid_end = SBI_EXT_BASE, + .handle = sbi_ecall_base_handler, +}; + +static int sbi_ecall_time_handler(struct sbi_scratch *scratch, + unsigned long extid, unsigned long funcid, + unsigned long *args, unsigned long *out_val, + struct sbi_trap_info *out_trap) { int ret = 0; @@ -149,8 +137,16 @@ int sbi_ecall_time_handler(struct sbi_scratch *scratch, unsigned long funcid, return ret; } -int sbi_ecall_ipi_handler(struct sbi_scratch *scratch, unsigned long funcid, - unsigned long *args, unsigned long *tval) +static struct sbi_ecall_extension ecall_time = { + .extid_start = SBI_EXT_TIME, + .extid_end = SBI_EXT_TIME, + .handle = sbi_ecall_time_handler, +}; + +static int sbi_ecall_ipi_handler(struct sbi_scratch *scratch, + unsigned long extid, unsigned long funcid, + unsigned long *args, unsigned long *out_val, + struct sbi_trap_info *out_trap) { int ret = 0; @@ -162,8 +158,16 @@ int sbi_ecall_ipi_handler(struct sbi_scratch *scratch, unsigned long funcid, return ret; } -int sbi_ecall_rfence_handler(struct sbi_scratch *scratch, unsigned long funcid, - unsigned long *args, unsigned long *tval) +static struct sbi_ecall_extension ecall_ipi = { + .extid_start = SBI_EXT_IPI, + .extid_end = SBI_EXT_IPI, + .handle = sbi_ecall_ipi_handler, +}; + +static int sbi_ecall_rfence_handler(struct sbi_scratch *scratch, + unsigned long extid, unsigned long funcid, + unsigned long *args, unsigned long *out_val, + struct sbi_trap_info *out_trap) { int ret = 0; struct sbi_tlb_info tlb_info; @@ -235,8 +239,16 @@ int sbi_ecall_rfence_handler(struct sbi_scratch *scratch, unsigned long funcid, return ret; } -int sbi_ecall_0_1_handler(struct sbi_scratch *scratch, unsigned long extid, - unsigned long *args, struct sbi_trap_info *out_trap) +static struct sbi_ecall_extension ecall_rfence = { + .extid_start = SBI_EXT_RFENCE, + .extid_end = SBI_EXT_RFENCE, + .handle = sbi_ecall_rfence_handler, +}; + +static int sbi_ecall_0_1_handler(struct sbi_scratch *scratch, + unsigned long extid, unsigned long funcid, + unsigned long *args, unsigned long *out_val, + struct sbi_trap_info *out_trap) { int ret = 0; struct sbi_tlb_info tlb_info; @@ -308,10 +320,92 @@ int sbi_ecall_0_1_handler(struct sbi_scratch *scratch, unsigned long extid, return ret; } +static struct sbi_ecall_extension ecall_0_1 = { + .extid_start = SBI_EXT_0_1_SET_TIMER, + .extid_end = SBI_EXT_0_1_SHUTDOWN, + .handle = sbi_ecall_0_1_handler, +}; + +static int sbi_ecall_vendor_probe(struct sbi_scratch *scratch, + unsigned long extid, + unsigned long *out_val) +{ + *out_val = sbi_platform_vendor_ext_check(sbi_platform_ptr(scratch), + extid); + return 0; +} + +static int sbi_ecall_vendor_handler(struct sbi_scratch *scratch, + unsigned long extid, unsigned long funcid, + unsigned long *args, unsigned long *out_val, + struct sbi_trap_info *out_trap) +{ + return sbi_platform_vendor_ext_provider(sbi_platform_ptr(scratch), + extid, funcid, args, + out_val, out_trap); +} + +static struct sbi_ecall_extension ecall_vendor = { + .extid_start = SBI_EXT_VENDOR_START, + .extid_end = SBI_EXT_VENDOR_END, + .probe = sbi_ecall_vendor_probe, + .handle = sbi_ecall_vendor_handler, +}; + +static SBI_LIST_HEAD(ecall_exts_list); + +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; +} + +int sbi_ecall_register_extension(struct sbi_ecall_extension *ext) +{ + if (!ext || (ext->extid_end < ext->extid_start) || !ext->handle) + return SBI_EINVAL; + if (sbi_ecall_find_extension(ext->extid_start) || + sbi_ecall_find_extension(ext->extid_end)) + return SBI_EINVAL; + + SBI_INIT_LIST_HEAD(&ext->head); + sbi_list_add_tail(&ext->head, &ecall_exts_list); + + return 0; +} + +void sbi_ecall_unregister_extension(struct sbi_ecall_extension *ext) +{ + bool found = FALSE; + struct sbi_ecall_extension *t; + + if (!ext) + return; + + sbi_list_for_each_entry(t, &ecall_exts_list, head) { + if (t == ext) { + found = TRUE; + break; + } + } + + if (found) + sbi_list_del_init(&ext->head); +} + int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs, struct sbi_scratch *scratch) { int ret = 0; + struct sbi_ecall_extension *ext; unsigned long extension_id = regs->a7; unsigned long func_id = regs->a6; struct sbi_trap_info trap = {0}; @@ -326,26 +420,13 @@ int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs, args[4] = regs->a4; args[5] = regs->a5; - if (extension_id >= SBI_EXT_0_1_SET_TIMER && - extension_id <= SBI_EXT_0_1_SHUTDOWN) { - ret = sbi_ecall_0_1_handler(scratch, extension_id, - args, &trap); - is_0_1_spec = 1; - } else if (extension_id == SBI_EXT_BASE) - ret = sbi_ecall_base_handler(scratch, extension_id, func_id, - args, &out_val, &trap); - else if (extension_id == SBI_EXT_TIME) - ret = sbi_ecall_time_handler(scratch, func_id, args); - else if (extension_id == SBI_EXT_IPI) - ret = sbi_ecall_ipi_handler(scratch, func_id, args, &out_val); - else if (extension_id == SBI_EXT_RFENCE) - ret = sbi_ecall_rfence_handler(scratch, func_id, - args, &out_val); - else if (extension_id >= SBI_EXT_VENDOR_START && - extension_id <= SBI_EXT_VENDOR_END) { - ret = sbi_ecall_vendor_ext_handler(scratch, extension_id, - func_id, args, &out_val, - &trap); + ext = sbi_ecall_find_extension(extension_id); + if (ext && ext->handle) { + ret = ext->handle(scratch, extension_id, func_id, + args, &out_val, &trap); + if (extension_id >= SBI_EXT_0_1_SET_TIMER && + extension_id <= SBI_EXT_0_1_SHUTDOWN) + is_0_1_spec = 1; } else { ret = SBI_ENOTSUPP; } @@ -369,3 +450,30 @@ int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs, return 0; } + +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_0_1); + if (ret) + return ret; + ret = sbi_ecall_register_extension(&ecall_vendor); + if (ret) + return ret; + + return 0; +} diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index c5a899c..8baaed4 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -112,6 +112,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) if (rc) sbi_hart_hang(); + rc = sbi_ecall_init(); + if (rc) + sbi_hart_hang(); + rc = sbi_system_final_init(scratch, TRUE); if (rc) sbi_hart_hang(); |