Coverage Report

Created: 2025-12-31 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/x86-0.47.0/src/debugregs.rs
Line
Count
Source
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
17
use bit_field::BitField;
18
use bitflags::bitflags;
19
20
use core::arch::asm;
21
22
/// An array list of all available breakpoint registers.
23
pub 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.
34
0
pub unsafe fn dr0() -> usize {
35
    let ret: usize;
36
0
    asm!("mov %dr0, {}", out(reg) ret, options(att_syntax));
37
0
    ret
38
0
}
39
40
/// Write dr0.
41
///
42
/// # Safety
43
/// Needs CPL 0.
44
0
pub unsafe fn dr0_write(val: usize) {
45
0
    asm!("mov {}, %dr0", in(reg) val, options(att_syntax));
46
0
}
47
48
/// Read dr1.
49
///
50
/// # Safety
51
/// Needs CPL 0.
52
0
pub unsafe fn dr1() -> usize {
53
    let ret: usize;
54
0
    asm!("mov %dr1, {}", out(reg) ret, options(att_syntax));
55
0
    ret
56
0
}
57
58
/// Write dr1.
59
///
60
/// # Safety
61
/// Needs CPL 0.
62
0
pub unsafe fn dr1_write(val: usize) {
63
0
    asm!("mov {}, %dr1", in(reg) val, options(att_syntax));
64
0
}
65
66
/// Read dr2.
67
///
68
/// # Safety
69
/// Needs CPL 0.
70
0
pub unsafe fn dr2() -> usize {
71
    let ret: usize;
72
0
    asm!("mov %dr2, {}", out(reg) ret, options(att_syntax));
73
0
    ret
74
0
}
75
76
/// Write dr2.
77
///
78
/// # Safety
79
/// Needs CPL 0.
80
0
pub unsafe fn dr2_write(val: usize) {
81
0
    asm!("mov {}, %dr2", in(reg) val, options(att_syntax));
82
0
}
83
84
/// Read dr3.
85
///
86
/// # Safety
87
/// Needs CPL 0.
88
0
pub unsafe fn dr3() -> usize {
89
    let ret: usize;
90
0
    asm!("mov %dr3, {}", out(reg) ret, options(att_syntax));
91
0
    ret
92
0
}
93
94
/// Write dr3.
95
///
96
/// # Safety
97
/// Needs CPL 0.
98
0
pub unsafe fn dr3_write(val: usize) {
99
0
    asm!("mov {}, %dr3", in(reg) val, options(att_syntax));
100
0
}
101
102
bitflags! {
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.
185
0
pub unsafe fn dr6() -> Dr6 {
186
    let ret: usize;
187
0
    asm!("mov %dr6, {}", out(reg) ret, options(att_syntax));
188
0
    Dr6::from_bits_truncate(ret)
189
0
}
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.
203
0
pub unsafe fn dr6_write(val: Dr6) {
204
0
    asm!("mov {}, %dr6", in(reg) val.bits, options(att_syntax));
205
0
}
206
207
/// Specifies available hardware breakpoints.
208
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
209
pub enum Breakpoint {
210
    Dr0 = 0,
211
    Dr1 = 1,
212
    Dr2 = 2,
213
    Dr3 = 3,
214
}
215
216
impl Breakpoint {
217
    /// Write dr{0-3} register based on provided enum variant.
218
    ///
219
    /// # Safety
220
    /// Needs CPL 0.
221
0
    pub unsafe fn write(&self, val: usize) {
222
0
        match self {
223
0
            Breakpoint::Dr0 => dr0_write(val),
224
0
            Breakpoint::Dr1 => dr1_write(val),
225
0
            Breakpoint::Dr2 => dr2_write(val),
226
0
            Breakpoint::Dr3 => dr3_write(val),
227
        }
228
0
    }
229
230
    /// Read dr{0-3} register based on enum variant.
231
    ///
232
    /// # Safety
233
    /// Needs CPL 0.
234
0
    pub unsafe fn dr(&self) -> usize {
235
0
        match self {
236
0
            Breakpoint::Dr0 => dr0(),
237
0
            Breakpoint::Dr1 => dr1(),
238
0
            Breakpoint::Dr2 => dr2(),
239
0
            Breakpoint::Dr3 => dr3(),
240
        }
241
0
    }
242
243
    /// Configures the breakpoint by writing `dr` registers.
244
    ///
245
    /// # Safety
246
    /// Needs CPL 0.
247
0
    pub unsafe fn configure(&self, addr: usize, bc: BreakCondition, bs: BreakSize) {
248
0
        self.write(addr);
249
0
        let mut dr7 = dr7();
250
0
        dr7.configure_bp(*self, bc, bs);
251
0
        dr7_write(dr7);
252
0
    }
253
254
    /// Enables the breakpoint with `dr7_write`.
255
    ///
256
    /// # Safety
257
    /// Needs CPL 0.
258
0
    unsafe fn enable(&self, global: bool) {
259
0
        let mut dr7 = dr7();
260
0
        dr7.enable_bp(*self, global);
261
0
        dr7_write(dr7);
262
0
    }
263
264
    /// Enable the breakpoint in global mode.
265
    ///
266
    /// # Safety
267
    /// Needs CPL 0.
268
0
    pub unsafe fn enable_global(&self) {
269
0
        self.enable(true);
270
0
    }
271
272
    /// Enable the breakpoint in local mode.
273
    ///
274
    /// # Safety
275
    /// Needs CPL 0.
276
0
    pub unsafe fn enable_local(&self) {
277
0
        self.enable(false);
278
0
    }
279
280
    /// Disable the breakpoint with `dr7_write`.
281
    ///
282
    /// # Safety
283
    /// Needs CPL 0.
284
0
    unsafe fn disable(&self, global: bool) {
285
0
        self.write(0x0);
286
0
        let mut dr7 = dr7();
287
0
        dr7.disable_bp(*self, global);
288
0
        dr7_write(dr7);
289
0
    }
290
291
    /// Disable breakpoint in global mode.
292
    ///
293
    /// # Safety
294
    /// Needs CPL 0.
295
0
    pub unsafe fn disable_global(&self) {
296
0
        self.disable(true);
297
0
    }
298
299
    /// Disable breakpoint in local mode.
300
    ///
301
    /// # Safety
302
    /// Needs CPL 0.
303
0
    pub unsafe fn disable_local(&self) {
304
0
        self.disable(false);
305
0
    }
306
}
307
308
/// Specifies the  breakpoint condition for a corresponding breakpoint.
309
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
310
pub 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)]
328
pub 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)]
340
pub struct Dr7(pub usize);
341
342
impl Default for Dr7 {
343
0
    fn default() -> Self {
344
0
        Self(Dr7::EMPTY)
345
0
    }
346
}
347
348
impl 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
0
    fn set_bp(&mut self, bp: Breakpoint, global: bool, enable: bool) {
371
0
        let bp = bp as usize;
372
0
        assert!(bp < 4);
373
0
        let idx = if global { bp * 2 + 1 } else { bp * 2 };
374
0
        assert!(idx <= 7);
375
0
        self.0.set_bit(idx, enable);
376
0
    }
377
378
    /// Set break condition `bc` for a given breakpoint `bc`.
379
0
    fn set_bc(&mut self, bp: Breakpoint, bc: BreakCondition) {
380
0
        let idx = 16 + (bp as usize * 4);
381
0
        assert!(idx == 16 || idx == 20 || idx == 24 || idx == 28);
382
0
        self.0.set_bits(idx..=idx + 1, bc as usize);
383
0
    }
384
385
    /// Set size `bs` for a given break point `bp`.
386
0
    fn set_bs(&mut self, bp: Breakpoint, bs: BreakSize) {
387
0
        let idx = 18 + (bp as usize * 4);
388
0
        assert!(idx == 18 || idx == 22 || idx == 26 || idx == 30);
389
0
        self.0.set_bits(idx..=idx + 1, bs as usize);
390
0
    }
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
0
    pub fn configure_bp(&mut self, bp: Breakpoint, bc: BreakCondition, bs: BreakSize) {
398
0
        assert!(
399
0
            !(bc == BreakCondition::Instructions && bs != BreakSize::Bytes1),
400
            "If bc is 00 (instruction execution), then the bs field should be 00"
401
        );
402
0
        self.set_bc(bp, bc);
403
0
        self.set_bs(bp, bs);
404
0
    }
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
0
    pub fn enable_bp(&mut self, bp: Breakpoint, global: bool) {
414
0
        self.set_bp(bp, global, true);
415
0
    }
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
0
    pub fn disable_bp(&mut self, bp: Breakpoint, global: bool) {
423
0
        self.set_bp(bp, global, false);
424
0
    }
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
0
    pub fn enable_exact_local_bp(&mut self) {
436
0
        self.0.set_bit(Dr7::LE_BIT, true);
437
0
    }
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
0
    pub fn enable_exact_global_bp(&mut self) {
449
0
        self.0.set_bit(Dr7::GE_BIT, true);
450
0
    }
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
0
    pub fn enable_rtm(&mut self) {
458
0
        self.0.set_bit(Dr7::RTM_BIT, true);
459
0
    }
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
0
    pub fn enable_general_detect(&mut self) {
464
0
        self.0.set_bit(Dr7::GD_BIT, true);
465
0
    }
466
}
467
468
/// Read dr7.
469
///
470
/// # Safety
471
/// Needs CPL 0.
472
0
pub unsafe fn dr7() -> Dr7 {
473
    let ret: usize;
474
0
    asm!("mov %dr7, {}", out(reg) ret, options(att_syntax));
475
0
    Dr7(ret)
476
0
}
477
478
/// Write dr7.
479
///
480
/// # Safety
481
/// Needs CPL 0.
482
0
pub unsafe fn dr7_write(val: Dr7) {
483
0
    asm!("mov {}, %dr7", in(reg) val.0, options(att_syntax));
484
0
}