xref: /kvm-unit-tests/lib/riscv/sbi.c (revision 695740795adee59b48599e2f1a6bf19866a77779)
19ccb00e4SAndrew Jones // SPDX-License-Identifier: GPL-2.0-only
29ccb00e4SAndrew Jones #include <libcflat.h>
325475fa5SAndrew Jones #include <cpumask.h>
425475fa5SAndrew Jones #include <limits.h>
598ea1f96SClément Léger #include <asm/io.h>
69ccb00e4SAndrew Jones #include <asm/sbi.h>
725475fa5SAndrew Jones #include <asm/setup.h>
89ccb00e4SAndrew Jones 
sbi_ecall(int ext,int fid,unsigned long arg0,unsigned long arg1,unsigned long arg2,unsigned long arg3,unsigned long arg4,unsigned long arg5)99ccb00e4SAndrew Jones struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
109ccb00e4SAndrew Jones 			unsigned long arg1, unsigned long arg2,
119ccb00e4SAndrew Jones 			unsigned long arg3, unsigned long arg4,
129ccb00e4SAndrew Jones 			unsigned long arg5)
139ccb00e4SAndrew Jones {
149ccb00e4SAndrew Jones 	register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
159ccb00e4SAndrew Jones 	register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
169ccb00e4SAndrew Jones 	register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
179ccb00e4SAndrew Jones 	register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
189ccb00e4SAndrew Jones 	register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
199ccb00e4SAndrew Jones 	register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
209ccb00e4SAndrew Jones 	register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
219ccb00e4SAndrew Jones 	register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
229ccb00e4SAndrew Jones 	struct sbiret ret;
239ccb00e4SAndrew Jones 
249ccb00e4SAndrew Jones 	asm volatile (
259ccb00e4SAndrew Jones 		"ecall"
269ccb00e4SAndrew Jones 		: "+r" (a0), "+r" (a1)
279ccb00e4SAndrew Jones 		: "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
289ccb00e4SAndrew Jones 		: "memory");
299ccb00e4SAndrew Jones 	ret.error = a0;
309ccb00e4SAndrew Jones 	ret.value = a1;
319ccb00e4SAndrew Jones 
329ccb00e4SAndrew Jones 	return ret;
339ccb00e4SAndrew Jones }
349ccb00e4SAndrew Jones 
sbi_sse_read_attrs_raw(unsigned long event_id,unsigned long base_attr_id,unsigned long attr_count,unsigned long phys_lo,unsigned long phys_hi)3598ea1f96SClément Léger struct sbiret sbi_sse_read_attrs_raw(unsigned long event_id, unsigned long base_attr_id,
3698ea1f96SClément Léger 				     unsigned long attr_count, unsigned long phys_lo,
3798ea1f96SClément Léger 				     unsigned long phys_hi)
3898ea1f96SClément Léger {
3998ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_READ_ATTRS, event_id, base_attr_id, attr_count,
4098ea1f96SClément Léger 			 phys_lo, phys_hi, 0);
4198ea1f96SClément Léger }
4298ea1f96SClément Léger 
sbi_sse_read_attrs(unsigned long event_id,unsigned long base_attr_id,unsigned long attr_count,unsigned long * values)4398ea1f96SClément Léger struct sbiret sbi_sse_read_attrs(unsigned long event_id, unsigned long base_attr_id,
4498ea1f96SClément Léger 				 unsigned long attr_count, unsigned long *values)
4598ea1f96SClément Léger {
4698ea1f96SClément Léger 	phys_addr_t p = virt_to_phys(values);
4798ea1f96SClément Léger 
4898ea1f96SClément Léger 	return sbi_sse_read_attrs_raw(event_id, base_attr_id, attr_count, lower_32_bits(p),
4998ea1f96SClément Léger 				      upper_32_bits(p));
5098ea1f96SClément Léger }
5198ea1f96SClément Léger 
sbi_sse_write_attrs_raw(unsigned long event_id,unsigned long base_attr_id,unsigned long attr_count,unsigned long phys_lo,unsigned long phys_hi)5298ea1f96SClément Léger struct sbiret sbi_sse_write_attrs_raw(unsigned long event_id, unsigned long base_attr_id,
5398ea1f96SClément Léger 				      unsigned long attr_count, unsigned long phys_lo,
5498ea1f96SClément Léger 				      unsigned long phys_hi)
5598ea1f96SClément Léger {
5698ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_WRITE_ATTRS, event_id, base_attr_id, attr_count,
5798ea1f96SClément Léger 			 phys_lo, phys_hi, 0);
5898ea1f96SClément Léger }
5998ea1f96SClément Léger 
sbi_sse_write_attrs(unsigned long event_id,unsigned long base_attr_id,unsigned long attr_count,unsigned long * values)6098ea1f96SClément Léger struct sbiret sbi_sse_write_attrs(unsigned long event_id, unsigned long base_attr_id,
6198ea1f96SClément Léger 				  unsigned long attr_count, unsigned long *values)
6298ea1f96SClément Léger {
6398ea1f96SClément Léger 	phys_addr_t p = virt_to_phys(values);
6498ea1f96SClément Léger 
6598ea1f96SClément Léger 	return sbi_sse_write_attrs_raw(event_id, base_attr_id, attr_count, lower_32_bits(p),
6698ea1f96SClément Léger 				       upper_32_bits(p));
6798ea1f96SClément Léger }
6898ea1f96SClément Léger 
sbi_sse_register_raw(unsigned long event_id,unsigned long entry_pc,unsigned long entry_arg)6998ea1f96SClément Léger struct sbiret sbi_sse_register_raw(unsigned long event_id, unsigned long entry_pc,
7098ea1f96SClément Léger 				   unsigned long entry_arg)
7198ea1f96SClément Léger {
7298ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, entry_pc, entry_arg, 0, 0, 0);
7398ea1f96SClément Léger }
7498ea1f96SClément Léger 
sbi_sse_register(unsigned long event_id,struct sbi_sse_handler_arg * arg)7598ea1f96SClément Léger struct sbiret sbi_sse_register(unsigned long event_id, struct sbi_sse_handler_arg *arg)
7698ea1f96SClément Léger {
7798ea1f96SClément Léger 	return sbi_sse_register_raw(event_id, (unsigned long)sbi_sse_entry, (unsigned long)arg);
7898ea1f96SClément Léger }
7998ea1f96SClément Léger 
sbi_sse_unregister(unsigned long event_id)8098ea1f96SClément Léger struct sbiret sbi_sse_unregister(unsigned long event_id)
8198ea1f96SClément Léger {
8298ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_UNREGISTER, event_id, 0, 0, 0, 0, 0);
8398ea1f96SClément Léger }
8498ea1f96SClément Léger 
sbi_sse_enable(unsigned long event_id)8598ea1f96SClément Léger struct sbiret sbi_sse_enable(unsigned long event_id)
8698ea1f96SClément Léger {
8798ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_ENABLE, event_id, 0, 0, 0, 0, 0);
8898ea1f96SClément Léger }
8998ea1f96SClément Léger 
sbi_sse_disable(unsigned long event_id)9098ea1f96SClément Léger struct sbiret sbi_sse_disable(unsigned long event_id)
9198ea1f96SClément Léger {
9298ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_DISABLE, event_id, 0, 0, 0, 0, 0);
9398ea1f96SClément Léger }
9498ea1f96SClément Léger 
sbi_sse_hart_mask(void)9598ea1f96SClément Léger struct sbiret sbi_sse_hart_mask(void)
9698ea1f96SClément Léger {
9798ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_HART_MASK, 0, 0, 0, 0, 0, 0);
9898ea1f96SClément Léger }
9998ea1f96SClément Léger 
sbi_sse_hart_unmask(void)10098ea1f96SClément Léger struct sbiret sbi_sse_hart_unmask(void)
10198ea1f96SClément Léger {
10298ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_HART_UNMASK, 0, 0, 0, 0, 0, 0);
10398ea1f96SClément Léger }
10498ea1f96SClément Léger 
sbi_sse_inject(unsigned long event_id,unsigned long hart_id)10598ea1f96SClément Léger struct sbiret sbi_sse_inject(unsigned long event_id, unsigned long hart_id)
10698ea1f96SClément Léger {
10798ea1f96SClément Léger 	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_INJECT, event_id, hart_id, 0, 0, 0, 0);
10898ea1f96SClément Léger }
10998ea1f96SClément Léger 
sbi_shutdown(void)1109ccb00e4SAndrew Jones void sbi_shutdown(void)
1119ccb00e4SAndrew Jones {
1129ccb00e4SAndrew Jones 	sbi_ecall(SBI_EXT_SRST, 0, 0, 0, 0, 0, 0, 0);
1139ccb00e4SAndrew Jones 	puts("SBI shutdown failed!\n");
1149ccb00e4SAndrew Jones }
1159c92b28eSAndrew Jones 
sbi_hart_start(unsigned long hartid,unsigned long entry,unsigned long sp)1169c92b28eSAndrew Jones struct sbiret sbi_hart_start(unsigned long hartid, unsigned long entry, unsigned long sp)
1179c92b28eSAndrew Jones {
1189c92b28eSAndrew Jones 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, entry, sp, 0, 0, 0);
1199c92b28eSAndrew Jones }
1207040d2a9SJames Raphael Tiovalen 
sbi_hart_stop(void)12167b8f462SJames Raphael Tiovalen struct sbiret sbi_hart_stop(void)
12267b8f462SJames Raphael Tiovalen {
12367b8f462SJames Raphael Tiovalen 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0);
12467b8f462SJames Raphael Tiovalen }
12567b8f462SJames Raphael Tiovalen 
sbi_hart_get_status(unsigned long hartid)12667b8f462SJames Raphael Tiovalen struct sbiret sbi_hart_get_status(unsigned long hartid)
12767b8f462SJames Raphael Tiovalen {
12867b8f462SJames Raphael Tiovalen 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STATUS, hartid, 0, 0, 0, 0, 0);
12967b8f462SJames Raphael Tiovalen }
13067b8f462SJames Raphael Tiovalen 
sbi_send_ipi(unsigned long hart_mask,unsigned long hart_mask_base)1316489b8b0SJames Raphael Tiovalen struct sbiret sbi_send_ipi(unsigned long hart_mask, unsigned long hart_mask_base)
1326489b8b0SJames Raphael Tiovalen {
1336489b8b0SJames Raphael Tiovalen 	return sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI, hart_mask, hart_mask_base, 0, 0, 0, 0);
1346489b8b0SJames Raphael Tiovalen }
1356489b8b0SJames Raphael Tiovalen 
sbi_send_ipi_cpu(int cpu)13625475fa5SAndrew Jones struct sbiret sbi_send_ipi_cpu(int cpu)
13725475fa5SAndrew Jones {
13825475fa5SAndrew Jones 	return sbi_send_ipi(1UL, cpus[cpu].hartid);
13925475fa5SAndrew Jones }
14025475fa5SAndrew Jones 
sbi_send_ipi_broadcast(void)14156ca8093SAndrew Jones struct sbiret sbi_send_ipi_broadcast(void)
14256ca8093SAndrew Jones {
14356ca8093SAndrew Jones 	return sbi_send_ipi(0, -1UL);
14456ca8093SAndrew Jones }
14556ca8093SAndrew Jones 
sbi_send_ipi_cpumask(const cpumask_t * mask)14625475fa5SAndrew Jones struct sbiret sbi_send_ipi_cpumask(const cpumask_t *mask)
14725475fa5SAndrew Jones {
14825475fa5SAndrew Jones 	struct sbiret ret;
14925475fa5SAndrew Jones 	cpumask_t tmp;
15025475fa5SAndrew Jones 
15125475fa5SAndrew Jones 	if (cpumask_full(mask))
15256ca8093SAndrew Jones 		return sbi_send_ipi_broadcast();
15325475fa5SAndrew Jones 
15425475fa5SAndrew Jones 	cpumask_copy(&tmp, mask);
15525475fa5SAndrew Jones 
15625475fa5SAndrew Jones 	while (!cpumask_empty(&tmp)) {
15725475fa5SAndrew Jones 		unsigned long base = ULONG_MAX;
15825475fa5SAndrew Jones 		unsigned long mask = 0;
15925475fa5SAndrew Jones 		int cpu;
16025475fa5SAndrew Jones 
16125475fa5SAndrew Jones 		for_each_cpu(cpu, &tmp) {
16225475fa5SAndrew Jones 			if (base > cpus[cpu].hartid)
16325475fa5SAndrew Jones 				base = cpus[cpu].hartid;
16425475fa5SAndrew Jones 		}
16525475fa5SAndrew Jones 
16625475fa5SAndrew Jones 		for_each_cpu(cpu, &tmp) {
16725475fa5SAndrew Jones 			if (cpus[cpu].hartid < base + BITS_PER_LONG) {
16825475fa5SAndrew Jones 				mask |= 1UL << (cpus[cpu].hartid - base);
16925475fa5SAndrew Jones 				cpumask_clear_cpu(cpu, &tmp);
17025475fa5SAndrew Jones 			}
17125475fa5SAndrew Jones 		}
17225475fa5SAndrew Jones 
17325475fa5SAndrew Jones 		ret = sbi_send_ipi(mask, base);
17425475fa5SAndrew Jones 		if (ret.error)
17525475fa5SAndrew Jones 			break;
17625475fa5SAndrew Jones 	}
17725475fa5SAndrew Jones 
17825475fa5SAndrew Jones 	return ret;
17925475fa5SAndrew Jones }
18025475fa5SAndrew Jones 
sbi_set_timer(unsigned long stime_value)1819340e4b7SAndrew Jones struct sbiret sbi_set_timer(unsigned long stime_value)
1829340e4b7SAndrew Jones {
1839340e4b7SAndrew Jones 	return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
1849340e4b7SAndrew Jones }
1859340e4b7SAndrew Jones 
sbi_get_imp_version(void)186*a3fc8778SAndrew Jones struct sbiret sbi_get_imp_version(void)
187*a3fc8778SAndrew Jones {
188*a3fc8778SAndrew Jones 	return sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_IMP_VERSION, 0, 0, 0, 0, 0, 0);
189*a3fc8778SAndrew Jones }
190*a3fc8778SAndrew Jones 
sbi_get_imp_id(void)191*a3fc8778SAndrew Jones struct sbiret sbi_get_imp_id(void)
192*a3fc8778SAndrew Jones {
193*a3fc8778SAndrew Jones 	return sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_IMP_ID, 0, 0, 0, 0, 0, 0);
194*a3fc8778SAndrew Jones }
195*a3fc8778SAndrew Jones 
__sbi_get_imp_version(void)196*a3fc8778SAndrew Jones unsigned long __sbi_get_imp_version(void)
197240729eeSClément Léger {
198240729eeSClément Léger 	struct sbiret ret;
199240729eeSClément Léger 
200*a3fc8778SAndrew Jones 	ret = sbi_get_imp_version();
201240729eeSClément Léger 	assert(!ret.error);
202240729eeSClément Léger 
203240729eeSClément Léger 	return ret.value;
204240729eeSClément Léger }
205240729eeSClément Léger 
__sbi_get_imp_id(void)206*a3fc8778SAndrew Jones unsigned long __sbi_get_imp_id(void)
207240729eeSClément Léger {
208240729eeSClément Léger 	struct sbiret ret;
209240729eeSClément Léger 
210*a3fc8778SAndrew Jones 	ret = sbi_get_imp_id();
211240729eeSClément Léger 	assert(!ret.error);
212240729eeSClément Léger 
213240729eeSClément Léger 	return ret.value;
214240729eeSClément Léger }
215240729eeSClément Léger 
sbi_get_spec_version(void)216b9d58c27SClément Léger struct sbiret sbi_get_spec_version(void)
217b9d58c27SClément Léger {
218b9d58c27SClément Léger 	return sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0, 0, 0, 0, 0, 0);
219b9d58c27SClément Léger }
220b9d58c27SClément Léger 
sbi_probe(int ext)2217040d2a9SJames Raphael Tiovalen long sbi_probe(int ext)
2227040d2a9SJames Raphael Tiovalen {
2237040d2a9SJames Raphael Tiovalen 	struct sbiret ret;
2247040d2a9SJames Raphael Tiovalen 
225b9d58c27SClément Léger 	ret = sbi_get_spec_version();
226b9d58c27SClément Léger 	assert(!ret.error && (ret.value & SBI_SPEC_VERSION_MASK) >= sbi_mk_version(0, 2));
2277040d2a9SJames Raphael Tiovalen 
2287040d2a9SJames Raphael Tiovalen 	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, ext, 0, 0, 0, 0, 0);
2297040d2a9SJames Raphael Tiovalen 	assert(!ret.error);
2307040d2a9SJames Raphael Tiovalen 
2317040d2a9SJames Raphael Tiovalen 	return ret.value;
2327040d2a9SJames Raphael Tiovalen }
233