1#![no_std]
10#![deny(missing_docs)]
12
13#[cfg(feature = "foreign")]
15pub mod foreign;
16
17#[derive(Clone)]
19#[repr(C)]
20pub struct LocalContext {
21 sctx: usize,
23 x: [usize; 31],
25 sepc: usize,
27 pub supervisor: bool,
29 pub interrupt: bool,
31}
32
33impl LocalContext {
34 #[inline]
36 pub const fn empty() -> Self {
37 Self {
38 sctx: 0,
39 x: [0; 31],
40 supervisor: false,
41 interrupt: false,
42 sepc: 0,
43 }
44 }
45
46 #[inline]
50 pub const fn user(pc: usize) -> Self {
51 Self {
52 sctx: 0,
53 x: [0; 31],
54 supervisor: false,
55 interrupt: true,
56 sepc: pc,
57 }
58 }
59
60 #[inline]
62 pub const fn thread(pc: usize, interrupt: bool) -> Self {
63 Self {
64 sctx: 0,
65 x: [0; 31],
66 supervisor: true,
67 interrupt,
68 sepc: pc,
69 }
70 }
71
72 #[inline]
74 pub fn x(&self, n: usize) -> usize {
75 self.x[n - 1]
76 }
77
78 #[inline]
80 pub fn x_mut(&mut self, n: usize) -> &mut usize {
81 &mut self.x[n - 1]
82 }
83
84 #[inline]
86 pub fn a(&self, n: usize) -> usize {
87 self.x(n + 10)
88 }
89
90 #[inline]
92 pub fn a_mut(&mut self, n: usize) -> &mut usize {
93 self.x_mut(n + 10)
94 }
95
96 #[inline]
98 pub fn ra(&self) -> usize {
99 self.x(1)
100 }
101
102 #[inline]
104 pub fn sp(&self) -> usize {
105 self.x(2)
106 }
107
108 #[inline]
110 pub fn sp_mut(&mut self) -> &mut usize {
111 self.x_mut(2)
112 }
113
114 #[inline]
116 pub fn pc(&self) -> usize {
117 self.sepc
118 }
119
120 #[inline]
122 pub fn pc_mut(&mut self) -> &mut usize {
123 &mut self.sepc
124 }
125
126 #[inline]
132 pub fn move_next(&mut self) {
133 self.sepc = self.sepc.wrapping_add(4);
134 }
135
136 #[inline(never)]
146 pub unsafe fn execute(&mut self) -> usize {
147 #[cfg(target_arch = "riscv64")]
148 {
149 let mut sstatus = build_sstatus(self.supervisor, self.interrupt);
151 let ctx_ptr = self as *mut Self;
153 let mut sepc = self.sepc;
154 let old_sscratch: usize;
155 core::arch::asm!(
158 " csrrw {old_ss}, sscratch, {ctx}
159 csrw sepc , {sepc}
160 csrw sstatus , {sstatus}
161 addi sp, sp, -8
162 sd ra, (sp)
163 call {execute_naked}
164 ld ra, (sp)
165 addi sp, sp, 8
166 csrw sscratch, {old_ss}
167 csrr {sepc} , sepc
168 csrr {sstatus}, sstatus
169 ",
170 ctx = in (reg) ctx_ptr,
171 old_ss = out (reg) old_sscratch,
172 sepc = inlateout(reg) sepc,
173 sstatus = inlateout(reg) sstatus,
174 execute_naked = sym execute_naked,
175 );
176 let _ = old_sscratch; (*ctx_ptr).sepc = sepc;
179 sstatus
180 }
181 #[cfg(not(target_arch = "riscv64"))]
182 unimplemented!("LocalContext::execute() is only supported on riscv64")
183 }
184}
185
186#[cfg(target_arch = "riscv64")]
187#[inline]
188fn build_sstatus(supervisor: bool, interrupt: bool) -> usize {
189 let mut sstatus: usize;
190 unsafe { core::arch::asm!("csrr {}, sstatus", out(reg) sstatus) };
192 const PREVILEGE_BIT: usize = 1 << 8;
193 const INTERRUPT_BIT: usize = 1 << 5;
194 match supervisor {
195 false => sstatus &= !PREVILEGE_BIT,
196 true => sstatus |= PREVILEGE_BIT,
197 }
198 match interrupt {
199 false => sstatus &= !INTERRUPT_BIT,
200 true => sstatus |= INTERRUPT_BIT,
201 }
202 sstatus
203}
204
205#[cfg(not(target_arch = "riscv64"))]
206#[allow(dead_code)]
207#[inline]
208fn build_sstatus(_supervisor: bool, _interrupt: bool) -> usize {
209 unimplemented!("build_sstatus() is only supported on riscv64")
210}
211
212#[cfg(target_arch = "riscv64")]
224#[unsafe(naked)]
225unsafe extern "C" fn execute_naked() {
226 core::arch::naked_asm!(
227 r" .altmacro
228 .macro SAVE n
229 sd x\n, \n*8(sp)
230 .endm
231 .macro SAVE_ALL
232 sd x1, 1*8(sp)
233 .set n, 3
234 .rept 29
235 SAVE %n
236 .set n, n+1
237 .endr
238 .endm
239
240 .macro LOAD n
241 ld x\n, \n*8(sp)
242 .endm
243 .macro LOAD_ALL
244 ld x1, 1*8(sp)
245 .set n, 3
246 .rept 29
247 LOAD %n
248 .set n, n+1
249 .endr
250 .endm
251 ",
252 " .option push
254 .option nopic
255 ",
256 " addi sp, sp, -32*8
258 SAVE_ALL
259 ",
260 " la t0, 1f
262 csrw stvec, t0
263 ",
264 " csrr t0, sscratch
266 sd sp, (t0)
267 mv sp, t0
268 ",
269 " LOAD_ALL
271 ld sp, 2*8(sp)
272 ",
273 " sret",
275 " .align 2",
277 "1: csrrw sp, sscratch, sp",
279 " SAVE_ALL
281 csrrw t0, sscratch, sp
282 sd t0, 2*8(sp)
283 ",
284 " ld sp, (sp)",
286 " LOAD_ALL
288 addi sp, sp, 32*8
289 ",
290 " ret",
292 " .option pop",
293 )
294}
295
296#[cfg(not(target_arch = "riscv64"))]
297#[allow(dead_code)]
298unsafe extern "C" fn execute_naked() {
299 unimplemented!("execute_naked() is only supported on riscv64")
300}