/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 | } |