sparreal_macros/
lib.rs

1extern crate proc_macro;
2#[macro_use]
3extern crate quote;
4extern crate core;
5extern crate proc_macro2;
6#[macro_use]
7extern crate syn;
8
9mod api_trait;
10mod arch;
11
12use darling::{FromMeta, ast::NestedMeta};
13use proc_macro::TokenStream;
14use proc_macro2::Span;
15use syn::{FnArg, ItemFn, PathArguments, Type, Visibility, parse, spanned::Spanned};
16
17/// Attribute to declare the entry point of the program
18///
19/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
20/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
21/// private modules between the item and the root of the crate); if the item is in the root of the
22/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
23///
24/// The specified function will be called by the reset handler *after* RAM has been initialized.
25/// If present, the FPU will also be enabled before the function is called.
26///
27/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
28///
29/// # Properties
30///
31/// The entry point will be called by the reset handler. The program can't reference to the entry
32/// point, much less invoke it.
33///
34/// # Examples
35///
36/// - Simple entry point
37///
38/// ``` no_run
39/// # #![no_main]
40/// # use sparreal_macros::entry;
41/// #[entry]
42/// fn main() -> ! {
43///     loop {
44///         /* .. */
45///     }
46/// }
47/// ```
48#[proc_macro_attribute]
49pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
50    let f = parse_macro_input!(input as ItemFn);
51
52    // check the function arguments
53    if f.sig.inputs.len() > 3 {
54        return parse::Error::new(
55            f.sig.inputs.last().unwrap().span(),
56            "`#[entry]` function has too many arguments",
57        )
58        .to_compile_error()
59        .into();
60    }
61    for arg in &f.sig.inputs {
62        match arg {
63            FnArg::Receiver(_) => {
64                return parse::Error::new(arg.span(), "invalid argument")
65                    .to_compile_error()
66                    .into();
67            }
68            FnArg::Typed(t) => {
69                if !is_simple_type(&t.ty, "usize") {
70                    return parse::Error::new(t.ty.span(), "argument type must be usize")
71                        .to_compile_error()
72                        .into();
73                }
74            }
75        }
76    }
77
78    // check the function signature
79    let valid_signature = f.sig.constness.is_none()
80        && f.sig.asyncness.is_none()
81        && f.vis == Visibility::Inherited
82        && f.sig.abi.is_none()
83        && f.sig.generics.params.is_empty()
84        && f.sig.generics.where_clause.is_none()
85        && f.sig.variadic.is_none()
86        // && match f.sig.output {
87        //     ReturnType::Default => false,
88        //     ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
89        // }
90        ;
91
92    if !valid_signature {
93        return parse::Error::new(
94            f.span(),
95            "`#[entry]` function must have signature `[unsafe] fn([arg0: usize, ...]) `",
96        )
97        .to_compile_error()
98        .into();
99    }
100
101    if !args.is_empty() {
102        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
103            .to_compile_error()
104            .into();
105    }
106
107    // XXX should we blacklist other attributes?
108    let attrs = f.attrs;
109    let unsafety = f.sig.unsafety;
110    let args = f.sig.inputs;
111    let stmts = f.block.stmts;
112
113    quote!(
114        #[allow(non_snake_case)]
115        #[unsafe(no_mangle)]
116        #(#attrs)*
117        pub #unsafety extern "C" fn __sparreal_rt_main(#args) {
118            #(#stmts)*
119        }
120    )
121    .into()
122}
123
124#[allow(unused)]
125fn is_simple_type(ty: &Type, name: &str) -> bool {
126    if let Type::Path(p) = ty
127        && p.qself.is_none()
128        && p.path.leading_colon.is_none()
129        && p.path.segments.len() == 1
130    {
131        let segment = p.path.segments.first().unwrap();
132        if segment.ident == name && segment.arguments == PathArguments::None {
133            return true;
134        }
135    }
136    false
137}
138
139const NAMESPACE: &str = "sparreal_os";
140
141#[proc_macro_attribute]
142pub fn api_trait(_args: TokenStream, item: TokenStream) -> TokenStream {
143    abi_singleton::api_trait(item, NAMESPACE)
144}
145
146#[proc_macro_attribute]
147pub fn api_impl(_args: TokenStream, item: TokenStream) -> TokenStream {
148    abi_singleton::api_impl(item, NAMESPACE)
149}
150
151#[proc_macro]
152pub fn build_test_setup(_input: TokenStream) -> TokenStream {
153    quote! {
154    println!("cargo::rustc-link-arg-tests=-Tlink.x");
155    println!("cargo::rustc-link-arg-tests=-no-pie");
156    println!("cargo::rustc-link-arg-tests=-znostart-stop-gc");
157    }
158    .into()
159}
160
161#[proc_macro]
162pub fn define_aarch64_tcb_switch(_input: TokenStream) -> TokenStream {
163    let fp = arch::aarch64::tcb_switch(true);
164    let sp = arch::aarch64::tcb_switch(false);
165
166    quote! {
167        #[cfg(hard_float)]
168        #fp
169
170        #[cfg(not(hard_float))]
171        #sp
172    }
173    .into()
174}
175
176/// A speaking volume. Deriving `FromMeta` will cause this to be usable
177/// as a string value for a meta-item key.
178#[derive(Debug, Clone, Copy, FromMeta)]
179#[darling(default)]
180enum Aarch64TrapHandlerKind {
181    Irq,
182    Fiq,
183    Sync,
184    #[darling(rename = "serror")]
185    SError,
186}
187
188#[derive(Debug, FromMeta)]
189struct Aarch64TrapHandlerArgs {
190    kind: Aarch64TrapHandlerKind,
191}
192
193#[proc_macro_attribute]
194pub fn aarch64_trap_handler(args: TokenStream, input: TokenStream) -> TokenStream {
195    let attr_args = match NestedMeta::parse_meta_list(args.into()) {
196        Ok(v) => v,
197        Err(e) => {
198            return TokenStream::from(darling::Error::from(e).write_errors());
199        }
200    };
201    let args = match Aarch64TrapHandlerArgs::from_list(&attr_args) {
202        Ok(v) => v,
203        Err(e) => {
204            return TokenStream::from(e.write_errors());
205        }
206    };
207
208    let func = parse_macro_input!(input as ItemFn);
209
210    match args.kind {
211        Aarch64TrapHandlerKind::Irq | Aarch64TrapHandlerKind::Fiq => {
212            arch::aarch64::trap_handle_irq(func).into()
213        }
214        Aarch64TrapHandlerKind::Sync => arch::aarch64::trap_handle_irq(func).into(),
215        Aarch64TrapHandlerKind::SError => arch::aarch64::trap_handle_irq(func).into(),
216    }
217}