x86_64/registers/
debug.rs

1//! Functions to read and write debug registers.
2
3#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
4use core::arch::asm;
5use core::ops::Range;
6
7use bit_field::BitField;
8use bitflags::bitflags;
9
10/// Debug Address Register
11///
12/// Holds the address of a hardware breakpoint.
13pub trait DebugAddressRegister {
14    /// The corresponding [`DebugAddressRegisterNumber`].
15    const NUM: DebugAddressRegisterNumber;
16
17    /// Reads the current breakpoint address.
18    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
19    fn read() -> u64;
20
21    /// Writes the provided breakpoint address.
22    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
23    fn write(addr: u64);
24}
25
26macro_rules! debug_address_register {
27    ($Dr:ident, $name:literal) => {
28        /// Debug Address Register
29        ///
30        /// Holds the address of a hardware breakpoint.
31        #[derive(Debug)]
32        pub struct $Dr;
33
34        impl DebugAddressRegister for $Dr {
35            const NUM: DebugAddressRegisterNumber = DebugAddressRegisterNumber::$Dr;
36
37            #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
38            #[inline]
39            fn read() -> u64 {
40                let addr;
41                unsafe {
42                    asm!(concat!("mov {}, ", $name), out(reg) addr, options(nomem, nostack, preserves_flags));
43                }
44                addr
45            }
46
47            #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
48            #[inline]
49            fn write(addr: u64) {
50                unsafe {
51                    asm!(concat!("mov ", $name, ", {}"), in(reg) addr, options(nomem, nostack, preserves_flags));
52                }
53            }
54        }
55    };
56}
57
58debug_address_register!(Dr0, "dr0");
59debug_address_register!(Dr1, "dr1");
60debug_address_register!(Dr2, "dr2");
61debug_address_register!(Dr3, "dr3");
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64/// A valid debug address register number.
65///
66/// Must be between 0 and 3 (inclusive).
67pub enum DebugAddressRegisterNumber {
68    /// The debug address register number of [`Dr0`] (0).
69    Dr0,
70
71    /// The debug address register number of [`Dr1`] (1).
72    Dr1,
73
74    /// The debug address register number of [`Dr2`] (2).
75    Dr2,
76
77    /// The debug address register number of [`Dr3`] (3).
78    Dr3,
79}
80
81impl DebugAddressRegisterNumber {
82    /// Creates a debug address register number if it is valid.
83    pub const fn new(n: u8) -> Option<Self> {
84        match n {
85            0 => Some(Self::Dr0),
86            1 => Some(Self::Dr1),
87            2 => Some(Self::Dr2),
88            3 => Some(Self::Dr3),
89            _ => None,
90        }
91    }
92
93    /// Returns the number as a primitive type.
94    pub const fn get(self) -> u8 {
95        match self {
96            Self::Dr0 => 0,
97            Self::Dr1 => 1,
98            Self::Dr2 => 2,
99            Self::Dr3 => 3,
100        }
101    }
102}
103
104/// Debug Status Register (DR6).
105///
106/// Reports debug conditions from the last debug exception.
107#[derive(Debug)]
108pub struct Dr6;
109
110bitflags! {
111    /// Debug condition flags of the [`Dr6`] register.
112    #[repr(transparent)]
113    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
114    pub struct Dr6Flags: u64 {
115        /// Breakpoint condition 0 was detected.
116        const TRAP0 = 1;
117
118        /// Breakpoint condition 1 was detected.
119        const TRAP1 = 1 << 1;
120
121        /// Breakpoint condition 2 was detected.
122        const TRAP2 = 1 << 2;
123
124        /// Breakpoint condition 3 was detected.
125        const TRAP3 = 1 << 3;
126
127        /// Breakpoint condition was detected.
128        const TRAP = Self::TRAP0.bits() | Self::TRAP1.bits() | Self::TRAP2.bits() | Self::TRAP3.bits();
129
130        /// Next instruction accesses one of the debug registers.
131        ///
132        /// Enabled via [`Dr7Flags::GENERAL_DETECT_ENABLE`].
133        const ACCESS_DETECTED = 1 << 13;
134
135        /// CPU is in single-step execution mode.
136        ///
137        /// Enabled via [`RFlags::TRAP_FLAG`].
138        const STEP = 1 << 14;
139
140        /// Task switch.
141        ///
142        /// Enabled via the debug trap flag in the TSS of the target task.
143        const SWITCH = 1 << 15;
144
145        /// When *clear*, indicates a debug or breakpoint exception inside an RTM region.
146        ///
147        /// Enabled via [`Dr7Flags::RESTRICTED_TRANSACTIONAL_MEMORY`] and the
148        /// RTM flag in the `IA32_DEBUGCTL` [`Msr`].
149        const RTM = 1 << 16;
150    }
151}
152
153impl Dr6Flags {
154    /// Returns the trap flag of the provided debug address register.
155    pub fn trap(n: DebugAddressRegisterNumber) -> Self {
156        match n {
157            DebugAddressRegisterNumber::Dr0 => Self::TRAP0,
158            DebugAddressRegisterNumber::Dr1 => Self::TRAP1,
159            DebugAddressRegisterNumber::Dr2 => Self::TRAP2,
160            DebugAddressRegisterNumber::Dr3 => Self::TRAP3,
161        }
162    }
163}
164
165bitflags! {
166    /// Debug control flags of the [`Dr7`] register.
167    #[repr(transparent)]
168    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
169    pub struct Dr7Flags: u64 {
170        /// Breakpoint 0 is enabled for the current task.
171        const LOCAL_BREAKPOINT_0_ENABLE = 1;
172
173        /// Breakpoint 1 is enabled for the current task.
174        const LOCAL_BREAKPOINT_1_ENABLE = 1 << 2;
175
176        /// Breakpoint 2 is enabled for the current task.
177        const LOCAL_BREAKPOINT_2_ENABLE = 1 << 4;
178
179        /// Breakpoint 3 is enabled for the current task.
180        const LOCAL_BREAKPOINT_3_ENABLE = 1 << 6;
181
182        /// Breakpoint 0 is enabled for all tasks.
183        const GLOBAL_BREAKPOINT_0_ENABLE = 1 << 1;
184
185        /// Breakpoint 1 is enabled for all tasks.
186        const GLOBAL_BREAKPOINT_1_ENABLE = 1 << 3;
187
188        /// Breakpoint 2 is enabled for all tasks.
189        const GLOBAL_BREAKPOINT_2_ENABLE = 1 << 5;
190
191        /// Breakpoint 3 is enabled for all tasks.
192        const GLOBAL_BREAKPOINT_3_ENABLE = 1 << 7;
193
194        /// Enable detection of exact instruction causing a data breakpoint condition for the current task.
195        ///
196        /// This is not supported by `x86_64` processors, but is recommended to be enabled for backward and forward compatibility.
197        const LOCAL_EXACT_BREAKPOINT_ENABLE = 1 << 8;
198
199        /// Enable detection of exact instruction causing a data breakpoint condition for all tasks.
200        ///
201        /// This is not supported by `x86_64` processors, but is recommended to be enabled for backward and forward compatibility.
202        const GLOBAL_EXACT_BREAKPOINT_ENABLE = 1 << 9;
203
204        /// Enables advanced debugging of RTM transactional regions.
205        ///
206        /// The RTM flag in the `IA32_DEBUGCTL` [`Msr`] must also be set.
207        const RESTRICTED_TRANSACTIONAL_MEMORY = 1 << 11;
208
209        /// Enables debug register protection.
210        ///
211        /// This will cause a debug exception before any access to a debug register.
212        const GENERAL_DETECT_ENABLE = 1 << 13;
213    }
214}
215
216impl Dr7Flags {
217    /// Returns the local breakpoint enable flag of the provided debug address register.
218    pub fn local_breakpoint_enable(n: DebugAddressRegisterNumber) -> Self {
219        match n {
220            DebugAddressRegisterNumber::Dr0 => Self::LOCAL_BREAKPOINT_0_ENABLE,
221            DebugAddressRegisterNumber::Dr1 => Self::LOCAL_BREAKPOINT_1_ENABLE,
222            DebugAddressRegisterNumber::Dr2 => Self::LOCAL_BREAKPOINT_2_ENABLE,
223            DebugAddressRegisterNumber::Dr3 => Self::LOCAL_BREAKPOINT_3_ENABLE,
224        }
225    }
226
227    /// Returns the global breakpoint enable flag of the provided debug address register.
228    pub fn global_breakpoint_enable(n: DebugAddressRegisterNumber) -> Self {
229        match n {
230            DebugAddressRegisterNumber::Dr0 => Self::GLOBAL_BREAKPOINT_0_ENABLE,
231            DebugAddressRegisterNumber::Dr1 => Self::GLOBAL_BREAKPOINT_1_ENABLE,
232            DebugAddressRegisterNumber::Dr2 => Self::GLOBAL_BREAKPOINT_2_ENABLE,
233            DebugAddressRegisterNumber::Dr3 => Self::GLOBAL_BREAKPOINT_3_ENABLE,
234        }
235    }
236}
237
238/// The condition for a hardware breakpoint.
239#[derive(Clone, Copy, Debug, PartialEq, Eq)]
240#[repr(u8)]
241pub enum BreakpointCondition {
242    /// Instruction execution
243    InstructionExecution = 0b00,
244
245    /// Data writes
246    DataWrites = 0b01,
247
248    /// I/O reads or writes
249    IoReadsWrites = 0b10,
250
251    /// Data reads or writes but not instruction fetches
252    DataReadsWrites = 0b11,
253}
254
255impl BreakpointCondition {
256    /// Creates a new hardware breakpoint condition if `bits` is valid.
257    pub const fn from_bits(bits: u64) -> Option<Self> {
258        match bits {
259            0b00 => Some(Self::InstructionExecution),
260            0b01 => Some(Self::DataWrites),
261            0b10 => Some(Self::IoReadsWrites),
262            0b11 => Some(Self::DataReadsWrites),
263            _ => None,
264        }
265    }
266
267    const fn bit_range(n: DebugAddressRegisterNumber) -> Range<usize> {
268        let lsb = (16 + 4 * n.get()) as usize;
269        lsb..lsb + 2
270    }
271}
272
273/// The size of a hardware breakpoint.
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
275#[repr(u8)]
276pub enum BreakpointSize {
277    /// 1 byte length
278    Length1B = 0b00,
279
280    /// 2 byte length
281    Length2B = 0b01,
282
283    /// 8 byte length
284    Length8B = 0b10,
285
286    /// 4 byte length
287    Length4B = 0b11,
288}
289
290impl BreakpointSize {
291    /// Creates a new hardware breakpoint size if `size` is valid.
292    pub const fn new(size: usize) -> Option<Self> {
293        match size {
294            1 => Some(Self::Length1B),
295            2 => Some(Self::Length2B),
296            8 => Some(Self::Length8B),
297            4 => Some(Self::Length4B),
298            _ => None,
299        }
300    }
301
302    /// Creates a new hardware breakpoint size if `bits` is valid.
303    pub const fn from_bits(bits: u64) -> Option<Self> {
304        match bits {
305            0b00 => Some(Self::Length1B),
306            0b01 => Some(Self::Length2B),
307            0b10 => Some(Self::Length8B),
308            0b11 => Some(Self::Length4B),
309            _ => None,
310        }
311    }
312
313    const fn bit_range(n: DebugAddressRegisterNumber) -> Range<usize> {
314        let lsb = (18 + 4 * n.get()) as usize;
315        lsb..lsb + 2
316    }
317}
318
319/// A valid value of the [`Dr7`] debug register.
320///
321/// In addition to the [`Dr7Flags`] this value has a condition field and a size field for each debug address register.
322#[derive(Clone, Copy, Debug, PartialEq, Eq)]
323#[repr(transparent)]
324pub struct Dr7Value {
325    bits: u64,
326}
327
328impl From<Dr7Flags> for Dr7Value {
329    fn from(dr7_flags: Dr7Flags) -> Self {
330        Self::from_bits_truncate(dr7_flags.bits())
331    }
332}
333
334impl Dr7Value {
335    const fn valid_bits() -> u64 {
336        let field_valid_bits = (1 << 32) - (1 << 16);
337        let flag_valid_bits = Dr7Flags::all().bits();
338        field_valid_bits | flag_valid_bits
339    }
340
341    /// Convert from underlying bit representation, unless that representation contains bits that do not correspond to a field.
342    #[inline]
343    pub const fn from_bits(bits: u64) -> Option<Self> {
344        if (bits & !Self::valid_bits()) == 0 {
345            Some(Self { bits })
346        } else {
347            None
348        }
349    }
350
351    /// Convert from underlying bit representation, dropping any bits that do not correspond to fields.
352    #[inline]
353    pub const fn from_bits_truncate(bits: u64) -> Self {
354        Self {
355            bits: bits & Self::valid_bits(),
356        }
357    }
358
359    /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined field).
360    ///
361    /// # Safety
362    ///
363    /// The bit representation must be a valid [`Dr7Value`].
364    #[inline]
365    pub const unsafe fn from_bits_unchecked(bits: u64) -> Self {
366        Self { bits }
367    }
368
369    /// Returns the raw value of the fields currently stored.
370    #[inline]
371    pub const fn bits(&self) -> u64 {
372        self.bits
373    }
374
375    /// Returns the [`Dr7Flags`] in this value.
376    #[inline]
377    pub const fn flags(self) -> Dr7Flags {
378        Dr7Flags::from_bits_truncate(self.bits)
379    }
380
381    /// Inserts the specified [`Dr7Flags`] in-place.
382    #[inline]
383    pub fn insert_flags(&mut self, flags: Dr7Flags) {
384        self.bits |= flags.bits();
385    }
386
387    /// Removes the specified [`Dr7Flags`] in-place.
388    #[inline]
389    pub fn remove_flags(&mut self, flags: Dr7Flags) {
390        self.bits &= !flags.bits();
391    }
392
393    /// Toggles the specified [`Dr7Flags`] in-place.
394    #[inline]
395    pub fn toggle_flags(&mut self, flags: Dr7Flags) {
396        self.bits ^= flags.bits();
397    }
398
399    /// Inserts or removes the specified [`Dr7Flags`] depending on the passed value.
400    #[inline]
401    pub fn set_flags(&mut self, flags: Dr7Flags, value: bool) {
402        if value {
403            self.insert_flags(flags);
404        } else {
405            self.remove_flags(flags);
406        }
407    }
408
409    /// Returns the condition field of a debug address register.
410    pub fn condition(&self, n: DebugAddressRegisterNumber) -> BreakpointCondition {
411        let condition = self.bits.get_bits(BreakpointCondition::bit_range(n));
412        BreakpointCondition::from_bits(condition).expect("condition should be always valid")
413    }
414
415    /// Sets the condition field of a debug address register.
416    pub fn set_condition(&mut self, n: DebugAddressRegisterNumber, condition: BreakpointCondition) {
417        self.bits
418            .set_bits(BreakpointCondition::bit_range(n), condition as u64);
419    }
420
421    /// Returns the size field of a debug address register.
422    pub fn size(&self, n: DebugAddressRegisterNumber) -> BreakpointSize {
423        let size = self.bits.get_bits(BreakpointSize::bit_range(n));
424        BreakpointSize::from_bits(size).expect("condition should be always valid")
425    }
426
427    /// Sets the size field of a debug address register.
428    pub fn set_size(&mut self, n: DebugAddressRegisterNumber, size: BreakpointSize) {
429        self.bits
430            .set_bits(BreakpointSize::bit_range(n), size as u64);
431    }
432}
433
434/// Debug Control Register (DR7).
435///
436/// Configures debug conditions for debug exceptions.
437#[derive(Debug)]
438pub struct Dr7;
439
440#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
441mod x86_64 {
442    use super::*;
443
444    impl Dr6 {
445        /// Read the current set of DR6 flags.
446        #[inline]
447        pub fn read() -> Dr6Flags {
448            Dr6Flags::from_bits_truncate(Self::read_raw())
449        }
450
451        /// Read the current raw DR6 value.
452        #[inline]
453        pub fn read_raw() -> u64 {
454            let value;
455
456            unsafe {
457                asm!("mov {}, dr6", out(reg) value, options(nomem, nostack, preserves_flags));
458            }
459
460            value
461        }
462    }
463
464    impl Dr7 {
465        /// Read the current set of DR7 flags.
466        #[inline]
467        pub fn read() -> Dr7Value {
468            Dr7Value::from_bits_truncate(Self::read_raw())
469        }
470
471        /// Read the current raw DR7 value.
472        #[inline]
473        pub fn read_raw() -> u64 {
474            let value;
475
476            unsafe {
477                asm!("mov {}, dr7", out(reg) value, options(nomem, nostack, preserves_flags));
478            }
479
480            value
481        }
482
483        /// Write DR7 value.
484        ///
485        /// Preserves the value of reserved fields.
486        #[inline]
487        pub fn write(value: Dr7Value) {
488            let old_value = Self::read_raw();
489            let reserved = old_value & !Dr7Value::valid_bits();
490            let new_value = reserved | value.bits();
491
492            Self::write_raw(new_value)
493        }
494
495        /// Write raw DR7 value.
496        #[inline]
497        pub fn write_raw(value: u64) {
498            unsafe {
499                asm!("mov dr7, {}", in(reg) value, options(nomem, nostack, preserves_flags));
500            }
501        }
502    }
503}