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        if p.qself.is_none() && p.path.leading_colon.is_none() && p.path.segments.len() == 1 {
128            let segment = p.path.segments.first().unwrap();
129            if segment.ident == name && segment.arguments == PathArguments::None {
130                return true;
131            }
132        }
133    }
134    false
135}
136
137const NAMESPACE: &str = "sparreal_os";
138
139#[proc_macro_attribute]
140pub fn api_trait(_args: TokenStream, item: TokenStream) -> TokenStream {
141    abi_singleton::api_trait(item, NAMESPACE)
142}
143
144#[proc_macro_attribute]
145pub fn api_impl(_args: TokenStream, item: TokenStream) -> TokenStream {
146    abi_singleton::api_impl(item, NAMESPACE)
147}
148
149#[proc_macro]
150pub fn build_test_setup(_input: TokenStream) -> TokenStream {
151    quote! {
152    println!("cargo::rustc-link-arg-tests=-Tlink.x");
153    println!("cargo::rustc-link-arg-tests=-no-pie");
154    println!("cargo::rustc-link-arg-tests=-znostart-stop-gc");
155    }
156    .into()
157}
158
159#[proc_macro]
160pub fn module_driver(input: TokenStream) -> TokenStream {
161    rdrive_macro_utils::module_driver_with_linker(input, "sparreal_kernel::driver")
162}
163
164#[proc_macro]
165pub fn define_aarch64_tcb_switch(_input: TokenStream) -> TokenStream {
166    let fp = arch::aarch64::tcb_switch(true);
167    let sp = arch::aarch64::tcb_switch(false);
168
169    quote! {
170        #[cfg(hard_float)]
171        #fp
172
173        #[cfg(not(hard_float))]
174        #sp
175    }
176    .into()
177}
178
179/// A speaking volume. Deriving `FromMeta` will cause this to be usable
180/// as a string value for a meta-item key.
181#[derive(Debug, Clone, Copy, FromMeta)]
182#[darling(default)]
183enum Aarch64TrapHandlerKind {
184    Irq,
185    Fiq,
186    Sync,
187    #[darling(rename = "serror")]
188    SError,
189}
190
191#[derive(Debug, FromMeta)]
192struct Aarch64TrapHandlerArgs {
193    kind: Aarch64TrapHandlerKind,
194}
195
196#[proc_macro_attribute]
197pub fn aarch64_trap_handler(args: TokenStream, input: TokenStream) -> TokenStream {
198    let attr_args = match NestedMeta::parse_meta_list(args.into()) {
199        Ok(v) => v,
200        Err(e) => {
201            return TokenStream::from(darling::Error::from(e).write_errors());
202        }
203    };
204    let args = match Aarch64TrapHandlerArgs::from_list(&attr_args) {
205        Ok(v) => v,
206        Err(e) => {
207            return TokenStream::from(e.write_errors());
208        }
209    };
210
211    let func = parse_macro_input!(input as ItemFn);
212
213    match args.kind {
214        Aarch64TrapHandlerKind::Irq | Aarch64TrapHandlerKind::Fiq => {
215            arch::aarch64::trap_handle_irq(func).into()
216        }
217        Aarch64TrapHandlerKind::Sync => arch::aarch64::trap_handle_irq(func).into(),
218        Aarch64TrapHandlerKind::SError => arch::aarch64::trap_handle_irq(func).into(),
219    }
220}