1
//! Debugger utilities
2

            
3
/// Returns whether a debugger is connected.
4
362
pub fn debugger_connected() -> bool {
5
    cfg_select! {
6
        xtensa => xtensa_lx::is_debugger_attached(),
7
        all(riscv, soc_has_assist_debug) => crate::peripherals::ASSIST_DEBUG::regs()
8
            .cpu(0)
9
            .debug_mode()
10
            .read()
11
            .debug_module_active()
12
            .bit_is_set(),
13
        _ => false,
14
    }
15
362
}
16

            
17
/// Sets a word-sized data breakpoint at the given address.
18
/// No breakpoint will be set when a debugger is currently attached if
19
/// the `stack_guard_monitoring_with_debugger_connected` option is false.
20
///
21
/// Breakpoint 0 is used.
22
///
23
/// # Safety
24
/// The address must be word aligned.
25
pub unsafe fn set_stack_watchpoint(addr: usize) {
26
1331
    assert!(addr.is_multiple_of(4));
27

            
28
    if cfg!(stack_guard_monitoring_with_debugger_connected)
29
        || !crate::debugger::debugger_connected()
30
    {
31
        cfg_select! {
32
            xtensa => {
33
                let addr = addr & !0b11;
34
                let dbreakc = 0b1111100 | (1 << 31); // bit 31 = STORE
35

            
36
                unsafe {
37
                    core::arch::asm!(
38
                        "
39
                        wsr {addr}, 144 // 144 = dbreaka0
40
                        wsr {dbreakc}, 160 // 160 = dbreakc0
41
                        ",
42
                        addr = in(reg) addr,
43
                        dbreakc = in(reg) dbreakc,
44
                    );
45
                }
46
            }
47
            _ => unsafe {
48
                set_watchpoint(0, addr, 4);
49
            },
50
        }
51
    }
52
1331
}
53

            
54
#[cfg(riscv)]
55
pub(crate) static DEBUGGER_LOCK: esp_sync::RawMutex = esp_sync::RawMutex::new();
56

            
57
#[cfg(riscv)]
58
const NAPOT_MATCH: u8 = 1;
59

            
60
#[cfg(riscv)]
61
bitfield::bitfield! {
62
    /// Only match type (0x2) triggers are supported.
63
    #[derive(Clone, Copy, Default)]
64
    pub(crate) struct Tdata1(u32);
65

            
66
    /// Sets this for configuring the selected trigger to fire right before a load operation with matching
67
    /// data address is executed by the CPU.
68
    pub bool, load, set_load: 0;
69

            
70
    /// Sets this for configuring the selected trigger to fire right before a store operation with matching
71
    /// data address is executed by the CPU.
72
    pub bool, store, set_store: 1;
73

            
74
    /// Sets this for configuring the selected trigger to fire right before an instruction with matching
75
    /// virtual address is executed by the CPU.
76
    pub bool, execute, set_execute: 2;
77

            
78
    /// Sets this for enabling selected trigger to operate in user mode.
79
    pub bool, u, set_u: 3;
80

            
81
    /// Sets this for enabling selected trigger to operate in machine mode.
82
    pub bool, m, set_m: 6;
83

            
84
    /// Configures the selected trigger to perform one of the available matching operations on a
85
    /// data/instruction address. Valid options are:
86
    /// 0x0: exact byte match, i.e. address corresponding to one of the bytes in an access must match
87
    /// the value of maddress exactly.
88
    /// 0x1: NAPOT match, i.e. at least one of the bytes of an access must lie in the NAPOT region
89
    /// specified in maddress.
90
    /// Writing a larger value clips it to the largest possible value 0x1.
91
    pub u8, _match, set_match: 10, 7;
92

            
93
    /// Configures the selected trigger to perform one of the available actions when firing. Valid
94
    /// options are:
95
    /// 0x0: cause breakpoint exception.
96
    /// 0x1: enter debug mode (only valid when dmode = 1)
97
    /// Writing an invalid value sets this to the default value 0x0.
98
    pub u8, action, set_action: 15, 12;
99

            
100
    /// This is found to be 1 if the selected trigger had fired previously. This bit is to be cleared manually.
101
    pub bool, hit, set_hit: 20;
102

            
103
    /// 0: Both Debug and M mode can write the tdata1 and tdata2 registers at the selected tselect.
104
    /// 1: Only Debug Mode can write the tdata1 and tdata2 registers at the selected tselect. Writes from
105
    /// other modes are ignored.
106
    /// Only writable from debug mode.
107
    pub bool, dmode, set_dmode: 27;
108
}
109

            
110
#[cfg(riscv)]
111
bitfield::bitfield! {
112
    /// Only match type (0x2) triggers are supported.
113
    #[derive(Clone, Copy, Default)]
114
    pub(crate) struct Tcontrol(u32);
115

            
116
    /// Current M mode trigger enable bit
117
    pub bool, mte, set_mte: 3;
118

            
119
    /// Previous M mode trigger enable bit
120
    pub bool, mpte, set_mpte: 7;
121

            
122
}
123

            
124
#[cfg(riscv)]
125
pub(crate) struct WatchPoint {
126
    tdata1: u32,
127
    tdata2: u32,
128
}
129

            
130
/// Clears the watchpoint.
131
#[cfg(riscv)]
132
pub(crate) unsafe fn clear_watchpoint(id: u8) -> WatchPoint {
133
    assert!(id < 4);
134

            
135
    // tdata1 is a WARL(write any read legal) register. We can just write 0 to it.
136
    let mut tdata1 = 0;
137
    let mut tdata2 = 0;
138

            
139
    DEBUGGER_LOCK.lock(|| unsafe {
140
65
        core::arch::asm!(
141
            "
142
            csrw 0x7a0, {id} // tselect
143
            csrrw {tdata1}, 0x7a1, {tdata2} // tdata1
144
            csrr {tdata2}, 0x7a2 // tdata2
145
            ", id = in(reg) id,
146
            tdata1 = inout(reg) tdata1,
147
            tdata2 = out(reg) tdata2,
148
        );
149
    });
150

            
151
    WatchPoint { tdata1, tdata2 }
152
}
153

            
154
/// Clears the watchpoint.
155
#[cfg(riscv)]
156
pub(crate) unsafe fn restore_watchpoint(id: u8, watchpoint: WatchPoint) {
157
    DEBUGGER_LOCK.lock(|| unsafe {
158
65
        core::arch::asm!(
159
            "
160
            csrw 0x7a0, {id} // tselect
161
            csrw 0x7a1, {tdata1} // tdata1
162
            csrw 0x7a2, {tdata2} // tdata2
163
            ", id = in(reg) id,
164
            tdata1 = in(reg) watchpoint.tdata1,
165
            tdata2 = in(reg) watchpoint.tdata2,
166
        );
167
    });
168
}
169

            
170
/// Clears the watchpoint.
171
#[cfg(all(riscv, feature = "exception-handler"))]
172
pub(crate) unsafe fn watchpoint_hit(id: u8) -> bool {
173
    assert!(id < 4);
174
    let mut tdata = Tdata1::default();
175

            
176
    DEBUGGER_LOCK.lock(|| unsafe {
177
        core::arch::asm!(
178
            "
179
            csrw 0x7a0, {id} // tselect
180
            csrr {tdata}, 0x7a1 // tdata1
181
            ", id = in(reg) id,
182
            tdata = out(reg) tdata.0,
183
        );
184
    });
185

            
186
    tdata.hit()
187
}
188

            
189
/// Sets the watchpoint and enables triggers.
190
#[cfg(riscv)]
191
pub(crate) unsafe fn set_watchpoint(id: u8, addr: usize, len: usize) {
192
    assert!(id < 4);
193
    assert!(len.is_power_of_two());
194
    assert!(addr.is_multiple_of(len));
195

            
196
    let z = len.trailing_zeros();
197
    let mask = {
198
        let mut mask: usize = 0;
199
        for i in 0..z {
200
            mask |= 1 << i;
201
        }
202
        mask
203
    };
204

            
205
    let napot_encoding = { mask & !(1 << (z - 1)) };
206
1588
    let addr = (addr & !mask) | napot_encoding;
207

            
208
    let mut tdata = Tdata1::default();
209
    tdata.set_m(true);
210
    tdata.set_store(true);
211
    tdata.set_match(NAPOT_MATCH);
212
    let tdata: u32 = tdata.0;
213

            
214
    let mut tcontrol = Tcontrol::default();
215
    tcontrol.set_mte(true);
216
    let tcontrol: u32 = tcontrol.0;
217

            
218
    DEBUGGER_LOCK.lock(|| unsafe {
219
1588
        core::arch::asm!(
220
            "
221
            csrw 0x7a0, {id} // tselect
222
            csrw 0x7a5, {tcontrol} // tcontrol
223
            csrw 0x7a1, {tdata} // tdata1
224
            csrw 0x7a2, {addr} // tdata2
225
            ", id = in(reg) id,
226
            addr = in(reg) addr,
227
            tdata = in(reg) tdata,
228
            tcontrol = in(reg) tcontrol,
229
        );
230
    });
231
}