Skip to content

Commit 1746a63

Browse files
authored
Merge pull request #476 from diondokter/optional-hardfault-trampoline
Hardfault trampoline is now optional
2 parents 4651808 + faf7bd3 commit 1746a63

File tree

8 files changed

+275
-87
lines changed

8 files changed

+275
-87
lines changed

cortex-m-rt/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
- Add `zero-init-ram` feature to initialize RAM with zeros on startup. This can be necessary on
1111
safety-critical hardware to properly initialize memory integrity measures.
12+
- 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.
1213

1314
## [v0.7.3]
1415

cortex-m-rt/macros/src/lib.rs

Lines changed: 178 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ extern crate proc_macro;
77
use proc_macro::TokenStream;
88
use proc_macro2::Span;
99
use quote::quote;
10-
use std::collections::HashSet;
1110
use std::iter;
11+
use std::{collections::HashSet, fmt::Display};
1212
use syn::{
13-
parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn,
14-
ItemStatic, ReturnType, Stmt, Type, Visibility,
13+
parse::{self, Parse},
14+
parse_macro_input,
15+
spanned::Spanned,
16+
AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type,
17+
Visibility,
1518
};
1619

1720
#[proc_macro_attribute]
@@ -113,21 +116,84 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
113116
#[derive(Debug, PartialEq)]
114117
enum Exception {
115118
DefaultHandler,
116-
HardFault,
119+
HardFault(HardFaultArgs),
117120
NonMaskableInt,
118121
Other,
119122
}
120123

124+
impl Display for Exception {
125+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126+
match self {
127+
Exception::DefaultHandler => write!(f, "`DefaultHandler`"),
128+
Exception::HardFault(_) => write!(f, "`HardFault` handler"),
129+
Exception::NonMaskableInt => write!(f, "`NonMaskableInt` handler"),
130+
Exception::Other => write!(f, "Other exception handler"),
131+
}
132+
}
133+
}
134+
135+
#[derive(Debug, PartialEq)]
136+
struct HardFaultArgs {
137+
trampoline: bool,
138+
}
139+
140+
impl Default for HardFaultArgs {
141+
fn default() -> Self {
142+
Self { trampoline: true }
143+
}
144+
}
145+
146+
impl Parse for HardFaultArgs {
147+
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
148+
let mut items = Vec::new();
149+
// Read a list of `ident = value,`
150+
loop {
151+
if input.is_empty() {
152+
break;
153+
}
154+
155+
let name = input.parse::<Ident>()?;
156+
input.parse::<syn::Token!(=)>()?;
157+
let value = input.parse::<syn::Lit>()?;
158+
159+
items.push((name, value));
160+
161+
if input.is_empty() {
162+
break;
163+
}
164+
165+
input.parse::<syn::Token!(,)>()?;
166+
}
167+
168+
let mut args = Self::default();
169+
170+
for (name, value) in items {
171+
match name.to_string().as_str() {
172+
"trampoline" => match value {
173+
syn::Lit::Bool(val) => {
174+
args.trampoline = val.value();
175+
}
176+
_ => {
177+
return Err(syn::Error::new_spanned(
178+
value,
179+
"Not a valid value. `trampoline` takes a boolean literal",
180+
))
181+
}
182+
},
183+
_ => {
184+
return Err(syn::Error::new_spanned(name, "Not a valid argument name"));
185+
}
186+
}
187+
}
188+
189+
Ok(args)
190+
}
191+
}
192+
121193
#[proc_macro_attribute]
122194
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
123195
let mut f = parse_macro_input!(input as ItemFn);
124196

125-
if !args.is_empty() {
126-
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
127-
.to_compile_error()
128-
.into();
129-
}
130-
131197
if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) {
132198
return error;
133199
}
@@ -137,13 +203,35 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
137203

138204
let ident_s = ident.to_string();
139205
let exn = match &*ident_s {
140-
"DefaultHandler" => Exception::DefaultHandler,
141-
"HardFault" => Exception::HardFault,
142-
"NonMaskableInt" => Exception::NonMaskableInt,
206+
"DefaultHandler" => {
207+
if !args.is_empty() {
208+
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
209+
.to_compile_error()
210+
.into();
211+
}
212+
Exception::DefaultHandler
213+
}
214+
"HardFault" => Exception::HardFault(parse_macro_input!(args)),
215+
"NonMaskableInt" => {
216+
if !args.is_empty() {
217+
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
218+
.to_compile_error()
219+
.into();
220+
}
221+
Exception::NonMaskableInt
222+
}
143223
// NOTE that at this point we don't check if the exception is available on the target (e.g.
144224
// MemoryManagement is not available on Cortex-M0)
145225
"MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
146-
| "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other,
226+
| "DebugMonitor" | "PendSV" | "SysTick" => {
227+
if !args.is_empty() {
228+
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
229+
.to_compile_error()
230+
.into();
231+
}
232+
233+
Exception::Other
234+
}
147235
_ => {
148236
return parse::Error::new(ident.span(), "This is not a valid exception name")
149237
.to_compile_error()
@@ -153,13 +241,9 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
153241

154242
if f.sig.unsafety.is_none() {
155243
match exn {
156-
Exception::DefaultHandler | Exception::HardFault | Exception::NonMaskableInt => {
244+
Exception::DefaultHandler | Exception::HardFault(_) | Exception::NonMaskableInt => {
157245
// These are unsafe to define.
158-
let name = if exn == Exception::DefaultHandler {
159-
"`DefaultHandler`".to_string()
160-
} else {
161-
format!("`{:?}` handler", exn)
162-
};
246+
let name = format!("{}", exn);
163247
return parse::Error::new(ident.span(), format_args!("defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)", name))
164248
.to_compile_error()
165249
.into();
@@ -232,17 +316,23 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
232316
#f
233317
)
234318
}
235-
Exception::HardFault => {
319+
Exception::HardFault(args) => {
236320
let valid_signature = f.sig.constness.is_none()
237321
&& f.vis == Visibility::Inherited
238322
&& f.sig.abi.is_none()
239-
&& f.sig.inputs.len() == 1
240-
&& match &f.sig.inputs[0] {
241-
FnArg::Typed(arg) => match arg.ty.as_ref() {
242-
Type::Reference(r) => r.lifetime.is_none() && r.mutability.is_none(),
243-
_ => false,
244-
},
245-
_ => false,
323+
&& if args.trampoline {
324+
f.sig.inputs.len() == 1
325+
&& match &f.sig.inputs[0] {
326+
FnArg::Typed(arg) => match arg.ty.as_ref() {
327+
Type::Reference(r) => {
328+
r.lifetime.is_none() && r.mutability.is_none()
329+
}
330+
_ => false,
331+
},
332+
_ => false,
333+
}
334+
} else {
335+
f.sig.inputs.is_empty()
246336
}
247337
&& f.sig.generics.params.is_empty()
248338
&& f.sig.generics.where_clause.is_none()
@@ -255,33 +345,75 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
255345
if !valid_signature {
256346
return parse::Error::new(
257347
fspan,
258-
"`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`",
348+
if args.trampoline {
349+
"`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`"
350+
} else {
351+
"`HardFault` handler must have signature `unsafe fn() -> !`"
352+
},
259353
)
260354
.to_compile_error()
261355
.into();
262356
}
263357

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

268-
let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
361+
if args.trampoline {
362+
let ident = &f.sig.ident;
269363

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

283-
#f
284-
)
366+
quote!(
367+
#(#cfgs)*
368+
#(#attrs)*
369+
#[doc(hidden)]
370+
#[export_name = "_HardFault"]
371+
unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) {
372+
#ident(frame)
373+
}
374+
375+
#f
376+
377+
// HardFault exceptions are bounced through this trampoline which grabs the stack pointer at
378+
// the time of the exception and passes it to the user's HardFault handler in r0.
379+
// Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP.
380+
core::arch::global_asm!(
381+
".cfi_sections .debug_frame
382+
.section .HardFault.user, \"ax\"
383+
.global HardFault
384+
.type HardFault,%function
385+
.thumb_func
386+
.cfi_startproc
387+
HardFault:",
388+
"mov r0, lr
389+
movs r1, #4
390+
tst r0, r1
391+
bne 0f
392+
mrs r0, MSP
393+
b _HardFault
394+
0:
395+
mrs r0, PSP
396+
b _HardFault",
397+
".cfi_endproc
398+
.size HardFault, . - HardFault",
399+
);
400+
)
401+
} else {
402+
quote!(
403+
#[doc(hidden)]
404+
#[export_name = "_HardFault"]
405+
unsafe extern "C" fn #tramp_ident() {
406+
// This trampoline has no function except making the compiler diagnostics better.
407+
}
408+
409+
#[export_name = "HardFault"]
410+
// Only emit link_section when building for embedded targets,
411+
// because some hosted platforms (used to check the build)
412+
// cannot handle the long link section names.
413+
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
414+
#f
415+
)
416+
}
285417
}
286418
Exception::NonMaskableInt | Exception::Other => {
287419
let valid_signature = f.sig.constness.is_none()
@@ -634,7 +766,7 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
634766
}
635767
};
636768

637-
return Err(parse::Error::new(attr.span(), &err_str)
769+
return Err(parse::Error::new(attr.span(), err_str)
638770
.to_compile_error()
639771
.into());
640772
}

0 commit comments

Comments
 (0)