stackdump_capture/
cortex_m.rs

1//! Capture functions for the cortex-m platform
2
3use stackdump_core::register_data::RegisterData;
4use stackdump_core::{memory_region::ArrayMemoryRegion, register_data::ArrayRegisterData};
5
6/// Capture the core registers and the stack
7#[cfg(not(has_fpu))]
8pub fn capture<const SIZE: usize>(
9    stack: &mut ArrayMemoryRegion<SIZE>,
10    core_registers: &mut ArrayRegisterData<16, u32>,
11) {
12    capture_core_registers(core_registers);
13    capture_stack(
14        core_registers
15            .register(stackdump_core::gimli::Arm::SP)
16            .unwrap(),
17        stack,
18    );
19}
20
21/// Capture the core & fpu registers and the stack
22#[cfg(has_fpu)]
23pub fn capture<const SIZE: usize>(
24    stack: &mut ArrayMemoryRegion<SIZE>,
25    core_registers: &mut ArrayRegisterData<16, u32>,
26    fpu_registers: &mut ArrayRegisterData<32, u32>,
27) {
28    capture_core_registers(core_registers);
29    capture_fpu_registers(fpu_registers);
30    capture_stack(
31        core_registers
32            .register(stackdump_core::gimli::Arm::SP)
33            .unwrap(),
34        stack,
35    );
36}
37
38fn capture_core_registers(buffer: &mut ArrayRegisterData<16, u32>) {
39    #[cfg(cortex_m)]
40    use core::arch::asm;
41
42    // This array is going to hold the register data
43    let mut register_array = arrayvec::ArrayVec::new();
44
45    unsafe {
46        // We've got 16 registers, so make space for that
47        register_array.set_len(16);
48
49        #[cfg(cortex_m)]
50        asm!(
51            "str r0, [{0}, #0]",
52            "str r1, [{0}, #4]",
53            "str r2, [{0}, #8]",
54            "str r3, [{0}, #12]",
55            "str r4, [{0}, #16]",
56            "str r5, [{0}, #20]",
57            "str r6, [{0}, #24]",
58            "str r7, [{0}, #28]",
59            "str r8, [{0}, #32]",
60            "str r9, [{0}, #36]",
61            "str r10, [{0}, #40]",
62            "str r11, [{0}, #44]",
63            "str r12, [{0}, #48]",
64            "str sp, [{0}, #52]",
65            "str lr, [{0}, #56]",
66            "mov {tmp}, pc", // We can't use the str instruction with the PC register directly, so store it in tmp
67            "str {tmp}, [{0}, #60]",
68            in(reg) register_array.as_mut_ptr(), // Every register is going to be written to an offset of this pointer
69            tmp = out(reg) _, // We need a temporary register
70        );
71    }
72
73    *buffer = ArrayRegisterData::new(stackdump_core::gimli::Arm::R0, register_array);
74}
75
76#[cfg(has_fpu)]
77fn capture_fpu_registers(buffer: &mut ArrayRegisterData<32, u32>) {
78    #[cfg(cortex_m)]
79    use core::arch::asm;
80
81    // This array is going to hold the register data
82    let mut register_array = arrayvec::ArrayVec::new();
83
84    unsafe {
85        // We've got 32 registers, so make space for that
86        register_array.set_len(32);
87
88        #[cfg(cortex_m)]
89        asm!(
90            "vstr s0, [{0}, #0]",
91            "vstr s1, [{0}, #4]",
92            "vstr s2, [{0}, #8]",
93            "vstr s3, [{0}, #12]",
94            "vstr s4, [{0}, #16]",
95            "vstr s5, [{0}, #20]",
96            "vstr s6, [{0}, #24]",
97            "vstr s7, [{0}, #28]",
98            "vstr s8, [{0}, #32]",
99            "vstr s9, [{0}, #36]",
100            "vstr s10, [{0}, #40]",
101            "vstr s11, [{0}, #44]",
102            "vstr s12, [{0}, #48]",
103            "vstr s13, [{0}, #52]",
104            "vstr s14, [{0}, #56]",
105            "vstr s15, [{0}, #60]",
106            "vstr s16, [{0}, #64]",
107            "vstr s17, [{0}, #68]",
108            "vstr s18, [{0}, #72]",
109            "vstr s19, [{0}, #76]",
110            "vstr s20, [{0}, #80]",
111            "vstr s21, [{0}, #84]",
112            "vstr s22, [{0}, #88]",
113            "vstr s23, [{0}, #92]",
114            "vstr s24, [{0}, #96]",
115            "vstr s25, [{0}, #100]",
116            "vstr s26, [{0}, #104]",
117            "vstr s27, [{0}, #108]",
118            "vstr s28, [{0}, #112]",
119            "vstr s29, [{0}, #116]",
120            "vstr s30, [{0}, #120]",
121            "vstr s31, [{0}, #124]",
122            in(reg) register_array.as_mut_ptr(), // Every register is going to be written to an offset of this pointer
123        );
124    }
125
126    *buffer = ArrayRegisterData::new(stackdump_core::gimli::Arm::S0, register_array)
127}
128
129/// Capture the stack from the current given stack pointer until the start of the stack into the given stack memory region.
130/// The captured stack will be the smallest of the sizes of the current stack size or the memory region size.
131///
132/// If the memory region is too small, it will contain the top stack space and miss the bottom stack space.
133/// This is done because the top of the stack is often more interesting than the bottom.
134fn capture_stack<const SIZE: usize>(stack_pointer: u32, stack: &mut ArrayMemoryRegion<SIZE>) {
135    extern "C" {
136        static mut _stack_start: core::ffi::c_void;
137    }
138
139    /// Get the start address of the stack. The stack grows to lower addresses,
140    /// so this should be the highest stack address you can get.
141    fn stack_start() -> u32 {
142        unsafe { &_stack_start as *const _ as u32 }
143    }
144
145    let stack_size = stack_start().saturating_sub(stack_pointer).min(SIZE as u32);
146    unsafe {
147        stack.copy_from_memory(stack_pointer as *const u8, stack_size as usize);
148    }
149}