1 | //! Inline assembly implementing the routines exposed in `cortex_m::asm`. |
2 | //! |
3 | //! If the `inline-asm` feature is enabled, these functions will be directly called by the |
4 | //! `cortex-m` wrappers. Otherwise, `cortex-m` links against them via prebuilt archives. |
5 | //! |
6 | //! All of these functions should be blanket-`unsafe`. `cortex-m` provides safe wrappers where |
7 | //! applicable. |
8 | |
9 | use core::arch::asm; |
10 | use core::sync::atomic::{compiler_fence, Ordering}; |
11 | |
12 | #[inline (always)] |
13 | pub unsafe fn __bkpt() { |
14 | asm!("bkpt" , options(nomem, nostack, preserves_flags)); |
15 | } |
16 | |
17 | #[inline (always)] |
18 | pub unsafe fn __control_r() -> u32 { |
19 | let r: u32; |
20 | asm!("mrs { }, CONTROL" , out(reg) r, options(nomem, nostack, preserves_flags)); |
21 | r |
22 | } |
23 | |
24 | #[inline (always)] |
25 | pub unsafe fn __control_w(w: u32) { |
26 | // ISB is required after writing to CONTROL, |
27 | // per ARM architectural requirements (see Application Note 321). |
28 | asm!( |
29 | "msr CONTROL, { }" , |
30 | "isb" , |
31 | in(reg) w, |
32 | options(nomem, nostack, preserves_flags), |
33 | ); |
34 | |
35 | // Ensure memory accesses are not reordered around the CONTROL update. |
36 | compiler_fence(order:Ordering::SeqCst); |
37 | } |
38 | |
39 | #[inline (always)] |
40 | pub unsafe fn __cpsid() { |
41 | asm!("cpsid i" , options(nomem, nostack, preserves_flags)); |
42 | |
43 | // Ensure no subsequent memory accesses are reordered to before interrupts are disabled. |
44 | compiler_fence(order:Ordering::SeqCst); |
45 | } |
46 | |
47 | #[inline (always)] |
48 | pub unsafe fn __cpsie() { |
49 | // Ensure no preceeding memory accesses are reordered to after interrupts are enabled. |
50 | compiler_fence(order:Ordering::SeqCst); |
51 | |
52 | asm!("cpsie i" , options(nomem, nostack, preserves_flags)); |
53 | } |
54 | |
55 | #[inline (always)] |
56 | pub unsafe fn __delay(cyc: u32) { |
57 | // The loop will normally take 3 to 4 CPU cycles per iteration, but superscalar cores |
58 | // (eg. Cortex-M7) can potentially do it in 2, so we use that as the lower bound, since delaying |
59 | // for more cycles is okay. |
60 | // Add 1 to prevent an integer underflow which would cause a long freeze |
61 | let real_cyc: u32 = 1 + cyc / 2; |
62 | asm!( |
63 | // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m. |
64 | "1:" , |
65 | "subs { }, #1" , |
66 | "bne 1b" , |
67 | inout(reg) real_cyc => _, |
68 | options(nomem, nostack), |
69 | ); |
70 | } |
71 | |
72 | #[inline (always)] |
73 | pub unsafe fn __dmb() { |
74 | compiler_fence(order:Ordering::SeqCst); |
75 | asm!("dmb" , options(nomem, nostack, preserves_flags)); |
76 | compiler_fence(order:Ordering::SeqCst); |
77 | } |
78 | |
79 | #[inline (always)] |
80 | pub unsafe fn __dsb() { |
81 | compiler_fence(order:Ordering::SeqCst); |
82 | asm!("dsb" , options(nomem, nostack, preserves_flags)); |
83 | compiler_fence(order:Ordering::SeqCst); |
84 | } |
85 | |
86 | #[inline (always)] |
87 | pub unsafe fn __isb() { |
88 | compiler_fence(order:Ordering::SeqCst); |
89 | asm!("isb" , options(nomem, nostack, preserves_flags)); |
90 | compiler_fence(order:Ordering::SeqCst); |
91 | } |
92 | |
93 | #[inline (always)] |
94 | pub unsafe fn __msp_r() -> u32 { |
95 | let r: u32; |
96 | asm!("mrs { }, MSP" , out(reg) r, options(nomem, nostack, preserves_flags)); |
97 | r |
98 | } |
99 | |
100 | #[inline (always)] |
101 | pub unsafe fn __msp_w(val: u32) { |
102 | // Technically is writing to the stack pointer "not pushing any data to the stack"? |
103 | // In any event, if we don't set `nostack` here, this method is useless as the new |
104 | // stack value is immediately mutated by returning. Really this is just not a good |
105 | // method and its higher-level use is marked as deprecated in cortex-m. |
106 | asm!("msr MSP, { }" , in(reg) val, options(nomem, nostack, preserves_flags)); |
107 | } |
108 | |
109 | // NOTE: No FFI shim, this requires inline asm. |
110 | #[inline (always)] |
111 | pub unsafe fn __apsr_r() -> u32 { |
112 | let r: u32; |
113 | asm!("mrs { }, APSR" , out(reg) r, options(nomem, nostack, preserves_flags)); |
114 | r |
115 | } |
116 | |
117 | #[inline (always)] |
118 | pub unsafe fn __nop() { |
119 | // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate |
120 | // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N |
121 | // nops when they call `nop` N times, let's not add that option. |
122 | asm!("nop" , options(nomem, nostack, preserves_flags)); |
123 | } |
124 | |
125 | // NOTE: No FFI shim, this requires inline asm. |
126 | #[inline (always)] |
127 | pub unsafe fn __pc_r() -> u32 { |
128 | let r: u32; |
129 | asm!("mov { }, pc" , out(reg) r, options(nomem, nostack, preserves_flags)); |
130 | r |
131 | } |
132 | |
133 | // NOTE: No FFI shim, this requires inline asm. |
134 | #[inline (always)] |
135 | pub unsafe fn __pc_w(val: u32) { |
136 | asm!("mov pc, { }" , in(reg) val, options(nomem, nostack, preserves_flags)); |
137 | } |
138 | |
139 | // NOTE: No FFI shim, this requires inline asm. |
140 | #[inline (always)] |
141 | pub unsafe fn __lr_r() -> u32 { |
142 | let r: u32; |
143 | asm!("mov { }, lr" , out(reg) r, options(nomem, nostack, preserves_flags)); |
144 | r |
145 | } |
146 | |
147 | // NOTE: No FFI shim, this requires inline asm. |
148 | #[inline (always)] |
149 | pub unsafe fn __lr_w(val: u32) { |
150 | asm!("mov lr, { }" , in(reg) val, options(nomem, nostack, preserves_flags)); |
151 | } |
152 | |
153 | #[inline (always)] |
154 | pub unsafe fn __primask_r() -> u32 { |
155 | let r: u32; |
156 | asm!("mrs { }, PRIMASK" , out(reg) r, options(nomem, nostack, preserves_flags)); |
157 | r |
158 | } |
159 | |
160 | #[inline (always)] |
161 | pub unsafe fn __psp_r() -> u32 { |
162 | let r: u32; |
163 | asm!("mrs { }, PSP" , out(reg) r, options(nomem, nostack, preserves_flags)); |
164 | r |
165 | } |
166 | |
167 | #[inline (always)] |
168 | pub unsafe fn __psp_w(val: u32) { |
169 | // See comment on __msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP |
170 | // if MSP is currently being used as the stack pointer. |
171 | asm!("msr PSP, { }" , in(reg) val, options(nomem, nostack, preserves_flags)); |
172 | } |
173 | |
174 | #[inline (always)] |
175 | pub unsafe fn __sev() { |
176 | asm!("sev" , options(nomem, nostack, preserves_flags)); |
177 | } |
178 | |
179 | #[inline (always)] |
180 | pub unsafe fn __udf() -> ! { |
181 | asm!("udf #0" , options(noreturn, nomem, nostack, preserves_flags)); |
182 | } |
183 | |
184 | #[inline (always)] |
185 | pub unsafe fn __wfe() { |
186 | asm!("wfe" , options(nomem, nostack, preserves_flags)); |
187 | } |
188 | |
189 | #[inline (always)] |
190 | pub unsafe fn __wfi() { |
191 | asm!("wfi" , options(nomem, nostack, preserves_flags)); |
192 | } |
193 | |
194 | /// Semihosting syscall. |
195 | #[inline (always)] |
196 | pub unsafe fn __sh_syscall(mut nr: u32, arg: u32) -> u32 { |
197 | asm!("bkpt #0xab" , inout("r0" ) nr, in("r1" ) arg, options(nomem, nostack, preserves_flags)); |
198 | nr |
199 | } |
200 | |
201 | /// Set CONTROL.SPSEL to 0, write `msp` to MSP, branch to `rv`. |
202 | #[inline (always)] |
203 | pub unsafe fn __bootstrap(msp: u32, rv: u32) -> ! { |
204 | asm!( |
205 | "mrs { tmp}, CONTROL" , |
206 | "bics { tmp}, { spsel}" , |
207 | "msr CONTROL, { tmp}" , |
208 | "isb" , |
209 | "msr MSP, { msp}" , |
210 | "bx { rv}" , |
211 | // `out(reg) _` is not permitted in a `noreturn` asm! call, |
212 | // so instead use `in(reg) 0` and don't restore it afterwards. |
213 | tmp = in(reg) 0, |
214 | spsel = in(reg) 2, |
215 | msp = in(reg) msp, |
216 | rv = in(reg) rv, |
217 | options(noreturn, nomem, nostack), |
218 | ); |
219 | } |
220 | |
221 | // v7m *AND* v8m.main, but *NOT* v8m.base |
222 | #[cfg (any(armv7m, armv8m_main))] |
223 | pub use self::v7m::*; |
224 | #[cfg (any(armv7m, armv8m_main))] |
225 | mod v7m { |
226 | use core::arch::asm; |
227 | use core::sync::atomic::{compiler_fence, Ordering}; |
228 | |
229 | #[inline (always)] |
230 | pub unsafe fn __basepri_max(val: u8) { |
231 | asm!("msr BASEPRI_MAX, {}" , in(reg) val, options(nomem, nostack, preserves_flags)); |
232 | } |
233 | |
234 | #[inline (always)] |
235 | pub unsafe fn __basepri_r() -> u8 { |
236 | let r; |
237 | asm!("mrs {}, BASEPRI" , out(reg) r, options(nomem, nostack, preserves_flags)); |
238 | r |
239 | } |
240 | |
241 | #[inline (always)] |
242 | pub unsafe fn __basepri_w(val: u8) { |
243 | asm!("msr BASEPRI, {}" , in(reg) val, options(nomem, nostack, preserves_flags)); |
244 | } |
245 | |
246 | #[inline (always)] |
247 | pub unsafe fn __faultmask_r() -> u32 { |
248 | let r; |
249 | asm!("mrs {}, FAULTMASK" , out(reg) r, options(nomem, nostack, preserves_flags)); |
250 | r |
251 | } |
252 | |
253 | #[inline (always)] |
254 | pub unsafe fn __enable_icache() { |
255 | asm!( |
256 | "ldr {0}, =0xE000ED14" , // CCR |
257 | "mrs {2}, PRIMASK" , // save critical nesting info |
258 | "cpsid i" , // mask interrupts |
259 | "ldr {1}, [{0}]" , // read CCR |
260 | "orr.w {1}, {1}, #(1 << 17)" , // Set bit 17, IC |
261 | "str {1}, [{0}]" , // write it back |
262 | "dsb" , // ensure store completes |
263 | "isb" , // synchronize pipeline |
264 | "msr PRIMASK, {2}" , // unnest critical section |
265 | out(reg) _, |
266 | out(reg) _, |
267 | out(reg) _, |
268 | options(nostack), |
269 | ); |
270 | compiler_fence(Ordering::SeqCst); |
271 | } |
272 | |
273 | #[inline (always)] |
274 | pub unsafe fn __enable_dcache() { |
275 | asm!( |
276 | "ldr {0}, =0xE000ED14" , // CCR |
277 | "mrs {2}, PRIMASK" , // save critical nesting info |
278 | "cpsid i" , // mask interrupts |
279 | "ldr {1}, [{0}]" , // read CCR |
280 | "orr.w {1}, {1}, #(1 << 16)" , // Set bit 16, DC |
281 | "str {1}, [{0}]" , // write it back |
282 | "dsb" , // ensure store completes |
283 | "isb" , // synchronize pipeline |
284 | "msr PRIMASK, {2}" , // unnest critical section |
285 | out(reg) _, |
286 | out(reg) _, |
287 | out(reg) _, |
288 | options(nostack), |
289 | ); |
290 | compiler_fence(Ordering::SeqCst); |
291 | } |
292 | } |
293 | |
294 | #[cfg (armv7em)] |
295 | pub use self::v7em::*; |
296 | #[cfg (armv7em)] |
297 | mod v7em { |
298 | use core::arch::asm; |
299 | |
300 | #[inline (always)] |
301 | pub unsafe fn __basepri_max_cm7_r0p1(val: u8) { |
302 | asm!( |
303 | "mrs {1}, PRIMASK" , |
304 | "cpsid i" , |
305 | "tst.w {1}, #1" , |
306 | "msr BASEPRI_MAX, {0}" , |
307 | "it ne" , |
308 | "bxne lr" , |
309 | "cpsie i" , |
310 | in(reg) val, |
311 | out(reg) _, |
312 | options(nomem, nostack, preserves_flags), |
313 | ); |
314 | } |
315 | |
316 | #[inline (always)] |
317 | pub unsafe fn __basepri_w_cm7_r0p1(val: u8) { |
318 | asm!( |
319 | "mrs {1}, PRIMASK" , |
320 | "cpsid i" , |
321 | "tst.w {1}, #1" , |
322 | "msr BASEPRI, {0}" , |
323 | "it ne" , |
324 | "bxne lr" , |
325 | "cpsie i" , |
326 | in(reg) val, |
327 | out(reg) _, |
328 | options(nomem, nostack, preserves_flags), |
329 | ); |
330 | } |
331 | } |
332 | |
333 | #[cfg (armv8m)] |
334 | pub use self::v8m::*; |
335 | /// Baseline and Mainline. |
336 | #[cfg (armv8m)] |
337 | mod v8m { |
338 | use core::arch::asm; |
339 | |
340 | #[inline (always)] |
341 | pub unsafe fn __tt(mut target: u32) -> u32 { |
342 | asm!( |
343 | "tt {target}, {target}" , |
344 | target = inout(reg) target, |
345 | options(nomem, nostack, preserves_flags), |
346 | ); |
347 | target |
348 | } |
349 | |
350 | #[inline (always)] |
351 | pub unsafe fn __ttt(mut target: u32) -> u32 { |
352 | asm!( |
353 | "ttt {target}, {target}" , |
354 | target = inout(reg) target, |
355 | options(nomem, nostack, preserves_flags), |
356 | ); |
357 | target |
358 | } |
359 | |
360 | #[inline (always)] |
361 | pub unsafe fn __tta(mut target: u32) -> u32 { |
362 | asm!( |
363 | "tta {target}, {target}" , |
364 | target = inout(reg) target, |
365 | options(nomem, nostack, preserves_flags), |
366 | ); |
367 | target |
368 | } |
369 | |
370 | #[inline (always)] |
371 | pub unsafe fn __ttat(mut target: u32) -> u32 { |
372 | asm!( |
373 | "ttat {target}, {target}" , |
374 | target = inout(reg) target, |
375 | options(nomem, nostack, preserves_flags), |
376 | ); |
377 | target |
378 | } |
379 | |
380 | #[inline (always)] |
381 | pub unsafe fn __msp_ns_r() -> u32 { |
382 | let r; |
383 | asm!("mrs {}, MSP_NS" , out(reg) r, options(nomem, nostack, preserves_flags)); |
384 | r |
385 | } |
386 | |
387 | #[inline (always)] |
388 | pub unsafe fn __msp_ns_w(val: u32) { |
389 | asm!("msr MSP_NS, {}" , in(reg) val, options(nomem, nostack, preserves_flags)); |
390 | } |
391 | |
392 | #[inline (always)] |
393 | pub unsafe fn __bxns(val: u32) { |
394 | asm!("BXNS {}" , in(reg) val, options(nomem, nostack, preserves_flags)); |
395 | } |
396 | } |
397 | |
398 | #[cfg (armv8m_main)] |
399 | pub use self::v8m_main::*; |
400 | /// Mainline only. |
401 | #[cfg (armv8m_main)] |
402 | mod v8m_main { |
403 | use core::arch::asm; |
404 | |
405 | #[inline (always)] |
406 | pub unsafe fn __msplim_r() -> u32 { |
407 | let r; |
408 | asm!("mrs {}, MSPLIM" , out(reg) r, options(nomem, nostack, preserves_flags)); |
409 | r |
410 | } |
411 | |
412 | #[inline (always)] |
413 | pub unsafe fn __msplim_w(val: u32) { |
414 | asm!("msr MSPLIM, {}" , in(reg) val, options(nomem, nostack, preserves_flags)); |
415 | } |
416 | |
417 | #[inline (always)] |
418 | pub unsafe fn __psplim_r() -> u32 { |
419 | let r; |
420 | asm!("mrs {}, PSPLIM" , out(reg) r, options(nomem, nostack, preserves_flags)); |
421 | r |
422 | } |
423 | |
424 | #[inline (always)] |
425 | pub unsafe fn __psplim_w(val: u32) { |
426 | asm!("msr PSPLIM, {}" , in(reg) val, options(nomem, nostack, preserves_flags)); |
427 | } |
428 | } |
429 | |
430 | #[cfg (has_fpu)] |
431 | pub use self::fpu::*; |
432 | /// All targets with FPU. |
433 | #[cfg (has_fpu)] |
434 | mod fpu { |
435 | use core::arch::asm; |
436 | |
437 | #[inline (always)] |
438 | pub unsafe fn __fpscr_r() -> u32 { |
439 | let r; |
440 | asm!("vmrs {}, fpscr" , out(reg) r, options(nomem, nostack, preserves_flags)); |
441 | r |
442 | } |
443 | |
444 | #[inline (always)] |
445 | pub unsafe fn __fpscr_w(val: u32) { |
446 | asm!("vmsr fpscr, {}" , in(reg) val, options(nomem, nostack)); |
447 | } |
448 | } |
449 | |