diff --git a/macros/Cargo.lock b/macros/Cargo.lock new file mode 100644 index 000000000..0b6e2b9bb --- /dev/null +++ b/macros/Cargo.lock @@ -0,0 +1,98 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "proc-macro2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "rand_chacha", + "rand_core", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core", +] + +[[package]] +name = "syn" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "xous-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "rand", + "syn", +] diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 000000000..389d17c6c --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,29 @@ +[package] +authors = [ + "Sean Cross " +] +categories = ["embedded", "no-std"] +description = "Attributes re-exported in `xous`" +documentation = "https://docs.rs/xous" +keywords = ["xous", "runtime", "startup"] +license = "MIT OR Apache-2.0" +name = "xous-macros" +repository = "https://github.com/betrusted/xous-core" +version = "0.1.0" +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" + +[dependencies.syn] +version = "1.0" +features = ["extra-traits", "full"] + +[dependencies.rand] +version = "0.7.3" +default-features = false +features = ["small_rng"] diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 000000000..8b1d73d71 --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,232 @@ +// #![deny(warnings)] + +extern crate proc_macro; +extern crate rand; +#[macro_use] +extern crate quote; +extern crate core; +extern crate proc_macro2; +#[macro_use] +extern crate syn; + +use proc_macro2::Span; +use rand::Rng; +use rand::SeedableRng; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::{SystemTime, UNIX_EPOCH}; +use syn::{parse, spanned::Spanned, Ident, ItemFn, ReturnType, Type, Visibility}; + +static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); + +use proc_macro::TokenStream; + +/// Attribute to declare the entry point of the program +/// +/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you +/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no +/// private modules between the item and the root of the crate); if the item is in the root of the +/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases. +/// +/// The specified function will be called by the reset handler *after* RAM has been initialized. +/// If present, the FPU will also be enabled before the function is called. +/// +/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function) +/// +/// # Properties +/// +/// The entry point will be called by the reset handler. The program can't reference to the entry +/// point, much less invoke it. +/// +/// # Examples +/// +/// - Simple entry point +/// +/// ``` no_run +/// # #![no_main] +/// # use riscv_rt_macros::entry; +/// #[entry] +/// fn main() -> ! { +/// loop { +/// /* .. */ +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn xous_main(args: TokenStream, input: TokenStream) -> TokenStream { + let f = parse_macro_input!(input as ItemFn); + + // let hosted = + // cfg!(target_os = "linux") || cfg!(target_os = "windows") || cfg!(target_os = "macos"); + // let is_none = cfg!(target_os = ""); + + // return parse::Error::new( + // f.span(), + // format!( + // "Target OS is none? {} - hosted? {} Unknown OS? {}", + // is_none, + // hosted, + // cfg!(target_os = "unknown") + // ), + // ) + // .to_compile_error() + // .into(); + + // check the function signature + let valid_signature = f.sig.constness.is_none() + && f.sig.asyncness.is_none() + && f.vis == Visibility::Inherited + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { + ReturnType::Default => false, + ReturnType::Type(_, ref ty) => match **ty { + Type::Never(_) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[xous_main]` function must have signature `[unsafe] fn() -> !`", + ) + .to_compile_error() + .into(); + } + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + // XXX should we blacklist other attributes? + let attrs = f.attrs; + let unsafety = f.sig.unsafety; + let hash = random_ident(); + let stmts = f.block.stmts; + + quote!( + xous::maybe_main!(); + #[export_name = "xous_entry"] + #(#attrs)* + pub #unsafety fn #hash() -> ! { + #(#stmts)* + } + + ) + .into() + // pub fn main() { + // #hash() + // } +} + +// /// Attribute to mark which function will be called at the beginning of the reset handler. +// /// +// /// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you +// /// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no +// /// private modules between the item and the root of the crate); if the item is in the root of the +// /// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer +// /// releases. +// /// +// /// The function must have the signature of `unsafe fn()`. +// /// +// /// The function passed will be called before static variables are initialized. Any access of static +// /// variables will result in undefined behavior. +// /// +// /// # Examples +// /// +// /// ``` +// /// # use riscv_rt_macros::pre_init; +// /// #[pre_init] +// /// unsafe fn before_main() { +// /// // do something here +// /// } +// /// +// /// # fn main() {} +// /// ``` +// #[proc_macro_attribute] +// pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { +// let f = parse_macro_input!(input as ItemFn); + +// // check the function signature +// let valid_signature = f.sig.constness.is_none() +// && f.sig.asyncness.is_none() +// && f.vis == Visibility::Inherited +// && f.sig.unsafety.is_some() +// && f.sig.abi.is_none() +// && f.sig.inputs.is_empty() +// && f.sig.generics.params.is_empty() +// && f.sig.generics.where_clause.is_none() +// && f.sig.variadic.is_none() +// && match f.sig.output { +// ReturnType::Default => true, +// ReturnType::Type(_, ref ty) => match **ty { +// Type::Tuple(ref tuple) => tuple.elems.is_empty(), +// _ => false, +// }, +// }; + +// if !valid_signature { +// return parse::Error::new( +// f.span(), +// "`#[pre_init]` function must have signature `unsafe fn()`", +// ) +// .to_compile_error() +// .into(); +// } + +// if !args.is_empty() { +// return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") +// .to_compile_error() +// .into(); +// } + +// // XXX should we blacklist other attributes? +// let attrs = f.attrs; +// let ident = f.sig.ident; +// let block = f.block; + +// quote!( +// #[export_name = "__pre_init"] +// #(#attrs)* +// pub unsafe fn #ident() #block +// ) +// .into() +// } + +// Creates a random identifier +fn random_ident() -> Ident { + let secs = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64; + let mut seed: [u8; 16] = [0; 16]; + + for (i, v) in seed.iter_mut().take(8).enumerate() { + *v = ((secs >> (i * 8)) & 0xFF) as u8 + } + + for (i, v) in seed.iter_mut().skip(8).enumerate() { + *v = ((count >> (i * 8)) & 0xFF) as u8 + } + + let mut rng = rand::rngs::SmallRng::from_seed(seed); + Ident::new( + &(0..16) + .map(|i| { + if i == 0 || rng.gen() { + ('a' as u8 + rng.gen::() % 25) as char + } else { + ('0' as u8 + rng.gen::() % 10) as char + } + }) + .collect::(), + Span::call_site(), + ) +} diff --git a/xous-rs/Cargo.toml b/xous-rs/Cargo.toml index 79e9ff1a2..d14e35cb5 100644 --- a/xous-rs/Cargo.toml +++ b/xous-rs/Cargo.toml @@ -7,6 +7,7 @@ description = "System call interface for Xous" [dependencies] bitflags = "1.2.1" +xous-macros = { path = "../macros", version = "0.1.0" } [features] # If this is set, then the "Drop" feature of MemoryMessage structs diff --git a/xous-rs/src/lib.rs b/xous-rs/src/lib.rs index 9268dd9a4..7fd160ed5 100644 --- a/xous-rs/src/lib.rs +++ b/xous-rs/src/lib.rs @@ -3,6 +3,10 @@ #[macro_use] extern crate bitflags; +extern crate xous_macros as macros; + +pub use macros::xous_main; + pub mod arch; pub mod carton; @@ -27,3 +31,41 @@ macro_rules! make_name { u32::from_le_bytes(c) as usize }}; } + +#[cfg(not(target_os = "none"))] +#[macro_export] +macro_rules! maybe_main { + () => { + extern "Rust" { + fn xous_entry() -> !; + } + fn main() { + unsafe { xous_entry() }; + } + }; +} + +#[cfg(target_os = "none")] +#[macro_export] +macro_rules! maybe_main { + () => { + use core::panic::PanicInfo; + + #[panic_handler] + fn handle_panic(_arg: &PanicInfo) -> ! { + // println!("PANIC!"); + // println!("Details: {:?}", arg); + xous::syscall::wait_event(); + loop {} + } + + extern "Rust" { + fn xous_entry() -> !; + } + + #[export_name = "_start"] + pub extern "C" fn _start() { + unsafe { xous_entry() }; + } + }; +}