diff --git a/.github/workflows/riscv-rt.yaml b/.github/workflows/riscv-rt.yaml index 34702a74..be49bbce 100644 --- a/.github/workflows/riscv-rt.yaml +++ b/.github/workflows/riscv-rt.yaml @@ -41,8 +41,10 @@ jobs: run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=single-hart - name : Build (v-trap) run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=v-trap - - name: Build (all features) - run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --all-features + - name : Build (all features except u-boot) + run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=s-mode,single-hart,v-trap + - name : Build (u-boot) + run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example empty --features=u-boot # Job to check that all the builds succeeded build-check: diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 59b407c1..d224e5cd 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add `v-trap` feature to enable interrupt handling in vectored mode. - Add `interrupt` proc macro to help defining interrupt handlers. If `v-trap` feature is enabled, this macro also generates its corresponding trap. +- Add `u-boot` feature, so that you can start your elf binary with u-boot and +work with passed arguments. ### Changed diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 2910e355..eb2e212f 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -23,3 +23,4 @@ panic-halt = "0.2.0" s-mode = ["riscv-rt-macros/s-mode"] single-hart = [] v-trap = ["riscv-rt-macros/v-trap"] +u-boot = ["riscv-rt-macros/u-boot", "single-hart"] diff --git a/riscv-rt/macros/Cargo.toml b/riscv-rt/macros/Cargo.toml index 19f6690d..b7dba6fb 100644 --- a/riscv-rt/macros/Cargo.toml +++ b/riscv-rt/macros/Cargo.toml @@ -23,3 +23,4 @@ syn = { version = "2.0", features = ["extra-traits", "full"] } [features] s-mode = [] v-trap = [] +u-boot = [] diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index c34e3a25..df7d5a01 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -11,8 +11,9 @@ extern crate syn; use proc_macro2::Span; use syn::{ parse::{self, Parse}, + punctuated::Punctuated, spanned::Spanned, - FnArg, ItemFn, LitInt, LitStr, PathArguments, ReturnType, Type, Visibility, + FnArg, ItemFn, LitInt, LitStr, PatType, ReturnType, Type, Visibility, }; use proc_macro::TokenStream; @@ -52,8 +53,13 @@ use proc_macro::TokenStream; pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); + #[cfg(not(feature = "u-boot"))] + let arguments_limit = 3; + #[cfg(feature = "u-boot")] + let arguments_limit = 2; + // check the function arguments - if f.sig.inputs.len() > 3 { + if f.sig.inputs.len() > arguments_limit { return parse::Error::new( f.sig.inputs.last().unwrap().span(), "`#[entry]` function has too many arguments", @@ -61,20 +67,43 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { .to_compile_error() .into(); } - for arg in &f.sig.inputs { - match arg { - FnArg::Receiver(_) => { - return parse::Error::new(arg.span(), "invalid argument") - .to_compile_error() - .into(); - } - FnArg::Typed(t) => { - if !is_simple_type(&t.ty, "usize") { - return parse::Error::new(t.ty.span(), "argument type must be usize") - .to_compile_error() - .into(); - } - } + + fn check_correct_type(argument: &PatType, ty: &str) -> Option { + let inv_type_message = format!("argument type must be {}", ty); + + if !is_correct_type(&argument.ty, ty) { + let error = parse::Error::new(argument.ty.span(), inv_type_message); + + Some(error.to_compile_error().into()) + } else { + None + } + } + fn check_argument_type(argument: &FnArg, ty: &str) -> Option { + let argument_error = parse::Error::new(argument.span(), "invalid argument"); + let argument_error = argument_error.to_compile_error().into(); + + match argument { + FnArg::Typed(argument) => check_correct_type(argument, ty), + FnArg::Receiver(_) => Some(argument_error), + } + } + #[cfg(not(feature = "u-boot"))] + for argument in f.sig.inputs.iter() { + if let Some(message) = check_argument_type(argument, "usize") { + return message; + }; + } + #[cfg(feature = "u-boot")] + if let Some(argument) = f.sig.inputs.get(0) { + if let Some(message) = check_argument_type(argument, "c_int") { + return message; + } + } + #[cfg(feature = "u-boot")] + if let Some(argument) = f.sig.inputs.get(1) { + if let Some(message) = check_argument_type(argument, "*const *const c_char") { + return message; } } @@ -123,17 +152,32 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { .into() } -#[allow(unused)] -fn is_simple_type(ty: &Type, name: &str) -> bool { - if let Type::Path(p) = ty { - if p.qself.is_none() && p.path.leading_colon.is_none() && p.path.segments.len() == 1 { - let segment = p.path.segments.first().unwrap(); - if segment.ident == name && segment.arguments == PathArguments::None { - return true; - } +fn strip_type_path(ty: &Type) -> Option { + match ty { + Type::Ptr(ty) => { + let mut ty = ty.clone(); + ty.elem = Box::new(strip_type_path(&ty.elem)?); + Some(Type::Ptr(ty)) + } + Type::Path(ty) => { + let mut ty = ty.clone(); + let last_segment = ty.path.segments.last().unwrap().clone(); + ty.path.segments = Punctuated::new(); + ty.path.segments.push_value(last_segment); + Some(Type::Path(ty)) } + _ => None, + } +} + +#[allow(unused)] +fn is_correct_type(ty: &Type, name: &str) -> bool { + let correct: Type = syn::parse_str(name).unwrap(); + if let Some(ty) = strip_type_path(ty) { + ty == correct + } else { + false } - false } /// Attribute to mark which function will be called at the beginning of the reset handler. @@ -505,15 +549,21 @@ _continue_interrupt_trap: } #[proc_macro_attribute] -/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. -/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. +/// Attribute to declare an interrupt handler. +/// +/// The function must have the signature `[unsafe] fn() [-> !]`. +/// If the `v-trap` feature is enabled, this macro generates the +/// interrupt trap handler in assembly for RISCV-32 targets. pub fn interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream { interrupt(args, input, RiscvArch::Rv32) } #[proc_macro_attribute] -/// Attribute to declare an interrupt handler. The function must have the signature `[unsafe] fn() [-> !]`. -/// If the `v-trap` feature is enabled, this macro generates the interrupt trap handler in assembly for RISCV-32 targets. +/// Attribute to declare an interrupt handler. +/// +/// The function must have the signature `[unsafe] fn() [-> !]`. +/// If the `v-trap` feature is enabled, this macro generates the +/// interrupt trap handler in assembly for RISCV-64 targets. pub fn interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream { interrupt(args, input, RiscvArch::Rv64) } diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 0ffb3441..c4572dc4 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -452,6 +452,19 @@ //! ``` //! //! This will generate a function named `_start_MachineTimer_trap` that calls the interrupt handler `MachineTimer`. +//! +//! ## `u-boot` +//! +//! The u-boot support feature (`u-boot`) can be activated via [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html). +//! +//! For example: +//! ``` text +//! [dependencies] +//! riscv-rt = { features = ["u-boot"] } +//! ``` +//! When the u-boot feature is enabled, acceptable signature for `#[entry]` macros is changed. This is required +//! because when booting from elf, u-boot passes `argc` and `argv`. This feature also implies `single-hart`. +//! The only way to get boot-hart is through fdt, so other harts initialization is up to you. // NOTE: Adapted from cortex-m/src/lib.rs #![no_std] diff --git a/riscv/src/interrupt.rs b/riscv/src/interrupt.rs index 0bd03b7e..b6fcae4a 100644 --- a/riscv/src/interrupt.rs +++ b/riscv/src/interrupt.rs @@ -142,6 +142,7 @@ pub mod supervisor { } /// Execute closure `f` with interrupts enabled in the current hart (supervisor mode). + /// /// This method is assumed to be called within an interrupt handler, and allows /// nested interrupts to occur. After the closure `f` is executed, the [`sstatus`] /// and [`sepc`] registers are properly restored to their previous values.