/* | |
* 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; | |
} |