Skip to content

Commit

Permalink
Removed the feature flag and moved the trampoline into the macro
Browse files Browse the repository at this point in the history
  • Loading branch information
diondokter committed Jun 16, 2023
1 parent 36419ff commit 54877ba
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 120 deletions.
4 changes: 1 addition & 3 deletions cortex-m-rt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Add `zero-init-ram` feature to initialize RAM with zeros on startup. This can be necessary on
safety-critical hardware to properly initialize memory integrity measures.
- Add `hardfault-trampoline` feature that is enabled by default which enables
the previously required hardfault trampoline. Now when this feature is not active, there will be
no trampoline and the function you write is the exact function that is used as hardfault handler.
- Add optional `exception` argument for `HardFault`. It has one option `trampoline` which is true by default. When set to false, no trampoline will be created and the function will be called as the exception handler directly.

## [v0.7.3]

Expand Down
2 changes: 0 additions & 2 deletions cortex-m-rt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,10 @@ name = "compiletest"
required-features = ["device"]

[features]
default = ["hardfault-trampoline"]
device = []
set-sp = []
set-vtor = []
zero-init-ram = []
hardfault-trampoline = ["cortex-m-rt-macros/hardfault-trampoline"]

[package.metadata.docs.rs]
features = ["device"]
3 changes: 0 additions & 3 deletions cortex-m-rt/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,3 @@ proc-macro2 = "1.0"
[dependencies.syn]
features = ["extra-traits", "full"]
version = "2.0"

[features]
hardfault-trampoline = []
226 changes: 158 additions & 68 deletions cortex-m-rt/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ use quote::quote;
use std::collections::HashSet;
use std::iter;
use syn::{
parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn,
ItemStatic, ReturnType, Stmt, Type, Visibility,
parse::{self, Parse},
parse_macro_input,
spanned::Spanned,
AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type,
Visibility,
};

#[proc_macro_attribute]
Expand Down Expand Up @@ -113,21 +116,73 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
#[derive(Debug, PartialEq)]
enum Exception {
DefaultHandler,
HardFault,
HardFault(HardFaultArgs),
NonMaskableInt,
Other,
}

#[derive(Debug, PartialEq)]
struct HardFaultArgs {
trampoline: bool,
}

impl Default for HardFaultArgs {
fn default() -> Self {
Self { trampoline: true }
}
}

impl Parse for HardFaultArgs {
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
let mut items = Vec::new();
// Read a list of `ident = value,`
loop {
if input.is_empty() {
break;
}

let name = input.parse::<Ident>()?;
input.parse::<syn::Token!(=)>()?;
let value = input.parse::<syn::Lit>()?;

items.push((name, value));

if input.is_empty() {
break;
}

input.parse::<syn::Token!(,)>()?;
}

let mut args = Self::default();

for (name, value) in items {
match name.to_string().as_str() {
"trampoline" => match value {
syn::Lit::Bool(val) => {
args.trampoline = val.value();
}
_ => {
return Err(syn::Error::new_spanned(
value,
"Not a valid value. `trampoline` takes a boolean literal",
))
}
},
_ => {
return Err(syn::Error::new_spanned(name, "Not a valid argument name"));
}
}
}

Ok(args)
}
}

#[proc_macro_attribute]
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
let mut f = parse_macro_input!(input as ItemFn);

if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}

if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) {
return error;
}
Expand All @@ -137,14 +192,34 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {

let ident_s = ident.to_string();
let exn = match &*ident_s {
"DefaultHandler" => Exception::DefaultHandler,
"HardFault" => Exception::HardFault,
"NonMaskableInt" => Exception::NonMaskableInt,
"DefaultHandler" => {
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
Exception::DefaultHandler
}
"HardFault" => Exception::HardFault(parse_macro_input!(args)),
"NonMaskableInt" => {
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
Exception::NonMaskableInt
}
// NOTE that at this point we don't check if the exception is available on the target (e.g.
// MemoryManagement is not available on Cortex-M0)
"MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
| "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other,
_ => {
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}

return parse::Error::new(ident.span(), "This is not a valid exception name")
.to_compile_error()
.into();
Expand All @@ -153,7 +228,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {

if f.sig.unsafety.is_none() {
match exn {
Exception::DefaultHandler | Exception::HardFault | Exception::NonMaskableInt => {
Exception::DefaultHandler | Exception::HardFault(_) | Exception::NonMaskableInt => {
// These are unsafe to define.
let name = if exn == Exception::DefaultHandler {
"`DefaultHandler`".to_string()
Expand Down Expand Up @@ -232,17 +307,24 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
#f
)
}
Exception::HardFault if cfg!(feature = "hardfault-trampoline") => {
Exception::HardFault(args) => {
let valid_signature = f.sig.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.inputs.len() == 1
&& match &f.sig.inputs[0] {
FnArg::Typed(arg) => match arg.ty.as_ref() {
Type::Reference(r) => r.lifetime.is_none() && r.mutability.is_none(),
&& if args.trampoline {
match &f.sig.inputs[0] {
FnArg::Typed(arg) => match arg.ty.as_ref() {
Type::Reference(r) => {
r.lifetime.is_none()
&& r.mutability.is_none()
&& f.sig.inputs.len() == 1
}
_ => false,
},
_ => false,
},
_ => false,
}
} else {
f.sig.inputs.is_empty()
}
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
Expand All @@ -255,66 +337,74 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
if !valid_signature {
return parse::Error::new(
fspan,
"`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`",
if args.trampoline {
"`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`"
} else {
"`HardFault` handler must have signature `unsafe fn() -> !`"
},
)
.to_compile_error()
.into();
}

f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
let ident = &f.sig.ident;

let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
if args.trampoline {
let tramp_ident =
Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
let ident = &f.sig.ident;

quote!(
#(#cfgs)*
#(#attrs)*
#[doc(hidden)]
#[export_name = "HardFault"]
// Only emit link_section when building for embedded targets,
// because some hosted platforms (used to check the build)
// cannot handle the long link section names.
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) {
#ident(frame)
}
let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());

#f
)
}
Exception::HardFault => {
let valid_signature = f.sig.constness.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) => matches!(**ty, Type::Never(_)),
};
quote!(
#(#cfgs)*
#(#attrs)*
#[doc(hidden)]
#[export_name = "HardFault"]
// Only emit link_section when building for embedded targets,
// because some hosted platforms (used to check the build)
// cannot handle the long link section names.
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) {
#ident(frame)
}

if !valid_signature {
return parse::Error::new(
fspan,
"`HardFault` handler must have signature `unsafe fn() -> !`",
#f

// HardFault exceptions are bounced through this trampoline which grabs the stack pointer at
// the time of the exception and passes it to the user's HardFault handler in r0.
// Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP.
core::arch::global_asm!(
".cfi_sections .debug_frame
.section .HardFaultTrampoline, \"ax\"
.global HardFaultTrampline
.type HardFaultTrampline,%function
.thumb_func
.cfi_startproc
HardFaultTrampoline:",
"mov r0, lr
movs r1, #4
tst r0, r1
bne 0f
mrs r0, MSP
b HardFault
0:
mrs r0, PSP
b HardFault",
".cfi_endproc
.size HardFaultTrampoline, . - HardFaultTrampoline",
);
)
} else {
quote!(
#[export_name = "HardFault"]
// Only emit link_section when building for embedded targets,
// because some hosted platforms (used to check the build)
// cannot handle the long link section names.
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
#f
)
.to_compile_error()
.into();
}

f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());

quote!(
#[export_name = "HardFault"]
// Only emit link_section when building for embedded targets,
// because some hosted platforms (used to check the build)
// cannot handle the long link section names.
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
#f
)
}
Exception::NonMaskableInt | Exception::Other => {
let valid_signature = f.sig.constness.is_none()
Expand Down
Loading

0 comments on commit 54877ba

Please sign in to comment.