Skip to content

Commit a4e66c2

Browse files
committed
Add registers::debug
1 parent b56649d commit a4e66c2

File tree

2 files changed

+399
-0
lines changed

2 files changed

+399
-0
lines changed

src/registers/debug.rs

Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
//! Functions to read and write debug registers.
2+
3+
#![allow(missing_docs)] // TODO: Add docs
4+
#![deny(unsafe_op_in_unsafe_fn)]
5+
6+
use core::convert::TryFrom;
7+
use core::mem;
8+
9+
use crate::VirtAddr;
10+
11+
use bitflags::bitflags;
12+
13+
pub trait DebugAddressRegister {
14+
/// Reads the current breakpoint address.
15+
fn read() -> VirtAddr;
16+
17+
/// Writes the provided breakpoint address.
18+
fn write(addr: VirtAddr);
19+
}
20+
21+
macro_rules! debug_address_register_impl {
22+
($Dr:ident, $dr:literal) => {
23+
#[doc = concat!("Debug Address Register (", $dr,") holding the address of a breakpoint.")]
24+
#[derive(Debug)]
25+
pub struct $Dr;
26+
27+
impl DebugAddressRegister for $Dr {
28+
#[inline]
29+
fn read() -> VirtAddr {
30+
let addr;
31+
unsafe {
32+
asm!(concat!("mov {}, ", $dr), out(reg) addr, options(nostack, preserves_flags));
33+
}
34+
VirtAddr::new(addr)
35+
}
36+
37+
#[inline]
38+
fn write(addr: VirtAddr) {
39+
unsafe {
40+
asm!(concat!("mov ", $dr, ", {}"), in(reg) addr.as_u64(), options(nostack, preserves_flags));
41+
}
42+
}
43+
}
44+
};
45+
}
46+
47+
debug_address_register_impl!(Dr0, "DR0");
48+
debug_address_register_impl!(Dr1, "DR1");
49+
debug_address_register_impl!(Dr2, "DR2");
50+
debug_address_register_impl!(Dr3, "DR3");
51+
52+
/// Debug Status Register (DR6).
53+
///
54+
/// Reports debug conditions from the last debug exception.
55+
#[derive(Debug)]
56+
pub struct Dr6;
57+
58+
bitflags! {
59+
/// Debug condition flags of the [`Dr6`] register.
60+
pub struct Dr6Flags: u64 {
61+
/// Breakpoint condition 0 was detected.
62+
const TRAP0 = 1;
63+
64+
/// Breakpoint condition 1 was detected.
65+
const TRAP1 = 1 << 1;
66+
67+
/// Breakpoint condition 2 was detected.
68+
const TRAP2 = 1 << 2;
69+
70+
/// Breakpoint condition 3 was detected.
71+
const TRAP3 = 1 << 3;
72+
73+
/// Breakpoint condition was detected.
74+
const TRAP = Self::TRAP0.bits | Self::TRAP1.bits | Self::TRAP2.bits | Self::TRAP3.bits;
75+
76+
/// Next instruction accesses one of the debug registers.
77+
///
78+
/// Enabled via [`Dr7Flags::GENERAL_DETECT_ENABLE`].
79+
const ACCESS_DETECTED = 1 << 13;
80+
81+
/// CPU is in single-step execution mode.
82+
///
83+
/// Enabled via [`RFlags::TRAP_FLAG`].
84+
const STEP = 1 << 14;
85+
86+
/// Task switch.
87+
///
88+
/// Enabled via the debug trap flag in the TSS of the target task.
89+
const SWITCH = 1 << 15;
90+
91+
/// When *clear*, indicates a debug or breakpoint exception inside an RTM region.
92+
///
93+
/// Enabled via [`Dr7Flags::RESTRICTED_TRANSACTIONAL_MEMORY`] and the
94+
/// RTM flag in the `IA32_DEBUGCTL` [`Msr`].
95+
const RTM = 1 << 16;
96+
}
97+
}
98+
99+
impl Dr6 {
100+
/// Read the current set of DR6 flags.
101+
#[inline]
102+
pub fn read() -> Dr6Flags {
103+
Dr6Flags::from_bits_truncate(Self::read_raw())
104+
}
105+
106+
/// Read the current raw DR6 value.
107+
#[inline]
108+
pub fn read_raw() -> u64 {
109+
let value;
110+
unsafe {
111+
asm!("mov {}, dr6", out(reg) value, options(nostack, preserves_flags));
112+
}
113+
value
114+
}
115+
}
116+
117+
bitflags! {
118+
#[repr(transparent)]
119+
pub struct Dr7Flags: u64 {
120+
const LOCAL_BREAKPOINT_0_ENABLE = 1;
121+
122+
const LOCAL_BREAKPOINT_1_ENABLE = 1 << 2;
123+
124+
const LOCAL_BREAKPOINT_2_ENABLE = 1 << 4;
125+
126+
const LOCAL_BREAKPOINT_3_ENABLE = 1 << 6;
127+
128+
const GLOBAL_BREAKPOINT_0_ENABLE = 1 << 1;
129+
130+
const GLOBAL_BREAKPOINT_1_ENABLE = 1 << 3;
131+
132+
const GLOBAL_BREAKPOINT_2_ENABLE = 1 << 5;
133+
134+
const GLOBAL_BREAKPOINT_3_ENABLE = 1 << 7;
135+
136+
const LOCAL_EXACT_BREAKPOINT_ENABLE = 1 << 8;
137+
138+
const GLOBAL_EXACT_BREAKPOINT_ENABLE = 1 << 9;
139+
140+
const RESTRICTED_TRANSACTIONAL_MEMORY = 1 << 11;
141+
142+
const GENERAL_DETECT_ENABLE = 1 << 13;
143+
}
144+
}
145+
146+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
147+
#[repr(u8)]
148+
pub enum HwBreakpointCondition {
149+
InstructionExecution = 0b00,
150+
DataWrites = 0b01,
151+
IoReadsWrites = 0b10,
152+
DataReadsWrites = 0b11,
153+
}
154+
155+
impl HwBreakpointCondition {
156+
const fn bit_position(n: u8) -> u8 {
157+
16 + 4 * n
158+
}
159+
}
160+
161+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
162+
#[repr(u8)]
163+
pub enum HwBreakpointSize {
164+
Length1B = 0b00,
165+
Length2B = 0b01,
166+
Length8B = 0b10,
167+
Length4B = 0b11,
168+
}
169+
170+
impl HwBreakpointSize {
171+
const fn bit_position(n: u8) -> u8 {
172+
18 + 4 * n
173+
}
174+
}
175+
176+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
177+
pub struct TryFromIntError(());
178+
179+
impl From<core::num::TryFromIntError> for TryFromIntError {
180+
fn from(_: core::num::TryFromIntError) -> Self {
181+
Self(())
182+
}
183+
}
184+
185+
impl TryFrom<usize> for HwBreakpointSize {
186+
type Error = TryFromIntError;
187+
188+
fn try_from(size: usize) -> Result<Self, Self::Error> {
189+
match size {
190+
1 => Ok(Self::Length1B),
191+
2 => Ok(Self::Length2B),
192+
8 => Ok(Self::Length8B),
193+
4 => Ok(Self::Length4B),
194+
_ => Err(TryFromIntError(())),
195+
}
196+
}
197+
}
198+
199+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200+
pub struct Dr7Value {
201+
bits: u64,
202+
}
203+
204+
impl From<Dr7Flags> for Dr7Value {
205+
fn from(dr7_flags: Dr7Flags) -> Self {
206+
Self::from_bits_truncate(dr7_flags.bits())
207+
}
208+
}
209+
210+
macro_rules! set_condition_impl {
211+
($f:ident, $n:expr) => {
212+
#[inline]
213+
pub const fn $f(&mut self, condition: HwBreakpointCondition) {
214+
unsafe {
215+
self.set_condition($n, condition);
216+
}
217+
}
218+
};
219+
}
220+
221+
macro_rules! get_condition_impl {
222+
($f:ident, $n:expr) => {
223+
#[inline]
224+
pub fn $f(&self) -> HwBreakpointCondition {
225+
unsafe { self.get_condition($n) }
226+
}
227+
};
228+
}
229+
230+
macro_rules! set_size_impl {
231+
($f:ident, $n:expr) => {
232+
#[inline]
233+
pub const fn $f(&mut self, size: HwBreakpointSize) {
234+
unsafe {
235+
self.set_size($n, size);
236+
}
237+
}
238+
};
239+
}
240+
241+
macro_rules! get_size_impl {
242+
($f:ident, $n:expr) => {
243+
#[inline]
244+
pub fn $f(&self) -> HwBreakpointSize {
245+
unsafe { self.get_size($n) }
246+
}
247+
};
248+
}
249+
250+
impl Dr7Value {
251+
const fn mask() -> u64 {
252+
let field_mask = (1 << 32) - (1 << 16);
253+
let flag_mask = Dr7Flags::all().bits();
254+
field_mask | flag_mask
255+
}
256+
257+
#[inline]
258+
pub const fn from_bits(bits: u64) -> Option<Self> {
259+
if (bits & Self::mask()) == 0 {
260+
Some(Self { bits })
261+
} else {
262+
None
263+
}
264+
}
265+
266+
#[inline]
267+
pub const fn from_bits_truncate(bits: u64) -> Self {
268+
Self {
269+
bits: bits & !Self::mask(),
270+
}
271+
}
272+
273+
#[inline]
274+
pub const unsafe fn from_bits_unchecked(bits: u64) -> Self {
275+
Self { bits }
276+
}
277+
278+
#[inline]
279+
pub const fn bits(&self) -> u64 {
280+
self.bits
281+
}
282+
283+
#[inline]
284+
pub const fn flags(&self) -> Dr7Flags {
285+
Dr7Flags::from_bits_truncate(self.bits)
286+
}
287+
288+
#[inline]
289+
pub fn flags_mut(&mut self) -> &mut Dr7Flags {
290+
// SAFETY: `Dr7Flags` has the same representation as
291+
unsafe { mem::transmute(self.bits) }
292+
}
293+
294+
/// # Safety
295+
///
296+
/// `n` must be in `0..=3`.
297+
const unsafe fn set_condition(&mut self, n: u8, condition: HwBreakpointCondition) {
298+
let pos = HwBreakpointCondition::bit_position(n);
299+
// Clear value
300+
self.bits &= !(0b11 << pos);
301+
// Insert value
302+
self.bits |= (condition as u64) << pos;
303+
}
304+
305+
set_condition_impl!(set_condition_0, 0);
306+
set_condition_impl!(set_condition_1, 1);
307+
set_condition_impl!(set_condition_2, 2);
308+
set_condition_impl!(set_condition_3, 3);
309+
310+
/// # Safety
311+
///
312+
/// `n` must be in `0..=3`.
313+
unsafe fn get_condition(&self, n: u8) -> HwBreakpointCondition {
314+
let pos = HwBreakpointCondition::bit_position(n);
315+
let condition = (self.bits >> pos) & 0b11;
316+
// SAFETY: All two bit patterns are valid variants
317+
unsafe { mem::transmute(condition as u8) }
318+
}
319+
320+
get_condition_impl!(get_condition_0, 0);
321+
get_condition_impl!(get_condition_1, 1);
322+
get_condition_impl!(get_condition_2, 2);
323+
get_condition_impl!(get_condition_3, 3);
324+
325+
/// # Safety
326+
///
327+
/// `n` must be in `0..=3`.
328+
const unsafe fn set_size(&mut self, n: u8, size: HwBreakpointSize) {
329+
let pos = HwBreakpointSize::bit_position(n);
330+
// Clear value
331+
self.bits &= !(0b11 << pos);
332+
// Insert value
333+
self.bits |= (size as u64) << pos;
334+
}
335+
336+
set_size_impl!(set_size_0, 0);
337+
set_size_impl!(set_size_1, 1);
338+
set_size_impl!(set_size_2, 2);
339+
set_size_impl!(set_size_3, 3);
340+
341+
/// # Safety
342+
///
343+
/// `n` must be in `0..=3`.
344+
unsafe fn get_size(&self, n: u8) -> HwBreakpointSize {
345+
let pos = HwBreakpointSize::bit_position(n);
346+
let size = (self.bits >> pos) & 0b11;
347+
// SAFETY: All two bit patterns are valid variants
348+
unsafe { mem::transmute(size as u8) }
349+
}
350+
351+
get_size_impl!(get_size_0, 0);
352+
get_size_impl!(get_size_1, 1);
353+
get_size_impl!(get_size_2, 2);
354+
get_size_impl!(get_size_3, 3);
355+
}
356+
357+
/// Debug Control Register (DR7).
358+
///
359+
/// Configures debug conditions for debug exceptions.
360+
#[derive(Debug)]
361+
pub struct Dr7;
362+
363+
impl Dr7 {
364+
/// Read the current set of DR6 flags.
365+
#[inline]
366+
pub fn read() -> Dr7Value {
367+
Dr7Value::from_bits_truncate(Self::read_raw())
368+
}
369+
370+
/// Read the current raw DR6 value.
371+
#[inline]
372+
pub fn read_raw() -> u64 {
373+
let value;
374+
unsafe {
375+
asm!("mov {}, dr7", out(reg) value, options(nostack, preserves_flags));
376+
}
377+
value
378+
}
379+
380+
/// Write DR7 value.
381+
///
382+
/// Preserves the value of reserved fields.
383+
#[inline]
384+
pub fn write(value: Dr7Value) {
385+
let old_value = Self::read_raw();
386+
let reserved = old_value & !Dr7Value::mask();
387+
let new_value = reserved | value.bits();
388+
389+
Self::write_raw(new_value)
390+
}
391+
392+
#[inline]
393+
pub fn write_raw(value: u64) {
394+
unsafe {
395+
asm!("mov dr7, {}", in(reg) value, options(nostack, preserves_flags));
396+
}
397+
}
398+
}

0 commit comments

Comments
 (0)