xref: /kvm-unit-tests/lib/riscv/sbi.c (revision 25475fa5487ee8b6a7bfe43441d1bd169330c3ae)
19ccb00e4SAndrew Jones // SPDX-License-Identifier: GPL-2.0-only
29ccb00e4SAndrew Jones #include <libcflat.h>
3*25475fa5SAndrew Jones #include <cpumask.h>
4*25475fa5SAndrew Jones #include <limits.h>
59ccb00e4SAndrew Jones #include <asm/sbi.h>
6*25475fa5SAndrew Jones #include <asm/setup.h>
79ccb00e4SAndrew Jones 
89ccb00e4SAndrew Jones struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
99ccb00e4SAndrew Jones 			unsigned long arg1, unsigned long arg2,
109ccb00e4SAndrew Jones 			unsigned long arg3, unsigned long arg4,
119ccb00e4SAndrew Jones 			unsigned long arg5)
129ccb00e4SAndrew Jones {
139ccb00e4SAndrew Jones 	register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
149ccb00e4SAndrew Jones 	register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
159ccb00e4SAndrew Jones 	register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
169ccb00e4SAndrew Jones 	register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
179ccb00e4SAndrew Jones 	register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
189ccb00e4SAndrew Jones 	register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
199ccb00e4SAndrew Jones 	register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
209ccb00e4SAndrew Jones 	register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
219ccb00e4SAndrew Jones 	struct sbiret ret;
229ccb00e4SAndrew Jones 
239ccb00e4SAndrew Jones 	asm volatile (
249ccb00e4SAndrew Jones 		"ecall"
259ccb00e4SAndrew Jones 		: "+r" (a0), "+r" (a1)
269ccb00e4SAndrew Jones 		: "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
279ccb00e4SAndrew Jones 		: "memory");
289ccb00e4SAndrew Jones 	ret.error = a0;
299ccb00e4SAndrew Jones 	ret.value = a1;
309ccb00e4SAndrew Jones 
319ccb00e4SAndrew Jones 	return ret;
329ccb00e4SAndrew Jones }
339ccb00e4SAndrew Jones 
349ccb00e4SAndrew Jones void sbi_shutdown(void)
359ccb00e4SAndrew Jones {
369ccb00e4SAndrew Jones 	sbi_ecall(SBI_EXT_SRST, 0, 0, 0, 0, 0, 0, 0);
379ccb00e4SAndrew Jones 	puts("SBI shutdown failed!\n");
389ccb00e4SAndrew Jones }
399c92b28eSAndrew Jones 
409c92b28eSAndrew Jones struct sbiret sbi_hart_start(unsigned long hartid, unsigned long entry, unsigned long sp)
419c92b28eSAndrew Jones {
429c92b28eSAndrew Jones 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, entry, sp, 0, 0, 0);
439c92b28eSAndrew Jones }
447040d2a9SJames Raphael Tiovalen 
456489b8b0SJames Raphael Tiovalen struct sbiret sbi_send_ipi(unsigned long hart_mask, unsigned long hart_mask_base)
466489b8b0SJames Raphael Tiovalen {
476489b8b0SJames Raphael Tiovalen 	return sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI, hart_mask, hart_mask_base, 0, 0, 0, 0);
486489b8b0SJames Raphael Tiovalen }
496489b8b0SJames Raphael Tiovalen 
50*25475fa5SAndrew Jones struct sbiret sbi_send_ipi_cpu(int cpu)
51*25475fa5SAndrew Jones {
52*25475fa5SAndrew Jones 	return sbi_send_ipi(1UL, cpus[cpu].hartid);
53*25475fa5SAndrew Jones }
54*25475fa5SAndrew Jones 
55*25475fa5SAndrew Jones struct sbiret sbi_send_ipi_cpumask(const cpumask_t *mask)
56*25475fa5SAndrew Jones {
57*25475fa5SAndrew Jones 	struct sbiret ret;
58*25475fa5SAndrew Jones 	cpumask_t tmp;
59*25475fa5SAndrew Jones 
60*25475fa5SAndrew Jones 	if (cpumask_full(mask))
61*25475fa5SAndrew Jones 		return sbi_send_ipi(0, -1UL);
62*25475fa5SAndrew Jones 
63*25475fa5SAndrew Jones 	cpumask_copy(&tmp, mask);
64*25475fa5SAndrew Jones 
65*25475fa5SAndrew Jones 	while (!cpumask_empty(&tmp)) {
66*25475fa5SAndrew Jones 		unsigned long base = ULONG_MAX;
67*25475fa5SAndrew Jones 		unsigned long mask = 0;
68*25475fa5SAndrew Jones 		int cpu;
69*25475fa5SAndrew Jones 
70*25475fa5SAndrew Jones 		for_each_cpu(cpu, &tmp) {
71*25475fa5SAndrew Jones 			if (base > cpus[cpu].hartid)
72*25475fa5SAndrew Jones 				base = cpus[cpu].hartid;
73*25475fa5SAndrew Jones 		}
74*25475fa5SAndrew Jones 
75*25475fa5SAndrew Jones 		for_each_cpu(cpu, &tmp) {
76*25475fa5SAndrew Jones 			if (cpus[cpu].hartid < base + BITS_PER_LONG) {
77*25475fa5SAndrew Jones 				mask |= 1UL << (cpus[cpu].hartid - base);
78*25475fa5SAndrew Jones 				cpumask_clear_cpu(cpu, &tmp);
79*25475fa5SAndrew Jones 			}
80*25475fa5SAndrew Jones 		}
81*25475fa5SAndrew Jones 
82*25475fa5SAndrew Jones 		ret = sbi_send_ipi(mask, base);
83*25475fa5SAndrew Jones 		if (ret.error)
84*25475fa5SAndrew Jones 			break;
85*25475fa5SAndrew Jones 	}
86*25475fa5SAndrew Jones 
87*25475fa5SAndrew Jones 	return ret;
88*25475fa5SAndrew Jones }
89*25475fa5SAndrew Jones 
909340e4b7SAndrew Jones struct sbiret sbi_set_timer(unsigned long stime_value)
919340e4b7SAndrew Jones {
929340e4b7SAndrew Jones 	return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
939340e4b7SAndrew Jones }
949340e4b7SAndrew Jones 
957040d2a9SJames Raphael Tiovalen long sbi_probe(int ext)
967040d2a9SJames Raphael Tiovalen {
977040d2a9SJames Raphael Tiovalen 	struct sbiret ret;
987040d2a9SJames Raphael Tiovalen 
997040d2a9SJames Raphael Tiovalen 	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0, 0, 0, 0, 0, 0);
1007040d2a9SJames Raphael Tiovalen 	assert(!ret.error && ret.value >= 2);
1017040d2a9SJames Raphael Tiovalen 
1027040d2a9SJames Raphael Tiovalen 	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, ext, 0, 0, 0, 0, 0);
1037040d2a9SJames Raphael Tiovalen 	assert(!ret.error);
1047040d2a9SJames Raphael Tiovalen 
1057040d2a9SJames Raphael Tiovalen 	return ret.value;
1067040d2a9SJames Raphael Tiovalen }
107