| 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 | |