diff --git a/MSP430EEMTarget.cpp b/MSP430EEMTarget.cpp index b51cb54..b724694 100644 --- a/MSP430EEMTarget.cpp +++ b/MSP430EEMTarget.cpp @@ -20,9 +20,9 @@ enum MSP430_MSG WMX_STOPPED, }; -bool MSP430Proxy::MSP430EEMTarget::Initialize( const char *pPortName ) +bool MSP430Proxy::MSP430EEMTarget::Initialize(const GlobalSettings &settings) { - if (!__super::Initialize(pPortName)) + if (!__super::Initialize(settings)) return false; if (m_pBreakpointManager) @@ -46,7 +46,7 @@ bool MSP430Proxy::MSP430EEMTarget::Initialize( const char *pPortName ) memset(&bkpt, 0, sizeof(bkpt)); bkpt.bpMode = BP_COMPLEX; - bkpt.lAddrVal = m_BreakpointInstruction; + bkpt.lAddrVal = settings.BreakpointInstruction; bkpt.bpType = BP_MDB; bkpt.bpAccess = BP_FETCH; bkpt.bpAction = BP_BRK; @@ -56,7 +56,7 @@ bool MSP430Proxy::MSP430EEMTarget::Initialize( const char *pPortName ) if (MSP430_EEM_SetBreakpoint(&m_SoftwareBreakpointWrapperHandle, &bkpt) != STATUS_OK) REPORT_AND_RETURN("Cannot create a MDB breakpoint for handling software breakpoints", false); - m_pBreakpointManager = new SoftwareBreakpointManager(m_DeviceInfo.mainStart, m_DeviceInfo.mainEnd, m_BreakpointInstruction); + m_pBreakpointManager = new SoftwareBreakpointManager(m_DeviceInfo.mainStart, m_DeviceInfo.mainEnd, settings.BreakpointInstruction, settings.InstantBreakpointCleanup); return true; } @@ -100,12 +100,18 @@ bool MSP430Proxy::MSP430EEMTarget::WaitForJTAGEvent() { m_TargetStopped.Wait(); + bool breakIn = m_BreakInPending; + m_BreakInPending = false; + LONG regPC = 0; if (MSP430_Read_Register(®PC, PC) != STATUS_OK) REPORT_AND_RETURN("Cannot read PC register", false); SoftwareBreakpointManager::BreakpointState bpState = m_pBreakpointManager->GetBreakpointState(regPC - 2); + if (m_RAMBreakpoints.IsBreakpointPresent((USHORT)regPC - 2)) + bpState = SoftwareBreakpointManager::BreakpointActive; + switch(bpState) { case SoftwareBreakpointManager::BreakpointActive: @@ -128,7 +134,7 @@ bool MSP430Proxy::MSP430EEMTarget::WaitForJTAGEvent() if (MSP430_Write_Register(®PC, PC) != STATUS_OK) REPORT_AND_RETURN("Cannot read PC register", false); - if (bpState == SoftwareBreakpointManager::BreakpointInactive && m_LastResumeMode != SINGLE_STEP) + if (bpState == SoftwareBreakpointManager::BreakpointInactive && m_LastResumeMode != SINGLE_STEP && !breakIn) { //Skip the breakpoint if (!DoResumeTarget(m_LastResumeMode)) @@ -151,19 +157,15 @@ GDBServerFoundation::GDBStatus MSP430Proxy::MSP430EEMTarget::CreateBreakpoint( B switch(type) { case bptSoftwareBreakpoint: - if (!m_pBreakpointManager->SetBreakpoint((unsigned)Address)) - return kGDBUnknownError; - return kGDBSuccess; + return DoCreateCodeBreakpoint(false, Address, pCookie); case bptHardwareBreakpoint: - bkpt.bpMode = BP_CODE; - bkpt.lAddrVal = (LONG)Address; - bkpt.bpCondition = BP_NO_COND; - bkpt.bpAction = BP_BRK; - break; + return DoCreateCodeBreakpoint(true, Address, pCookie); default: return kGDBNotSupported; } + //TODO: support memory breakpoints + WORD bpHandle = 0; if (MSP430_EEM_SetBreakpoint(&bpHandle, &bkpt) != STATUS_OK) REPORT_AND_RETURN("Cannot set an EEM breakpoint", kGDBUnknownError); @@ -175,22 +177,16 @@ GDBServerFoundation::GDBStatus MSP430Proxy::MSP430EEMTarget::CreateBreakpoint( B GDBServerFoundation::GDBStatus MSP430Proxy::MSP430EEMTarget::RemoveBreakpoint( BreakpointType type, ULONGLONG Address, INT_PTR Cookie ) { - if (type == bptSoftwareBreakpoint) + switch(type) { - if (!m_pBreakpointManager->RemoveBreakpoint((unsigned)Address)) - return kGDBUnknownError; - return kGDBSuccess; + case bptSoftwareBreakpoint: + return DoRemoveCodeBreakpoint(false, Address, Cookie); + case bptHardwareBreakpoint: + return DoRemoveCodeBreakpoint(true, Address, Cookie); + default: + return kGDBNotSupported; } - BpParameter_t bkpt; - memset(&bkpt, 0, sizeof(bkpt)); - bkpt.bpMode = BP_CLEAR; - - WORD bpHandle = (WORD)Cookie; - - if (MSP430_EEM_SetBreakpoint(&bpHandle, &bkpt) != STATUS_OK) - REPORT_AND_RETURN("Cannot remove an EEM breakpoint", kGDBUnknownError); - return kGDBSuccess; } @@ -222,6 +218,7 @@ bool MSP430Proxy::MSP430EEMTarget::DoResumeTarget( RUN_MODES_t mode ) GDBServerFoundation::GDBStatus MSP430Proxy::MSP430EEMTarget::SendBreakInRequestAsync() { LONG state; + m_BreakInPending = true; if (MSP430_State(&state, TRUE, NULL) != STATUS_OK) REPORT_AND_RETURN("Cannot stop device", kGDBNotSupported); return kGDBSuccess; @@ -248,3 +245,112 @@ GDBServerFoundation::GDBStatus MSP430Proxy::MSP430EEMTarget::WriteTargetMemory( return kGDBSuccess; } + +//Breakpoint cookie format: +//32 bits: [type : 4] [reserved : 12] [user_data : 16] +//For RAM breakpoints user_data contains the original insn +//For hardware breakpoints user_data contains the handle returned by EEM API + +enum {kBpCookieTypeMask = 0xF0000000, + kBpCookieTypeHardware = 0x10000000, + kBpCookieTypeSoftwareFLASH = 0x20000000, + kBpCookieTypeSoftwareRAM = 0x30000000, + kBpCookieDataMask = 0x0000FFFF}; + +#define MAKE_BP_COOKIE(type, data) (((type) & kBpCookieTypeMask) | ((data) & kBpCookieDataMask)) + +GDBServerFoundation::GDBStatus MSP430Proxy::MSP430EEMTarget::DoCreateCodeBreakpoint( bool hardware, ULONGLONG Address, INT_PTR *pCookie ) +{ + if (hardware) + { + BpParameter_t bkpt; + memset(&bkpt, 0, sizeof(bkpt)); + bkpt.bpMode = BP_CODE; + bkpt.lAddrVal = (LONG)Address; + bkpt.bpCondition = BP_NO_COND; + bkpt.bpAction = BP_BRK; + + WORD bpHandle = 0; + if (MSP430_EEM_SetBreakpoint(&bpHandle, &bkpt) != STATUS_OK) + REPORT_AND_RETURN("Cannot set an EEM breakpoint", kGDBUnknownError); + + C_ASSERT(sizeof(bpHandle) == 2); + *pCookie = MAKE_BP_COOKIE(kBpCookieTypeHardware, bpHandle); + + return kGDBSuccess; + } + else + { + if (!IsFLASHAddress(Address)) + { + ULONG addr = (ULONG)(Address & ~1); + + if (m_RAMBreakpoints.IsBreakpointPresent((USHORT)addr)) + { + printf("Cannot set a breakpoint at 0x%04x. Breakpoint already exists.\n", (unsigned)Address); + return kGDBUnknownError; + } + + unsigned short insn; + if (MSP430_Read_Memory(addr, (char *)&insn, 2) != STATUS_OK) + REPORT_AND_RETURN("Cannot set a software breakpoint in RAM", kGDBUnknownError); + + *pCookie = MAKE_BP_COOKIE(kBpCookieTypeSoftwareRAM, insn); + + insn = m_BreakpointInstruction; + if (MSP430_Write_Memory(addr, (char *)&insn, 2) != STATUS_OK) + REPORT_AND_RETURN("Cannot set a software breakpoint in RAM", kGDBUnknownError); + + m_RAMBreakpoints.InsertBreakpoint((USHORT)addr); + } + else + { + if (!m_pBreakpointManager->SetBreakpoint((unsigned)Address)) + return kGDBUnknownError; + *pCookie = MAKE_BP_COOKIE(kBpCookieTypeSoftwareFLASH, 0); + } + return kGDBSuccess; + } +} + +GDBServerFoundation::GDBStatus MSP430Proxy::MSP430EEMTarget::DoRemoveCodeBreakpoint( bool hardware, ULONGLONG Address, INT_PTR Cookie ) +{ + if (hardware) + { + ASSERT((Cookie & kBpCookieTypeMask) == kBpCookieTypeHardware); + BpParameter_t bkpt; + memset(&bkpt, 0, sizeof(bkpt)); + bkpt.bpMode = BP_CLEAR; + + WORD bpHandle = (WORD)(Cookie & kBpCookieDataMask); + + if (MSP430_EEM_SetBreakpoint(&bpHandle, &bkpt) != STATUS_OK) + REPORT_AND_RETURN("Cannot remove an EEM breakpoint", kGDBUnknownError); + return kGDBSuccess; + } + else + { + if ((Cookie & kBpCookieTypeMask) == kBpCookieTypeHardware) + return DoRemoveCodeBreakpoint(true, Address, Cookie); + + if (!IsFLASHAddress(Address)) + { + ASSERT((Cookie & kBpCookieTypeMask) == kBpCookieTypeSoftwareRAM); + unsigned short originalINSN = (unsigned short)(Cookie & kBpCookieDataMask); + + if (MSP430_Write_Memory(Address & ~1, (char *)&originalINSN, 2) != STATUS_OK) + REPORT_AND_RETURN("Cannot remove a software breakpoint from RAM", kGDBUnknownError); + + m_RAMBreakpoints.RemoveBreakpoint((USHORT)(Address & ~1)); + } + else + { + ASSERT((Cookie & kBpCookieTypeMask) == kBpCookieTypeSoftwareFLASH); + if (!m_pBreakpointManager->RemoveBreakpoint((unsigned)Address)) + return kGDBUnknownError; + } + + return kGDBSuccess; + } + +} diff --git a/MSP430EEMTarget.h b/MSP430EEMTarget.h index e0d03bd..13a6071 100644 --- a/MSP430EEMTarget.h +++ b/MSP430EEMTarget.h @@ -2,6 +2,7 @@ #include "MSP430Target.h" #include +#include namespace MSP430Proxy { @@ -17,29 +18,64 @@ namespace MSP430Proxy WORD m_SoftwareBreakpointWrapperHandle; SoftwareBreakpointManager *m_pBreakpointManager; - unsigned short m_BreakpointInstruction; RUN_MODES_t m_LastResumeMode; - + //! If the last resume operation was resuming from a breakpoint, this field contains its address. If not, it contains -1 LONG m_BreakpointAddrOfLastResumeOp; + private: + class RAMBreakpointDatabase + { + private: + unsigned char Flags[65536 / 8]; + + public: + RAMBreakpointDatabase() + { + memset(Flags, 0, sizeof(Flags)); + } + + void InsertBreakpoint(USHORT addr) + { + Flags[addr >> 3] |= (1 << (addr & 7)); + } + + void RemoveBreakpoint(USHORT addr) + { + Flags[addr >> 3] &= ~(1 << (addr & 7)); + } + + bool IsBreakpointPresent(USHORT addr) + { + return (Flags[addr >> 3] & (1 << (addr & 7))) != 0; + } + }; + + RAMBreakpointDatabase m_RAMBreakpoints; + unsigned short m_BreakpointInstruction; + protected: virtual bool DoResumeTarget(RUN_MODES_t mode) override; + bool IsFLASHAddress(ULONGLONG addr) + { + return addr >= m_DeviceInfo.mainStart && addr <= m_DeviceInfo.mainEnd; + } + public: - MSP430EEMTarget(unsigned short breakpointInstruction = 0x4343) + MSP430EEMTarget() : m_bEEMInitialized(false) , m_SoftwareBreakpointWrapperHandle(0) , m_pBreakpointManager(NULL) - , m_BreakpointInstruction(breakpointInstruction) , m_LastResumeMode(RUN_TO_BREAKPOINT) , m_BreakpointAddrOfLastResumeOp(-1) + , m_BreakpointInstruction(0) //Will be updated in Initialize() { } public: - virtual bool Initialize(const char *pPortName) override; + virtual bool Initialize(const GlobalSettings &settings) override; ~MSP430EEMTarget(); virtual bool WaitForJTAGEvent() override; @@ -48,6 +84,9 @@ namespace MSP430Proxy static void sEEMHandler(UINT MsgId, UINT wParam, LONG lParam, LONG clientHandle); void EEMNotificationHandler(MSP430_MSG wMsg, WPARAM wParam, LPARAM lParam); + GDBStatus DoCreateCodeBreakpoint(bool hardware, ULONGLONG Address, INT_PTR *pCookie); + GDBStatus DoRemoveCodeBreakpoint(bool hardware, ULONGLONG Address, INT_PTR Cookie); + public: virtual GDBStatus CreateBreakpoint(BreakpointType type, ULONGLONG Address, unsigned kind, OUT INT_PTR *pCookie) override; virtual GDBStatus RemoveBreakpoint(BreakpointType type, ULONGLONG Address, INT_PTR Cookie) override; diff --git a/MSP430Target.cpp b/MSP430Target.cpp index 5d654ae..d4d0f40 100644 --- a/MSP430Target.cpp +++ b/MSP430Target.cpp @@ -8,17 +8,17 @@ using namespace MSP430Proxy; #define REPORT_AND_RETURN(msg, result) { ReportLastMSP430Error(msg); return result; } #define MAIN_SEGMENT_SIZE 512 -bool MSP430Proxy::MSP430GDBTarget::Initialize( const char *pPortName ) +bool MSP430Proxy::MSP430GDBTarget::Initialize(const GlobalSettings &settings) { if (m_bClosePending) return m_bValid; LONG version = 0; - if (MSP430_Initialize((char *)pPortName, &version) != STATUS_OK) + if (MSP430_Initialize((char *)settings.PortName, &version) != STATUS_OK) REPORT_AND_RETURN("Cannot initialize MSP430.DLL", false); m_bClosePending = true; - if (MSP430_VCC(3333) != STATUS_OK) + if (MSP430_VCC(settings.Voltage) != STATUS_OK) REPORT_AND_RETURN("Cannot enable Vcc", false); if (MSP430_Identify((char *)&m_DeviceInfo, sizeof(m_DeviceInfo), DEVICE_UNKNOWN) != STATUS_OK) @@ -27,6 +27,9 @@ bool MSP430Proxy::MSP430GDBTarget::Initialize( const char *pPortName ) if (MSP430_Reset(ALL_RESETS, FALSE, FALSE) != STATUS_OK) REPORT_AND_RETURN("Cannot reset the MSP430 device", false); + m_DeviceInfo.string[__countof(m_DeviceInfo.string) - 1] = 0; + printf("Found an %s device with %d hardware breakpoints.\n", m_DeviceInfo.string, m_DeviceInfo.nBreakpoints); + m_UsedBreakpoints.resize(m_DeviceInfo.nBreakpoints); m_bValid = true; @@ -168,7 +171,7 @@ GDBServerFoundation::GDBStatus MSP430GDBTarget::WriteTargetRegisters( int thread if (registers[i].Valid) { mask |= MASKREG(i); - rawRegs[i] = registers[i].ToUInt32() & 0xFFFF; + rawRegs[i] = registers[i].ToUInt16(); } if (MSP430_Write_Registers(rawRegs, mask) != STATUS_OK) diff --git a/MSP430Target.h b/MSP430Target.h index 996d063..8970ac5 100644 --- a/MSP430Target.h +++ b/MSP430Target.h @@ -6,6 +6,7 @@ #include "registers-msp430.h" #include +#include "settings.h" enum MSP430_MSG; @@ -22,9 +23,11 @@ namespace MSP430Proxy bool m_bClosePending, m_bValid; std::vector m_UsedBreakpoints; - bool m_BreakInPending; bool m_bFLASHErased; + protected: + bool m_BreakInPending; + protected: virtual bool WaitForJTAGEvent(); void ReportLastMSP430Error(const char *pHint); @@ -41,7 +44,7 @@ namespace MSP430Proxy ~MSP430GDBTarget(); - virtual bool Initialize(const char *pPortName); + virtual bool Initialize(const GlobalSettings &settings); virtual GDBStatus GetLastStopRecord(TargetStopRecord *pRec); virtual GDBStatus ResumeAndWait(int threadID); diff --git a/SoftwareBreakpointManager.cpp b/SoftwareBreakpointManager.cpp index b138355..5f668d9 100644 --- a/SoftwareBreakpointManager.cpp +++ b/SoftwareBreakpointManager.cpp @@ -5,11 +5,12 @@ using namespace MSP430Proxy; -MSP430Proxy::SoftwareBreakpointManager::SoftwareBreakpointManager( unsigned flashStart, unsigned flashEnd, unsigned short breakInstruction ) +MSP430Proxy::SoftwareBreakpointManager::SoftwareBreakpointManager( unsigned flashStart, unsigned flashEnd, unsigned short breakInstruction, bool instantCleanup ) : m_FlashStart(flashStart) , m_FlashEnd(flashEnd) , m_FlashSize(flashEnd - flashStart + 1) , m_BreakInstruction(breakInstruction) + , m_bInstantCleanup(instantCleanup) { ASSERT(!(m_FlashSize & 1)); size_t segmentCount = (m_FlashSize + MAIN_SEGMENT_SIZE - 1) / MAIN_SEGMENT_SIZE; @@ -63,11 +64,12 @@ bool MSP430Proxy::SoftwareBreakpointManager::SegmentRecord::SetBreakpoint( unsig case BreakpointPending: return false; //Breakpoint is already set case BreakpointInactive: + InactiveBreakpointCount--; BpState[offset / 2] = BreakpointActive; return true; case NoBreakpoint: - BpState[offset / 2] = BreakpointPending; PendingBreakpointCount++; + BpState[offset / 2] = BreakpointPending; return true; default: return false; @@ -79,6 +81,7 @@ bool MSP430Proxy::SoftwareBreakpointManager::SegmentRecord::RemoveBreakpoint( un switch(BpState[offset / 2]) { case BreakpointActive: + InactiveBreakpointCount++; BpState[offset / 2] = BreakpointInactive; return true; case BreakpointPending: @@ -98,7 +101,10 @@ bool MSP430Proxy::SoftwareBreakpointManager::CommitBreakpoints() for (size_t i = 0; i < m_Segments.size(); i++) { if (!m_Segments[i].PendingBreakpointCount) - continue; + { + if (!m_bInstantCleanup || !m_Segments[i].InactiveBreakpointCount) + continue; + } unsigned segBase = m_FlashStart + i * MAIN_SEGMENT_SIZE; unsigned short data[MAIN_SEGMENT_SIZE / 2], data2[MAIN_SEGMENT_SIZE / 2]; @@ -114,6 +120,7 @@ bool MSP430Proxy::SoftwareBreakpointManager::CommitBreakpoints() case BreakpointInactive: m_Segments[i].BpState[j] = NoBreakpoint; data[j] = m_Segments[i].OriginalInstructions[j]; + eraseNeeded = true; break; case BreakpointPending: m_Segments[i].BpState[j] = BreakpointActive; @@ -155,6 +162,7 @@ bool MSP430Proxy::SoftwareBreakpointManager::CommitBreakpoints() } m_Segments[i].PendingBreakpointCount = 0; + m_Segments[i].InactiveBreakpointCount = 0; } return true; diff --git a/SoftwareBreakpointManager.h b/SoftwareBreakpointManager.h index b937d6f..bb9e298 100644 --- a/SoftwareBreakpointManager.h +++ b/SoftwareBreakpointManager.h @@ -24,13 +24,13 @@ namespace MSP430Proxy unsigned BpState[MAIN_SEGMENT_SIZE / 2]; unsigned short OriginalInstructions[MAIN_SEGMENT_SIZE /2]; - int PendingBreakpointCount; + int PendingBreakpointCount, InactiveBreakpointCount; SegmentRecord() { memset(BpState, 0, sizeof(BpState)); memset(OriginalInstructions, 0, sizeof(OriginalInstructions)); - PendingBreakpointCount = 0; + PendingBreakpointCount = InactiveBreakpointCount = 0; } bool SetBreakpoint(unsigned offset); @@ -40,6 +40,8 @@ namespace MSP430Proxy std::vector m_Segments; unsigned short m_BreakInstruction; + bool m_bInstantCleanup; + struct TranslatedAddr { bool Valid; @@ -61,7 +63,7 @@ namespace MSP430Proxy void HideOrRestoreBreakpointsInMemorySnapshot(unsigned addr, void *pBlock, size_t length, bool hideBreakpoints); public: - SoftwareBreakpointManager(unsigned flashStart, unsigned flashEnd, unsigned short breakInstruction); + SoftwareBreakpointManager(unsigned flashStart, unsigned flashEnd, unsigned short breakInstruction, bool instantCleanup); }; } diff --git a/msp430-gdbproxy.cpp b/msp430-gdbproxy.cpp index f1166d9..6759e34 100644 --- a/msp430-gdbproxy.cpp +++ b/msp430-gdbproxy.cpp @@ -68,7 +68,9 @@ class MSP430StubFactory : public IGDBStubFactory return NULL; } - if (!pTarget->Initialize(m_Port.c_str())) + GlobalSettings settings; + + if (!pTarget->Initialize(settings)) { printf("Failed to initialize MSP430 debugging engine. Aborting.\n"); delete pTarget; diff --git a/msp430-gdbproxy.vcxproj b/msp430-gdbproxy.vcxproj index 5dd0798..600f408 100644 --- a/msp430-gdbproxy.vcxproj +++ b/msp430-gdbproxy.vcxproj @@ -83,6 +83,7 @@ + diff --git a/msp430-gdbproxy.vcxproj.filters b/msp430-gdbproxy.vcxproj.filters index c655775..0d9de11 100644 --- a/msp430-gdbproxy.vcxproj.filters +++ b/msp430-gdbproxy.vcxproj.filters @@ -39,6 +39,9 @@ Header Files + + Header Files + diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..a5a7b5a --- /dev/null +++ b/settings.h @@ -0,0 +1,33 @@ +#pragma once + +namespace MSP430Proxy +{ + enum BreakpointPolicy + { + HardwareOnly, + HardwareThenSoftware, + SoftwareOnly, + }; + + struct GlobalSettings + { + bool EnableEEMMode; + bool InstantBreakpointCleanup; + unsigned short BreakpointInstruction; + BreakpointPolicy SoftBreakPolicy; + const char *PortName; + unsigned Voltage; + int ListenPort; + + GlobalSettings() + { + EnableEEMMode = true; + InstantBreakpointCleanup = true; + BreakpointInstruction = 0x4343; + SoftBreakPolicy = HardwareThenSoftware; + PortName = "USB"; + Voltage = 3333; + ListenPort = 2000; + } + }; +} \ No newline at end of file