Skip to content

Commit 54877ba

Browse files
committed
Removed the feature flag and moved the trampoline into the macro
1 parent 36419ff commit 54877ba

File tree

5 files changed

+159
-120
lines changed

5 files changed

+159
-120
lines changed

cortex-m-rt/CHANGELOG.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +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 `hardfault-trampoline` feature that is enabled by default which enables
13-
the previously required hardfault trampoline. Now when this feature is not active, there will be
14-
no trampoline and the function you write is the exact function that is used as hardfault handler.
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.
1513

1614
## [v0.7.3]
1715

cortex-m-rt/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,10 @@ name = "compiletest"
4242
required-features = ["device"]
4343

4444
[features]
45-
default = ["hardfault-trampoline"]
4645
device = []
4746
set-sp = []
4847
set-vtor = []
4948
zero-init-ram = []
50-
hardfault-trampoline = ["cortex-m-rt-macros/hardfault-trampoline"]
5149

5250
[package.metadata.docs.rs]
5351
features = ["device"]

cortex-m-rt/macros/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,3 @@ proc-macro2 = "1.0"
2121
[dependencies.syn]
2222
features = ["extra-traits", "full"]
2323
version = "2.0"
24-
25-
[features]
26-
hardfault-trampoline = []

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

Lines changed: 158 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ use quote::quote;
1010
use std::collections::HashSet;
1111
use std::iter;
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,73 @@ 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+
#[derive(Debug, PartialEq)]
125+
struct HardFaultArgs {
126+
trampoline: bool,
127+
}
128+
129+
impl Default for HardFaultArgs {
130+
fn default() -> Self {
131+
Self { trampoline: true }
132+
}
133+
}
134+
135+
impl Parse for HardFaultArgs {
136+
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
137+
let mut items = Vec::new();
138+
// Read a list of `ident = value,`
139+
loop {
140+
if input.is_empty() {
141+
break;
142+
}
143+
144+
let name = input.parse::<Ident>()?;
145+
input.parse::<syn::Token!(=)>()?;
146+
let value = input.parse::<syn::Lit>()?;
147+
148+
items.push((name, value));
149+
150+
if input.is_empty() {
151+
break;
152+
}
153+
154+
input.parse::<syn::Token!(,)>()?;
155+
}
156+
157+
let mut args = Self::default();
158+
159+
for (name, value) in items {
160+
match name.to_string().as_str() {
161+
"trampoline" => match value {
162+
syn::Lit::Bool(val) => {
163+
args.trampoline = val.value();
164+
}
165+
_ => {
166+
return Err(syn::Error::new_spanned(
167+
value,
168+
"Not a valid value. `trampoline` takes a boolean literal",
169+
))
170+
}
171+
},
172+
_ => {
173+
return Err(syn::Error::new_spanned(name, "Not a valid argument name"));
174+
}
175+
}
176+
}
177+
178+
Ok(args)
179+
}
180+
}
181+
121182
#[proc_macro_attribute]
122183
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
123184
let mut f = parse_macro_input!(input as ItemFn);
124185

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-
131186
if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) {
132187
return error;
133188
}
@@ -137,14 +192,34 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
137192

138193
let ident_s = ident.to_string();
139194
let exn = match &*ident_s {
140-
"DefaultHandler" => Exception::DefaultHandler,
141-
"HardFault" => Exception::HardFault,
142-
"NonMaskableInt" => Exception::NonMaskableInt,
195+
"DefaultHandler" => {
196+
if !args.is_empty() {
197+
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
198+
.to_compile_error()
199+
.into();
200+
}
201+
Exception::DefaultHandler
202+
}
203+
"HardFault" => Exception::HardFault(parse_macro_input!(args)),
204+
"NonMaskableInt" => {
205+
if !args.is_empty() {
206+
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
207+
.to_compile_error()
208+
.into();
209+
}
210+
Exception::NonMaskableInt
211+
}
143212
// NOTE that at this point we don't check if the exception is available on the target (e.g.
144213
// MemoryManagement is not available on Cortex-M0)
145214
"MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
146215
| "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other,
147216
_ => {
217+
if !args.is_empty() {
218+
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
219+
.to_compile_error()
220+
.into();
221+
}
222+
148223
return parse::Error::new(ident.span(), "This is not a valid exception name")
149224
.to_compile_error()
150225
.into();
@@ -153,7 +228,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
153228

154229
if f.sig.unsafety.is_none() {
155230
match exn {
156-
Exception::DefaultHandler | Exception::HardFault | Exception::NonMaskableInt => {
231+
Exception::DefaultHandler | Exception::HardFault(_) | Exception::NonMaskableInt => {
157232
// These are unsafe to define.
158233
let name = if exn == Exception::DefaultHandler {
159234
"`DefaultHandler`".to_string()
@@ -232,17 +307,24 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
232307
#f
233308
)
234309
}
235-
Exception::HardFault if cfg!(feature = "hardfault-trampoline") => {
310+
Exception::HardFault(args) => {
236311
let valid_signature = f.sig.constness.is_none()
237312
&& f.vis == Visibility::Inherited
238313
&& 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(),
314+
&& if args.trampoline {
315+
match &f.sig.inputs[0] {
316+
FnArg::Typed(arg) => match arg.ty.as_ref() {
317+
Type::Reference(r) => {
318+
r.lifetime.is_none()
319+
&& r.mutability.is_none()
320+
&& f.sig.inputs.len() == 1
321+
}
322+
_ => false,
323+
},
243324
_ => false,
244-
},
245-
_ => false,
325+
}
326+
} else {
327+
f.sig.inputs.is_empty()
246328
}
247329
&& f.sig.generics.params.is_empty()
248330
&& f.sig.generics.where_clause.is_none()
@@ -255,66 +337,74 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
255337
if !valid_signature {
256338
return parse::Error::new(
257339
fspan,
258-
"`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`",
340+
if args.trampoline {
341+
"`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`"
342+
} else {
343+
"`HardFault` handler must have signature `unsafe fn() -> !`"
344+
},
259345
)
260346
.to_compile_error()
261347
.into();
262348
}
263349

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

268-
let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
352+
if args.trampoline {
353+
let tramp_ident =
354+
Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site());
355+
let ident = &f.sig.ident;
269356

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-
}
357+
let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
282358

283-
#f
284-
)
285-
}
286-
Exception::HardFault => {
287-
let valid_signature = f.sig.constness.is_none()
288-
&& f.vis == Visibility::Inherited
289-
&& f.sig.abi.is_none()
290-
&& f.sig.inputs.is_empty()
291-
&& f.sig.generics.params.is_empty()
292-
&& f.sig.generics.where_clause.is_none()
293-
&& f.sig.variadic.is_none()
294-
&& match f.sig.output {
295-
ReturnType::Default => false,
296-
ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
297-
};
359+
quote!(
360+
#(#cfgs)*
361+
#(#attrs)*
362+
#[doc(hidden)]
363+
#[export_name = "HardFault"]
364+
// Only emit link_section when building for embedded targets,
365+
// because some hosted platforms (used to check the build)
366+
// cannot handle the long link section names.
367+
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
368+
pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) {
369+
#ident(frame)
370+
}
298371

299-
if !valid_signature {
300-
return parse::Error::new(
301-
fspan,
302-
"`HardFault` handler must have signature `unsafe fn() -> !`",
372+
#f
373+
374+
// HardFault exceptions are bounced through this trampoline which grabs the stack pointer at
375+
// the time of the exception and passes it to the user's HardFault handler in r0.
376+
// Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP.
377+
core::arch::global_asm!(
378+
".cfi_sections .debug_frame
379+
.section .HardFaultTrampoline, \"ax\"
380+
.global HardFaultTrampline
381+
.type HardFaultTrampline,%function
382+
.thumb_func
383+
.cfi_startproc
384+
HardFaultTrampoline:",
385+
"mov r0, lr
386+
movs r1, #4
387+
tst r0, r1
388+
bne 0f
389+
mrs r0, MSP
390+
b HardFault
391+
0:
392+
mrs r0, PSP
393+
b HardFault",
394+
".cfi_endproc
395+
.size HardFaultTrampoline, . - HardFaultTrampoline",
396+
);
397+
)
398+
} else {
399+
quote!(
400+
#[export_name = "HardFault"]
401+
// Only emit link_section when building for embedded targets,
402+
// because some hosted platforms (used to check the build)
403+
// cannot handle the long link section names.
404+
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
405+
#f
303406
)
304-
.to_compile_error()
305-
.into();
306407
}
307-
308-
f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site());
309-
310-
quote!(
311-
#[export_name = "HardFault"]
312-
// Only emit link_section when building for embedded targets,
313-
// because some hosted platforms (used to check the build)
314-
// cannot handle the long link section names.
315-
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
316-
#f
317-
)
318408
}
319409
Exception::NonMaskableInt | Exception::Other => {
320410
let valid_signature = f.sig.constness.is_none()

0 commit comments

Comments
 (0)