Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make support for u-boot #227

Merged
merged 13 commits into from
Aug 27, 2024
6 changes: 4 additions & 2 deletions .github/workflows/riscv-rt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
mekosko marked this conversation as resolved.
Show resolved Hide resolved
- 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:
Expand Down
2 changes: 2 additions & 0 deletions riscv-rt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions riscv-rt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
1 change: 1 addition & 0 deletions riscv-rt/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ syn = { version = "2.0", features = ["extra-traits", "full"] }
[features]
s-mode = []
v-trap = []
u-boot = []
108 changes: 79 additions & 29 deletions riscv-rt/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -52,29 +53,57 @@ 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",
)
.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<TokenStream> {
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<TokenStream> {
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;
}
}

Expand Down Expand Up @@ -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<Type> {
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.
Expand Down Expand Up @@ -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)
}
Expand Down
13 changes: 13 additions & 0 deletions riscv-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
1 change: 1 addition & 0 deletions riscv/src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading