forked from Mirrors/opensbi
		
	lib: Add dynamic registration of SBI extensions
This patch extends our SBI ecall implementation to allow dynamic registration of various SBI extensions. Using this dynamic registration we can break-up SBI ecall implementation into multiple files and even register experimental/custom SBI extensions from platform code. Signed-off-by: Anup Patel <anup.patel@wdc.com> Reviewed-by: Atish Patra <atish.patra@wdc.com>
This commit is contained in:
		@@ -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.
 | 
			
		||||
	 */
 | 
			
		||||
	struct sbi_ecall_extension *ext;
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	ext = sbi_ecall_find_extension(extid);
 | 
			
		||||
	if (!ext) {
 | 
			
		||||
		*out_val = 0;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ext->probe)
 | 
			
		||||
		return ext->probe(scratch, extid, out_val);
 | 
			
		||||
 | 
			
		||||
	*out_val = 1;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user