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