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]
50pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
51 let f = parse_macro_input!(input as ItemFn);
52
53 let valid_signature = f.sig.constness.is_none()
57 && f.vis == Visibility::Inherited
58 && f.sig.abi.is_none()
59 && f.sig.inputs.is_empty()
60 && f.sig.generics.params.is_empty()
61 && f.sig.generics.where_clause.is_none()
62 && f.sig.variadic.is_none()
63 && match f.sig.output {
64 ReturnType::Default => false,
65 ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
66 };
67
68 if !valid_signature {
69 return parse::Error::new(
70 f.span(),
71 "`#[entry]` function must have signature `[unsafe] fn() -> !`",
72 )
73 .to_compile_error()
74 .into();
75 }
76
77 if !args.is_empty() {
78 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
79 .to_compile_error()
80 .into();
81 }
82
83 let tramp_ident = Ident::new("__aarch32_rt_kmain", Span::call_site());
92 let block = f.block;
93
94 if let Err(error) = check_attr_whitelist(&f.attrs, VectorKind::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 = "kmain"]
105 pub unsafe extern "C" fn #tramp_ident() -> ! {
106 #block
107 }
108 )
109 .into()
110}
111
112#[derive(Debug, PartialEq)]
114enum Exception {
115 Undefined,
116 SupervisorCall,
117 PrefetchAbort,
118 DataAbort,
119 Irq,
120}
121
122impl std::fmt::Display for Exception {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 Exception::Undefined => write!(f, "Undefined"),
126 Exception::SupervisorCall => write!(f, "SupervisorCall"),
127 Exception::PrefetchAbort => write!(f, "PrefetchAbort"),
128 Exception::DataAbort => write!(f, "DataAbort"),
129 Exception::Irq => write!(f, "Irq"),
130 }
131 }
132}
133
134#[proc_macro_attribute]
170pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
171 handle_vector(args, input, VectorKind::Exception)
172}
173
174#[proc_macro_attribute]
205pub fn irq(args: TokenStream, input: TokenStream) -> TokenStream {
206 handle_vector(args, input, VectorKind::Interrupt)
207}
208
209#[derive(PartialEq, Eq, Clone, Copy, Debug)]
211enum VectorKind {
212 Entry,
214 Exception,
216 Interrupt,
218}
219
220fn handle_vector(args: TokenStream, input: TokenStream, kind: VectorKind) -> TokenStream {
222 let f = parse_macro_input!(input as ItemFn);
223
224 if let Err(error) = check_attr_whitelist(&f.attrs, kind) {
225 return error;
226 }
227
228 let returns_never = match f.sig.output {
229 ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
230 _ => false,
231 };
232
233 let exception = match kind {
234 VectorKind::Entry => {
235 panic!("Don't handle #[entry] with `handle_vector`!");
236 }
237 VectorKind::Exception => {
238 let mut args_iter = args.into_iter();
239 let Some(TokenTree::Ident(exception_name)) = args_iter.next() else {
240 return parse::Error::new(
241 Span::call_site(),
242 "This attribute requires the name of the exception as the first argument",
243 )
244 .to_compile_error()
245 .into();
246 };
247 if args_iter.next().is_some() {
248 return parse::Error::new(
249 Span::call_site(),
250 "This attribute accepts only one argument",
251 )
252 .to_compile_error()
253 .into();
254 }
255 match exception_name.to_string().as_str() {
256 "Undefined" => {
257 if !returns_never && f.sig.unsafety.is_none() {
258 return parse::Error::new(
259 exception_name.span().into(),
260 "Undefined handlers that don't return ! must be unsafe",
261 )
262 .to_compile_error()
263 .into();
264 }
265 Exception::Undefined
266 }
267 "SupervisorCall" => Exception::SupervisorCall,
268 "PrefetchAbort" => {
269 if !returns_never && f.sig.unsafety.is_none() {
270 return parse::Error::new(
271 exception_name.span().into(),
272 "PrefetchAbort handlers that don't return ! must be unsafe",
273 )
274 .to_compile_error()
275 .into();
276 }
277 Exception::PrefetchAbort
278 }
279 "DataAbort" => {
280 if !returns_never && f.sig.unsafety.is_none() {
281 return parse::Error::new(
282 exception_name.span().into(),
283 "DataAbort handlers that don't return ! must be unsafe",
284 )
285 .to_compile_error()
286 .into();
287 }
288 Exception::DataAbort
289 }
290 "Irq" => Exception::Irq,
291 _ => {
292 return parse::Error::new(
293 exception_name.span().into(),
294 "This is not a valid exception name",
295 )
296 .to_compile_error()
297 .into();
298 }
299 }
300 }
301 VectorKind::Interrupt => Exception::Irq,
302 };
303
304 let block = f.block;
305 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
306
307 let handler = match exception {
308 Exception::Undefined => {
311 let tramp_ident = Ident::new("__aarch32_rt_undefined_handler", Span::call_site());
312 if returns_never {
313 quote!(
314 #(#cfgs)*
315 #(#attrs)*
316 #[doc(hidden)]
317 #[export_name = "_undefined_handler"]
318 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! {
319 #block
320 }
321
322 )
323 } else {
324 quote!(
325 #(#cfgs)*
326 #(#attrs)*
327 #[doc(hidden)]
328 #[export_name = "_undefined_handler"]
329 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize {
330 #block
331 }
332
333 )
334 }
335 }
336 Exception::PrefetchAbort => {
339 let tramp_ident = Ident::new("__aarch32_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 #block
348 }
349 )
350 } else {
351 quote!(
352 #(#cfgs)*
353 #(#attrs)*
354 #[doc(hidden)]
355 #[export_name = "_prefetch_abort_handler"]
356 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize {
357 #block
358 }
359
360 )
361 }
362 }
363 Exception::DataAbort => {
366 let tramp_ident = Ident::new("__aarch32_rt_data_abort_handler", Span::call_site());
367 if returns_never {
368 quote!(
369 #(#cfgs)*
370 #(#attrs)*
371 #[doc(hidden)]
372 #[export_name = "_data_abort_handler"]
373 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> ! {
374 #block
375 }
376
377 )
378 } else {
379 quote!(
380 #(#cfgs)*
381 #(#attrs)*
382 #[doc(hidden)]
383 #[export_name = "_data_abort_handler"]
384 pub unsafe extern "C" fn #tramp_ident(addr: usize) -> usize {
385 #block
386 }
387 )
388 }
389 }
390 Exception::SupervisorCall => {
392 let tramp_ident = Ident::new("__aarch32_rt_svc_handler", Span::call_site());
393 quote!(
394 #(#cfgs)*
395 #(#attrs)*
396 #[doc(hidden)]
397 #[export_name = "_svc_handler"]
398 pub unsafe extern "C" fn #tramp_ident(arg: u32) {
399 #block
400 }
401 )
402 }
403 Exception::Irq => {
405 let tramp_ident = Ident::new("__aarch32_rt_irq_handler", Span::call_site());
406 quote!(
407 #(#cfgs)*
408 #(#attrs)*
409 #[doc(hidden)]
410 #[export_name = "_irq_handler"]
411 pub unsafe extern "C" fn #tramp_ident() {
412 #block
413 }
414 )
415 }
416 };
417
418 quote!(
419 #handler
420 )
421 .into()
422}
423
424fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
428 let mut cfgs = vec![];
429 let mut not_cfgs = vec![];
430
431 for attr in attrs {
432 if eq(&attr, "cfg") {
433 cfgs.push(attr);
434 } else {
435 not_cfgs.push(attr);
436 }
437 }
438
439 (cfgs, not_cfgs)
440}
441
442fn check_attr_whitelist(attrs: &[Attribute], caller: VectorKind) -> Result<(), TokenStream> {
444 let whitelist = &[
445 "doc",
446 "link_section",
447 "cfg",
448 "allow",
449 "warn",
450 "deny",
451 "forbid",
452 "cold",
453 "naked",
454 "expect",
455 ];
456
457 'o: for attr in attrs {
458 for val in whitelist {
459 if eq(attr, val) {
460 continue 'o;
461 }
462 }
463
464 let err_str = match caller {
465 VectorKind::Entry => "this attribute is not allowed on an aarch32-rt entry point",
466 VectorKind::Exception => {
467 "this attribute is not allowed on an exception handler controlled by aarch32-rt"
468 }
469 VectorKind::Interrupt => {
470 "this attribute is not allowed on an interrupt handler controlled by aarch32-rt"
471 }
472 };
473
474 return Err(parse::Error::new(attr.span(), err_str)
475 .to_compile_error()
476 .into());
477 }
478
479 Ok(())
480}
481
482fn eq(attr: &Attribute, name: &str) -> bool {
484 attr.style == AttrStyle::Outer && attr.path().is_ident(name)
485}