Skip to content

Commit

Permalink
Merge #1113
Browse files Browse the repository at this point in the history
1113: Move architecture-dependent syscall code to arch/cortex-m r=bradjc a=bradjc

### Pull Request Overview

This pull request moves all of the code that is cortex-m syscall specific out of process.rs and into the cortex-m crate. This is part of #985.

It does this by:
- Making a new `SyscallInterface` trait that is implemented in the cortex-m crate. Each process has a reference to the implementing struct.
- Making a new `ProcessType` trait that all process types implement (right now we only have one, `Process` in process.rs). This allows the `Process` struct to be templated over the `SyscallInterface` type while the `processes` array in the `Kernel` struct keeps an array of `ProcessType` implementations:

    ```rust
    Kernel {
        processes: &'static [Option<&'static process::ProcessType>]
    }
    ```

    which means the template parameter stays only in the process.rs file and does not spread to all of Tock.
    
The panic! prints in process.rs are partially commented out because they relied on many architecture-specific fields. Also the `SCB_REGISTERS` global static variable still exists. Moving those to the cortex-m crate is forthcoming in a different PR.

This is blocked on #1111.

### Testing Strategy

Hail on Hail, but needs more.


### TODO or Help Wanted
n/a


### Documentation Updated

- [x] Updated the relevant files in `/docs`, or no updates are required.

### Formatting

- [x] Ran `make formatall`.


Co-authored-by: Brad Campbell <bradjc5@gmail.com>
Co-authored-by: Pat Pannuto <pat.pannuto@gmail.com>
  • Loading branch information
3 people committed Aug 13, 2018
2 parents 4ead531 + 841d531 commit 7cd0eb3
Show file tree
Hide file tree
Showing 32 changed files with 1,169 additions and 703 deletions.
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 7cd0eb3

Please sign in to comment.