xref: /kvm-unit-tests/lib/riscv/sbi.c (revision 240729eef4e29e5698ad2150435d7f44036ec1d8)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <libcflat.h>
3 #include <cpumask.h>
4 #include <limits.h>
5 #include <asm/sbi.h>
6 #include <asm/setup.h>
7 
8 struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
9 			unsigned long arg1, unsigned long arg2,
10 			unsigned long arg3, unsigned long arg4,
11 			unsigned long arg5)
12 {
13 	register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
14 	register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
15 	register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
16 	register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
17 	register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
18 	register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
19 	register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
20 	register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
21 	struct sbiret ret;
22 
23 	asm volatile (
24 		"ecall"
25 		: "+r" (a0), "+r" (a1)
26 		: "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
27 		: "memory");
28 	ret.error = a0;
29 	ret.value = a1;
30 
31 	return ret;
32 }
33 
34 void sbi_shutdown(void)
35 {
36 	sbi_ecall(SBI_EXT_SRST, 0, 0, 0, 0, 0, 0, 0);
37 	puts("SBI shutdown failed!\n");
38 }
39 
40 struct sbiret sbi_hart_start(unsigned long hartid, unsigned long entry, unsigned long sp)
41 {
42 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, entry, sp, 0, 0, 0);
43 }
44 
45 struct sbiret sbi_hart_stop(void)
46 {
47 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0);
48 }
49 
50 struct sbiret sbi_hart_get_status(unsigned long hartid)
51 {
52 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STATUS, hartid, 0, 0, 0, 0, 0);
53 }
54 
55 struct sbiret sbi_send_ipi(unsigned long hart_mask, unsigned long hart_mask_base)
56 {
57 	return sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI, hart_mask, hart_mask_base, 0, 0, 0, 0);
58 }
59 
60 struct sbiret sbi_send_ipi_cpu(int cpu)
61 {
62 	return sbi_send_ipi(1UL, cpus[cpu].hartid);
63 }
64 
65 struct sbiret sbi_send_ipi_broadcast(void)
66 {
67 	return sbi_send_ipi(0, -1UL);
68 }
69 
70 struct sbiret sbi_send_ipi_cpumask(const cpumask_t *mask)
71 {
72 	struct sbiret ret;
73 	cpumask_t tmp;
74 
75 	if (cpumask_full(mask))
76 		return sbi_send_ipi_broadcast();
77 
78 	cpumask_copy(&tmp, mask);
79 
80 	while (!cpumask_empty(&tmp)) {
81 		unsigned long base = ULONG_MAX;
82 		unsigned long mask = 0;
83 		int cpu;
84 
85 		for_each_cpu(cpu, &tmp) {
86 			if (base > cpus[cpu].hartid)
87 				base = cpus[cpu].hartid;
88 		}
89 
90 		for_each_cpu(cpu, &tmp) {
91 			if (cpus[cpu].hartid < base + BITS_PER_LONG) {
92 				mask |= 1UL << (cpus[cpu].hartid - base);
93 				cpumask_clear_cpu(cpu, &tmp);
94 			}
95 		}
96 
97 		ret = sbi_send_ipi(mask, base);
98 		if (ret.error)
99 			break;
100 	}
101 
102 	return ret;
103 }
104 
105 struct sbiret sbi_set_timer(unsigned long stime_value)
106 {
107 	return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
108 }
109 
110 unsigned long sbi_get_imp_version(void)
111 {
112 	struct sbiret ret;
113 
114 	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_IMP_VERSION, 0, 0, 0, 0, 0, 0);
115 	assert(!ret.error);
116 
117 	return ret.value;
118 }
119 
120 unsigned long sbi_get_imp_id(void)
121 {
122 	struct sbiret ret;
123 
124 	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_IMP_ID, 0, 0, 0, 0, 0, 0);
125 	assert(!ret.error);
126 
127 	return ret.value;
128 }
129 
130 struct sbiret sbi_get_spec_version(void)
131 {
132 	return sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0, 0, 0, 0, 0, 0);
133 }
134 
135 long sbi_probe(int ext)
136 {
137 	struct sbiret ret;
138 
139 	ret = sbi_get_spec_version();
140 	assert(!ret.error && (ret.value & SBI_SPEC_VERSION_MASK) >= sbi_mk_version(0, 2));
141 
142 	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, ext, 0, 0, 0, 0, 0);
143 	assert(!ret.error);
144 
145 	return ret.value;
146 }
147