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 errors.New("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
}
|