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