x86/
debugregs.rs

1//! Functions to read and write debug registers.
2//!
3//! * The dr{0,1,2,3} registers are used to set break points.
4//! * The dr6 register contains debug conditions that were sampled at the time
5//!   the last debug exception.
6//! * The dr7 register enables or disables breakpoints and sets breakpoint
7//!   conditions.
8//!
9//! See Intel Vol. 3a Chapter 17, "Debug, Branch, Profile, TSC ... Features"
10//!
11//! # Potential API Improvements
12//! Maybe `Breakpoint` should be a linear type, and functions that mutate
13//! dr0-dr3 should take `&mut self`. That would mean we'd have to remove
14//! `BREAKPOINT_REGS` and a client maintains some mutable instance to the
15//! registers on every core on its own.
16
17use bit_field::BitField;
18use bitflags::bitflags;
19
20use core::arch::asm;
21
22/// An array list of all available breakpoint registers.
23pub const BREAKPOINT_REGS: [Breakpoint; 4] = [
24    Breakpoint::Dr0,
25    Breakpoint::Dr1,
26    Breakpoint::Dr2,
27    Breakpoint::Dr3,
28];
29
30/// Read dr0.
31///
32/// # Safety
33/// Needs CPL 0.
34pub unsafe fn dr0() -> usize {
35    let ret: usize;
36    asm!("mov %dr0, {}", out(reg) ret, options(att_syntax));
37    ret
38}
39
40/// Write dr0.
41///
42/// # Safety
43/// Needs CPL 0.
44pub unsafe fn dr0_write(val: usize) {
45    asm!("mov {}, %dr0", in(reg) val, options(att_syntax));
46}
47
48/// Read dr1.
49///
50/// # Safety
51/// Needs CPL 0.
52pub unsafe fn dr1() -> usize {
53    let ret: usize;
54    asm!("mov %dr1, {}", out(reg) ret, options(att_syntax));
55    ret
56}
57
58/// Write dr1.
59///
60/// # Safety
61/// Needs CPL 0.
62pub unsafe fn dr1_write(val: usize) {
63    asm!("mov {}, %dr1", in(reg) val, options(att_syntax));
64}
65
66/// Read dr2.
67///
68/// # Safety
69/// Needs CPL 0.
70pub unsafe fn dr2() -> usize {
71    let ret: usize;
72    asm!("mov %dr2, {}", out(reg) ret, options(att_syntax));
73    ret
74}
75
76/// Write dr2.
77///
78/// # Safety
79/// Needs CPL 0.
80pub unsafe fn dr2_write(val: usize) {
81    asm!("mov {}, %dr2", in(reg) val, options(att_syntax));
82}
83
84/// Read dr3.
85///
86/// # Safety
87/// Needs CPL 0.
88pub unsafe fn dr3() -> usize {
89    let ret: usize;
90    asm!("mov %dr3, {}", out(reg) ret, options(att_syntax));
91    ret
92}
93
94/// Write dr3.
95///
96/// # Safety
97/// Needs CPL 0.
98pub unsafe fn dr3_write(val: usize) {
99    asm!("mov {}, %dr3", in(reg) val, options(att_syntax));
100}
101
102bitflags! {
103    /// Debug register 6 (dr6) flags.
104    pub struct Dr6: usize {
105        /// B0 breakpoint condition detected
106        ///
107        /// # Notes
108        ///
109        /// The flag is set if the condition described for the breakpoint by
110        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
111        /// may or may not be set if the breakpoint is not enabled by the Ln or
112        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
113        /// should check only those B0-B3 bits which correspond to an enabled
114        /// breakpoint.
115        const B0 = 0b0001;
116
117        /// B1 breakpoint condition detected
118        ///
119        /// # Notes
120        ///
121        /// The flag is set if the condition described for the breakpoint by
122        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
123        /// may or may not be set if the breakpoint is not enabled by the Ln or
124        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
125        /// should check only those B0-B3 bits which correspond to an enabled
126        /// breakpoint.
127        const B1 = 0b0010;
128
129        /// B2 breakpoint condition detected
130        ///
131        /// # Notes
132        ///
133        /// The flag is set if the condition described for the breakpoint by
134        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
135        /// may or may not be set if the breakpoint is not enabled by the Ln or
136        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
137        /// should check only those B0-B3 bits which correspond to an enabled
138        /// breakpoint.
139        const B2 = 0b0100;
140
141        /// B3 breakpoint condition detected
142        ///
143        /// # Notes
144        ///
145        /// The flag is set if the condition described for the breakpoint by
146        /// the LENn, and R/Wn flags in debug control register DR7 is true. They
147        /// may or may not be set if the breakpoint is not enabled by the Ln or
148        /// the Gn flags in register DR7. Therefore on a #DB, a debug handler
149        /// should check only those B0-B3 bits which correspond to an enabled
150        /// breakpoint.
151        const B3 = 0b1000;
152
153        /// BD debug register access detected
154        ///
155        /// Indicates that the next instruction in the instruction stream
156        /// accesses one of the debug registers.
157        ///
158        /// This flag is enabled when the GD (general detect) flag in debug
159        /// control register DR7 is set.
160        const BD = 1 << 13;
161
162        /// BS single step
163        ///
164        /// Indicates (when set) that the debug exception was triggered by the
165        /// single- step execution mode (enabled with the TF flag in the EFLAGS
166        /// register).
167        const BS = 1 << 14;
168
169        /// BT task switch
170        ///
171        /// Indicates (when set) that the debug exception resulted from a task
172        /// switch where the T flag (debug trap flag) in the TSS of the target
173        /// task was set.
174        const BT = 1 << 15;
175
176        /// Enables (when set) advanced debugging of RTM transactional regions.
177        const RTM = 1 << 16;
178    }
179}
180
181/// Read dr6.
182///
183/// # Safety
184/// Needs CPL 0.
185pub unsafe fn dr6() -> Dr6 {
186    let ret: usize;
187    asm!("mov %dr6, {}", out(reg) ret, options(att_syntax));
188    Dr6::from_bits_truncate(ret)
189}
190
191/// Write dr6.
192///
193/// # Notes
194///
195/// Certain debug exceptions may clear bits 0-3. The remaining contents of the
196/// DR6 register are never cleared by the processor. To avoid confusion in
197/// identifying debug exceptions, debug handlers should clear the register
198/// (except bit 16, which they should set) before returning to the interrupted
199/// task).
200///
201/// # Safety
202/// Needs CPL 0.
203pub unsafe fn dr6_write(val: Dr6) {
204    asm!("mov {}, %dr6", in(reg) val.bits, options(att_syntax));
205}
206
207/// Specifies available hardware breakpoints.
208#[derive(Debug, Copy, Clone, PartialEq, Eq)]
209pub enum Breakpoint {
210    Dr0 = 0,
211    Dr1 = 1,
212    Dr2 = 2,
213    Dr3 = 3,
214}
215
216impl Breakpoint {
217    /// Write dr{0-3} register based on provided enum variant.
218    ///
219    /// # Safety
220    /// Needs CPL 0.
221    pub unsafe fn write(&self, val: usize) {
222        match self {
223            Breakpoint::Dr0 => dr0_write(val),
224            Breakpoint::Dr1 => dr1_write(val),
225            Breakpoint::Dr2 => dr2_write(val),
226            Breakpoint::Dr3 => dr3_write(val),
227        }
228    }
229
230    /// Read dr{0-3} register based on enum variant.
231    ///
232    /// # Safety
233    /// Needs CPL 0.
234    pub unsafe fn dr(&self) -> usize {
235        match self {
236            Breakpoint::Dr0 => dr0(),
237            Breakpoint::Dr1 => dr1(),
238            Breakpoint::Dr2 => dr2(),
239            Breakpoint::Dr3 => dr3(),
240        }
241    }
242
243    /// Configures the breakpoint by writing `dr` registers.
244    ///
245    /// # Safety
246    /// Needs CPL 0.
247    pub unsafe fn configure(&self, addr: usize, bc: BreakCondition, bs: BreakSize) {
248        self.write(addr);
249        let mut dr7 = dr7();
250        dr7.configure_bp(*self, bc, bs);
251        dr7_write(dr7);
252    }
253
254    /// Enables the breakpoint with `dr7_write`.
255    ///
256    /// # Safety
257    /// Needs CPL 0.
258    unsafe fn enable(&self, global: bool) {
259        let mut dr7 = dr7();
260        dr7.enable_bp(*self, global);
261        dr7_write(dr7);
262    }
263
264    /// Enable the breakpoint in global mode.
265    ///
266    /// # Safety
267    /// Needs CPL 0.
268    pub unsafe fn enable_global(&self) {
269        self.enable(true);
270    }
271
272    /// Enable the breakpoint in local mode.
273    ///
274    /// # Safety
275    /// Needs CPL 0.
276    pub unsafe fn enable_local(&self) {
277        self.enable(false);
278    }
279
280    /// Disable the breakpoint with `dr7_write`.
281    ///
282    /// # Safety
283    /// Needs CPL 0.
284    unsafe fn disable(&self, global: bool) {
285        self.write(0x0);
286        let mut dr7 = dr7();
287        dr7.disable_bp(*self, global);
288        dr7_write(dr7);
289    }
290
291    /// Disable breakpoint in global mode.
292    ///
293    /// # Safety
294    /// Needs CPL 0.
295    pub unsafe fn disable_global(&self) {
296        self.disable(true);
297    }
298
299    /// Disable breakpoint in local mode.
300    ///
301    /// # Safety
302    /// Needs CPL 0.
303    pub unsafe fn disable_local(&self) {
304        self.disable(false);
305    }
306}
307
308/// Specifies the  breakpoint condition for a corresponding breakpoint.
309#[derive(Debug, Copy, Clone, PartialEq, Eq)]
310pub enum BreakCondition {
311    /// 00 — Break on instruction execution only.
312    Instructions = 0b00,
313    /// 01 — Break on data writes only.
314    DataWrites = 0b01,
315    /// 10 — Break on I/O reads or writes.
316    ///
317    /// # Notes
318    /// For this type to be available, the DE (debug extensions) flag in control
319    /// register CR4 must be set.
320    IoReadsWrites = 0b10,
321    /// 11 — Break on data reads or writes but not instruction fetches.
322    DataReadsWrites = 0b11,
323}
324
325/// Specify the size of the memory location at the address specified in the
326/// corresponding breakpoint address register (DR0 through DR3).
327#[derive(Debug, Copy, Clone, PartialEq, Eq)]
328pub enum BreakSize {
329    /// 00 — 1-byte length.
330    Bytes1 = 0b00,
331    /// 01 — 2-byte length.
332    Bytes2 = 0b01,
333    /// 10 — 8 byte length (or undefined, on older processors).
334    Bytes8 = 0b10,
335    /// 11 — 4-byte length.
336    Bytes4 = 0b11,
337}
338
339#[derive(Debug, Copy, Clone, PartialEq, Eq)]
340pub struct Dr7(pub usize);
341
342impl Default for Dr7 {
343    fn default() -> Self {
344        Self(Dr7::EMPTY)
345    }
346}
347
348impl Dr7 {
349    /// Empty Dr7 has bit 10 always set.
350    pub const EMPTY: usize = 1 << 10;
351
352    /// Bit that controls debug-register protection.
353    pub const GD_BIT: usize = 13;
354
355    /// Bit that controls debugging of RTM transactional regions.
356    pub const RTM_BIT: usize = 11;
357
358    /// Bit that controls global exact breakpoints.
359    pub const GE_BIT: usize = 9;
360
361    /// Bit that controls local exact breakpoints.
362    pub const LE_BIT: usize = 8;
363
364    /// Enable/disable a breakpoint either as global or local.
365    ///
366    /// # Arguments
367    /// * `bp` - The breakpoint to enable/disable.
368    /// * `global` - Whether the breakpoint is global or local.
369    /// * `enable` - Whether to enable or disable the breakpoint.
370    fn set_bp(&mut self, bp: Breakpoint, global: bool, enable: bool) {
371        let bp = bp as usize;
372        assert!(bp < 4);
373        let idx = if global { bp * 2 + 1 } else { bp * 2 };
374        assert!(idx <= 7);
375        self.0.set_bit(idx, enable);
376    }
377
378    /// Set break condition `bc` for a given breakpoint `bc`.
379    fn set_bc(&mut self, bp: Breakpoint, bc: BreakCondition) {
380        let idx = 16 + (bp as usize * 4);
381        assert!(idx == 16 || idx == 20 || idx == 24 || idx == 28);
382        self.0.set_bits(idx..=idx + 1, bc as usize);
383    }
384
385    /// Set size `bs` for a given break point `bp`.
386    fn set_bs(&mut self, bp: Breakpoint, bs: BreakSize) {
387        let idx = 18 + (bp as usize * 4);
388        assert!(idx == 18 || idx == 22 || idx == 26 || idx == 30);
389        self.0.set_bits(idx..=idx + 1, bs as usize);
390    }
391
392    /// Configures a breakpoint condition `bc` and size `bs` for the associated
393    /// breakpoint `bp`.
394    ///
395    /// # Note
396    /// This should be called before `enable_bp`.
397    pub fn configure_bp(&mut self, bp: Breakpoint, bc: BreakCondition, bs: BreakSize) {
398        assert!(
399            !(bc == BreakCondition::Instructions && bs != BreakSize::Bytes1),
400            "If bc is 00 (instruction execution), then the bs field should be 00"
401        );
402        self.set_bc(bp, bc);
403        self.set_bs(bp, bs);
404    }
405
406    /// Enables the breakpoint condition for the associated breakpoint.
407    ///
408    /// # Arguments
409    /// * `bp` - The breakpoint to enable.
410    /// * `global` - If true, the breakpoint is global (e.g., never reset on
411    ///   task switches). If false, the CPU resets the flag (disables bp) on
412    ///   task switch.
413    pub fn enable_bp(&mut self, bp: Breakpoint, global: bool) {
414        self.set_bp(bp, global, true);
415    }
416
417    /// Disables the breakpoint condition for the associated breakpoint.
418    ///
419    /// - `bp`: The breakpoint to disable.
420    /// - `global`: If true, the global breakpoint bit is unset (e.g., never
421    ///   reset on task switches). If false, the local breakpoint bit is unset.
422    pub fn disable_bp(&mut self, bp: Breakpoint, global: bool) {
423        self.set_bp(bp, global, false);
424    }
425
426    /// Local exact breakpoint enable.
427    ///
428    /// This flag causes the processor to detect the exact instruction that
429    /// caused a data breakpoint condition. This feature is not supported in the
430    /// P6 family processors, later IA-32 processors, and Intel 64 processors.
431    ///
432    /// For backward and forward compatibility with other Intel processors,
433    /// Intel recommends that the LE flag be set to 1 if exact breakpoints are
434    /// required.
435    pub fn enable_exact_local_bp(&mut self) {
436        self.0.set_bit(Dr7::LE_BIT, true);
437    }
438
439    /// Global exact breakpoint enable.
440    ///
441    /// This flag causes the processor to detect the exact instruction that
442    /// caused a data breakpoint condition. This feature is not supported in the
443    /// P6 family processors, later IA-32 processors, and Intel 64 processors.
444    ///
445    /// For backward and forward compatibility with other Intel processors,
446    /// Intel recommends that the GE flag be set to 1 if exact breakpoints are
447    /// required.
448    pub fn enable_exact_global_bp(&mut self) {
449        self.0.set_bit(Dr7::GE_BIT, true);
450    }
451
452    /// Enables advanced debugging of RTM transactional regions.
453    ///
454    /// # Note
455    /// This advanced debugging is enabled only if IA32_DEBUGCTL.RTM is also
456    /// set.
457    pub fn enable_rtm(&mut self) {
458        self.0.set_bit(Dr7::RTM_BIT, true);
459    }
460
461    /// Enables debug-register protection, which causes a debug exception to be
462    /// generated prior to any MOV instruction that accesses a debug register.
463    pub fn enable_general_detect(&mut self) {
464        self.0.set_bit(Dr7::GD_BIT, true);
465    }
466}
467
468/// Read dr7.
469///
470/// # Safety
471/// Needs CPL 0.
472pub unsafe fn dr7() -> Dr7 {
473    let ret: usize;
474    asm!("mov %dr7, {}", out(reg) ret, options(att_syntax));
475    Dr7(ret)
476}
477
478/// Write dr7.
479///
480/// # Safety
481/// Needs CPL 0.
482pub unsafe fn dr7_write(val: Dr7) {
483    asm!("mov {}, %dr7", in(reg) val.0, options(att_syntax));
484}