1extern crate proc_macro;
10
11use proc_macro::{TokenStream, TokenTree};
12use proc_macro2::Span;
13use quote::quote;
14use syn::{
15 parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, Ident, ItemFn, ReturnType,
16 Type, Visibility,
17};
18
19#[proc_macro_attribute]
51pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
52 let f = parse_macro_input!(input as ItemFn);
53
54 let valid_signature = f.sig.constness.is_none()
58 && f.vis == Visibility::Inherited
59 && f.sig.abi.is_none()
60 && f.sig.inputs.is_empty()
61 && f.sig.generics.params.is_empty()
62 && f.sig.generics.where_clause.is_none()
63 && f.sig.variadic.is_none()
64 && match f.sig.output {
65 ReturnType::Default => false,
66 ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
67 };
68
69 if !valid_signature {
70 return parse::Error::new(
71 f.span(),
72 "`#[entry]` function must have signature `[unsafe] fn() -> !`",
73 )
74 .to_compile_error()
75 .into();
76 }
77
78 if !args.is_empty() {
79 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
80 .to_compile_error()
81 .into();
82 }
83
84 let tramp_ident = Ident::new("__cortex_ar_rt_kmain", Span::call_site());
85 let ident = &f.sig.ident;
86
87 if let Err(error) = check_attr_whitelist(&f.attrs, Kind::Entry) {
88 return error;
89 }
90
91 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
92
93 quote!(
94 #(#cfgs)*
95 #(#attrs)*
96 #[doc(hidden)]
97 #[export_name = "kmain"]
98 pub unsafe extern "C" fn #tramp_ident() -> ! {
99 #ident()
100 }
101
102 #f
103 )
104 .into()
105}
106
107#[derive(Debug, PartialEq)]
109enum Exception {
110 Undefined,
111 SupervisorCall,
112 PrefetchAbort,
113 DataAbort,
114 Irq,
115}
116
117impl std::fmt::Display for Exception {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 match self {
120 Exception::Undefined => write!(f, "Undefined"),
121 Exception::SupervisorCall => write!(f, "SupervisorCall"),
122 Exception::PrefetchAbort => write!(f, "PrefetchAbort"),
123 Exception::DataAbort => write!(f, "DataAbort"),
124 Exception::Irq => write!(f, "Irq"),
125 }
126 }
127}
128
129#[proc_macro_attribute]
165pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
166 handle_exception_interrupt(args, input, Kind::Exception)
167}
168
169#[proc_macro_attribute]
200pub fn irq(args: TokenStream, input: TokenStream) -> TokenStream {
201 handle_exception_interrupt(args, input, Kind::Interrupt)
202}
203
204#[derive(PartialEq, Eq, Clone, Copy, Debug)]
206enum Kind {
207 Entry,
209 Exception,
211 Interrupt,
213}
214
215fn handle_exception_interrupt(args: TokenStream, input: TokenStream, kind: Kind) -> TokenStream {
217 let f = parse_macro_input!(input as ItemFn);
218
219 if let Err(error) = check_attr_whitelist(&f.attrs, kind) {
220 return error;
221 }
222
223 let returns_never = match f.sig.output {
224 ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
225 _ => false,
226 };
227
228 let exception = match kind {
229 Kind::Entry => {
230 panic!("Don't handle #[entry] with `handle_exception_interrupt`!");
231 }
232 Kind::Exception => {
233 let mut args_iter = args.into_iter();
234 let Some(TokenTree::Ident(exception_name)) = args_iter.next() else {
235 return parse::Error::new(
236 Span::call_site(),
237 "This attribute requires the name of the exception as the first argument",
238 )
239 .to_compile_error()
240 .into();
241 };
242 if args_iter.next().is_some() {
243 return parse::Error::new(
244 Span::call_site(),
245 "This attribute accepts only one argument",
246 )
247 .to_compile_error()
248 .into();
249 }
250 match exception_name.to_string().as_str() {
251 "Undefined" => {
252 if !returns_never && f.sig.unsafety.is_none() {
253 return parse::Error::new(
254 exception_name.span().into(),
255 "Undefined handlers that don't return ! must be unsafe",
256 )
257 .to_compile_error()
258 .into();
259 }
260 Exception::Undefined
261 }
262 "SupervisorCall" => Exception::SupervisorCall,
263 "PrefetchAbort" => {
264 if !returns_never && f.sig.unsafety.is_none() {
265 return parse::Error::new(
266 exception_name.span().into(),
267 "PrefetchAbort handlers that don't return ! must be unsafe",
268 )
269 .to_compile_error()
270 .into();
271 }
272 Exception::PrefetchAbort
273 }
274 "DataAbort" => {
275 if !returns_never && f.sig.unsafety.is_none() {
276 return parse::Error::new(
277 exception_name.span().into(),
278 "DataAbort handlers that don't return ! must be unsafe",
279 )
280 .to_compile_error()
281 .into();
282 }
283 Exception::DataAbort
284 }
285 "Irq" => Exception::Irq,
286 _ => {
287 return parse::Error::new(
288 exception_name.span().into(),
289 "This is not a valid exception name",
290 )
291 .to_compile_error()
292 .into();
293 }
294 }
295 }
296 Kind::Interrupt => Exception::Irq,
297 };
298
299 let ident = &f.sig.ident;
300 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
301
302 let handler = match exception {
303 Exception::Undefined => {
306 let tramp_ident = Ident::new("__cortex_ar_rt_undefined_handler", Span::call_site());
307 if returns_never {
308 quote!(
309 #(#cfgs)*
310 #(#attrs)*
311 #[doc(hidden)]
312 #[export_name = "_undefined_handler"]
313 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! {
314 #ident(addr)
315 }
316
317 #f
318 )
319 } else {
320 quote!(
321 #(#cfgs)*
322 #(#attrs)*
323 #[doc(hidden)]
324 #[export_name = "_undefined_handler"]
325 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize {
326 unsafe {
327 #ident(addr)
328 }
329 }
330
331 #f
332 )
333 }
334 }
335 Exception::PrefetchAbort => {
338 let tramp_ident =
339 Ident::new("__cortex_ar_rt_prefetch_abort_handler", Span::call_site());
340 if returns_never {
341 quote!(
342 #(#cfgs)*
343 #(#attrs)*
344 #[doc(hidden)]
345 #[export_name = "_prefetch_abort_handler"]
346 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! {
347 #ident(addr)
348 }
349
350 #f
351 )
352 } else {
353 quote!(
354 #(#cfgs)*
355 #(#attrs)*
356 #[doc(hidden)]
357 #[export_name = "_prefetch_abort_handler"]
358 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize {
359 unsafe {
360 #ident(addr)
361 }
362 }
363
364 #f
365 )
366 }
367 }
368 Exception::DataAbort => {
371 let tramp_ident = Ident::new("__cortex_ar_rt_data_abort_handler", Span::call_site());
372 if returns_never {
373 quote!(
374 #(#cfgs)*
375 #(#attrs)*
376 #[doc(hidden)]
377 #[export_name = "_data_abort_handler"]
378 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! {
379 #ident(addr)
380 }
381
382 #f
383 )
384 } else {
385 quote!(
386 #(#cfgs)*
387 #(#attrs)*
388 #[doc(hidden)]
389 #[export_name = "_data_abort_handler"]
390 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize {
391 unsafe {
392 #ident(addr)
393 }
394 }
395
396 #f
397 )
398 }
399 }
400 Exception::SupervisorCall => {
402 let tramp_ident = Ident::new("__cortex_ar_rt_svc_handler", Span::call_site());
403 quote!(
404 #(#cfgs)*
405 #(#attrs)*
406 #[doc(hidden)]
407 #[export_name = "_svc_handler"]
408 pub unsafe extern "C" fn #tramp_ident(arg: u32) {
409 #ident(arg)
410 }
411
412 #f
413 )
414 }
415 Exception::Irq => {
417 let tramp_ident = Ident::new("__cortex_ar_rt_irq_handler", Span::call_site());
418 quote!(
419 #(#cfgs)*
420 #(#attrs)*
421 #[doc(hidden)]
422 #[export_name = "_irq_handler"]
423 pub unsafe extern "C" fn #tramp_ident() {
424 #ident()
425 }
426
427 #f
428 )
429 }
430 };
431
432 quote!(
433 #handler
434 )
435 .into()
436}
437
438fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
442 let mut cfgs = vec![];
443 let mut not_cfgs = vec![];
444
445 for attr in attrs {
446 if eq(&attr, "cfg") {
447 cfgs.push(attr);
448 } else {
449 not_cfgs.push(attr);
450 }
451 }
452
453 (cfgs, not_cfgs)
454}
455
456fn check_attr_whitelist(attrs: &[Attribute], caller: Kind) -> Result<(), TokenStream> {
458 let whitelist = &[
459 "doc",
460 "link_section",
461 "cfg",
462 "allow",
463 "warn",
464 "deny",
465 "forbid",
466 "cold",
467 "naked",
468 "expect",
469 ];
470
471 'o: for attr in attrs {
472 for val in whitelist {
473 if eq(attr, val) {
474 continue 'o;
475 }
476 }
477
478 let err_str = match caller {
479 Kind::Entry => {
480 "this attribute is not allowed on a cortex-r-rt/cortex-a-rt entry point"
481 }
482 Kind::Exception => {
483 "this attribute is not allowed on an exception handler controlled by cortex-r-rt/cortex-a-rt"
484 }
485 Kind::Interrupt => {
486 "this attribute is not allowed on an interrupt handler controlled by cortex-r-rt/cortex-a-rt"
487 }
488 };
489
490 return Err(parse::Error::new(attr.span(), err_str)
491 .to_compile_error()
492 .into());
493 }
494
495 Ok(())
496}
497
498fn eq(attr: &Attribute, name: &str) -> bool {
500 attr.style == AttrStyle::Outer && attr.path().is_ident(name)
501}