@@ -18,6 +18,9 @@ use syn::{
18
18
19
19
/// Creates an `unsafe` program entry point (i.e. a `kmain` function).
20
20
///
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
+ ///
21
24
/// When placed on a function like:
22
25
///
23
26
/// ```rust ignore
@@ -81,7 +84,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
81
84
let tramp_ident = Ident :: new ( "__cortex_ar_rt_kmain" , Span :: call_site ( ) ) ;
82
85
let ident = & f. sig . ident ;
83
86
84
- if let Err ( error) = check_attr_whitelist ( & f. attrs , WhiteListCaller :: Entry ) {
87
+ if let Err ( error) = check_attr_whitelist ( & f. attrs , Kind :: Entry ) {
85
88
return error;
86
89
}
87
90
@@ -125,6 +128,9 @@ impl std::fmt::Display for Exception {
125
128
126
129
/// Creates an `unsafe` exception handler.
127
130
///
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
+ ///
128
134
/// When placed on a function like:
129
135
///
130
136
/// ```rust ignore
@@ -153,34 +159,91 @@ impl std::fmt::Display for Exception {
153
159
/// * SvcHandler (creates `_svc_handler`)
154
160
/// * PrefetchHandler (creates `_prefetch_handler`)
155
161
/// * AbortHandler (creates `_abort_handler`)
156
- /// * IrqHandler (creates `_irq_handler`)
162
+ /// * IrqHandler (creates `_irq_handler`) - you can also use `#[interrupt]` if you prefer.
157
163
#[ proc_macro_attribute]
158
164
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 {
159
215
let f = parse_macro_input ! ( input as ItemFn ) ;
160
216
161
- if let Err ( error) = check_attr_whitelist ( & f. attrs , WhiteListCaller :: Exception ) {
217
+ if let Err ( error) = check_attr_whitelist ( & f. attrs , kind ) {
162
218
return error;
163
219
}
164
220
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 ( ) ,
173
246
} ;
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 ( ) ;
184
247
185
248
let exn = match exception_name. as_str ( ) {
186
249
"UndefinedHandler" => Exception :: UndefinedHandler ,
@@ -328,6 +391,9 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
328
391
. into ( )
329
392
}
330
393
394
+ /// Given a list of attributes, split them into `cfg` and non-`cfg`.
395
+ ///
396
+ /// Returns `(cfgs, non_cfgs)`.
331
397
fn extract_cfgs ( attrs : Vec < Attribute > ) -> ( Vec < Attribute > , Vec < Attribute > ) {
332
398
let mut cfgs = vec ! [ ] ;
333
399
let mut not_cfgs = vec ! [ ] ;
@@ -343,12 +409,8 @@ fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
343
409
( cfgs, not_cfgs)
344
410
}
345
411
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 > {
352
414
let whitelist = & [
353
415
"doc" ,
354
416
"link_section" ,
@@ -370,12 +432,15 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
370
432
}
371
433
372
434
let err_str = match caller {
373
- WhiteListCaller :: Entry => {
435
+ Kind :: Entry => {
374
436
"this attribute is not allowed on a cortex-r-rt/cortex-a-rt entry point"
375
437
}
376
- WhiteListCaller :: Exception => {
438
+ Kind :: Exception => {
377
439
"this attribute is not allowed on an exception handler controlled by cortex-r-rt/cortex-a-rt"
378
440
}
441
+ Kind :: Interrupt => {
442
+ "this attribute is not allowed on an interrupt handler controlled by cortex-r-rt/cortex-a-rt"
443
+ }
379
444
} ;
380
445
381
446
return Err ( parse:: Error :: new ( attr. span ( ) , err_str)
0 commit comments