1extern crate proc_macro;
6
7use proc_macro::TokenStream;
8use proc_macro2::Span;
9use quote::quote;
10use std::iter;
11use std::{collections::HashSet, fmt::Display};
12use syn::{
13 parse::{self, Parse},
14 parse_macro_input,
15 spanned::Spanned,
16 AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type,
17 Visibility,
18};
19
20#[proc_macro_attribute]
21pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
22 let mut f = parse_macro_input!(input as ItemFn);
23
24 let valid_signature = f.sig.constness.is_none()
26 && f.vis == Visibility::Inherited
27 && f.sig.abi.is_none()
28 && f.sig.inputs.is_empty()
29 && f.sig.generics.params.is_empty()
30 && f.sig.generics.where_clause.is_none()
31 && f.sig.variadic.is_none()
32 && match f.sig.output {
33 ReturnType::Default => false,
34 ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
35 };
36
37 if !valid_signature {
38 return parse::Error::new(
39 f.span(),
40 "`#[entry]` function must have signature `[unsafe] fn() -> !`",
41 )
42 .to_compile_error()
43 .into();
44 }
45
46 if !args.is_empty() {
47 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
48 .to_compile_error()
49 .into();
50 }
51
52 let (statics, stmts) = match extract_static_muts(f.block.stmts) {
54 Err(e) => return e.to_compile_error().into(),
55 Ok(x) => x,
56 };
57
58 f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
59 f.sig.inputs.extend(statics.iter().map(|statik| {
60 let ident = &statik.ident;
61 let ty = &statik.ty;
62 let attrs = &statik.attrs;
63
64 syn::parse::<FnArg>(
67 quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &'static mut #ty).into(),
68 )
69 .unwrap()
70 }));
71 f.block.stmts = stmts;
72
73 let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
74 let ident = &f.sig.ident;
75
76 let resource_args = statics
77 .iter()
78 .map(|statik| {
79 let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
80 let ident = &statik.ident;
81 let ty = &statik.ty;
82 let expr = &statik.expr;
83 quote! {
84 #(#cfgs)*
85 {
86 #(#attrs)*
87 static mut #ident: #ty = #expr;
88 unsafe { &mut #ident }
89 }
90 }
91 })
92 .collect::<Vec<_>>();
93
94 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Entry) {
95 return error;
96 }
97
98 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
99
100 quote!(
101 #(#cfgs)*
102 #(#attrs)*
103 #[doc(hidden)]
104 #[export_name = "main"]
105 pub unsafe extern "C" fn #tramp_ident() {
106 #[allow(static_mut_refs)]
107 #ident(
108 #(#resource_args),*
109 )
110 }
111
112 #f
113 )
114 .into()
115}
116
117#[derive(Debug, PartialEq)]
118enum Exception {
119 DefaultHandler,
120 HardFault(HardFaultArgs),
121 NonMaskableInt,
122 Other,
123}
124
125impl Display for Exception {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 match self {
128 Exception::DefaultHandler => write!(f, "`DefaultHandler`"),
129 Exception::HardFault(_) => write!(f, "`HardFault` handler"),
130 Exception::NonMaskableInt => write!(f, "`NonMaskableInt` handler"),
131 Exception::Other => write!(f, "Other exception handler"),
132 }
133 }
134}
135
136#[derive(Debug, PartialEq)]
137struct HardFaultArgs {
138 trampoline: bool,
139}
140
141impl Default for HardFaultArgs {
142 fn default() -> Self {
143 Self { trampoline: true }
144 }
145}
146
147impl Parse for HardFaultArgs {
148 fn parse(input: parse::ParseStream) -> syn::Result<Self> {
149 let mut items = Vec::new();
150 loop {
152 if input.is_empty() {
153 break;
154 }
155
156 let name = input.parse::<Ident>()?;
157 input.parse::<syn::Token!(=)>()?;
158 let value = input.parse::<syn::Lit>()?;
159
160 items.push((name, value));
161
162 if input.is_empty() {
163 break;
164 }
165
166 input.parse::<syn::Token!(,)>()?;
167 }
168
169 let mut args = Self::default();
170
171 for (name, value) in items {
172 match name.to_string().as_str() {
173 "trampoline" => match value {
174 syn::Lit::Bool(val) => {
175 args.trampoline = val.value();
176 }
177 _ => {
178 return Err(syn::Error::new_spanned(
179 value,
180 "Not a valid value. `trampoline` takes a boolean literal",
181 ))
182 }
183 },
184 _ => {
185 return Err(syn::Error::new_spanned(name, "Not a valid argument name"));
186 }
187 }
188 }
189
190 Ok(args)
191 }
192}
193
194#[proc_macro_attribute]
195pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
196 let mut f = parse_macro_input!(input as ItemFn);
197
198 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) {
199 return error;
200 }
201
202 let fspan = f.span();
203 let ident = f.sig.ident.clone();
204
205 let ident_s = ident.to_string();
206 let exn = match &*ident_s {
207 "DefaultHandler" => {
208 if !args.is_empty() {
209 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
210 .to_compile_error()
211 .into();
212 }
213 Exception::DefaultHandler
214 }
215 "HardFault" => Exception::HardFault(parse_macro_input!(args)),
216 "NonMaskableInt" => {
217 if !args.is_empty() {
218 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
219 .to_compile_error()
220 .into();
221 }
222 Exception::NonMaskableInt
223 }
224 "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
227 | "DebugMonitor" | "PendSV" | "SysTick" => {
228 if !args.is_empty() {
229 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
230 .to_compile_error()
231 .into();
232 }
233
234 Exception::Other
235 }
236 _ => {
237 return parse::Error::new(ident.span(), "This is not a valid exception name")
238 .to_compile_error()
239 .into();
240 }
241 };
242
243 if f.sig.unsafety.is_none() {
244 match exn {
245 Exception::DefaultHandler | Exception::HardFault(_) | Exception::NonMaskableInt => {
246 let name = format!("{}", exn);
248 return parse::Error::new(ident.span(), format_args!("defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)", name))
249 .to_compile_error()
250 .into();
251 }
252 Exception::Other => {}
253 }
254 }
255
256 let assertion = match exn {
259 Exception::Other => {
260 quote! {
261 const _: () = {
262 let _ = ::cortex_m_rt::Exception::#ident;
263 };
264 }
265 }
266 _ => quote!(),
267 };
268
269 let handler = match exn {
270 Exception::DefaultHandler => {
271 let valid_signature = f.sig.constness.is_none()
272 && f.vis == Visibility::Inherited
273 && f.sig.abi.is_none()
274 && f.sig.inputs.len() == 1
275 && f.sig.generics.params.is_empty()
276 && f.sig.generics.where_clause.is_none()
277 && f.sig.variadic.is_none()
278 && match f.sig.output {
279 ReturnType::Default => true,
280 ReturnType::Type(_, ref ty) => match **ty {
281 Type::Tuple(ref tuple) => tuple.elems.is_empty(),
282 Type::Never(..) => true,
283 _ => false,
284 },
285 };
286
287 if !valid_signature {
288 return parse::Error::new(
289 fspan,
290 "`DefaultHandler` must have signature `unsafe fn(i16) [-> !]`",
291 )
292 .to_compile_error()
293 .into();
294 }
295
296 f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
297 let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
298 let ident = &f.sig.ident;
299
300 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
301
302 quote!(
303 #(#cfgs)*
304 #(#attrs)*
305 #[doc(hidden)]
306 #[export_name = #ident_s]
307 pub unsafe extern "C" fn #tramp_ident() {
308 extern crate core;
309
310 const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
311
312 let irqn = unsafe { (core::ptr::read_volatile(SCB_ICSR) & 0x1FF) as i16 - 16 };
313
314 #ident(irqn)
315 }
316
317 #f
318 )
319 }
320 Exception::HardFault(args) => {
321 let valid_signature = f.sig.constness.is_none()
322 && f.vis == Visibility::Inherited
323 && f.sig.abi.is_none()
324 && if args.trampoline {
325 f.sig.inputs.len() == 1
326 && match &f.sig.inputs[0] {
327 FnArg::Typed(arg) => match arg.ty.as_ref() {
328 Type::Reference(r) => {
329 r.lifetime.is_none() && r.mutability.is_none()
330 }
331 _ => false,
332 },
333 _ => false,
334 }
335 } else {
336 f.sig.inputs.is_empty()
337 }
338 && f.sig.generics.params.is_empty()
339 && f.sig.generics.where_clause.is_none()
340 && f.sig.variadic.is_none()
341 && match f.sig.output {
342 ReturnType::Default => false,
343 ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
344 };
345
346 if !valid_signature {
347 return parse::Error::new(
348 fspan,
349 if args.trampoline {
350 "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`"
351 } else {
352 "`HardFault` handler must have signature `unsafe fn() -> !`"
353 },
354 )
355 .to_compile_error()
356 .into();
357 }
358
359 f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
360 let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
361
362 if args.trampoline {
363 let ident = &f.sig.ident;
364
365 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
366
367 quote!(
368 #(#cfgs)*
369 #(#attrs)*
370 #[doc(hidden)]
371 #[export_name = "_HardFault"]
372 #[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
376 unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) {
377 #ident(frame)
378 }
379
380 #f
381
382 core::arch::global_asm!(
386 ".cfi_sections .debug_frame
387 .section .HardFaultTrampoline, \"ax\"
388 .global HardFault
389 .type HardFault,%function
390 .thumb_func
391 .cfi_startproc
392 HardFault:",
393 "mov r0, lr
394 movs r1, #4
395 tst r0, r1
396 bne 0f
397 mrs r0, MSP
398 b _HardFault
399 0:
400 mrs r0, PSP
401 b _HardFault",
402 ".cfi_endproc
403 .size HardFault, . - HardFault",
404 );
405 )
406 } else {
407 quote!(
408 #[doc(hidden)]
409 #[export_name = "_HardFault"]
410 unsafe extern "C" fn #tramp_ident() {
411 }
413
414 #[export_name = "HardFault"]
415 #[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
419 #f
420 )
421 }
422 }
423 Exception::NonMaskableInt | Exception::Other => {
424 let valid_signature = f.sig.constness.is_none()
425 && f.vis == Visibility::Inherited
426 && f.sig.abi.is_none()
427 && f.sig.inputs.is_empty()
428 && f.sig.generics.params.is_empty()
429 && f.sig.generics.where_clause.is_none()
430 && f.sig.variadic.is_none()
431 && match f.sig.output {
432 ReturnType::Default => true,
433 ReturnType::Type(_, ref ty) => match **ty {
434 Type::Tuple(ref tuple) => tuple.elems.is_empty(),
435 Type::Never(..) => true,
436 _ => false,
437 },
438 };
439
440 if !valid_signature {
441 return parse::Error::new(
442 fspan,
443 "`#[exception]` handlers other than `DefaultHandler` and `HardFault` must have \
444 signature `[unsafe] fn() [-> !]`",
445 )
446 .to_compile_error()
447 .into();
448 }
449
450 let (statics, stmts) = match extract_static_muts(f.block.stmts) {
451 Err(e) => return e.to_compile_error().into(),
452 Ok(x) => x,
453 };
454
455 f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
456 f.sig.inputs.extend(statics.iter().map(|statik| {
457 let ident = &statik.ident;
458 let ty = &statik.ty;
459 let attrs = &statik.attrs;
460 syn::parse::<FnArg>(
461 quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(),
462 )
463 .unwrap()
464 }));
465 f.block.stmts = iter::once(
466 syn::parse2(quote! {{
467 ::cortex_m_rt::exception::#ident;
469 }})
470 .unwrap(),
471 )
472 .chain(stmts)
473 .collect();
474
475 let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
476 let ident = &f.sig.ident;
477
478 let resource_args = statics
479 .iter()
480 .map(|statik| {
481 let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
482 let ident = &statik.ident;
483 let ty = &statik.ty;
484 let expr = &statik.expr;
485 quote! {
486 #(#cfgs)*
487 {
488 #(#attrs)*
489 static mut #ident: #ty = #expr;
490 unsafe { &mut #ident }
491 }
492 }
493 })
494 .collect::<Vec<_>>();
495
496 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
497
498 quote!(
499 #(#cfgs)*
500 #(#attrs)*
501 #[doc(hidden)]
502 #[export_name = #ident_s]
503 pub unsafe extern "C" fn #tramp_ident() {
504 #[allow(static_mut_refs)]
505 #ident(
506 #(#resource_args),*
507 )
508 }
509
510 #f
511 )
512 }
513 };
514
515 quote!(
516 #assertion
517 #handler
518 )
519 .into()
520}
521
522#[proc_macro_attribute]
523pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
524 let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
525
526 if !args.is_empty() {
527 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
528 .to_compile_error()
529 .into();
530 }
531
532 let fspan = f.span();
533 let ident = f.sig.ident.clone();
534 let ident_s = ident.to_string();
535
536 let valid_signature = f.sig.constness.is_none()
539 && f.vis == Visibility::Inherited
540 && f.sig.abi.is_none()
541 && f.sig.inputs.is_empty()
542 && f.sig.generics.params.is_empty()
543 && f.sig.generics.where_clause.is_none()
544 && f.sig.variadic.is_none()
545 && match f.sig.output {
546 ReturnType::Default => true,
547 ReturnType::Type(_, ref ty) => match **ty {
548 Type::Tuple(ref tuple) => tuple.elems.is_empty(),
549 Type::Never(..) => true,
550 _ => false,
551 },
552 };
553
554 if !valid_signature {
555 return parse::Error::new(
556 fspan,
557 "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
558 )
559 .to_compile_error()
560 .into();
561 }
562
563 let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) {
564 Err(e) => return e.to_compile_error().into(),
565 Ok(x) => x,
566 };
567
568 f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
569 f.sig.inputs.extend(statics.iter().map(|statik| {
570 let ident = &statik.ident;
571 let ty = &statik.ty;
572 let attrs = &statik.attrs;
573 syn::parse::<FnArg>(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into())
574 .unwrap()
575 }));
576 f.block.stmts = iter::once(
577 syn::parse2(quote! {{
578 interrupt::#ident;
580 }})
581 .unwrap(),
582 )
583 .chain(stmts)
584 .collect();
585
586 let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
587 let ident = &f.sig.ident;
588
589 let resource_args = statics
590 .iter()
591 .map(|statik| {
592 let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
593 let ident = &statik.ident;
594 let ty = &statik.ty;
595 let expr = &statik.expr;
596 quote! {
597 #(#cfgs)*
598 {
599 #(#attrs)*
600 static mut #ident: #ty = #expr;
601 unsafe { &mut #ident }
602 }
603 }
604 })
605 .collect::<Vec<_>>();
606
607 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) {
608 return error;
609 }
610
611 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
612
613 quote!(
614 #(#cfgs)*
615 #(#attrs)*
616 #[doc(hidden)]
617 #[export_name = #ident_s]
618 pub unsafe extern "C" fn #tramp_ident() {
619 #[allow(static_mut_refs)]
620 #ident(
621 #(#resource_args),*
622 )
623 }
624
625 #f
626 )
627 .into()
628}
629
630#[proc_macro_attribute]
631pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
632 let f = parse_macro_input!(input as ItemFn);
633
634 let valid_signature = f.sig.constness.is_none()
636 && f.vis == Visibility::Inherited
637 && f.sig.unsafety.is_some()
638 && f.sig.abi.is_none()
639 && f.sig.inputs.is_empty()
640 && f.sig.generics.params.is_empty()
641 && f.sig.generics.where_clause.is_none()
642 && f.sig.variadic.is_none()
643 && match f.sig.output {
644 ReturnType::Default => true,
645 ReturnType::Type(_, ref ty) => match **ty {
646 Type::Tuple(ref tuple) => tuple.elems.is_empty(),
647 _ => false,
648 },
649 };
650
651 if !valid_signature {
652 return parse::Error::new(
653 f.span(),
654 "`#[pre_init]` function must have signature `unsafe fn()`",
655 )
656 .to_compile_error()
657 .into();
658 }
659
660 if !args.is_empty() {
661 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
662 .to_compile_error()
663 .into();
664 }
665
666 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::PreInit) {
667 return error;
668 }
669
670 let attrs = f.attrs;
672 let ident = f.sig.ident;
673 let block = f.block;
674
675 quote!(
676 #[export_name = "__pre_init"]
677 #[allow(missing_docs)] #(#attrs)*
679 pub unsafe fn #ident() #block
680 )
681 .into()
682}
683
684fn extract_static_muts(
686 stmts: impl IntoIterator<Item = Stmt>,
687) -> Result<(Vec<ItemStatic>, Vec<Stmt>), parse::Error> {
688 let mut istmts = stmts.into_iter();
689
690 let mut seen = HashSet::new();
691 let mut statics = vec![];
692 let mut stmts = vec![];
693 for stmt in istmts.by_ref() {
694 match stmt {
695 Stmt::Item(Item::Static(var)) => match var.mutability {
696 syn::StaticMutability::Mut(_) => {
697 if seen.contains(&var.ident) {
698 return Err(parse::Error::new(
699 var.ident.span(),
700 format!("the name `{}` is defined multiple times", var.ident),
701 ));
702 }
703
704 seen.insert(var.ident.clone());
705 statics.push(var);
706 }
707 _ => stmts.push(Stmt::Item(Item::Static(var))),
708 },
709 _ => {
710 stmts.push(stmt);
711 break;
712 }
713 }
714 }
715
716 stmts.extend(istmts);
717
718 Ok((statics, stmts))
719}
720
721fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
722 let mut cfgs = vec![];
723 let mut not_cfgs = vec![];
724
725 for attr in attrs {
726 if eq(&attr, "cfg") {
727 cfgs.push(attr);
728 } else {
729 not_cfgs.push(attr);
730 }
731 }
732
733 (cfgs, not_cfgs)
734}
735
736enum WhiteListCaller {
737 Entry,
738 Exception,
739 Interrupt,
740 PreInit,
741}
742
743fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<(), TokenStream> {
744 let whitelist = &[
745 "doc",
746 "link_section",
747 "cfg",
748 "allow",
749 "warn",
750 "deny",
751 "forbid",
752 "cold",
753 "naked",
754 "expect",
755 ];
756
757 'o: for attr in attrs {
758 for val in whitelist {
759 if eq(attr, val) {
760 continue 'o;
761 }
762 }
763
764 let err_str = match caller {
765 WhiteListCaller::Entry => "this attribute is not allowed on a cortex-m-rt entry point",
766 WhiteListCaller::Exception => {
767 "this attribute is not allowed on an exception handler controlled by cortex-m-rt"
768 }
769 WhiteListCaller::Interrupt => {
770 "this attribute is not allowed on an interrupt handler controlled by cortex-m-rt"
771 }
772 WhiteListCaller::PreInit => {
773 "this attribute is not allowed on a pre-init controlled by cortex-m-rt"
774 }
775 };
776
777 return Err(parse::Error::new(attr.span(), err_str)
778 .to_compile_error()
779 .into());
780 }
781
782 Ok(())
783}
784
785fn eq(attr: &Attribute, name: &str) -> bool {
787 attr.style == AttrStyle::Outer && attr.path().is_ident(name)
788}