blob: 74f70d42a67f8723534aa94cde79d7a0ff761f80 [file] [log] [blame]
/*
* Copyright (c) 2011 Nokia and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation
*/
/*
* WinHWBkptMgr.cpp
*
* This class manages the use of the X86 Hardware Debug Registers
* to set Hardware Breakpoints and Hardware Watchpoints.
*
* Rules governing the use of X86 Debug registers:
* - there are four debug registers (DR0, DR1, DR2 & DR3)
* which can be set with an address in memory that will be
* monitored for access
* - there is a management debug register (DR7) that contains
* the bits governing which of the DR0-DR3 registers will be
* monitored, the type of access, and the size of the memory
* to be monitored.
* - eligible access modes are {execution, write, read+write}
* - eligible sizes are 1, 2, 4 and 8
* - monitoring is only legal for each size at proper alignment
* (i.e. a watchpoint of size 8 must have an address that is
* aligned on a 8-byte boundary; a watchpoint of size 2 can
* be on any 2-byte boundary; a watchpoint of size 1 can be
* at any address)
*
* This implementation currently allows watchpoints only, and
* will monitor memory regions of sizes from 1-32, depending
* upon alignment, by combining the use of available registers
* when necessary and available.
*
* The use of this manager thus restricts the caller to a
* maximum of 4 hardware watchpoints, and fewer if any user
* watchpoints are unaligned for size and/or greater in size
* than 8 bytes.
*
* The use of this manager also restricts watchpoints to being
* identified on a per-process basis. The thread accessing the
* watchpoint will be identified, but the caller cannot
* establish a watchpoint only for specific threads; all
* threads in a process will be established.
*
* This implementation does not currently allow hardware
* breakpoints for execution, nor does it manage debug registers
* in such a way as to optimize for overlapping watchpoint
* regions.
*
* Created on: Aug 17, 2011
* Author: bkirk
*/
#include "WinHWBkptMgr.h"
#include "errors.h"
#include "BreakpointsService.h"
#include "WinProcess.h"
#include "WinThread.h"
static const WinHWBkptMgr::Mode
sDR7ModeBitsMap[9]
= { WinHWBkptMgr::invalid,
WinHWBkptMgr::invalid,
WinHWBkptMgr::write,
WinHWBkptMgr::readwrite,
WinHWBkptMgr::execute,
WinHWBkptMgr::invalid,
WinHWBkptMgr::invalid,
WinHWBkptMgr::invalid,
WinHWBkptMgr::invalid
};
// invalid bits in the following 2 masks
#define XFF 0xFF
/*
* DR0 size bits are 18 & 19
* DR1 size bits are 22 & 23
* DR2 size bits are 26 & 27
* DR3 size bits are 30 & 31
*
* so, the following are the mask bits within each
* DR7 highword nibble for each debug-register that
* makes for the simplest masking later
*
*/
#define SB1 0x00
#define SB2 0x04
#define SB4 0x0C
#define SB8 0x08
static const WinHWBkptMgr::SizeBits
sDR7SizeBitsMap[9]
= {XFF, SB1, SB2, XFF, SB4, XFF, XFF, XFF, SB8};
/*
* the following table represents a mapping of
* the number of registers necessary to set a
* hardware watchpoint of a given size at an
* address of a given alignment.
*
* e.g. for a watchpoint of size 8, the following
* number of DR registers would be required for each of
* the following addresses:
* 0x10: 1 (0x10 size 8)
* 0x11: 4 (0x11 size 1, 0x12 size 2, 0x14 size 4, 0x18 size 1)
* 0x12: 3 (0x12 size 2, 0x14 size 4, 0x18 size 2)
* 0x13: 4 (0x13 size 1, 0x14 size 4, 0x18 size 2, 0x1a size 1)
* 0x14: 2 (0x14 size 4, 0x14 size 4)
* 0x15: 4 (0x15 size 1, 0x16 size 2, 0x18 size 4, 0x1c size 1)
* 0x16: 3 (0x16 size 2, 0x18 size 4, 0x1c size 2)
* 0x17: 4 (0x17 size 1, 0x18 size 4, 0x1c size 2, 0x1e size 1)
*
*
* first index is size, second index is alignBits
*/
static const unsigned char
sRequiredRegsMap[33][8] // size
= { {XFF,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 0 - unused
{ 1, 1, 1, 1, 1, 1, 1, 1}, // 1
{ 1, 2, 1, 2, 1, 2, 1, 2}, // 2
{ 2, 2, 2, 2, 2, 2, 2, 2}, // 3
{ 1, 3, 2, 3, 1, 3, 2, 3}, // 4
{ 2, 3, 3, 2, 2, 3, 3, 2}, // 5
{ 2, 4, 2, 3, 2, 4, 2, 3}, // 6
{ 3, 3, 3, 3, 3, 3, 3, 3}, // 7
{ 1, 4, 3, 4, 2, 4, 3, 4}, // 8
{ 2, 4, 4, 3, 3, 4, 4, 2}, // 9
{ 2,XFF, 3, 4, 3,XFF, 2, 3}, // 10
{ 3, 4, 4, 4, 4, 3, 3, 3}, // 11
{ 2,XFF, 4,XFF, 2, 4, 3, 4}, // 12
{ 3,XFF,XFF, 3, 3, 4, 4, 3}, // 13
{ 3,XFF, 3, 4, 3,XFF, 3, 4}, // 14
{ 4, 4, 4, 4, 4, 4, 4, 4}, // 15
{ 2,XFF, 4,XFF, 3,XFF, 4,XFF}, // 16
{ 3,XFF,XFF, 4, 4,XFF,XFF, 3}, // 17
{ 3,XFF, 4,XFF, 4,XFF, 3, 4}, // 18
{ 4,XFF,XFF,XFF,XFF, 4, 4, 4}, // 19
{ 3,XFF,XFF,XFF, 3,XFF, 4,XFF}, // 20
{ 4,XFF,XFF, 4, 4,XFF,XFF, 4}, // 21
{ 4,XFF, 4,XFF, 4,XFF, 4,XFF}, // 22
{XFF,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 23
{ 3,XFF,XFF,XFF, 4,XFF,XFF,XFF}, // 24
{ 4,XFF,XFF,XFF,XFF,XFF,XFF, 4}, // 25
{ 4,XFF,XFF,XFF,XFF,XFF, 4,XFF}, // 26
{XFF,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 27
{ 4,XFF,XFF,XFF, 4,XFF,XFF,XFF}, // 28
{XFF,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 29
{XFF,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 30
{XFF,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 31
{ 4,XFF,XFF,XFF,XFF,XFF,XFF,XFF}, // 32
};
WinHWBkptMgr::DRMask WinHWBkptMgr::inUse = 0;
WinHWBkptMgr::HWBreakInfo WinHWBkptMgr::hwBkpt[4];
unsigned char WinHWBkptMgr::InUseCount() {
return (DR0 & inUse)
+ ((DR1 & inUse) >> 1)
+ ((DR2 & inUse) >> 2)
+ ((DR3 & inUse) >> 3);
}
unsigned char WinHWBkptMgr::UnusedCount() {
return MAX_HWBP - InUseCount();
}
WinHWBkptMgr::DRMask WinHWBkptMgr::UseUnusedRegister() {
if (!(DR0 & inUse)) { inUse |= DR0; return DR0; }
if (!(DR1 & inUse)) { inUse |= DR1; return DR1; }
if (!(DR2 & inUse)) { inUse |= DR2; return DR2; }
if (!(DR3 & inUse)) { inUse |= DR3; return DR3; }
return 0;
}
unsigned char WinHWBkptMgr::GetUnusedWatchpoint() {
for (int i = 0 ; i < MAX_HWBP; ++i) {
if (hwBkpt[i].inUse == 0)
return i;
}
return XFF;
}
int WinHWBkptMgr::SetHardwareBreak(TBreakpoint* bp) {
if (0xF == inUse) // all regs already being used
return ERR_HWBRK_NO_RESOURCES;
if (bp->accessMode == ACCESSMODE_EXECUTE) {
// support for hardware breakpoints for execution
// currently not yet implemented
return ERR_HWBRK_NOT_SET;
}
unsigned char locSize = bp->size;
if (locSize > 32)
return ERR_HWBRK_INVALID_SIZE;
ContextAddress locAddr = bp->address;
unsigned char reqdRegs = sRequiredRegsMap[locSize][locAddr&7];
if (reqdRegs == XFF)
return ERR_HWBRK_NOT_ALIGNED;
if (reqdRegs > UnusedCount())
return ERR_HWBRK_NO_RESOURCES;
unsigned char wp = GetUnusedWatchpoint();
hwBkpt[wp].address = locAddr;
hwBkpt[wp].size = locSize;
hwBkpt[wp].accessMode = sDR7ModeBitsMap[bp->accessMode];
for (;reqdRegs > 0;--reqdRegs) {
DRMask dr = UseUnusedRegister();
hwBkpt[wp].inUse |= dr;
unsigned char drSz; // size to set in DR register
switch (locAddr & 7) {
// keep falling through cases until drSz is set
case 0: if (locSize >= 8) { drSz = 8; break; }
case 4: if (locSize >= 4) { drSz = 4; break; }
case 6:
case 2: if (locSize >= 2) { drSz = 2; break; }
default: drSz = 1;
}
// these are shifted inside based on the reg used
DRFlags dr7bits = sDR7SizeBitsMap[drSz] | hwBkpt[wp].accessMode;
bp->process->SetDebugRegister(dr, locAddr, dr7bits);
locAddr += drSz;
locSize -= drSz;
}
bp->hwBreakID = wp;
return 0;
}
int WinHWBkptMgr::ClearHardwareBreak(TBreakpoint* bp) {
if (!bp->process)
return ERR_INV_CONTEXT;
unsigned char& wp = bp->hwBreakID;
for (int i = 0; i < MAX_HWBP; ++i) {
DRMask dr = 1 << i;
if (hwBkpt[wp].inUse & dr) {
inUse &= ~dr;
bp->process->ClearDebugRegister(dr);
}
}
bp->process = NULL;
wp = 0;
return 0;
}