Skip to content

Commit 39e3e39

Browse files
committed
Support #[interrupt] as an alternative to #[exception(IrqHandler)]
1 parent 62ae2ff commit 39e3e39

File tree

4 files changed

+116
-37
lines changed

4 files changed

+116
-37
lines changed

cortex-a-rt/src/lib.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@
143143
//! }
144144
//! ```
145145
//!
146+
//! Or you can use `#[interrupt]`:
147+
//!
148+
//! ```rust no_run
149+
//! #[cortex_a_rt::interrupt]
150+
//! fn irq_handler() {
151+
//! todo!();
152+
//! }
153+
//! ```
154+
//!
146155
//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined
147156
//! Exception occurs. Our linker script PROVIDEs a default implementation at
148157
//! `_default_handler` which is used if `_undefined_handler` is missing.
@@ -320,9 +329,7 @@ use cortex_ar::{
320329
register::{cpsr::ProcessorMode, Cpsr},
321330
};
322331

323-
pub use cortex_ar_rt_macros::entry;
324-
325-
pub use cortex_ar_rt_macros::exception;
332+
pub use cortex_ar_rt_macros::{entry, exception, interrupt};
326333

327334
/// Our default exception handler.
328335
///

cortex-ar-rt-macros/src/lib.rs

Lines changed: 94 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ use syn::{
1818

1919
/// Creates an `unsafe` program entry point (i.e. a `kmain` function).
2020
///
21+
/// It's `unsafe` because you are not supposed to call it - it should only be
22+
/// called from the start-up code once initialisation is complete.
23+
///
2124
/// When placed on a function like:
2225
///
2326
/// ```rust ignore
@@ -81,7 +84,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
8184
let tramp_ident = Ident::new("__cortex_ar_rt_kmain", Span::call_site());
8285
let ident = &f.sig.ident;
8386

84-
if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Entry) {
87+
if let Err(error) = check_attr_whitelist(&f.attrs, Kind::Entry) {
8588
return error;
8689
}
8790

@@ -125,6 +128,9 @@ impl std::fmt::Display for Exception {
125128

126129
/// Creates an `unsafe` exception handler.
127130
///
131+
/// It's `unsafe` because you are not supposed to call it - it should only be
132+
/// called from assembly routines registered in the interrupt vector table.
133+
///
128134
/// When placed on a function like:
129135
///
130136
/// ```rust ignore
@@ -153,34 +159,91 @@ impl std::fmt::Display for Exception {
153159
/// * SvcHandler (creates `_svc_handler`)
154160
/// * PrefetchHandler (creates `_prefetch_handler`)
155161
/// * AbortHandler (creates `_abort_handler`)
156-
/// * IrqHandler (creates `_irq_handler`)
162+
/// * IrqHandler (creates `_irq_handler`) - you can also use `#[interrupt]` if you prefer.
157163
#[proc_macro_attribute]
158164
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
165+
handle_exception_interrupt(args, input, Kind::Exception)
166+
}
167+
168+
/// Creates an `unsafe` interrupt handler.
169+
///
170+
/// It's `unsafe` because you are not supposed to call it - it should only be
171+
/// called from assembly routines registered in the interrupt vector table.
172+
///
173+
/// When placed on a function like:
174+
///
175+
/// ```rust ignore
176+
/// #[interrupt]
177+
/// fn foo(addr: usize) -> ! {
178+
/// panic!("On no")
179+
/// }
180+
/// ```
181+
///
182+
/// You get something like:
183+
///
184+
/// ```rust
185+
/// #[export_name = "_interrupt_handler"]
186+
/// pub unsafe extern "C" fn __cortex_ar_rt_interrupt_handler(addr: usize) -> ! {
187+
/// foo(addr)
188+
/// }
189+
///
190+
/// fn foo(addr: usize) -> ! {
191+
/// panic!("On no")
192+
/// }
193+
/// ```
194+
///
195+
/// This is just a convienient short-hand for `#[exception(IrqHandler)` because
196+
/// you may not consider interrupts to be a form of exception.
197+
#[proc_macro_attribute]
198+
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
199+
handle_exception_interrupt(args, input, Kind::Interrupt)
200+
}
201+
202+
/// Note if we got `#[entry]`, `#[exception(...)]` or `#[interrupt]`
203+
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
204+
enum Kind {
205+
/// Corresponds to `#[entry]`
206+
Entry,
207+
/// Corresponds to `#[exception(...)]`
208+
Exception,
209+
/// Corresponds to `#[interrupt]`
210+
Interrupt,
211+
}
212+
213+
/// A common routine for handling exception or interrupt functions
214+
fn handle_exception_interrupt(args: TokenStream, input: TokenStream, kind: Kind) -> TokenStream {
159215
let f = parse_macro_input!(input as ItemFn);
160216

161-
if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) {
217+
if let Err(error) = check_attr_whitelist(&f.attrs, kind) {
162218
return error;
163219
}
164220

165-
let mut args_iter = args.into_iter();
166-
let Some(TokenTree::Ident(exception_name)) = args_iter.next() else {
167-
return parse::Error::new(
168-
Span::call_site(),
169-
"This attribute requires the name of the exception as the first argument",
170-
)
171-
.to_compile_error()
172-
.into();
221+
let exception_name = match kind {
222+
Kind::Entry => {
223+
panic!("Don't handle #[entry] with `handle_exception_interrupt`!");
224+
}
225+
Kind::Exception => {
226+
let mut args_iter = args.into_iter();
227+
let Some(TokenTree::Ident(exception_name)) = args_iter.next() else {
228+
return parse::Error::new(
229+
Span::call_site(),
230+
"This attribute requires the name of the exception as the first argument",
231+
)
232+
.to_compile_error()
233+
.into();
234+
};
235+
if !args_iter.next().is_none() {
236+
return parse::Error::new(
237+
Span::call_site(),
238+
"This attribute accepts only one argument",
239+
)
240+
.to_compile_error()
241+
.into();
242+
}
243+
exception_name.to_string()
244+
}
245+
Kind::Interrupt => "IrqHandler".to_string(),
173246
};
174-
if !args_iter.next().is_none() {
175-
return parse::Error::new(
176-
Span::call_site(),
177-
"This attribute accepts only one argument",
178-
)
179-
.to_compile_error()
180-
.into();
181-
}
182-
183-
let exception_name = exception_name.to_string();
184247

185248
let exn = match exception_name.as_str() {
186249
"UndefinedHandler" => Exception::UndefinedHandler,
@@ -328,6 +391,9 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
328391
.into()
329392
}
330393

394+
/// Given a list of attributes, split them into `cfg` and non-`cfg`.
395+
///
396+
/// Returns `(cfgs, non_cfgs)`.
331397
fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
332398
let mut cfgs = vec![];
333399
let mut not_cfgs = vec![];
@@ -343,12 +409,8 @@ fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
343409
(cfgs, not_cfgs)
344410
}
345411

346-
enum WhiteListCaller {
347-
Entry,
348-
Exception,
349-
}
350-
351-
fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<(), TokenStream> {
412+
/// Check whether any disallowed attributes have been applied to our entry/exception function.
413+
fn check_attr_whitelist(attrs: &[Attribute], caller: Kind) -> Result<(), TokenStream> {
352414
let whitelist = &[
353415
"doc",
354416
"link_section",
@@ -370,12 +432,15 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
370432
}
371433

372434
let err_str = match caller {
373-
WhiteListCaller::Entry => {
435+
Kind::Entry => {
374436
"this attribute is not allowed on a cortex-r-rt/cortex-a-rt entry point"
375437
}
376-
WhiteListCaller::Exception => {
438+
Kind::Exception => {
377439
"this attribute is not allowed on an exception handler controlled by cortex-r-rt/cortex-a-rt"
378440
}
441+
Kind::Interrupt => {
442+
"this attribute is not allowed on an interrupt handler controlled by cortex-r-rt/cortex-a-rt"
443+
}
379444
};
380445

381446
return Err(parse::Error::new(attr.span(), err_str)

cortex-r-rt/src/lib.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@
142142
//! }
143143
//! ```
144144
//!
145+
//! Or you can use `#[interrupt]`:
146+
//!
147+
//! ```rust no_run
148+
//! #[cortex_a_rt::interrupt]
149+
//! fn irq_handler() {
150+
//! todo!();
151+
//! }
152+
//! ```
153+
//!
145154
//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined
146155
//! Exception occurs. Our linker script PROVIDEs a default implementation at
147156
//! `_default_handler` which is used if `_undefined_handler` is missing.
@@ -325,9 +334,7 @@ use cortex_ar::{
325334
#[cfg(arm_architecture = "v8-r")]
326335
use cortex_ar::register::Hactlr;
327336

328-
pub use cortex_ar_rt_macros::entry;
329-
330-
pub use cortex_ar_rt_macros::exception;
337+
pub use cortex_ar_rt_macros::{entry, exception, interrupt};
331338

332339
/// Our default exception handler.
333340
///

examples/mps3-an536/src/bin/gic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#![no_main]
55

66
// pull in our start-up code
7-
use cortex_r_rt::{entry, exception};
7+
use cortex_r_rt::{entry, interrupt};
88

99
// pull in our library
1010
use mps3_an536 as _;
@@ -94,7 +94,7 @@ fn dump_cpsr() {
9494
println!("CPSR: {:?}", cpsr);
9595
}
9696

97-
#[exception(IrqHandler)]
97+
#[interrupt]
9898
fn irq_handler() {
9999
println!("> IRQ");
100100
while let Some(int_id) = SingleCoreGic::get_and_acknowledge_interrupt() {

0 commit comments

Comments
 (0)