cortexm/
syscall.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Cortex-M System Call Interface
6//!
7//! Implementation of the architecture-specific portions of the kernel-userland
8//! system call interface.
9
10use core::fmt::Write;
11use core::marker::PhantomData;
12use core::mem::{self, size_of};
13use core::ops::Range;
14use core::ptr::{self, addr_of, addr_of_mut, read_volatile, write_volatile};
15use kernel::errorcode::ErrorCode;
16
17use crate::CortexMVariant;
18
19/// This is used in the syscall handler. When set to 1 this means the
20/// svc_handler was called. Marked `pub` because it is used in the cortex-m*
21/// specific handler.
22#[no_mangle]
23#[used]
24pub static mut SYSCALL_FIRED: usize = 0;
25
26/// This is called in the hard fault handler. When set to 1 this means the hard
27/// fault handler was called. Marked `pub` because it is used in the cortex-m*
28/// specific handler.
29///
30/// n.b. If the kernel hard faults, it immediately panic's. This flag is only
31/// for handling application hard faults.
32#[no_mangle]
33#[used]
34pub static mut APP_HARD_FAULT: usize = 0;
35
36/// This is used in the hardfault handler.
37///
38/// When an app faults, the hardfault handler stores the value of the
39/// SCB registers in this static array. This makes them available to
40/// be displayed in a diagnostic fault message.
41#[no_mangle]
42#[used]
43pub static mut SCB_REGISTERS: [u32; 5] = [0; 5];
44
45/// This holds all of the state that the kernel must keep for the process when
46/// the process is not executing.
47#[derive(Default)]
48pub struct CortexMStoredState {
49    regs: [usize; 8],
50    yield_pc: usize,
51    psr: usize,
52    psp: usize,
53}
54
55// Space for 8 u32s: r0-r3, r12, lr, pc, and xPSR
56const SVC_FRAME_SIZE: usize = 32;
57
58/// Values for encoding the stored state buffer in a binary slice.
59const VERSION: usize = 1;
60const STORED_STATE_SIZE: usize = size_of::<CortexMStoredState>();
61const TAG: [u8; 4] = [b'c', b't', b'x', b'm'];
62const METADATA_LEN: usize = 3;
63
64const VERSION_IDX: usize = 0;
65const SIZE_IDX: usize = 1;
66const TAG_IDX: usize = 2;
67const YIELDPC_IDX: usize = 3;
68const PSR_IDX: usize = 4;
69const PSP_IDX: usize = 5;
70const REGS_IDX: usize = 6;
71const REGS_RANGE: Range<usize> = REGS_IDX..REGS_IDX + 8;
72
73const USIZE_SZ: usize = size_of::<usize>();
74
75fn usize_byte_range(index: usize) -> Range<usize> {
76    index * USIZE_SZ..(index + 1) * USIZE_SZ
77}
78
79fn usize_from_u8_slice(slice: &[u8], index: usize) -> Result<usize, ErrorCode> {
80    let range = usize_byte_range(index);
81    Ok(usize::from_le_bytes(
82        slice
83            .get(range)
84            .ok_or(ErrorCode::SIZE)?
85            .try_into()
86            .or(Err(ErrorCode::FAIL))?,
87    ))
88}
89
90fn write_usize_to_u8_slice(val: usize, slice: &mut [u8], index: usize) {
91    let range = usize_byte_range(index);
92    slice[range].copy_from_slice(&val.to_le_bytes());
93}
94
95impl core::convert::TryFrom<&[u8]> for CortexMStoredState {
96    type Error = ErrorCode;
97    fn try_from(ss: &[u8]) -> Result<CortexMStoredState, Self::Error> {
98        if ss.len() == size_of::<CortexMStoredState>() + METADATA_LEN * USIZE_SZ
99            && usize_from_u8_slice(ss, VERSION_IDX)? == VERSION
100            && usize_from_u8_slice(ss, SIZE_IDX)? == STORED_STATE_SIZE
101            && usize_from_u8_slice(ss, TAG_IDX)? == u32::from_le_bytes(TAG) as usize
102        {
103            let mut res = CortexMStoredState {
104                regs: [0; 8],
105                yield_pc: usize_from_u8_slice(ss, YIELDPC_IDX)?,
106                psr: usize_from_u8_slice(ss, PSR_IDX)?,
107                psp: usize_from_u8_slice(ss, PSP_IDX)?,
108            };
109            for (i, v) in (REGS_RANGE).enumerate() {
110                res.regs[i] = usize_from_u8_slice(ss, v)?;
111            }
112            Ok(res)
113        } else {
114            Err(ErrorCode::FAIL)
115        }
116    }
117}
118
119/// Implementation of the
120/// [`UserspaceKernelBoundary`](kernel::syscall::UserspaceKernelBoundary) for
121/// the Cortex-M non-floating point architecture.
122pub struct SysCall<A: CortexMVariant>(PhantomData<A>);
123
124impl<A: CortexMVariant> SysCall<A> {
125    pub const unsafe fn new() -> SysCall<A> {
126        SysCall(PhantomData)
127    }
128}
129
130impl<A: CortexMVariant> kernel::syscall::UserspaceKernelBoundary for SysCall<A> {
131    type StoredState = CortexMStoredState;
132
133    fn initial_process_app_brk_size(&self) -> usize {
134        // Cortex-M hardware uses 8 words on the stack to implement context
135        // switches. So we need at least 32 bytes.
136        SVC_FRAME_SIZE
137    }
138
139    unsafe fn initialize_process(
140        &self,
141        accessible_memory_start: *const u8,
142        app_brk: *const u8,
143        state: &mut Self::StoredState,
144    ) -> Result<(), ()> {
145        // We need to initialize the stored state for the process here. This
146        // initialization can be called multiple times for a process, for
147        // example if the process is restarted.
148        state.regs.iter_mut().for_each(|x| *x = 0);
149        state.yield_pc = 0;
150        state.psr = 0x01000000; // Set the Thumb bit and clear everything else.
151        state.psp = app_brk as usize; // Set to top of process-accessible memory.
152
153        // Make sure there's enough room on the stack for the initial SVC frame.
154        if (app_brk as usize - accessible_memory_start as usize) < SVC_FRAME_SIZE {
155            // Not enough room on the stack to add a frame.
156            return Err(());
157        }
158
159        // Allocate the kernel frame
160        state.psp -= SVC_FRAME_SIZE;
161        Ok(())
162    }
163
164    unsafe fn set_syscall_return_value(
165        &self,
166        accessible_memory_start: *const u8,
167        app_brk: *const u8,
168        state: &mut Self::StoredState,
169        return_value: kernel::syscall::SyscallReturn,
170    ) -> Result<(), ()> {
171        // For the Cortex-M arch, write the return values in the same
172        // place that they were originally passed in (i.e. at the
173        // bottom the SVC structure on the stack)
174
175        // First, we need to validate that this location is inside of the
176        // process's accessible memory. Alignment is guaranteed by hardware.
177        if state.psp < accessible_memory_start as usize
178            || state.psp.saturating_add(mem::size_of::<u32>() * 4) > app_brk as usize
179        {
180            return Err(());
181        }
182
183        let sp = state.psp as *mut u32;
184        let (r0, r1, r2, r3) = (sp.offset(0), sp.offset(1), sp.offset(2), sp.offset(3));
185
186        // These operations are only safe so long as
187        // - the pointers are properly aligned. This is guaranteed because the
188        //   pointers are all offset multiples of 4 bytes from the stack
189        //   pointer, which is guaranteed to be properly aligned after
190        //   exception entry on Cortex-M. See
191        //   https://github.com/tock/tock/pull/2478#issuecomment-796389747
192        //   for more details.
193        // - the pointer is dereferencable, i.e. the memory range of
194        //   the given size starting at the pointer must all be within
195        //   the bounds of a single allocated object
196        // - the pointer must point to an initialized instance of its
197        //   type
198        // - during the lifetime of the returned reference (of the
199        //   cast, essentially an arbitrary 'a), the memory must not
200        //   get accessed (read or written) through any other pointer.
201        //
202        // Refer to
203        // https://doc.rust-lang.org/std/primitive.pointer.html#safety-13
204        kernel::utilities::arch_helpers::encode_syscall_return_trd104(
205            &kernel::utilities::arch_helpers::TRD104SyscallReturn::from_syscall_return(
206                return_value,
207            ),
208            &mut *r0,
209            &mut *r1,
210            &mut *r2,
211            &mut *r3,
212        );
213
214        Ok(())
215    }
216
217    /// When the process calls `svc` to enter the kernel, the hardware
218    /// automatically pushes an SVC frame that will be unstacked when the kernel
219    /// returns to the process. In the special case of process startup,
220    /// `initialize_new_process` sets up an empty SVC frame as if an `svc` had
221    /// been called.
222    ///
223    /// Here, we modify this stack frame such that the process resumes at the
224    /// beginning of the callback function that we want the process to run. We
225    /// place the originally intended return address in the link register so
226    /// that when the function completes execution continues.
227    ///
228    /// In effect, this converts `svc` into `bl callback`.
229    unsafe fn set_process_function(
230        &self,
231        accessible_memory_start: *const u8,
232        app_brk: *const u8,
233        state: &mut CortexMStoredState,
234        callback: kernel::process::FunctionCall,
235    ) -> Result<(), ()> {
236        // Ensure that [`state.psp`, `state.psp + SVC_FRAME_SIZE`] is within
237        // process-accessible memory. Alignment is guaranteed by hardware.
238        if state.psp < accessible_memory_start as usize
239            || state.psp.saturating_add(SVC_FRAME_SIZE) > app_brk as usize
240        {
241            return Err(());
242        }
243
244        // Notes:
245        //  - Instruction addresses require `|1` to indicate thumb code
246        //  - Stack offset 4 is R12, which the syscall interface ignores
247        let stack_bottom = state.psp as *mut usize;
248        ptr::write(stack_bottom.offset(7), state.psr); //......... -> APSR
249        ptr::write(stack_bottom.offset(6), callback.pc.addr() | 1); //... -> PC
250        ptr::write(stack_bottom.offset(5), state.yield_pc | 1); // -> LR
251        ptr::write(stack_bottom.offset(3), callback.argument3.as_usize()); // -> R3
252        ptr::write(stack_bottom.offset(2), callback.argument2); // -> R2
253        ptr::write(stack_bottom.offset(1), callback.argument1); // -> R1
254        ptr::write(stack_bottom.offset(0), callback.argument0); // -> R0
255
256        Ok(())
257    }
258
259    unsafe fn switch_to_process(
260        &self,
261        accessible_memory_start: *const u8,
262        app_brk: *const u8,
263        state: &mut CortexMStoredState,
264    ) -> (kernel::syscall::ContextSwitchReason, Option<*const u8>) {
265        let new_stack_pointer = A::switch_to_user(state.psp as *const usize, &mut state.regs);
266
267        // We need to keep track of the current stack pointer.
268        state.psp = new_stack_pointer as usize;
269
270        // We need to validate that the stack pointer and the SVC frame are
271        // within process accessible memory. Alignment is guaranteed by
272        // hardware.
273        let invalid_stack_pointer = state.psp < accessible_memory_start as usize
274            || state.psp.saturating_add(SVC_FRAME_SIZE) > app_brk as usize;
275
276        // Determine why this returned and the process switched back to the
277        // kernel.
278
279        // Check to see if the fault handler was called while the process was
280        // running.
281        let app_fault = read_volatile(&*addr_of!(APP_HARD_FAULT));
282        write_volatile(&mut *addr_of_mut!(APP_HARD_FAULT), 0);
283
284        // Check to see if the svc_handler was called and the process called a
285        // syscall.
286        let syscall_fired = read_volatile(&*addr_of!(SYSCALL_FIRED));
287        write_volatile(&mut *addr_of_mut!(SYSCALL_FIRED), 0);
288
289        // Now decide the reason based on which flags were set.
290        let switch_reason = if app_fault == 1 || invalid_stack_pointer {
291            // APP_HARD_FAULT takes priority. This means we hit the hardfault
292            // handler and this process faulted.
293            kernel::syscall::ContextSwitchReason::Fault
294        } else if syscall_fired == 1 {
295            // Save these fields after a syscall. If this is a synchronous
296            // syscall (i.e. we return a value to the app immediately) then this
297            // will have no effect. If we are doing something like `yield()`,
298            // however, then we need to have this state.
299            state.yield_pc = ptr::read(new_stack_pointer.offset(6));
300            state.psr = ptr::read(new_stack_pointer.offset(7));
301
302            // Get the syscall arguments and return them along with the syscall.
303            // It's possible the app did something invalid, in which case we put
304            // the app in the fault state.
305            let r0 = ptr::read(new_stack_pointer.offset(0));
306            let r1 = ptr::read(new_stack_pointer.offset(1));
307            let r2 = ptr::read(new_stack_pointer.offset(2));
308            let r3 = ptr::read(new_stack_pointer.offset(3));
309
310            // Get the actual SVC number.
311            let pcptr = ptr::read((new_stack_pointer as *const *const u16).offset(6));
312            let svc_instr = ptr::read(pcptr.offset(-1));
313            let svc_num = (svc_instr & 0xff) as u8;
314
315            // Use the helper function to convert these raw values into a Tock
316            // `Syscall` type.
317            let syscall = kernel::syscall::Syscall::from_register_arguments(
318                svc_num,
319                r0,
320                r1.into(),
321                r2.into(),
322                r3.into(),
323            );
324
325            match syscall {
326                Some(s) => kernel::syscall::ContextSwitchReason::SyscallFired { syscall: s },
327                None => kernel::syscall::ContextSwitchReason::Fault,
328            }
329        } else {
330            // If none of the above cases are true its because the process was interrupted by an
331            // ISR for a hardware event
332            kernel::syscall::ContextSwitchReason::Interrupted
333        };
334
335        (switch_reason, Some(new_stack_pointer as *const u8))
336    }
337
338    unsafe fn print_context(
339        &self,
340        accessible_memory_start: *const u8,
341        app_brk: *const u8,
342        state: &CortexMStoredState,
343        writer: &mut dyn Write,
344    ) {
345        // Check if the stored stack pointer is valid. Alignment is guaranteed
346        // by hardware.
347        let invalid_stack_pointer = state.psp < accessible_memory_start as usize
348            || state.psp.saturating_add(SVC_FRAME_SIZE) > app_brk as usize;
349
350        let stack_pointer = state.psp as *const usize;
351
352        // If we cannot use the stack pointer, generate default bad looking
353        // values we can use for the printout. Otherwise, read the correct
354        // values.
355        let (r0, r1, r2, r3, r12, lr, pc, xpsr) = if invalid_stack_pointer {
356            (
357                0xBAD00BAD, 0xBAD00BAD, 0xBAD00BAD, 0xBAD00BAD, 0xBAD00BAD, 0xBAD00BAD, 0xBAD00BAD,
358                0xBAD00BAD,
359            )
360        } else {
361            let r0 = ptr::read(stack_pointer.offset(0));
362            let r1 = ptr::read(stack_pointer.offset(1));
363            let r2 = ptr::read(stack_pointer.offset(2));
364            let r3 = ptr::read(stack_pointer.offset(3));
365            let r12 = ptr::read(stack_pointer.offset(4));
366            let lr = ptr::read(stack_pointer.offset(5));
367            let pc = ptr::read(stack_pointer.offset(6));
368            let xpsr = ptr::read(stack_pointer.offset(7));
369            (r0, r1, r2, r3, r12, lr, pc, xpsr)
370        };
371
372        let _ = writer.write_fmt(format_args!(
373            "\
374             \r\n  R0 : {:#010X}    R6 : {:#010X}\
375             \r\n  R1 : {:#010X}    R7 : {:#010X}\
376             \r\n  R2 : {:#010X}    R8 : {:#010X}\
377             \r\n  R3 : {:#010X}    R10: {:#010X}\
378             \r\n  R4 : {:#010X}    R11: {:#010X}\
379             \r\n  R5 : {:#010X}    R12: {:#010X}\
380             \r\n  R9 : {:#010X} (Static Base Register)\
381             \r\n  SP : {:#010X} (Process Stack Pointer)\
382             \r\n  LR : {:#010X}\
383             \r\n  PC : {:#010X}\
384             \r\n YPC : {:#010X}\
385             \r\n",
386            r0,
387            state.regs[2],
388            r1,
389            state.regs[3],
390            r2,
391            state.regs[4],
392            r3,
393            state.regs[6],
394            state.regs[0],
395            state.regs[7],
396            state.regs[1],
397            r12,
398            state.regs[5],
399            stack_pointer as usize,
400            lr,
401            pc,
402            state.yield_pc,
403        ));
404        let _ = writer.write_fmt(format_args!(
405            "\
406             \r\n APSR: N {} Z {} C {} V {} Q {}\
407             \r\n       GE {} {} {} {}",
408            (xpsr >> 31) & 0x1,
409            (xpsr >> 30) & 0x1,
410            (xpsr >> 29) & 0x1,
411            (xpsr >> 28) & 0x1,
412            (xpsr >> 27) & 0x1,
413            (xpsr >> 19) & 0x1,
414            (xpsr >> 18) & 0x1,
415            (xpsr >> 17) & 0x1,
416            (xpsr >> 16) & 0x1,
417        ));
418        let ici_it = (((xpsr >> 25) & 0x3) << 6) | ((xpsr >> 10) & 0x3f);
419        let thumb_bit = ((xpsr >> 24) & 0x1) == 1;
420        let _ = writer.write_fmt(format_args!(
421            "\
422             \r\n EPSR: ICI.IT {:#04x}\
423             \r\n       ThumbBit {} {}\r\n",
424            ici_it,
425            thumb_bit,
426            if thumb_bit {
427                ""
428            } else {
429                "!!ERROR - Cortex M Thumb only!"
430            },
431        ));
432    }
433
434    fn store_context(
435        &self,
436        state: &CortexMStoredState,
437        out: &mut [u8],
438    ) -> Result<usize, ErrorCode> {
439        if out.len() >= size_of::<CortexMStoredState>() + 3 * USIZE_SZ {
440            write_usize_to_u8_slice(VERSION, out, VERSION_IDX);
441            write_usize_to_u8_slice(STORED_STATE_SIZE, out, SIZE_IDX);
442            write_usize_to_u8_slice(u32::from_le_bytes(TAG) as usize, out, TAG_IDX);
443            write_usize_to_u8_slice(state.yield_pc, out, YIELDPC_IDX);
444            write_usize_to_u8_slice(state.psr, out, PSR_IDX);
445            write_usize_to_u8_slice(state.psp, out, PSP_IDX);
446            for (i, v) in state.regs.iter().enumerate() {
447                write_usize_to_u8_slice(*v, out, REGS_IDX + i);
448            }
449            // + 3 for yield_pc, psr, psp
450            Ok((state.regs.len() + 3 + METADATA_LEN) * USIZE_SZ)
451        } else {
452            Err(ErrorCode::SIZE)
453        }
454    }
455}