pie_boot_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Ident, ItemFn, parse::Parse, parse_macro_input};
4
5mod entry;
6
7#[proc_macro_attribute]
8pub fn start_code(args: TokenStream, input: TokenStream) -> TokenStream {
9    // 解析参数中的选项,例如 naked
10    let parsed_args = parse_macro_input!(args as StartCodeArgs);
11
12    let f = parse_macro_input!(input as ItemFn);
13    let attrs = f.attrs;
14    let vis = f.vis;
15    let name = f.sig.ident;
16    let args = f.sig.inputs;
17    let stmts = f.block.stmts;
18    let ret = f.sig.output;
19
20    let naked_prefix;
21    let naked_attr;
22    if parsed_args.naked {
23        naked_attr = quote! {
24            #[unsafe(naked)]
25        };
26        naked_prefix = quote! {
27            unsafe extern "C"
28        };
29    } else {
30        naked_attr = quote! {};
31        naked_prefix = quote! {};
32    };
33
34    quote!(
35        #naked_attr
36        #[unsafe(link_section = ".idmap.text")]
37        #(#attrs)*
38        #vis #naked_prefix fn #name(#args) #ret {
39            #(#stmts)*
40        }
41    )
42    .into()
43}
44
45struct StartCodeArgs {
46    naked: bool,
47}
48
49impl Parse for StartCodeArgs {
50    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
51        let mut naked = false;
52
53        if !input.is_empty() {
54            let ident: Ident = input.parse()?;
55            if ident == "naked" {
56                naked = true;
57            } else {
58                return Err(input.error("unexpected argument, expected `naked`"));
59            }
60        }
61
62        Ok(StartCodeArgs { naked })
63    }
64}
65
66/// Attribute to declare the entry point of the program
67///
68/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
69/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
70/// private modules between the item and the root of the crate); if the item is in the root of the
71/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
72///
73/// The specified function will be called by the reset handler *after* RAM has been initialized.
74/// If present, the FPU will also be enabled before the function is called.
75///
76/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
77///
78/// # Properties
79///
80/// The entry point will be called by the reset handler. The program can't reference to the entry
81/// point, much less invoke it.
82///
83/// # Examples
84///
85/// - Simple entry point
86///
87/// ``` no_run
88/// # #![no_main]
89/// # use pie_boot::entry;
90/// #[entry]
91/// fn main(args: &pie_boot::BootArgs) -> ! {
92///     loop {
93///         /* .. */
94///     }
95/// }
96/// ```
97#[proc_macro_attribute]
98pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
99    entry::entry(args, input, "__pie_boot_main")
100}
101
102/// Attribute to declare the secondary entry point of the program
103///
104/// # Examples
105///
106/// - Simple entry point
107///
108/// ``` no_run
109/// # #![no_main]
110/// # use pie_boot::secondary_entry;
111/// #[entry]
112/// fn secondary(cpu_id: usize) -> ! {
113///     loop {
114///         /* .. */
115///     }
116/// }
117/// ```
118#[proc_macro_attribute]
119pub fn secondary_entry(args: TokenStream, input: TokenStream) -> TokenStream {
120    entry::entry(args, input, "__pie_boot_secondary")
121}
122
123#[proc_macro_attribute]
124pub fn irq_handler(_args: TokenStream, input: TokenStream) -> TokenStream {
125    let func = parse_macro_input!(input as ItemFn);
126    let block = func.block;
127    quote! {
128        #[unsafe(no_mangle)]
129        extern "Rust" fn __somehal_handle_irq() {
130            #block
131        }
132    }
133    .into()
134}