aboutsummaryrefslogtreecommitdiff
path: root/asm.rs
blob: f5b0179be017460ea486e326972055520333fe61 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
//! Assembly stubs for the `cortex-m` crate.
//!
//! We use this file to precompile some assembly stubs into the static libraries you can find in
//! `bin`. Apps using the `cortex-m` crate then link against those static libraries and don't need
//! to build this file themselves.
//!
//! Nowadays the assembly stubs are no longer actual assembly files, but actually just this Rust
//! file `asm.rs` that uses unstable inline assembly, coupled with the `xtask` tool to invoke rustc
//! and build the files.
//!
//! Precompiling this to a static lib allows users to call assembly routines from stable Rust, but
//! also perform [linker plugin LTO] with the precompiled artifacts to completely inline the
//! assembly routines into their code, which brings the "outline assembly" on par with "real" inline
//! assembly.
//!
//! For developers and contributors to `cortex-m`, this setup means that they don't have to install
//! any binutils, assembler, or C compiler to hack on the crate. All they need is to run `cargo
//! xtask assemble` to rebuild the archives from this file.
//!
//! Cool, right?
//!
//! # Rust version management
//!
//! Since inline assembly is still unstable, and we want to ensure that the created blobs are
//! up-to-date in CI, we have to pin the nightly version we use for this. The nightly toolchain is
//! stored in `asm-toolchain`.
//!
//! The `cargo xtask` automation will automatically install the `asm-toolchain` as well as all
//! Cortex-M targets needed to generate the blobs.
//!
//! [linker plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html

#![feature(asm)]
#![no_std]
#![crate_type = "staticlib"]
#![deny(warnings)]

#[no_mangle]
pub unsafe extern "C" fn __bkpt() {
    asm!("bkpt");
}

#[no_mangle]
pub unsafe extern "C" fn __control_r() -> u32 {
    let r;
    asm!("mrs {}, CONTROL", out(reg) r);
    r
}

#[no_mangle]
pub unsafe extern "C" fn __control_w(w: u32) {
    asm!("msr CONTROL, {}", in(reg) w);
}

#[no_mangle]
pub unsafe extern "C" fn __cpsid() {
    asm!("cpsid i");
}

#[no_mangle]
pub unsafe extern "C" fn __cpsie() {
    asm!("cpsie i");
}

#[no_mangle]
pub unsafe extern "C" fn __delay(cyc: u32) {
    asm!("
    1:
        nop
        subs {}, #1
        bne 1b
        // Branch to 1 instead of __delay does not generate R_ARM_THM_JUMP8 relocation, which breaks
        // linking on the thumbv6m-none-eabi target
    ", in(reg) cyc);
}

// FIXME do we need compiler fences here or should we expect them in the caller?
#[no_mangle]
pub unsafe extern "C" fn __dmb() {
    asm!("dmb 0xF");
}

#[no_mangle]
pub unsafe extern "C" fn __dsb() {
    asm!("dsb 0xF");
}

#[no_mangle]
pub unsafe extern "C" fn __isb() {
    asm!("isb 0xF");
}

#[no_mangle]
pub unsafe extern "C" fn __msp_r() -> u32 {
    let r;
    asm!("mrs {}, MSP", out(reg) r);
    r
}

#[no_mangle]
pub unsafe extern "C" fn __msp_w(val: u32) {
    asm!("msr MSP, {}", in(reg) val);
}

#[no_mangle]
pub unsafe extern "C" fn __nop() {
    // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate
    // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N
    // nops when they call `nop` N times, let's not add that option.
    asm!("nop");
}

#[no_mangle]
pub unsafe extern "C" fn __primask() -> u32 {
    // FIXME: rename to __primask_r
    let r;
    asm!("mrs {}, PRIMASK", out(reg) r);
    r
}

#[no_mangle]
pub unsafe extern "C" fn __psp_r() -> u32 {
    let r;
    asm!("mrs {}, PSP", out(reg) r);
    r
}

#[no_mangle]
pub unsafe extern "C" fn __psp_w(val: u32) {
    asm!("msr PSP, {}", in(reg) val);
}

#[no_mangle]
pub unsafe extern "C" fn __sev() {
    asm!("sev");
}

#[no_mangle]
pub unsafe extern "C" fn __udf() {
    asm!("udf #0");
}

#[no_mangle]
pub unsafe extern "C" fn __wfe() {
    asm!("wfe");
}

#[no_mangle]
pub unsafe extern "C" fn __wfi() {
    asm!("wfi");
}

#[cfg(armv7m)]
pub mod v7m {
    #[no_mangle]
    pub unsafe extern "C" fn __basepri_max(val: u8) {
        asm!("msr BASEPRI_MAX, {}", in(reg) val);
    }

    #[no_mangle]
    pub unsafe extern "C" fn __basepri_r() -> u8 {
        let r;
        asm!("mrs {}, BASEPRI", out(reg) r);
        r
    }

    #[no_mangle]
    pub unsafe extern "C" fn __basepri_w(val: u8) {
        asm!("msr BASEPRI, {}", in(reg) val);
    }

    #[no_mangle]
    pub unsafe extern "C" fn __faultmask() -> u32 {
        let r;
        asm!("mrs {}, FAULTMASK", out(reg) r);
        r
    }

    // FIXME: compiler_fences necessary?
    #[no_mangle]
    pub unsafe extern "C" fn __enable_icache() {
        asm!(
            "
            ldr r0, =0xE000ED14       @ CCR
            mrs r2, PRIMASK           @ save critical nesting info
            cpsid i                   @ mask interrupts
            ldr r1, [r0]              @ read CCR
            orr.w r1, r1, #(1 << 17)  @ Set bit 17, IC
            str r1, [r0]              @ write it back
            dsb                       @ ensure store completes
            isb                       @ synchronize pipeline
            msr PRIMASK, r2           @ unnest critical section
            ",
            out("r0") _,
            out("r1") _,
            out("r2") _,
        );
    }

    #[no_mangle]
    pub unsafe extern "C" fn __enable_dcache() {
        asm!(
            "
            ldr r0, =0xE000ED14       @ CCR
            mrs r2, PRIMASK           @ save critical nesting info
            cpsid i                   @ mask interrupts
            ldr r1, [r0]              @ read CCR
            orr.w r1, r1, #(1 << 16)  @ Set bit 16, DC
            str r1, [r0]              @ write it back
            dsb                       @ ensure store completes
            isb                       @ synchronize pipeline
            msr PRIMASK, r2           @ unnest critical section
            ",
            out("r0") _,
            out("r1") _,
            out("r2") _,
        );
    }
}

#[cfg(armv7em)]
mod v7em {
    #[no_mangle]
    pub unsafe extern "C" fn __basepri_max_cm7_r0p1(val: u8) {
        asm!(
            "
            mrs r1, PRIMASK
            cpsid i
            tst.w r1, #1
            msr BASEPRI_MAX, {}
            it ne
            bxne lr
            cpsie i
            ",
            in(reg) val,
            out("r1") _,
        );
    }

    #[no_mangle]
    pub unsafe extern "C" fn __basepri_w_cm7_r0p1(val: u8) {
        asm!(
            "
            mrs r1, PRIMASK
            cpsid i
            tst.w r1, #1
            msr BASEPRI, {}
            it ne
            bxne lr
            cpsie i
            ",
            in(reg) val,
            out("r1") _,
        );
    }
}

/// Baseline and Mainline.
#[cfg(armv8m)]
pub mod v8m {
    #[no_mangle]
    pub unsafe extern "C" fn __tt(mut target: u32) -> u32 {
        asm!("tt {target}, {target}", target = inout(reg) target);
        target
    }

    #[no_mangle]
    pub unsafe extern "C" fn __ttt(mut target: u32) -> u32 {
        asm!("ttt {target}, {target}", target = inout(reg) target);
        target
    }

    #[no_mangle]
    pub unsafe extern "C" fn __tta(mut target: u32) -> u32 {
        asm!("tta {target}, {target}", target = inout(reg) target);
        target
    }

    #[no_mangle]
    pub unsafe extern "C" fn __ttat(mut target: u32) -> u32 {
        asm!("ttat {target}, {target}", target = inout(reg) target);
        target
    }
}

/// Mainline only.
#[cfg(armv8m_main)]
pub mod v8m_main {
    #[no_mangle]
    pub unsafe extern "C" fn __msplim_r() -> u32 {
        let r;
        asm!("mrs {}, MSPLIM", out(reg) r);
        r
    }

    #[no_mangle]
    pub unsafe extern "C" fn __msplim_w(val: u32) {
        asm!("msr MSPLIM, {}", in(reg) val);
    }

    #[no_mangle]
    pub unsafe extern "C" fn __psplim_r() -> u32 {
        let r;
        asm!("mrs {}, PSPLIM", out(reg) r);
        r
    }

    #[no_mangle]
    pub unsafe extern "C" fn __psplim_w(val: u32) {
        asm!("msr PSPLIM, {}", in(reg) val);
    }
}

/// All targets with FPU.
#[cfg(has_fpu)]
pub mod fpu {
    #[no_mangle]
    pub unsafe extern "C" fn __get_FPSCR() -> u32 {
        let r;
        asm!("vmrs {}, fpscr", out(reg) r);
        r
    }

    #[no_mangle]
    pub unsafe extern "C" fn __set_FPSCR(val: u32) {
        asm!("vmsr fpscr, {}", in(reg) val);
    }
}

/// We *must* define a panic handler here, even though nothing here should ever be able to panic.
///
/// We prove that nothing will ever panic by calling a function that doesn't exist. If the panic
/// handler gets linked in, this causes a linker error. We always build this file with optimizations
/// enabled, but even without them the panic handler should never be linked in.
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    extern "C" {
        #[link_name = "cortex-m internal error: panic handler not optimized out, please file an \
        issue at https://github.com/rust-embedded/cortex-m"]
        fn __cortex_m_should_not_panic() -> !;
    }

    unsafe {
        __cortex_m_should_not_panic();
    }
}