/
debugregs.go
130 lines (118 loc) · 3.1 KB
/
debugregs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package amd64util
import (
"errors"
"fmt"
)
// DebugRegisters represents x86 debug registers described in the Intel 64
// and IA-32 Architectures Software Developer's Manual, Vol. 3B, section
// 17.2
type DebugRegisters struct {
pAddrs [4]*uint64
pDR6, pDR7 *uint64
Dirty bool
}
func NewDebugRegisters(pDR0, pDR1, pDR2, pDR3, pDR6, pDR7 *uint64) *DebugRegisters {
return &DebugRegisters{
pAddrs: [4]*uint64{pDR0, pDR1, pDR2, pDR3},
pDR6: pDR6,
pDR7: pDR7,
Dirty: false,
}
}
func lenrwBitsOffset(idx uint8) uint8 {
return 16 + idx*4
}
func enableBitOffset(idx uint8) uint8 {
return idx * 2
}
func (drs *DebugRegisters) breakpoint(idx uint8) (addr uint64, read, write bool, sz int) {
enable := *(drs.pDR7) & (1 << enableBitOffset(idx))
if enable == 0 {
return 0, false, false, 0
}
addr = *(drs.pAddrs[idx])
lenrw := (*(drs.pDR7) >> lenrwBitsOffset(idx)) & 0xf
write = (lenrw & 0x1) != 0
read = (lenrw & 0x2) != 0
switch lenrw >> 2 {
case 0x0:
sz = 1
case 0x1:
sz = 2
case 0x2:
sz = 8 // sic
case 0x3:
sz = 4
}
return addr, read, write, sz
}
// SetBreakpoint sets hardware breakpoint at index 'idx' to the specified
// address, read/write flags and size.
// If the breakpoint is already in use but the parameters match it does
// nothing.
func (drs *DebugRegisters) SetBreakpoint(idx uint8, addr uint64, read, write bool, sz int) error {
if int(idx) >= len(drs.pAddrs) {
return fmt.Errorf("hardware breakpoints exhausted")
}
curaddr, curread, curwrite, cursz := drs.breakpoint(idx)
if curaddr != 0 {
if (curaddr != addr) || (curread != read) || (curwrite != write) || (cursz != sz) {
return fmt.Errorf("hardware breakpoint %d already in use (address %#x)", idx, curaddr)
}
// hardware breakpoint already set
return nil
}
if read && !write {
return errors.New("break on read only not supported")
}
*(drs.pAddrs[idx]) = addr
var lenrw uint64
if write {
lenrw |= 0x1
}
if read {
lenrw |= 0x2
}
switch sz {
case 1:
// already ok
case 2:
lenrw |= 0x1 << 2
case 4:
lenrw |= 0x3 << 2
case 8:
lenrw |= 0x2 << 2
default:
return fmt.Errorf("data breakpoint of size %d not supported", sz)
}
*(drs.pDR7) &^= (0xf << lenrwBitsOffset(idx)) // clear old settings
*(drs.pDR7) |= lenrw << lenrwBitsOffset(idx)
*(drs.pDR7) |= 1 << enableBitOffset(idx) // enable
drs.Dirty = true
return nil
}
// ClearBreakpoint disables the hardware breakpoint at index 'idx'. If the
// breakpoint was already disabled it does nothing.
func (drs *DebugRegisters) ClearBreakpoint(idx uint8) {
if *(drs.pDR7)&(1<<enableBitOffset(idx)) == 0 {
return
}
*(drs.pDR7) &^= (1 << enableBitOffset(idx))
drs.Dirty = true
}
// GetActiveBreakpoint returns the active hardware breakpoint and resets the
// condition flags.
func (drs *DebugRegisters) GetActiveBreakpoint() (ok bool, idx uint8) {
for idx := uint8(0); idx < 3; idx++ {
enable := *(drs.pDR7) & (1 << enableBitOffset(idx))
if enable == 0 {
continue
}
if *(drs.pDR6)&(1<<idx) != 0 {
*drs.pDR6 &^= 0xf // it is our responsibility to clear the condition bits
drs.Dirty = true
return true, idx
}
}
return false, 0
}