-
Notifications
You must be signed in to change notification settings - Fork 665
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #739 from cpluss/cortex-m3
9 approve votes for merging in Cortex-M3 support.
- Loading branch information
Showing
7 changed files
with
405 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
Oops, something went wrong.