Skip to content

Commit

Permalink
Merge pull request #739 from cpluss/cortex-m3
Browse files Browse the repository at this point in the history
9 approve votes for merging in Cortex-M3 support.
  • Loading branch information
phil-levis committed Feb 9, 2018
2 parents 7750f3a + dfd8e14 commit 4f6f73f
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 1 deletion.
7 changes: 7 additions & 0 deletions arch/cortex-m3/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "cortexm3"
version = "0.1.0"
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]

[dependencies]
kernel = { path = "../../kernel" }
158 changes: 158 additions & 0 deletions arch/cortex-m3/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#![feature(asm, const_fn, naked_functions)]
#![no_std]

extern crate kernel;

pub mod systick;
pub mod nvic;

#[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:
/* Set thread mode to privileged */
mov r0, #0
msr CONTROL, r0
movw LR, #0xFFF9
movt LR, #0xFFFF
"
);
}

#[no_mangle]
#[naked]
/// All ISRs are caught by this handler which indirects to a custom handler by
/// indexing into `INTERRUPT_TABLE` based on the ISR number.
pub unsafe extern "C" fn generic_isr() {
asm!(
"
/* Skip saving process state if not coming from user-space */
cmp lr, #0xfffffffd
bne _ggeneric_isr_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}
/* Set thread mode to privileged */
mov r0, #0
msr CONTROL, r0
movw LR, #0xFFF9
movt LR, #0xFFFF
_ggeneric_isr_no_stacking:
/* Find the ISR number by looking at the low byte of the IPSR registers */
mrs r0, IPSR
and r0, #0xff
/* ISRs start at 16, so substract 16 to get zero-indexed */
sub r0, #16
/*
* High level:
* NVIC.ICER[r0 / 32] = 1 << (r0 & 31)
* */
lsrs r2, r0, #5 /* r2 = r0 / 32 */
/* r0 = 1 << (r0 & 31) */
movs r3, #1 /* r3 = 1 */
and r0, r0, #31 /* r0 = r0 & 31 */
lsl r0, r3, r0 /* r0 = r3 << r0 */
/* r3 = &NVIC.ICER */
mov r3, #0xe180
movt r3, #0xe000
/* here:
*
* `r2` is r0 / 32
* `r3` is &NVIC.ICER
* `r0` is 1 << (r0 & 31)
*
* So we just do:
*
* `*(r3 + r2 * 4) = r0`
*
* */
str r0, [r3, r2, lsl #2]"
);
}

#[no_mangle]
#[naked]
#[allow(non_snake_case)]
pub unsafe extern "C" fn SVC_Handler() {
asm!(
"
cmp lr, #0xfffffff9
bne to_kernel
/* Set thread mode to unprivileged */
mov r0, #1
msr CONTROL, r0
movw lr, #0xfffd
movt lr, #0xffff
bx lr
to_kernel:
ldr r0, =SYSCALL_FIRED
mov r1, #1
str r1, [r0, #0]
/* Set thread mode to privileged */
mov r0, #0
msr CONTROL, r0
movw LR, #0xFFF9
movt LR, #0xFFFF"
);
}

#[cfg(not(target_os = "none"))]
pub unsafe extern "C" fn switch_to_user(user_stack: *const u8, process_got: *const u8) -> *mut u8 {
user_stack as *mut u8
}

#[cfg(target_os = "none")]
#[no_mangle]
/// r0 is top of user stack, r1 Process GOT
pub unsafe extern "C" fn switch_to_user(
mut user_stack: *const u8,
process_regs: &mut [usize; 8],
) -> *mut u8 {
asm!("
/* Load bottom of stack into Process Stack Pointer */
msr psp, $0
/* Load non-hardware-stacked registers from Process stack */
/* Ensure that $2 is stored in a callee saved register */
ldmia $2, {r4-r11}
/* SWITCH */
svc 0xff /* It doesn't matter which SVC number we use here */
/* Push non-hardware-stacked registers into Process struct's */
/* regs field */
stmia $2, {r4-r11}
mrs $0, PSP /* PSP into r0 */"
: "={r0}"(user_stack)
: "{r0}"(user_stack), "{r1}"(process_regs)
: "r4","r5","r6","r7","r8","r9","r10","r11");
user_stack as *mut u8
}
110 changes: 110 additions & 0 deletions arch/cortex-m3/src/nvic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! Cortex-M3 NVIC

use kernel::common::volatile_cell::VolatileCell;

#[repr(C)]
// Registers for the NVIC
struct Registers {
// Interrupt set-enable
iser: [VolatileCell<u32>; 8],
_reserved1: [u32; 24],
// Interrupt clear-enable
icer: [VolatileCell<u32>; 8],
_reserved2: [u32; 24],
// Interrupt set-pending (and read pending state)
ispr: [VolatileCell<u32>; 8],
_reserved3: [VolatileCell<u32>; 24],
// Interrupt clear-pending (and read pending state)
icpr: [VolatileCell<u32>; 8],
}

// NVIC base address
const BASE_ADDRESS: *mut Registers = 0xe000e100 as *mut Registers;

/// Clear all pending interrupts
pub unsafe fn clear_all_pending() {
let nvic: &Registers = &*BASE_ADDRESS;
for icpr in nvic.icpr.iter() {
icpr.set(!0)
}
}

/// Enable all interrupts
pub unsafe fn enable_all() {
let nvic: &Registers = &*BASE_ADDRESS;
for icer in nvic.iser.iter() {
icer.set(!0)
}
}

/// Disable all interrupts
pub unsafe fn disable_all() {
let nvic: &Registers = &*BASE_ADDRESS;
for icer in nvic.icer.iter() {
icer.set(!0)
}
}

/// Get the index (0-240) the lowest number pending interrupt, or `None` if none
/// are pending.
pub unsafe fn next_pending() -> Option<u32> {
let nvic: &Registers = &*BASE_ADDRESS;

for (block, ispr) in nvic.ispr.iter().enumerate() {
let ispr = ispr.get();

// If there are any high bits there is a pending interrupt
if ispr != 0 {
// trailing_zeros == index of first high bit
let bit = ispr.trailing_zeros();
return Some(block as u32 * 32 + bit);
}
}
None
}

pub unsafe fn has_pending() -> bool {
let nvic: &Registers = &*BASE_ADDRESS;

nvic.ispr.iter().fold(0, |i, ispr| ispr.get() | i) != 0
}

/// An opaque wrapper for a single NVIC interrupt.
///
/// Hand these out to low-level driver to let them control their own interrupts
/// but not others.
pub struct Nvic(u32);

impl Nvic {
/// Creates a new `Nvic`
///
/// Marked unsafe because only chip/platform configuration code should be
/// able to create these.
pub const unsafe fn new(idx: u32) -> Nvic {
Nvic(idx)
}

/// Enable the interrupt
pub fn enable(&self) {
let nvic: &Registers = unsafe { &*BASE_ADDRESS };
let idx = self.0 as usize;

nvic.iser[idx / 32].set(1 << (self.0 & 31));
}

/// Disable the interrupt
pub fn disable(&self) {
let nvic: &Registers = unsafe { &*BASE_ADDRESS };
let idx = self.0 as usize;

nvic.icer[idx / 32].set(1 << (self.0 & 31));
}

/// Clear pending state
pub fn clear_pending(&self) {
let nvic: &Registers = unsafe { &*BASE_ADDRESS };
let idx = self.0 as usize;

nvic.icpr[idx / 32].set(1 << (self.0 & 31));
}
}
Loading

0 comments on commit 4f6f73f

Please sign in to comment.