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}