1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
//! The Supervisor module. //! //! Supervisor is an abstraction for the `SVC` assembly instruction, which means //! **S**uper**V**isor **C**all, and the `SV_CALL` exception. //! //! # Usage //! //! ``` //! # #![feature(const_fn)] //! # fn main() {} //! use drone_cortex_m::{sv, thr}; //! //! sv! { //! /// The supervisor. //! pub struct Sv; //! //! /// Array of services. //! static SERVICES; //! //! // The list of attached services goes here. //! // SwitchContextService; //! // SwitchBackService; //! } //! //! thr::vtable! { //! use Thr; //! pub struct Vtable; //! pub struct Handlers; //! pub struct Thrs; //! static THREADS; //! //! // Define an external function handler for the SV_CALL exception. //! fn SV_CALL; //! } //! //! thr! { //! use THREADS; //! pub struct Thr {} //! pub struct ThrLocal {} //! } //! //! #[no_mangle] //! pub static VTABLE: Vtable = Vtable::new(Handlers { //! reset, //! // Attach the SV_CALL handler for the supervisor `Sv`. //! sv_call: drone_cortex_m::sv::sv_handler::<Sv>, //! }); //! //! unsafe extern "C" fn reset() -> ! { //! loop {} //! } //! ``` //! //! # Predefined Services //! //! If [`SwitchContextService`](sv::SwitchContextService) and //! [`SwitchBackService`](sv::SwitchBackService) are defined for the supervisor, //! [`switch_context`](sv::Switch::switch_context) and //! [`switch_back`](sv::Switch::switch_back) functions become available to //! switch the program stack. //! //! ```no_run //! # #![feature(new_uninit)] //! use drone_cortex_m::sv::{Switch, SwitchBackService, SwitchContextService}; //! //! use drone_cortex_m::sv; //! //! sv! { //! /// The supervisor. //! pub struct Sv; //! //! /// Array of services. //! static SERVICES; //! //! SwitchContextService; //! SwitchBackService; //! } //! //! # fn main() { //! unsafe { //! // Allocate the stack. //! let stack = Box::<[u8]>::new_uninit_slice(0x800).assume_init(); //! // `stack_ptr` will store the current stack pointer. //! let mut stack_ptr = stack.as_ptr(); //! let mut data = Box::<u32>::new(0); //! let mut data_ptr = &mut *data as *mut u32; //! Sv::switch_context(data_ptr, &mut stack_ptr); //! // ------------------- //! // Using the new stack. //! // ------------------- //! Sv::switch_back(&mut data_ptr); //! } //! # } //! ``` #![cfg_attr(feature = "std", allow(unreachable_code, unused_variables))] mod switch; pub use self::switch::{Switch, SwitchBackService, SwitchContextService}; use core::{intrinsics::unreachable, mem::size_of}; /// Generic supervisor. pub trait Supervisor: Sized + 'static { /// Returns a pointer to the first service in the services array. fn first() -> *const Self; } /// A supervisor call. pub trait SvCall<T: SvService>: Supervisor { /// Calls the supervisor service `service`. Translates to `SVC num` /// instruction, where `num` corresponds to the service `T`. /// /// # Safety /// /// Safety is implementation defined. unsafe fn call(service: &mut T); } /// Generic supervisor service. pub trait SvService: Sized + Send + 'static { /// Called when `SVC num` instruction was invoked and `num` corresponds to /// the service. /// /// # Safety /// /// This function should not be called directly. unsafe extern "C" fn handler(&mut self); } /// Calls `SVC num` instruction. /// /// # Safety /// /// This function should not be called directly. #[inline(always)] pub unsafe fn sv_call<T: SvService>(service: &mut T, num: u8) { #[cfg(feature = "std")] return unimplemented!(); if size_of::<T>() == 0 { asm!(" svc $0 " : : "i"(num) : : "volatile" ); } else { asm!(" svc $0 " : : "i"(num), "{r12}"(service) : : "volatile" ); } } /// This function is called by [`sv_handler`] for the supervisor service /// `T`. Parameter `T` is based on the number `num` in the `SVC num` /// instruction. /// /// # Safety /// /// This function should not be called directly. pub unsafe extern "C" fn service_handler<T: SvService>(mut frame: *mut *mut u8) { if size_of::<T>() == 0 { T::handler(&mut *(frame as *mut T)); } else { frame = frame.add(4); // Stacked R12 T::handler(&mut *(*frame as *mut T)); } } /// `SV_CALL` exception handler for the supervisor `T`. /// /// # Safety /// /// This function should be called only by NVIC as part of a vector table. #[naked] pub unsafe extern "C" fn sv_handler<T: Supervisor>() { #[cfg(feature = "std")] return unimplemented!(); asm!(" tst lr, #4 ite eq mrseq r0, msp mrsne r0, psp ldr r1, [r0, #24] ldrb r1, [r1, #-2] ldr pc, [r2, r1, lsl #2] " : : "{r2}"(T::first()) : "r0", "r1", "cc" : "volatile" ); unreachable(); }