1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Western Digital Corporation or its affiliates.
5  *
6  * Authors:
7  *   Anup Patel <anup.patel@wdc.com>
8  */
9 
10 #include <sbi/sbi_console.h>
11 #include <sbi/sbi_ecall.h>
12 #include <sbi/sbi_ecall_interface.h>
13 #include <sbi/sbi_error.h>
14 #include <sbi/sbi_ipi.h>
15 #include <sbi/sbi_platform.h>
16 #include <sbi/sbi_system.h>
17 #include <sbi/sbi_timer.h>
18 #include <sbi/sbi_tlb.h>
19 #include <sbi/sbi_trap.h>
20 #include <sbi/sbi_hart.h>
21 #include <sbi/sbi_version.h>
22 #include <sbi/riscv_asm.h>
23 
24 #define SBI_ECALL_VERSION_MAJOR 0
25 #define SBI_ECALL_VERSION_MINOR 2
26 #define SBI_OPENSBI_IMPID 1
27 
sbi_ecall_version_major(void)28 u16 sbi_ecall_version_major(void)
29 {
30 	return SBI_ECALL_VERSION_MAJOR;
31 }
32 
sbi_ecall_version_minor(void)33 u16 sbi_ecall_version_minor(void)
34 {
35 	return SBI_ECALL_VERSION_MINOR;
36 }
37 
sbi_check_extension(struct sbi_scratch * scratch,unsigned long extid,unsigned long * out_val)38 int sbi_check_extension(struct sbi_scratch *scratch, unsigned long extid,
39 			unsigned long *out_val)
40 {
41 	/**
42 	 * Each extension apart from base & 0.1, will be implemented as
43 	 * platform specific feature. Thus, extension probing can be achieved
44 	 * by checking the feature bits of the platform. We can create a map
45 	 * between extension ID & feature and use a generic function to check
46 	 * or just use a switch case for every new extension support added
47 	 * TODO: Implement it.
48 	 */
49 
50 	if ((extid >= SBI_EXT_0_1_SET_TIMER &&
51 	    extid <= SBI_EXT_0_1_SHUTDOWN) || (extid == SBI_EXT_BASE)) {
52 		*out_val = 1;
53 	} else if (extid >= SBI_EXT_VENDOR_START &&
54 		   extid <= SBI_EXT_VENDOR_END) {
55 		*out_val = sbi_platform_vendor_ext_check(
56 						sbi_platform_ptr(scratch),
57 						extid);
58 	} else
59 		*out_val = 0;
60 
61 	return 0;
62 }
63 
sbi_ecall_vendor_ext_handler(struct sbi_scratch * scratch,unsigned long extid,unsigned long funcid,unsigned long * args,unsigned long * out_val,unsigned long * out_tcause,unsigned long * out_tval)64 int sbi_ecall_vendor_ext_handler(struct sbi_scratch *scratch,
65 				 unsigned long extid, unsigned long funcid,
66 				 unsigned long *args, unsigned long *out_val,
67 				 unsigned long *out_tcause,
68 				 unsigned long *out_tval)
69 {
70 	return sbi_platform_vendor_ext_provider(sbi_platform_ptr(scratch),
71 					       extid, funcid, args, out_val,
72 					       out_tcause, out_tval);
73 }
74 
sbi_ecall_base_handler(struct sbi_scratch * scratch,unsigned long extid,unsigned long funcid,unsigned long * args,unsigned long * out_val,unsigned long * out_tcause,unsigned long * out_tval)75 int sbi_ecall_base_handler(struct sbi_scratch *scratch, unsigned long extid,
76 			   unsigned long funcid, unsigned long *args,
77 			   unsigned long *out_val, unsigned long *out_tcause,
78 			   unsigned long *out_tval)
79 {
80 	int ret = 0;
81 
82 	switch (funcid) {
83 	case SBI_EXT_BASE_GET_SPEC_VERSION:
84 		*out_val = (SBI_ECALL_VERSION_MAJOR <<
85 			   SBI_SPEC_VERSION_MAJOR_OFFSET) &
86 			   (SBI_SPEC_VERSION_MAJOR_MASK <<
87 			    SBI_SPEC_VERSION_MAJOR_OFFSET);
88 		*out_val = *out_val | SBI_ECALL_VERSION_MINOR;
89 		break;
90 	case SBI_EXT_BASE_GET_IMP_ID:
91 		*out_val = SBI_OPENSBI_IMPID;
92 		break;
93 	case SBI_EXT_BASE_GET_IMP_VERSION:
94 		*out_val = OPENSBI_VERSION;
95 		break;
96 	case SBI_EXT_BASE_GET_MVENDORID:
97 		*out_val = csr_read(CSR_MVENDORID);
98 		break;
99 	case SBI_EXT_BASE_GET_MARCHID:
100 		*out_val = csr_read(CSR_MARCHID);
101 		break;
102 	case SBI_EXT_BASE_GET_MIMPID:
103 		*out_val = csr_read(CSR_MIMPID);
104 		break;
105 	case SBI_EXT_BASE_PROBE_EXT:
106 		ret = sbi_check_extension(scratch, args[0], out_val);
107 	default:
108 		ret = SBI_ENOTSUPP;
109 	}
110 
111 	return ret;
112 }
113 
sbi_ecall_0_1_handler(struct sbi_scratch * scratch,unsigned long extid,unsigned long * args,unsigned long * tval,unsigned long * tcause)114 int sbi_ecall_0_1_handler(struct sbi_scratch *scratch, unsigned long extid,
115 			   unsigned long *args, unsigned long *tval,
116 			   unsigned long *tcause)
117 {
118 	int ret = 0;
119 	struct sbi_tlb_info tlb_info;
120 	u32 source_hart = sbi_current_hartid();
121 	struct unpriv_trap uptrap = {0};
122 
123 	switch (extid) {
124 	case SBI_EXT_0_1_SET_TIMER:
125 #if __riscv_xlen == 32
126 		sbi_timer_event_start(scratch,
127 				      (((u64)args[1] << 32) | (u64)args[0]));
128 #else
129 		sbi_timer_event_start(scratch, (u64)args[0]);
130 #endif
131 		break;
132 	case SBI_EXT_0_1_CONSOLE_PUTCHAR:
133 		sbi_putc(args[0]);
134 		break;
135 	case SBI_EXT_0_1_CONSOLE_GETCHAR:
136 		ret = sbi_getc();
137 		break;
138 	case SBI_EXT_0_1_CLEAR_IPI:
139 		sbi_ipi_clear_smode(scratch);
140 		break;
141 	case SBI_EXT_0_1_SEND_IPI:
142 		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
143 					SBI_IPI_EVENT_SOFT, NULL);
144 		break;
145 	case SBI_EXT_0_1_REMOTE_FENCE_I:
146 		tlb_info.start  = 0;
147 		tlb_info.size  = 0;
148 		tlb_info.type  = SBI_ITLB_FLUSH;
149 		tlb_info.shart_mask = 1UL << source_hart;
150 		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
151 					SBI_IPI_EVENT_FENCE_I, &tlb_info);
152 		break;
153 	case SBI_EXT_0_1_REMOTE_SFENCE_VMA:
154 		tlb_info.start = (unsigned long)args[1];
155 		tlb_info.size  = (unsigned long)args[2];
156 		tlb_info.type  = SBI_TLB_FLUSH_VMA;
157 		tlb_info.shart_mask = 1UL << source_hart;
158 
159 		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
160 					SBI_IPI_EVENT_SFENCE_VMA, &tlb_info);
161 		break;
162 	case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID:
163 		tlb_info.start = (unsigned long)args[1];
164 		tlb_info.size  = (unsigned long)args[2];
165 		tlb_info.asid  = (unsigned long)args[3];
166 		tlb_info.type  = SBI_TLB_FLUSH_VMA_ASID;
167 		tlb_info.shart_mask = 1UL << source_hart;
168 
169 		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)args[0],
170 					SBI_IPI_EVENT_SFENCE_VMA_ASID,
171 					&tlb_info);
172 		break;
173 	case SBI_EXT_0_1_SHUTDOWN:
174 		sbi_system_shutdown(scratch, 0);
175 		break;
176 	default:
177 		ret = SBI_ENOTSUPP;
178 	};
179 
180 	if (ret == SBI_ETRAP) {
181 		*tcause = uptrap.cause;
182 		*tval = uptrap.tval;
183 	}
184 	return ret;
185 }
186 
sbi_ecall_handler(u32 hartid,ulong mcause,struct sbi_trap_regs * regs,struct sbi_scratch * scratch)187 int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
188 		      struct sbi_scratch *scratch)
189 {
190 	int ret = 0;
191 	unsigned long extension_id = regs->a7;
192 	unsigned long func_id = regs->a6;
193 	unsigned long out_val;
194 	unsigned long out_tval;
195 	unsigned long out_tcause;
196 	bool is_0_1_spec = 0;
197 	unsigned long args[6];
198 
199 	args[0] = regs->a0;
200 	args[1] = regs->a1;
201 	args[2] = regs->a2;
202 	args[3] = regs->a3;
203 	args[4] = regs->a4;
204 	args[5] = regs->a5;
205 
206 	if (extension_id >= SBI_EXT_0_1_SET_TIMER &&
207 	    extension_id <= SBI_EXT_0_1_SHUTDOWN) {
208 		ret = sbi_ecall_0_1_handler(scratch, extension_id, args,
209 					    &out_tval, &out_tcause);
210 		is_0_1_spec = 1;
211 	} else if (extension_id == SBI_EXT_BASE)
212 		ret = sbi_ecall_base_handler(scratch, extension_id, func_id,
213 					     args, &out_val,
214 					     &out_tval, &out_tcause);
215 	else if (extension_id >= SBI_EXT_VENDOR_START &&
216 		extension_id <= SBI_EXT_VENDOR_END) {
217 		ret = sbi_ecall_vendor_ext_handler(scratch, extension_id,
218 						   func_id, args, &out_val,
219 						   &out_tval, &out_tcause);
220 	} else {
221 		ret = SBI_ENOTSUPP;
222 	}
223 
224 	if (ret == SBI_ETRAP) {
225 		sbi_trap_redirect(regs, scratch, regs->mepc,
226 				  out_tcause, out_tval);
227 	} else {
228 		/* This function should return non-zero value only in case of
229 		 * fatal error. However, there is no good way to distinguish
230 		 * between a fatal and non-fatal errors yet. That's why we treat
231 		 * every return value except ETRAP as non-fatal and just return
232 		 * accordingly for now. Once fatal errors are defined, that
233 		 * case should be handled differently.
234 		 */
235 		regs->mepc += 4;
236 		regs->a0 = ret;
237 		if (!is_0_1_spec)
238 			regs->a1 = out_val;
239 	}
240 
241 	return 0;
242 }
243