Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move architecture-dependent syscall code to arch/cortex-m #1113

Merged
merged 11 commits into from Aug 13, 2018
9 changes: 7 additions & 2 deletions arch/cortex-m/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion arch/cortex-m/src/lib.rs
Expand Up @@ -2,7 +2,7 @@

#![crate_name = "cortexm"]
#![crate_type = "rlib"]
#![feature(asm, const_fn, lang_items)]
#![feature(asm, const_fn, lang_items, used)]
#![no_std]

#[macro_use(register_bitfields, register_bitmasks)]
Expand All @@ -11,4 +11,5 @@ extern crate kernel;
pub mod nvic;
pub mod scb;
pub mod support;
pub mod syscall;
pub mod systick;
218 changes: 218 additions & 0 deletions arch/cortex-m/src/syscall.rs
@@ -0,0 +1,218 @@
//! Implementation of the architecture-specific portions of the kernel-userland
//! system call interface.

use core::ptr::{read_volatile, write_volatile};

use kernel;

/// This is used in the syscall handler. When set to 1 this means the
/// svc_handler was called. Marked `pub` because it is used in the cortex-m*
/// specific handler.
#[no_mangle]
#[used]
pub static mut SYSCALL_FIRED: usize = 0;

/// This is called in the hard fault handler. When set to 1 this means the hard
/// fault handler was called. Marked `pub` because it is used in the cortex-m*
/// specific handler.
///
/// n.b. If the kernel hard faults, it immediately panic's. This flag is only
/// for handling application hard faults.
#[no_mangle]
#[used]
pub static mut APP_HARD_FAULT: usize = 0;

/// This is called in the systick handler. When set to 1 this means the process
/// exceeded its timeslice. Marked `pub` because it is used in the cortex-m*
/// specific handler.
#[no_mangle]
#[used]
pub static mut SYSTICK_EXPIRED: usize = 0;

#[allow(improper_ctypes)]
extern "C" {
pub fn switch_to_user(user_stack: *const u8, process_regs: &mut [usize; 8]) -> *mut u8;
}

/// This holds all of the state that the kernel must keep for the process when
/// the process is not executing.
pub struct CortexMStoredState {
pub r4: usize,
pub r5: usize,
pub r6: usize,
pub r7: usize,
pub r8: usize,
pub r9: usize,
pub r10: usize,
pub r11: usize,
yield_pc: usize,
psr: usize,
}

// Need a custom define for `default()` so we can set the initial PSR value.
impl Default for CortexMStoredState {
fn default() -> CortexMStoredState {
CortexMStoredState {
r4: 0,
r5: 0,
r6: 0,
r7: 0,
r8: 0,
r9: 0,
r10: 0,
r11: 0,
yield_pc: 0,
// Set the Thumb bit and clear everything else
psr: 0x01000000,
}
}
}

/// Implementation of the `UserspaceKernelBoundary` for the Cortex-M non-floating point
/// architecture.
pub struct SysCall();

impl SysCall {
pub const unsafe fn new() -> SysCall {
SysCall()
}
}

impl kernel::syscall::UserspaceKernelBoundary for SysCall {
type StoredState = CortexMStoredState;

/// Get the syscall that the process called.
unsafe fn get_syscall(&self, stack_pointer: *const usize) -> Option<kernel::syscall::Syscall> {
// Get the four values that are passed with the syscall.
let r0 = read_volatile(stack_pointer.offset(0));
let r1 = read_volatile(stack_pointer.offset(1));
let r2 = read_volatile(stack_pointer.offset(2));
let r3 = read_volatile(stack_pointer.offset(3));

// Get the actual SVC number.
let pcptr = read_volatile((stack_pointer as *const *const u16).offset(6));
let svc_instr = read_volatile(pcptr.offset(-1));
let svc_num = (svc_instr & 0xff) as u8;
match svc_num {
0 => Some(kernel::syscall::Syscall::YIELD),
1 => Some(kernel::syscall::Syscall::SUBSCRIBE {
driver_number: r0,
subdriver_number: r1,
callback_ptr: r2 as *mut (),
appdata: r3,
}),
2 => Some(kernel::syscall::Syscall::COMMAND {
driver_number: r0,
subdriver_number: r1,
arg0: r2,
arg1: r3,
}),
3 => Some(kernel::syscall::Syscall::ALLOW {
driver_number: r0,
subdriver_number: r1,
allow_address: r2 as *mut u8,
allow_size: r3,
}),
4 => Some(kernel::syscall::Syscall::MEMOP {
operand: r0,
arg0: r1,
}),
_ => None,
}
}

unsafe fn set_syscall_return_value(&self, stack_pointer: *const usize, return_value: isize) {
// For the Cortex-M arch we set this in the same place that r0 was
// passed.
let sp = stack_pointer as *mut isize;
write_volatile(sp, return_value);
}

unsafe fn pop_syscall_stack_frame(
&self,
stack_pointer: *const usize,
state: &mut CortexMStoredState,
) -> *mut usize {
state.yield_pc = read_volatile(stack_pointer.offset(6));
state.psr = read_volatile(stack_pointer.offset(7));
(stack_pointer as *mut usize).offset(8)
}

unsafe fn push_function_call(
&self,
stack_pointer: *const usize,
remaining_stack_memory: usize,
callback: kernel::procs::FunctionCall,
state: &CortexMStoredState,
) -> Result<*mut usize, *mut usize> {
// We need 32 bytes to add this frame. Ensure that there are 32 bytes
// available on the stack.
if remaining_stack_memory < 32 {
// Not enough room on the stack to add a frame. Return an error
// and where the stack would be to help with debugging.
Err((stack_pointer as *mut usize).offset(-8))
} else {
// Fill in initial stack expected by SVC handler
// Top minus 8 u32s for r0-r3, r12, lr, pc and xPSR
let stack_bottom = (stack_pointer as *mut usize).offset(-8);
write_volatile(stack_bottom.offset(7), state.psr);
write_volatile(stack_bottom.offset(6), callback.pc | 1);

// Set the LR register to the saved PC so the callback returns to
// wherever wait was called. Set lowest bit to one because of THUMB
// instruction requirements.
write_volatile(stack_bottom.offset(5), state.yield_pc | 0x1);
write_volatile(stack_bottom, callback.argument0);
write_volatile(stack_bottom.offset(1), callback.argument1);
write_volatile(stack_bottom.offset(2), callback.argument2);
write_volatile(stack_bottom.offset(3), callback.argument3);

Ok(stack_bottom)
}
}

unsafe fn switch_to_process(
&self,
stack_pointer: *const usize,
state: &mut CortexMStoredState,
) -> (*mut usize, kernel::syscall::ContextSwitchReason) {
let new_stack_pointer = switch_to_user(
stack_pointer as *const u8,
&mut *(state as *mut CortexMStoredState as *mut [usize; 8]),
);

// Determine why this returned and the process switched back to the
// kernel.

// Check to see if the fault handler was called while the process was
// running.
let app_fault = read_volatile(&APP_HARD_FAULT);
write_volatile(&mut APP_HARD_FAULT, 0);

// Check to see if the svc_handler was called and the process called a
// syscall.
let syscall_fired = read_volatile(&SYSCALL_FIRED);
write_volatile(&mut SYSCALL_FIRED, 0);

// Check to see if the systick timer for the process expired.
let systick_expired = read_volatile(&SYSTICK_EXPIRED);
write_volatile(&mut SYSTICK_EXPIRED, 0);

// Now decide the reason based on which flags were set.
let switch_reason = if app_fault == 1 {
// APP_HARD_FAULT takes priority. This means we hit the hardfault
// handler and this process faulted.
kernel::syscall::ContextSwitchReason::Fault
} else if syscall_fired == 1 {
kernel::syscall::ContextSwitchReason::SyscallFired
} else if systick_expired == 1 {
kernel::syscall::ContextSwitchReason::TimesliceExpired
} else {
// If something else happened, which shouldn't, we fallback to this
// process having faulted.
kernel::syscall::ContextSwitchReason::Fault
};

(new_stack_pointer as *mut usize, switch_reason)
}
}
12 changes: 12 additions & 0 deletions arch/cortex-m0/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions arch/cortex-m0/src/lib.rs
Expand Up @@ -9,6 +9,7 @@ extern crate kernel;
pub use cortexm::support;

pub use cortexm::nvic;
pub use cortexm::syscall;

#[cfg(not(target_os = "none"))]
pub unsafe extern "C" fn generic_isr() {}
Expand Down
12 changes: 12 additions & 0 deletions arch/cortex-m3/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 7 additions & 11 deletions arch/cortex-m3/src/lib.rs
Expand Up @@ -12,24 +12,20 @@ pub use cortexm::support;

pub use cortexm::nvic;
pub use cortexm::scb;
pub use cortexm::syscall;
pub use cortexm::systick;

#[no_mangle]
#[naked]
pub unsafe extern "C" fn systick_handler() {
asm!(
"
/* Skip saving process state if not coming from user-space */
cmp lr, #0xfffffffd
bne _systick_handler_no_stacking

/* We need the most recent kernel's version of r1, which points */
/* to the Process struct's stored registers field. The kernel's r1 */
/* lives in the second word of the hardware stacked registers on MSP */
mov r1, sp
ldr r1, [r1, #4]
stmia r1, {r4-r11}
_systick_handler_no_stacking:
/* Mark that the systick handler was called meaning that the process */
/* stopped executing because it has exceeded its timeslice. */
ldr r0, =SYSTICK_EXPIRED
mov r1, #1
str r1, [r0, #0]

/* Set thread mode to privileged */
mov r0, #0
msr CONTROL, r0
Expand Down
23 changes: 8 additions & 15 deletions arch/cortex-m4/src/lib.rs
Expand Up @@ -18,6 +18,7 @@ pub use cortexm::support;

pub use cortexm::nvic;
pub use cortexm::scb;
pub use cortexm::syscall;
pub use cortexm::systick;

extern "C" {
Expand All @@ -40,17 +41,12 @@ pub unsafe extern "C" fn systick_handler() {}
pub unsafe extern "C" fn systick_handler() {
asm!(
"
/* Skip saving process state if not coming from user-space */
cmp lr, #0xfffffffd
bne _systick_handler_no_stacking
/* Mark that the systick handler was called meaning that the process */
/* stopped executing because it has exceeded its timeslice. */
ldr r0, =SYSTICK_EXPIRED
mov r1, #1
str r1, [r0, #0]

/* We need the most recent kernel's version of r1, which points */
/* to the Process struct's stored registers field. The kernel's r1 */
/* lives in the second word of the hardware stacked registers on MSP */
mov r1, sp
ldr r1, [r1, #4]
stmia r1, {r4-r11}
_systick_handler_no_stacking:
/* Set thread mode to privileged */
mov r0, #0
msr CONTROL, r0
Expand Down Expand Up @@ -351,11 +347,8 @@ pub unsafe extern "C" fn hard_fault_handler() {
// hard fault occurred in an app, not the kernel. The app should be
// marked as in an error state and handled by the kernel
asm!(
"ldr r0, =SYSCALL_FIRED
mov r1, #1
str r1, [r0, #0]

ldr r0, =APP_FAULT
"ldr r0, =APP_HARD_FAULT
mov r1, #1 /* Fault */
str r1, [r0, #0]

/* Read the SCB registers. */
Expand Down
1 change: 1 addition & 0 deletions arch/cortex-m4/src/mpu.rs
Expand Up @@ -142,6 +142,7 @@ impl kernel::mpu::MPU for MPU {
}

fn create_region(
&self,
region_num: usize,
start: usize,
len: usize,
Expand Down
3 changes: 2 additions & 1 deletion boards/ek-tm4c1294xl/src/main.rs
Expand Up @@ -33,7 +33,7 @@ const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultRespons
static mut APP_MEMORY: [u8; 10240] = [0; 10240];

// Actual memory for holding the active process structures.
static mut PROCESSES: [Option<&'static kernel::procs::Process<'static>>; NUM_PROCS] =
static mut PROCESSES: [Option<&'static kernel::procs::ProcessType>; NUM_PROCS] =
[None, None, None, None];

/// Dummy buffer that causes the linker to reserve enough space for the stack.
Expand Down Expand Up @@ -240,6 +240,7 @@ pub unsafe fn reset_handler() {
}
kernel::procs::load_processes(
board_kernel,
&cortexm4::syscall::SysCall::new(),
&_sapps as *const u8,
&mut APP_MEMORY,
&mut PROCESSES,
Expand Down