1 /*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <arch/riscv/sbi.h>
9
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <lk/debug.h>
13 #include <lk/trace.h>
14 #include <lk/err.h>
15 #include <arch/riscv.h>
16
17 #include "riscv_priv.h"
18
19 #if RISCV_S_MODE
20
21 // bitmap of locally detected SBI extensions
22 enum sbi_extension {
23 SBI_EXTENSION_TIMER = 1,
24 SBI_EXTENSION_IPI,
25 SBI_EXTENSION_RFENCE,
26 SBI_EXTENSION_HSM,
27 SBI_EXTENSION_SRST,
28 };
29
30 static uint sbi_ext;
31
32 // make a SBI call according to the SBI spec at https://github.com/riscv/riscv-sbi-doc
33 // Note: it seems ambigious whether or not a2-a7 are trashed in the call, but the
34 // OpenSBI and linux implementations seem to assume that all of the regs are restored
35 // aside from a0 and a1 which are used for return values.
36 #define _sbi_call(extension, function, arg0, arg1, arg2, arg3, arg4, arg5, ...) ({ \
37 register unsigned long a0 asm("a0") = (unsigned long)arg0; \
38 register unsigned long a1 asm("a1") = (unsigned long)arg1; \
39 register unsigned long a2 asm("a2") = (unsigned long)arg2; \
40 register unsigned long a3 asm("a3") = (unsigned long)arg3; \
41 register unsigned long a4 asm("a4") = (unsigned long)arg4; \
42 register unsigned long a5 asm("a5") = (unsigned long)arg5; \
43 register unsigned long a6 asm("a6") = (unsigned long)function; \
44 register unsigned long a7 asm("a7") = (unsigned long)extension; \
45 asm volatile ("ecall" \
46 : "+r" (a0), "+r" (a1) \
47 : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r"(a6), "r"(a7) \
48 : "memory"); \
49 (struct sbiret){ .error = a0, .value = a1 }; \
50 })
51 #define sbi_call(...) \
52 _sbi_call(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0)
53
sbi_ext_present(enum sbi_extension e)54 static inline bool sbi_ext_present(enum sbi_extension e) {
55 return sbi_ext & (1 << e);
56 }
57
sbi_generic_call_2(ulong extension,ulong function)58 struct sbiret sbi_generic_call_2(ulong extension, ulong function) {
59 return sbi_call(extension, function);
60 }
61
sbi_probe_extension(ulong extension)62 bool sbi_probe_extension(ulong extension) {
63 return sbi_call(SBI_PROBE_EXTENSION, extension).value != 0;
64 }
65
sbi_set_timer(uint64_t stime_value)66 void sbi_set_timer(uint64_t stime_value) {
67 // use the new IPI extension
68 if (likely(sbi_ext_present(SBI_EXTENSION_TIMER))) {
69 sbi_call(SBI_EXT_TIMER_SIG, 0, stime_value);
70 } else {
71 sbi_call(SBI_SET_TIMER, stime_value);
72 }
73 }
74
sbi_send_ipis(const unsigned long * hart_mask)75 void sbi_send_ipis(const unsigned long *hart_mask) {
76 // use the new IPI extension
77 if (likely(sbi_ext_present(SBI_EXTENSION_IPI))) {
78 sbi_call(SBI_EXT_IPI_SIG, 0, *hart_mask, -1);
79 } else {
80 // legacy ipi call
81 sbi_call(SBI_SEND_IPI, hart_mask);
82 }
83 }
84
sbi_clear_ipi(void)85 void sbi_clear_ipi(void) {
86 // deprecated, clear sip.SSIP directly
87 riscv_csr_clear(RISCV_CSR_XIP, RISCV_CSR_XIP_SIP);
88 //sbi_call(SBI_CLEAR_IPI);
89 }
90
sbi_boot_hart(uint hartid,paddr_t start_addr,ulong arg)91 status_t sbi_boot_hart(uint hartid, paddr_t start_addr, ulong arg) {
92 if (!sbi_ext_present(SBI_EXTENSION_HSM))
93 return ERR_NOT_IMPLEMENTED;
94
95 // try to use the HSM implementation to boot a cpu
96 struct sbiret ret = sbi_call(SBI_EXT_HSM_SIG, 0, hartid, start_addr, arg);
97 if (ret.error < 0) {
98 return ERR_INVALID_ARGS;
99 }
100
101 return NO_ERROR;
102 }
103
sbi_rfence_vma(const unsigned long * hart_mask,vaddr_t vma,size_t size)104 void sbi_rfence_vma(const unsigned long *hart_mask, vaddr_t vma, size_t size) {
105 // use the new IPI extension
106 if (likely(sbi_ext_present(SBI_EXTENSION_RFENCE))) {
107 sbi_call(SBI_EXT_RFENCE_SIG, 1, *hart_mask, 0, vma, size);
108 } else {
109 PANIC_UNIMPLEMENTED;
110 }
111 }
112
sbi_early_init(void)113 void sbi_early_init(void) {
114 // read the presence of some features
115 sbi_ext |= sbi_probe_extension(SBI_EXT_TIMER_SIG) ? (1<<SBI_EXTENSION_TIMER) : 0;
116 sbi_ext |= sbi_probe_extension(SBI_EXT_IPI_SIG) ? (1<<SBI_EXTENSION_IPI) : 0;
117 sbi_ext |= sbi_probe_extension(SBI_EXT_RFENCE_SIG) ? (1<<SBI_EXTENSION_RFENCE) : 0;
118 sbi_ext |= sbi_probe_extension(SBI_EXT_HSM_SIG) ? (1<<SBI_EXTENSION_HSM) : 0;
119 sbi_ext |= sbi_probe_extension(SBI_EXT_SRST_SIG) ? (1<<SBI_EXTENSION_SRST) : 0;
120 }
121
sbi_init(void)122 void sbi_init(void) {
123 dprintf(INFO, "RISCV: SBI spec version %ld impl id %ld version %ld\n",
124 sbi_generic_call_2(SBI_GET_SBI_SPEC_VERSION).value,
125 sbi_generic_call_2(SBI_GET_SBI_IMPL_ID).value,
126 sbi_generic_call_2(SBI_GET_SBI_IMPL_VERSION).value);
127
128 // print the extensions detected
129 dprintf(INFO, "RISCV: SBI extension TIMER %d\n", sbi_ext_present(SBI_EXTENSION_TIMER));
130 dprintf(INFO, "RISCV: SBI extension IPI %d\n", sbi_ext_present(SBI_EXTENSION_IPI));
131 dprintf(INFO, "RISCV: SBI extension RFENCE %d\n", sbi_ext_present(SBI_EXTENSION_RFENCE));
132 dprintf(INFO, "RISCV: SBI extension HSM %d\n", sbi_ext_present(SBI_EXTENSION_HSM));
133 dprintf(INFO, "RISCV: SBI extension SRST %d\n", sbi_ext_present(SBI_EXTENSION_SRST));
134
135 // print a line via the console
136 const char test[] = "SBI console test\n\r";
137 for (const char *c = test; *c != 0; c++) {
138 sbi_call(SBI_CONSOLE_PUTCHAR, *c);
139 }
140 }
141
142 #endif
143
144