xous: add #[xous::xous_main] proc-macro

This is used to annotate a main function that is compatible with both
hosted and baremetal environments.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-08-14 16:44:08 +08:00
parent 45bd670138
commit 3ed964507d
5 changed files with 402 additions and 0 deletions

98
macros/Cargo.lock generated Normal file
View file

@ -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",
]

29
macros/Cargo.toml Normal file
View file

@ -0,0 +1,29 @@
[package]
authors = [
"Sean Cross <sean@xobs.io>"
]
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"]

232
macros/src/lib.rs Normal file
View file

@ -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::<u8>() % 25) as char
} else {
('0' as u8 + rng.gen::<u8>() % 10) as char
}
})
.collect::<String>(),
Span::call_site(),
)
}

View file

@ -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

View file

@ -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() };
}
};
}