use bit_field::BitField;
use bitflags::bitflags;
use core::arch::asm;
pub const BREAKPOINT_REGS: [Breakpoint; 4] = [
Breakpoint::Dr0,
Breakpoint::Dr1,
Breakpoint::Dr2,
Breakpoint::Dr3,
];
pub unsafe fn dr0() -> usize {
let ret: usize;
asm!("mov %dr0, {}", out(reg) ret, options(att_syntax));
ret
}
pub unsafe fn dr0_write(val: usize) {
asm!("mov {}, %dr0", in(reg) val, options(att_syntax));
}
pub unsafe fn dr1() -> usize {
let ret: usize;
asm!("mov %dr1, {}", out(reg) ret, options(att_syntax));
ret
}
pub unsafe fn dr1_write(val: usize) {
asm!("mov {}, %dr1", in(reg) val, options(att_syntax));
}
pub unsafe fn dr2() -> usize {
let ret: usize;
asm!("mov %dr2, {}", out(reg) ret, options(att_syntax));
ret
}
pub unsafe fn dr2_write(val: usize) {
asm!("mov {}, %dr2", in(reg) val, options(att_syntax));
}
pub unsafe fn dr3() -> usize {
let ret: usize;
asm!("mov %dr3, {}", out(reg) ret, options(att_syntax));
ret
}
pub unsafe fn dr3_write(val: usize) {
asm!("mov {}, %dr3", in(reg) val, options(att_syntax));
}
bitflags! {
pub struct Dr6: usize {
const B0 = 0b0001;
const B1 = 0b0010;
const B2 = 0b0100;
const B3 = 0b1000;
const BD = 1 << 13;
const BS = 1 << 14;
const BT = 1 << 15;
const RTM = 1 << 16;
}
}
pub unsafe fn dr6() -> Dr6 {
let ret: usize;
asm!("mov %dr6, {}", out(reg) ret, options(att_syntax));
Dr6::from_bits_truncate(ret)
}
pub unsafe fn dr6_write(val: Dr6) {
asm!("mov {}, %dr6", in(reg) val.bits, options(att_syntax));
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Breakpoint {
Dr0 = 0,
Dr1 = 1,
Dr2 = 2,
Dr3 = 3,
}
impl Breakpoint {
pub unsafe fn write(&self, val: usize) {
match self {
Breakpoint::Dr0 => dr0_write(val),
Breakpoint::Dr1 => dr1_write(val),
Breakpoint::Dr2 => dr2_write(val),
Breakpoint::Dr3 => dr3_write(val),
}
}
pub unsafe fn dr(&self) -> usize {
match self {
Breakpoint::Dr0 => dr0(),
Breakpoint::Dr1 => dr1(),
Breakpoint::Dr2 => dr2(),
Breakpoint::Dr3 => dr3(),
}
}
pub unsafe fn configure(&self, addr: usize, bc: BreakCondition, bs: BreakSize) {
self.write(addr);
let mut dr7 = dr7();
dr7.configure_bp(*self, bc, bs);
dr7_write(dr7);
}
unsafe fn enable(&self, global: bool) {
let mut dr7 = dr7();
dr7.enable_bp(*self, global);
dr7_write(dr7);
}
pub unsafe fn enable_global(&self) {
self.enable(true);
}
pub unsafe fn enable_local(&self) {
self.enable(false);
}
unsafe fn disable(&self, global: bool) {
self.write(0x0);
let mut dr7 = dr7();
dr7.disable_bp(*self, global);
dr7_write(dr7);
}
pub unsafe fn disable_global(&self) {
self.disable(true);
}
pub unsafe fn disable_local(&self) {
self.disable(false);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BreakCondition {
Instructions = 0b00,
DataWrites = 0b01,
IoReadsWrites = 0b10,
DataReadsWrites = 0b11,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BreakSize {
Bytes1 = 0b00,
Bytes2 = 0b01,
Bytes8 = 0b10,
Bytes4 = 0b11,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Dr7(pub usize);
impl Default for Dr7 {
fn default() -> Self {
Self(Dr7::EMPTY)
}
}
impl Dr7 {
pub const EMPTY: usize = 1 << 10;
pub const GD_BIT: usize = 13;
pub const RTM_BIT: usize = 11;
pub const GE_BIT: usize = 9;
pub const LE_BIT: usize = 8;
fn set_bp(&mut self, bp: Breakpoint, global: bool, enable: bool) {
let bp = bp as usize;
assert!(bp < 4);
let idx = if global { bp * 2 + 1 } else { bp * 2 };
assert!(idx <= 7);
self.0.set_bit(idx, enable);
}
fn set_bc(&mut self, bp: Breakpoint, bc: BreakCondition) {
let idx = 16 + (bp as usize * 4);
assert!(idx == 16 || idx == 20 || idx == 24 || idx == 28);
self.0.set_bits(idx..=idx + 1, bc as usize);
}
fn set_bs(&mut self, bp: Breakpoint, bs: BreakSize) {
let idx = 18 + (bp as usize * 4);
assert!(idx == 18 || idx == 22 || idx == 26 || idx == 30);
self.0.set_bits(idx..=idx + 1, bs as usize);
}
pub fn configure_bp(&mut self, bp: Breakpoint, bc: BreakCondition, bs: BreakSize) {
assert!(
!(bc == BreakCondition::Instructions && bs != BreakSize::Bytes1),
"If bc is 00 (instruction execution), then the bs field should be 00"
);
self.set_bc(bp, bc);
self.set_bs(bp, bs);
}
pub fn enable_bp(&mut self, bp: Breakpoint, global: bool) {
self.set_bp(bp, global, true);
}
pub fn disable_bp(&mut self, bp: Breakpoint, global: bool) {
self.set_bp(bp, global, false);
}
pub fn enable_exact_local_bp(&mut self) {
self.0.set_bit(Dr7::LE_BIT, true);
}
pub fn enable_exact_global_bp(&mut self) {
self.0.set_bit(Dr7::GE_BIT, true);
}
pub fn enable_rtm(&mut self) {
self.0.set_bit(Dr7::RTM_BIT, true);
}
pub fn enable_general_detect(&mut self) {
self.0.set_bit(Dr7::GD_BIT, true);
}
}
pub unsafe fn dr7() -> Dr7 {
let ret: usize;
asm!("mov %dr7, {}", out(reg) ret, options(att_syntax));
Dr7(ret)
}
pub unsafe fn dr7_write(val: Dr7) {
asm!("mov {}, %dr7", in(reg) val.0, options(att_syntax));
}