diff -r 07b41fa8d1dd -r ca8a1b6995f6 debugsrv/runmodedebug/rmdriver/src/rm_debug_kerneldriver.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debugsrv/runmodedebug/rmdriver/src/rm_debug_kerneldriver.cpp Tue Aug 31 16:45:49 2010 +0300 @@ -0,0 +1,3530 @@ +// Copyright (c) 2004-2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// Device driver for kernel side debug assist +// + +#ifdef __WINS__ +#error - this driver cannot be built for emulation +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug_logging.h" +#include "d_rmd_breakpoints.h" // moved breakpoints code lives here +#include "d_rmd_stepping.h" // moved stepping code lives here +#include "rm_debug_kerneldriver.h" +#include "d_list_manager.h" +#include "rm_debug_driver.h" +#include "rm_debug_eventhandler.h" +#include "d_debug_functionality.h" +#include "d_process_tracker.h" +#include "debug_utils.h" + +using namespace Debug; + +///////////////////////////////////////////////////////////////////////// +// +// DRM_DebugDriverFactory implementation +// +///////////////////////////////////////////////////////////////////////// + +// +// DRM_DebugDriverFactory constructor +// +DRM_DebugDriverFactory::DRM_DebugDriverFactory() + { + iVersion = TVersion(KMajorVersionNumber,KMinorVersionNumber,KBuildVersionNumber); + } + +// +// DRM_DebugDriverFactory::Create +// +TInt DRM_DebugDriverFactory::Create(DLogicalChannelBase*& aChannel) + { + if (iOpenChannels != 0) + return KErrInUse; // a channel is already open + + aChannel = new DRM_DebugChannel(this); + + return aChannel ? KErrNone : KErrNoMemory; + } + +// +// DRM_DebugDriverFactory::Install +// +TInt DRM_DebugDriverFactory::Install() + { + return(SetName(&KRM_DebugDriverName)); + } + +// +// DRM_DebugDriverFactory::Install +// +void DRM_DebugDriverFactory::GetCaps(TDes8& aDes) const + { + TCapsRM_DebugDriver b; + b.iVersion = TVersion(KMajorVersionNumber, KMinorVersionNumber, KBuildVersionNumber); + + Kern::InfoCopy(aDes,(TUint8*)&b,sizeof(b)); + } + +///////////////////////////////////////////////////////////////////////// +// +// DRM_DebugChannel implementation +// +///////////////////////////////////////////////////////////////////////// + +// +// DRM_DebugChannel constructor +// +DRM_DebugChannel::DRM_DebugChannel(DLogicalDevice* aLogicalDevice) + : iExcludedROMAddressStart(ROM_LINEAR_BASE), + iExcludedROMAddressEnd(0), + iPageSize(0x1000), + iBreakManager(0), + iStepper(0), + iStepLock(0), + iDfcQ(NULL), + iInitialisedCodeModifier(0), + iAsyncGetValueRequest(NULL) + { + LOG_MSG("DRM_DebugChannel::DRM_DebugChannel()"); + + iDevice = aLogicalDevice; + + iClientThread = &Kern::CurrentThread(); + + // Opening handle to current thread, so no need to check for return-value + (void)iClientThread->Open(); + + LOG_MSG3("DRM_DebugChannel::DRM_DebugChannel() clientThread = 0x%08x, id=%d", + iClientThread, iClientThread->iId ); + + + iPageSize = Kern::RoundToPageSize(1); + } + +// +// DRM_DebugChannel destructor +// +DRM_DebugChannel::~DRM_DebugChannel() + { + LOG_MSG("DRM_DebugChannel::~DRM_DebugChannel()"); + + if (iAsyncGetValueRequest) + { + Kern::QueueRequestComplete(iClientThread, iAsyncGetValueRequest, KErrCancel); // does nothing if request not pending + Kern::DestroyClientRequest(iAsyncGetValueRequest); + } + + NKern::ThreadEnterCS(); + Kern::SafeClose((DObject*&)iClientThread, NULL); + NKern::ThreadLeaveCS(); + + // Close breakpoint manager + if (iBreakManager) + { + NKern::ThreadEnterCS(); + delete iBreakManager; + NKern::ThreadLeaveCS(); + } + + // Close stepping manager + if (iStepper) + { + NKern::ThreadEnterCS(); + delete iStepper; + NKern::ThreadLeaveCS(); + } + + //close the debug process list + iDebugProcessList.Close(); + + DestroyDfcQ(); + + //close the code modifier + if (iInitialisedCodeModifier) + { + DebugSupport::CloseCodeModifier(); + } + } + +void DRM_DebugChannel::DestroyDfcQ() + { + LOG_MSG("DRM_DebugChannel::DestroyDfcQ()"); + if (iDfcQ) + { + NKern::ThreadEnterCS(); + iDfcQ->Destroy(); + NKern::ThreadLeaveCS(); + } + } + +// +// DRM_DebugChannel::DoCreate +// +TInt DRM_DebugChannel::DoCreate(TInt /*aUnit*/, const TDesC* anInfo, const TVersion& aVer) + { + LOG_MSG("DRM_DebugChannel::DoCreate()"); + TInt err = Kern::CreateClientDataRequest(iAsyncGetValueRequest); + if(err != KErrNone) + return err; + + if (!Kern::QueryVersionSupported(TVersion(KMajorVersionNumber, KMinorVersionNumber, KBuildVersionNumber), aVer)) + return KErrNotSupported; + + // Do the security check here so that any arbitrary application doesn't make + // use of Trk kernel driver. + if (!DoSecurityCheck()) + { + LOG_MSG("DRM_DebugChannel::DoCreate() - permission denied!"); + return KErrPermissionDenied; + } + + if (anInfo) + { + // this is the end address of the user library. + // this doesn't seem to be valid for EKA2. + // right now we dont need this for EKA2 since we are not worried + // about kernel being stopped as kernel is multithreaded. + // just retaining this for future use. + TBuf8<32> buf; + TInt err = Kern::ThreadRawRead(iClientThread, anInfo, &buf, 32); + if(err != KErrNone) + return err; + } + + // Allocate a D_RMD_Breakpoints class as a breakpoint manager + NKern::ThreadEnterCS(); + iBreakManager = new D_RMD_Breakpoints(this); + NKern::ThreadLeaveCS(); + if (iBreakManager == NULL) + { + LOG_MSG("DRM_DebugChannel::DRM_DebugChannel - could not construct breakpoint manager"); + return KErrNoMemory; + } + + // Initialise the new breakpoint manager object + iBreakManager->Init(); + + // Allocate a DRMDStepping class as the stepping manager + NKern::ThreadEnterCS(); + iStepper = new DRMDStepping(this); + NKern::ThreadLeaveCS(); + if (iStepper == NULL) + { + LOG_MSG("DRM_DebugChannel::DRM_DebugChannel - could not construct stepper manager"); + return KErrNoMemory; + } + + // Initialize the code modifier for managing breakpoints. + TUint caps; //ignored for now + err = DebugSupport::InitialiseCodeModifier(caps, NUMBER_OF_MAX_BREAKPOINTS); + //if code modifier initializer failed, + //return here, since we can't set an breakpoints + if(err != KErrNone) + { + return err; + } + else + { + iInitialisedCodeModifier = ETrue; + } + + //create and set the driver's Dfc queue + err = CreateDfcQ(); + if(err != KErrNone) + { + LOG_MSG("DRM_DebugChannel::DoCreate() Creating Dfc queue failed."); + } + SetDfcQ(iDfcQ); + + iMsgQ.Receive(); + + iEventHandler = new DRM_DebugEventHandler; + if (!iEventHandler) + return KErrNoMemory; + err = iEventHandler->Create(iDevice, this, iClientThread); + if (err != KErrNone) + return err; + + //return KErrNone; + return iEventHandler->Start(); + } + +/** +Forward call to either synch or asynch methods while serialising all calls via lock. + +Protect access via a the event handler lock to +serialise all calls and protect concurrent access to data structures + +@param aMsg pointer to a TMessageBase object + +@return error returned by called methods + +@see DRM_DebugEventHandler::HandleSpecificEvent where lock is also used +@see DRM_DebugEventHandler::iProtectionLock + +*/ +TInt DRM_DebugChannel::SendMsg(TMessageBase* aMsg) + { + DThread * currThread = &Kern::CurrentThread(); + + iEventHandler->LockDataAccess(); + LOG_MSG3("DRM_DebugChannel::SendMsg() currThread = 0x%08x, iClientThread=0x%08x", currThread, iClientThread ); + + TThreadMessage& m = *(TThreadMessage*)aMsg; + TInt id = m.iValue; + TInt err = KErrNone; + + if (id != (TInt)ECloseMsg && id != KMaxTInt && id < 0) + { + // DoRequest + TRequestStatus* pStatus = (TRequestStatus*)m.Ptr0(); + err = SendRequest(aMsg); + if (err != KErrNone) + Kern::RequestComplete(pStatus,err); + } + else + { + err = DLogicalChannel::SendMsg(aMsg); + } + + iEventHandler->ReleaseDataAccess(); + return err; + } + +// +// DRM_DebugChannel::SendRequest +// +TInt DRM_DebugChannel::SendRequest(TMessageBase* aMsg) + { + LOG_MSG("DRM_DebugChannel::SendRequest()"); + + TThreadMessage& m = *(TThreadMessage*)aMsg; + TInt function = ~m.iValue; + TRequestStatus* pStatus = (TRequestStatus*)m.Ptr0(); + TAny* a1 = m.Ptr1(); + + TInt err = KErrNotSupported; + switch(function) + { + case RRM_DebugDriver::ERequestGetEvent: + err = PreAsyncGetValue((TEventInfo*)a1,pStatus); + break; + } + if (err == KErrNone) + err = DLogicalChannel::SendMsg(aMsg); + return err; + } + +// +// DRM_DebugChannel::PreAsyncGetValue +// +TInt DRM_DebugChannel::PreAsyncGetValue(TEventInfo* aValue, TRequestStatus* aStatus) + { + LOG_MSG3("DRM_DebugChannel::PreAsyncGetValue() TEventInfo=0x%08x, TRequestStatus=0x%08x", + aValue, aStatus ); + + iAsyncGetValueRequest->Reset(); + + TInt err = iAsyncGetValueRequest->SetStatus(aStatus); + if (err != KErrNone) + return err; + + iAsyncGetValueRequest->SetDestPtr(aValue); + return KErrNone; + } + +/** + Create the Dfc queue for receiving messages + */ +TInt DRM_DebugChannel::CreateDfcQ() + { + LOG_MSG("DRM_DebugChannel::CreateDfcQ()"); + TInt r = Kern::DynamicDfcQCreate(iDfcQ, KRmDebugDriverThreadPriority, KRM_DebugDriverName); + + if (r == KErrNone) + iDfcQ->SetRealtimeState(ERealtimeStateOff); + return r; + } + +// +// DRM_DebugChannel::DoCancel +// +// New: The cancel call does not take an enum parameter describing +// the request to be cancelled. Rather it supplies a pointer +// to a user-side struct defining the cancellation +// +void DRM_DebugChannel::DoCancel(TInt aReqNo) + { + LOG_MSG("DRM_DebugChannel::DoCancel()"); + + TRMD_DebugCancelInfo info; + + TInt err = Kern::ThreadRawRead(iClientThread,(TAny*)aReqNo,(TAny*)&info,sizeof(info)); + if (err != KErrNone) + { + // How do we cancel something we know nothing about??? + LOG_MSG("DRM_DebugChannel::DoCancel - bad arguments"); + return; + } + + DDebugAgent* debugAgent = TheDProcessTracker.FindAgentForProcessAndId(info.iProcessName, info.iAgentId); + if (debugAgent == NULL) + { + // Bad agent means there is no tracking agent + LOG_MSG2("Cannot locate debug agent with pid 0x%x", info.iAgentId); + return; + } + + // Agent completes/pends the request as appropriate. + debugAgent->CancelGetEvent(); + + } + +// +// DRM_DebugChannel::DoRequest +// +void DRM_DebugChannel::DoRequest(TInt aReqNo, TRequestStatus* aStatus, TAny* a1, TAny* a2) + { + LOG_MSG4("DRM_DebugChannel::DoRequest(), iClientThread=0x%08x, tid=0x%08x, TRequestStatus=0x%08x", + iClientThread, I64LOW(iClientThread->iId), aStatus); + + switch(aReqNo) + { + case RRM_DebugDriver::ERequestGetEvent: + { + TEventMetaData eventMetaData; + TInt err = Kern::ThreadRawRead(iClientThread, a2, (TUint8 *)&eventMetaData, sizeof(TEventMetaData) ); + if (err != KErrNone) + { + LOG_MSG("Error: could not read argument data from the DSS (TEventMetaData)"); + + // We could not read information from the user, so the a2 argument is probably wrong + Kern::RequestComplete(iClientThread, aStatus, KErrArgument); + return; + } + + DDebugAgent* debugAgent = TheDProcessTracker.FindAgentForProcessAndId(eventMetaData.iTargetProcessName, eventMetaData.iDebugAgentProcessId); + if (debugAgent == NULL) + { + // Bad agent means there is no tracking agent + LOG_MSG2("Cannot locate debug agent with pid 0x%x", eventMetaData.iDebugAgentProcessId); + Kern::RequestComplete(iClientThread, aStatus, KErrNotFound); + return; + } + // Agent completes/pends the request as appropriate. + debugAgent->GetEvent(iAsyncGetValueRequest, iClientThread); + + break; + } + default: + { + // Should not get here! + LOG_MSG2("DRM_DebugChannel::DoRequest was passed unsupported request aReqNo=%d", aReqNo ); + Kern::RequestComplete(iClientThread, aStatus, KErrNotSupported); + } + } + } + +// +// DRM_DebugChannel::DoControl +// +TInt DRM_DebugChannel::DoControl(TInt aFunction, TAny* a1, TAny* a2) + { + LOG_MSG("DRM_DebugChannel::DoControl()"); + + LOG_MSG2("DoControl Function %d", aFunction); + + TInt err = KErrNone; + DThread* threadObj = NULL; + // Open a thread handle for the operations that need one + switch (aFunction) + { + case RRM_DebugDriver::EControlSuspendThread: + case RRM_DebugDriver::EControlResumeThread: + case RRM_DebugDriver::EControlStepRange: + case RRM_DebugDriver::EControlReadMemory: + case RRM_DebugDriver::EControlWriteMemory: + case RRM_DebugDriver::EControlReadRegistersLegacy: + case RRM_DebugDriver::EControlWriteRegistersLegacy: + case RRM_DebugDriver::EControlReadRegisters: + case RRM_DebugDriver::EControlWriteRegisters: + { + NKern::ThreadEnterCS(); + threadObj = DebugUtils::OpenThreadHandle((TUint32)a1); + if (!threadObj) + { + NKern::ThreadLeaveCS(); + return KErrBadHandle; + } + break; + } + default: + break; + } + + switch(aFunction) + { + /* Security first */ + case RRM_DebugDriver::EControlIsDebuggable: + { + err = IsDebuggable((TUint32)a1); + break; + } + case RRM_DebugDriver::EControlSetBreak: + { + err = SetBreak((TSetBreakInfo*)a1); + break; + } + case RRM_DebugDriver::EControlClearBreak: + { + err = iBreakManager->DoClearBreak((TInt32)a1); + break; + } + case RRM_DebugDriver::EControlModifyBreak: + { + err = iBreakManager->DoModifyBreak((TModifyBreakInfo*)a1); + break; + } + case RRM_DebugDriver::EControlModifyProcessBreak: + { + err = iBreakManager->DoModifyProcessBreak((TModifyProcessBreakInfo*)a1); + break; + } + case RRM_DebugDriver::EControlBreakInfo: + { + err = iBreakManager->DoBreakInfo((TGetBreakInfo*)a1); + break; + } + case RRM_DebugDriver::EControlSuspendThread: + { + err = DoSuspendThread(threadObj); + break; + } + case RRM_DebugDriver::EControlResumeThread: + { + err = DoResumeThread(threadObj); + break; + } + case RRM_DebugDriver::EControlStepRange: + { + err = StepRange(threadObj, (TRM_DebugStepInfo*)a2); + break; + } + case RRM_DebugDriver::EControlReadMemory: + { + err = ReadMemory(threadObj, (TRM_DebugMemoryInfo*)a2); + break; + } + case RRM_DebugDriver::EControlWriteMemory: + { + err = WriteMemory(threadObj, (TRM_DebugMemoryInfo*)a2); + break; + } + case RRM_DebugDriver::EControlReadRegistersLegacy: + { + err = ReadRegistersLegacy(threadObj, (TRM_DebugRegisterInfo*)a2); + break; + } + case RRM_DebugDriver::EControlWriteRegistersLegacy: + { + err = WriteRegistersLegacy(threadObj, (TRM_DebugRegisterInfo*)a2); + break; + } + case RRM_DebugDriver::EControlReadRegisters: + { + err = ReadRegisters(threadObj, (TRM_DebugRegisterInformation*)a2); + break; + } + case RRM_DebugDriver::EControlWriteRegisters: + { + err = WriteRegisters(threadObj, (TRM_DebugRegisterInformation*)a2); + break; + } + case RRM_DebugDriver::EControlGetDebugFunctionalityBufSize: + { + LOG_MSG("RRM_DebugDriver::EControlGetDebugFunctionalityBufSize\n"); + + TDebugFunctionality df; + + TUint size = df.GetDebugFunctionalityBufSize(); + + // Return size to user-side in a safe manner + err = Kern::ThreadRawWrite(iClientThread, a1, (TUint8*)&size, sizeof(TUint), iClientThread); + break; + } + case RRM_DebugDriver::EControlGetDebugFunctionality: + { + LOG_MSG("RRM_DebugDriver::EControlGetDebugFunctionality\n"); + + TDebugFunctionality df; + + TUint32 dfsize = df.GetDebugFunctionalityBufSize(); + + // Alloc tmp buffer for Debug Functionality data + NKern::ThreadEnterCS(); + TUint8* dfbuffer = (TUint8*)Kern::AllocZ(dfsize); + if (dfbuffer==NULL) + { + LOG_MSG2("Could not allocate memory for %d bytes\n",dfsize); + NKern::ThreadLeaveCS(); + // could not allocate memory + return KErrNoMemory; + } + + // Temporary descriptor to hold DF data + TPtr8 tmpPtr(dfbuffer,0,dfsize); + + // Obtain the DF data + if (df.GetDebugFunctionality(tmpPtr) ) + { + // Return the DF data to the user-side + err = Kern::ThreadDesWrite(iClientThread, a1, tmpPtr, 0, KChunkShiftBy0, iClientThread); + } + else + { + // Failed. + err = KErrGeneral; + } + + // Free tmp buffer + Kern::Free(dfbuffer); + NKern::ThreadLeaveCS(); + break; + } + case RRM_DebugDriver::EControlAttachProcess: + { + LOG_MSG("RRM_DebugDriver::EControlAttachProcess"); + + err = AttachProcess(a1,a2); + break; + } + case RRM_DebugDriver::EControlDetachProcess: + { + LOG_MSG("RRM_DebugDriver::EControlDetachProcess"); + + err = DetachProcess(a1,a2); + break; + } + case RRM_DebugDriver::EControlDetachAgent: + { + LOG_MSG("RRM_DebugDriver::EControlDetachAgent"); + + err = DetachAgent(a1,a2); + break; + } + case RRM_DebugDriver::EControlSetEventAction: + { + LOG_MSG("RRM_DebugDriver::EControlSetEventAction"); + + err = SetEventAction(a1,a2); + break; + } + case RRM_DebugDriver::EControlGetMemoryOperationMaxBlockSize: + { + LOG_MSG("RRM_DebugDriver::EControlGetMemoryOperationMaxBlockSize\n"); + + TUint32 maxSize = TDebugFunctionality::GetMemoryOperationMaxBlockSize(); + + // Return size to user-side in a safe manner + err = Kern::ThreadRawWrite(iClientThread, a1, (TUint8*)&maxSize, sizeof(TUint32), iClientThread); + break; + } + case RRM_DebugDriver::EControlGetList: + { + LOG_MSG("RRM_DebugDriver::EControlGetList\n"); + err = GetList((TListInformation*)a1); + break; + } + case RRM_DebugDriver::EControlStep: + { + LOG_MSG("RRM_DebugDriver::EControlStep\n"); + + err = Step((TUint32)a1,(TUint32)a2); + break; + } + case RRM_DebugDriver::EControlKillProcess: + { + LOG_MSG("RRM_DebugDriver::EControlKillProcess\n"); + + err = KillProcess((TUint32)a1,(TUint32)a2); + break; + } + default: + { + err = KErrGeneral; + } + } + + if (KErrNone != err) + { + LOG_MSG2("Error %d from control function", err); + } + + if (threadObj) + { + // Close the thread handle which has been opened by DebugUtils::OpenThreadHandle + threadObj->Close(NULL); + NKern::ThreadLeaveCS(); + } + + return err; + } + +void DRM_DebugChannel::HandleMsg(TMessageBase* aMsg) + { + LOG_MSG("DRM_DebugChannel::HandleMsg()"); + + TThreadMessage& m = *(TThreadMessage*)aMsg; + TInt id = m.iValue; + + if (id == (TInt)ECloseMsg) + { + if (iEventHandler) + { + iEventHandler->Stop(); + iEventHandler->Close(); + iEventHandler = NULL; + } + m.Complete(KErrNone, EFalse); + return; + } + + if (id == KMaxTInt) + { + // DoCancel + DoCancel(m.Int0()); + m.Complete(KErrNone, ETrue); + return; + } + + if (id < 0) + { + // DoRequest + TRequestStatus* pStatus = (TRequestStatus*)m.Ptr0(); + DoRequest(~id, pStatus, m.Ptr1(), m.Ptr2()); + m.Complete(KErrNone, ETrue); + } + else + { + // DoControl + TInt err = DoControl(id, m.Ptr0(), m.Ptr1()); + m.Complete(err, ETrue); + } + } + +// +// DRM_DebugChannel::RemoveProcess +// +TBool DRM_DebugChannel::RemoveProcess(TAny* a1, TAny* a2) + { + LOG_MSG("DRM_DebugChannel::RemoveProcess()"); + + DProcess *aProcess = (DProcess*)a1; + + // Sanity check + if (!aProcess) + { + // No process was specified! + LOG_MSG("DRM_DebugChannel::RemoveProcess was called with an invalid process ID"); + return EFalse; + } + + // this is called when a process dies. we want to mark any breakpoints in this + // process space as obsolete. the main reason for this is so we don't return + // an error when the host debugger tries to clear breakpoints for the process + + TUint32 codeAddress = 0; + TUint32 codeSize = 0; + + LOG_EVENT_MSG2("Process being removed, Name %S", aProcess->iName); + + DCodeSeg* codeSeg = aProcess->iCodeSeg; + + if (codeSeg) + { + TModuleMemoryInfo processMemoryInfo; + TInt err = codeSeg->GetMemoryInfo(processMemoryInfo, aProcess); + if (err != KErrNone) + { + codeAddress = processMemoryInfo.iCodeBase; + codeSize = processMemoryInfo.iCodeSize; + } + else + { + LOG_MSG2("Error in getting memory info: %d", err); + } + } + + if (!codeAddress || !codeSize) + { + LOG_EVENT_MSG2("Code segment not available for process %d", aProcess->iId); + // make sure there is not already a breakpoint at this address + for (TInt i = 0; i < iDebugProcessList.Count(); i++) + { + if (iDebugProcessList[i].iId == aProcess->iId) + { + codeAddress = iDebugProcessList[i].iCodeAddress; + codeSize = iDebugProcessList[i].iCodeSize; + + //now remove from the list + iDebugProcessList.Remove(i); + break; + } + } + } + + if (!codeAddress || !codeSize) + { + return EFalse; + } + + iBreakManager->RemoveBreaksForProcess(aProcess->iId, codeAddress, codeSize); + return EFalse; + } + +// +// DRM_DebugChannel::StartThread +// +TBool DRM_DebugChannel::StartThread(TAny* a1, TAny* a2) + { + LOG_EVENT_MSG("DRM_DebugChannel::StartThread()"); + + DThread *aThread = (DThread*)a1; + if(!aThread) + { + LOG_MSG("Error getting DThread object"); + __NK_ASSERT_DEBUG(aThread); + return EFalse; + } + + //a2 points to the thread creating the new thread. + //We have no use for it at the moment so just ignore it for now + + TDriverEventInfo info; + info.iEventType = EEventsStartThread; + info.iThreadId = aThread->iId; + info.iThreadIdValid = ETrue; + DProcess* owningProcess = aThread->iOwningProcess; + if(owningProcess) + { + info.iProcessId = owningProcess->iId; + info.iProcessIdValid = ETrue; + DCodeSeg* p = owningProcess->iCodeSeg; + if(p && p->iFileName) + { + info.iFileName.Copy(*(p->iFileName)); + TheDProcessTracker.NotifyAgentsForProcessEvent(*p->iFileName, info); + } + else + { + if(p) + { + LOG_EVENT_MSG("\tCode segment name missing"); + } + else + { + LOG_EVENT_MSG("\tCode segment is NULL"); + } + } + } + return EFalse; + } + +// +// DRM_DebugChannel::HandleAddProcessEvent +// +TBool DRM_DebugChannel::HandleAddProcessEvent(TAny* a1, TAny* a2) + { + LOG_EVENT_MSG("DRM_DebugChannel::AddProcess()"); + + DProcess *aProcess = (DProcess*)a1; + // a2 points to the thread creating the new process. + DThread *aThread = (DThread*)a2; + + if(!aProcess) + { + LOG_MSG("Error getting DProcess object"); + __NK_ASSERT_DEBUG(aProcess); + return EFalse; + } + + TDriverEventInfo info; + info.iEventType = EEventsAddProcess; + info.iProcessId = aProcess->iId; + + info.iCreatorThreadId = aThread ? aThread->iId : 0; + info.iProcessIdValid = ETrue; + + // Copy TUids + info.iUids = aProcess->iUids; + + info.iUidsValid = ETrue; + + // copy name of the process + if (aProcess->iName) + { + // copy the name of the process + info.iFileName.Copy(*aProcess->iName); + // AddProcess event does not have fully-qualified path, it has "filename.exe" + // So we allow a less-precise match by passing in ETrue + TheDProcessTracker.NotifyAgentsForProcessEvent(*aProcess->iName, info, ETrue); + } + else + { + LOG_EVENT_MSG("DRM_DebugChannel::AddProcess - No iName for this process"); + } + + return EFalse; + } + +// +// DRM_DebugChannel::HandleRemoveProcessEvent +// +TBool DRM_DebugChannel::HandleRemoveProcessEvent(TAny* a1, TAny* a2) + { + LOG_MSG("DRM_DebugChannel::HandleRemoveProcessEvent()"); + + DProcess *aProcess = (DProcess*)a1; + if(!aProcess) + { + LOG_MSG("Error getting DProcess object"); + __NK_ASSERT_DEBUG(aProcess); + return EFalse; + } + + // a2 points to the thread creating the new process. + // We have no use for it at the moment so just ignore it for now + // Also, it may not be known and therefore NULL + + TDriverEventInfo info; + info.iEventType = EEventsRemoveProcess; + info.iProcessId = aProcess->iId; + info.iProcessIdValid = ETrue; + + // copy name of the process + if (aProcess->iName) + { + info.iFileName.Copy(*aProcess->iName); + + // RemoveProcess event does not have fully-qualified path, it has "filename.exe" + // So we allow a less-precise match by passing in ETrue + TheDProcessTracker.NotifyAgentsForProcessEvent(*aProcess->iName, info, ETrue); + } + else + { + LOG_EVENT_MSG("DRM_DebugChannel::AddProcess - No iName for this process"); + } + + return EFalse; + } + +// +// DRM_DebugChannel::AddLibrary +// +TBool DRM_DebugChannel::AddLibrary(TAny* a1, TAny* a2) + { + LOG_EVENT_MSG("DRM_DebugChannel::AddLibrary()"); + + DLibrary *aLibrary = (DLibrary*)a1; + DThread *aThread = (DThread*)a2; + + // sanity check + if (!aLibrary) + { + LOG_EVENT_MSG("DRM_DebugChannel::AddLibrary called with no library specified"); + return EFalse; + } + + if (!aThread) + { + LOG_EVENT_MSG("DRM_DebugChannel::AddLibrary called with no thread specified"); + return EFalse; + } + +#ifdef __LOG_EVENTS__ + TFullName threadName; + aThread->FullName(threadName); + LOG_EVENT_MSG3(("Lib %S loaded by %S"), aLibrary->iName, &threadName); +#endif + + if (aThread) + { + // make sure this is not the debugger thread + if ((aThread != iClientThread) && (aThread->iOwningProcess->iId != iClientThread->iOwningProcess->iId)) + { + TDriverEventInfo info; + + info.iEventType = EEventsAddLibrary; + info.iProcessId = aThread->iOwningProcess->iId; + info.iProcessIdValid = ETrue; + info.iThreadId = aThread->iId; + info.iThreadIdValid = ETrue; + + //get the code address + DCodeSeg* codeSeg = aLibrary->iCodeSeg; + if (!codeSeg) + { + LOG_EVENT_MSG2("Code segment not available for library %S", aLibrary->iName); + return EFalse; + } + + // Uid3 + info.iUids = codeSeg->iUids; + info.iUidsValid = ETrue; + + TModuleMemoryInfo memoryInfo; + TInt err = codeSeg->GetMemoryInfo(memoryInfo, NULL); //NULL for DProcess should be ok; + if (err != KErrNone) + { + LOG_EVENT_MSG2("Error in getting memory info: %d", err); + return EFalse; + } + + info.iCodeAddress = memoryInfo.iCodeBase; + info.iDataAddress = memoryInfo.iInitialisedDataBase; + + info.iFileName.Copy(*(aLibrary->iName)); //just the name, without uid info. + + //queue up or complete the event + info.iArg1 = a1; + info.iArg2 = a2; + NotifyAgentsFromEventPid(info); + } + + } + return EFalse; + } + +// +// DRM_DebugChannel::RemoveLibrary +// +TBool DRM_DebugChannel::RemoveLibrary(TAny* a1, TAny* a2) + { + LOG_EVENT_MSG("DRM_DebugChannel::RemoveLibrary()"); + DLibrary *aLibrary = (DLibrary*)a1; + + // sanity check + if (!aLibrary) + { + LOG_EVENT_MSG("DRM_DebugChannel::RemoveLibrary called with no library specified"); + return EFalse; + } + + LOG_EVENT_MSG2(("Lib unloaded: %S"), aLibrary->iName); + + // this is called when all handles to this library have been closed. this can happen when a process dies, or when a dll is + // unloaded while the process lives on. in former case, we don't need to notify the host debugger because that process is + // dying anyway. for the latter case, we do need to notify the host so it can unload the symbolics, etc. + + DThread* aThread = &Kern::CurrentThread(); + + if ((aThread) && + (aThread != iClientThread) && + (aThread->iOwningProcess->iId != iClientThread->iOwningProcess->iId)) + { + //the library gets unloaded only when the mapcount is 0. + if (aLibrary->iMapCount != 0) + return EFalse; + + DCodeSeg* codeSeg = aLibrary->iCodeSeg; + if (!codeSeg) + { + LOG_EVENT_MSG2("Code segment not available for library %S", aLibrary->iName); + return EFalse; + } + + TModuleMemoryInfo processMemoryInfo; + TInt err = codeSeg->GetMemoryInfo(processMemoryInfo, NULL); //passing NULL for the DProcess argument should be ok; + if (err != KErrNone) + { + LOG_EVENT_MSG2("Error in getting memory info: %d", err); + return EFalse; + } + + TUint32 codeAddress = processMemoryInfo.iCodeBase; + TUint32 codeSize = processMemoryInfo.iCodeSize; + + // first invalidate all breakpoints that were set in the library code + iBreakManager->InvalidateLibraryBreakPoints(codeAddress, codeSize); + DProcess *process = &Kern::CurrentProcess(); + RArray* dynamicCode = &(process->iDynamicCode); + + for (TInt j=0; jCount(); j++) + { + if ((*dynamicCode)[j].iLib == aLibrary) + { + TDriverEventInfo info; + + info.iEventType = EEventsRemoveLibrary; + info.iFileName.Copy(*(aLibrary->iName)); //lib name without uid info + //info.iFileName.ZeroTerminate(); + info.iProcessId = process->iId; + info.iProcessIdValid = ETrue; + info.iThreadId = 0xFFFFFFFF; // don't care! + info.iThreadIdValid = EFalse; + // Uid3 + info.iUids = codeSeg->iUids; + info.iUidsValid = ETrue; + + //queue up or complete the event + info.iArg1 = a1; + info.iArg2 = a2; + NotifyAgentsFromEventPid(info); + } + } + } + return EFalse; + } + +// +// DRM_DebugChannel::HandleEventKillThread +// +TBool DRM_DebugChannel::HandleEventKillThread(TAny* a1, TAny* a2) + { + DThread* currentThread = &Kern::CurrentThread(); + + // a1 should point to the current thread, check this to make sure it does + __NK_ASSERT_DEBUG((DThread*)a1 == currentThread); + + TDriverEventInfo info; + + LOG_MSG5(" HandleEventKillThread for thread 0x%x, CritScount=%d, suspCnt=%d, waitObj=0x%x", + currentThread->iId, + currentThread->iNThread.iCsCount, + currentThread->iNThread.iSuspendCount, + currentThread->iWaitObj); + + info.iProcessId = currentThread->iOwningProcess->iId; + info.iProcessIdValid = ETrue; + info.iThreadId = currentThread->iId; + info.iThreadIdValid = ETrue; + + TInt err = ReadKernelRegisterValue(currentThread, 14, info.iCurrentPC); + if(err != KErrNone) + { + LOG_EVENT_MSG2("DRM_DebugChannel::HandleEventKillThread - Non-zero error code discarded: %d", err); + } + + LOG_MSG5(" HandleEventKillThread for thread exit category=%S reason=%d, exitType=0x%x, PC=0x%x", + ¤tThread->iExitCategory, + currentThread->iExitReason, + currentThread->iExitType, + info.iCurrentPC); + + if (currentThread->iExitType == EExitPanic) + { + info.iPanicCategory.Copy(currentThread->iExitCategory); + } + info.iExceptionNumber = currentThread->iExitReason; + info.iExitType = currentThread->iExitType; + info.iEventType = EEventsKillThread; + info.iThreadFlags = currentThread->iFlags; + + // remove all the breakpoints in this thread, whether we are debugging it or not. + iBreakManager->DoRemoveThreadBreaks(info.iThreadId); + + info.iArg1 = a1; + info.iArg2 = a2; + NotifyAgentsFromEventPid(info); + + return ETrue; + } + +// +// DRM_DebugChannel::HandleSwException +// +TBool DRM_DebugChannel::HandleSwException(TAny* a1, TAny* a2) + { + LOG_EVENT_MSG("DRM_DebugChannel::HandleSwException"); + TExcType aExcType = (TExcType)(TInt)a1; + + TDriverEventInfo info; + + DThread* currentThread = &Kern::CurrentThread(); + if (!currentThread) + { + LOG_MSG("Error getting current thread"); + __NK_ASSERT_DEBUG(currentThread); + return EFalse; + } + + info.iProcessId = currentThread->iOwningProcess->iId; + info.iProcessIdValid = ETrue; + info.iThreadId = currentThread->iId; + info.iThreadIdValid = ETrue; + TInt err = ReadKernelRegisterValue(currentThread, PC_REGISTER, info.iCurrentPC); + if(err != KErrNone) + { + LOG_EVENT_MSG2("DRM_DebugChannel::HandleSwException - Non-zero error code discarded: %d", err); + } + info.iExceptionNumber = aExcType; + info.iEventType = EEventsSwExc; + info.iThreadFlags = currentThread->iFlags; + info.iArg1 = a1; + info.iArg2 = a2; + + NotifyAgentsFromEventPid(info); + + return EFalse; + } + +// +// DRM_DebugChannel::HandleHwException +// +TBool DRM_DebugChannel::HandleHwException(TAny* a1, TAny* a2) + { + TArmExcInfo* aExcInfo = (TArmExcInfo*)a1; + + // sanity check + if (!aExcInfo) + { + LOG_MSG("DRM_DebugChannel::HandleHwException called with no aExcInfo"); + __NK_ASSERT_DEBUG(aExcInfo); + return EFalse; + } + + TDriverEventInfo info; + + DThread* currentThread = &Kern::CurrentThread(); + + if (!currentThread) + { + LOG_MSG("Error getting current thread"); + __NK_ASSERT_DEBUG(currentThread); + return EFalse; + } + + info.iProcessId = currentThread->iOwningProcess->iId; + info.iProcessIdValid = ETrue; + info.iThreadId = currentThread->iId; + info.iThreadIdValid = ETrue; + info.iRmdArmExcInfo.iFaultAddress= aExcInfo->iFaultAddress; + info.iRmdArmExcInfo.iFaultStatus= aExcInfo->iFaultStatus; + + LOG_MSG5("DRM_DebugChannel::HandleHwException current thread = 0x%08x, CritSect count=%d,\n" + " iFaultAddress=0x%08x, iFaultStatus=0x%08x", + currentThread, currentThread->iNThread.iCsCount, aExcInfo->iFaultAddress, aExcInfo->iFaultStatus); + + + LOG_MSG3(" HandleHwException CsFunc=%d, suspCount=%d", + currentThread->iNThread.iCsFunction, currentThread->iNThread.iSuspendCount ); + + info.iRmdArmExcInfo.iR0= aExcInfo->iR0; + info.iRmdArmExcInfo.iR1= aExcInfo->iR1; + info.iRmdArmExcInfo.iR2= aExcInfo->iR2; + info.iRmdArmExcInfo.iR3= aExcInfo->iR3; + + info.iRmdArmExcInfo.iR4= aExcInfo->iR4; + info.iRmdArmExcInfo.iR5= aExcInfo->iR5; + info.iRmdArmExcInfo.iR6= aExcInfo->iR6; + info.iRmdArmExcInfo.iR7= aExcInfo->iR7; + info.iRmdArmExcInfo.iR8= aExcInfo->iR8; + info.iRmdArmExcInfo.iR9= aExcInfo->iR9; + info.iRmdArmExcInfo.iR10= aExcInfo->iR10; + info.iRmdArmExcInfo.iR11= aExcInfo->iR11; + info.iRmdArmExcInfo.iR12= aExcInfo->iR12; + + info.iRmdArmExcInfo.iR13= aExcInfo->iR13; + info.iRmdArmExcInfo.iR14= aExcInfo->iR14; + info.iRmdArmExcInfo.iR15= aExcInfo->iR15; + + info.iRmdArmExcInfo.iCpsr= aExcInfo->iCpsr; + info.iRmdArmExcInfo.iR13Svc= aExcInfo->iR13Svc; + info.iRmdArmExcInfo.iR14Svc= aExcInfo->iR14Svc; + info.iRmdArmExcInfo.iSpsrSvc= aExcInfo->iSpsrSvc; + LOG_MSG5(" iCpsr=0x%x, iExcCode=0x%x, R14=0x%x, R15=0x%x", + aExcInfo->iCpsr, aExcInfo->iExcCode, aExcInfo->iR14, aExcInfo->iR15); + + switch (aExcInfo->iExcCode) + { + case 0: + info.iExceptionNumber = EExcCodeAbort; + LOG_MSG(" iExcCode == 0 => EExcCodeAbort"); + break; + case 1: + info.iExceptionNumber = EExcDataAbort; + LOG_MSG(" iExcCode == 1 => EExcDataAbort"); + break; + case 2: + info.iExceptionNumber = EExcInvalidOpCode; + LOG_MSG(" iExcCode == 2 => EExcInvalidOpCode"); + break; + default: + // new event? Something gone wrong? + __NK_ASSERT_DEBUG(EFalse); + return EFalse; + } + + info.iEventType = EEventsHwExc; + info.iThreadFlags = currentThread->iFlags; + + info.iArg1 = a1; + info.iArg2 = a2; + + if(EExcInvalidOpCode == info.iExceptionNumber) + { + return HandleInvalidOpCodeException(info, currentThread); + } + + NotifyAgentsFromEventPid(info); + return EFalse; + } + +// +// DRM_DebugChannel::HandUserTrace +// +TBool DRM_DebugChannel::HandleUserTrace(TAny* a1, TAny* a2) + { + LOG_EVENT_MSG("DRM_DebugChannel::HandleUserTrace()"); + + DThread* currentThread = &Kern::CurrentThread(); + if (!currentThread) + { + LOG_EVENT_MSG("Error getting current thread"); + __NK_ASSERT_DEBUG(currentThread); + return EFalse; + } + + TDriverEventInfo info; + info.iProcessId = currentThread->iOwningProcess->iId; + info.iProcessIdValid = ETrue; + info.iThreadId = currentThread->iId; + info.iThreadIdValid = ETrue; + info.iEventType = EEventsUserTrace; + info.iArg1 = a1; + info.iArg2 = a2; + + TInt err = KErrNone; + + //User Trace info + XTRAP(err, XT_DEFAULT, kumemget(info.iUserTraceText, info.iArg1, (TInt)a2)); + if(KErrNone != err) + { + return EFalse; + } + + info.iMessageStatus = ESingleMessage; + + NotifyAgentsFromEventPid(info); + + return EFalse; + } + +// +// DRM_DebugChannel::HandleException +// +TBool DRM_DebugChannel::HandleInvalidOpCodeException(TDriverEventInfo& aEventInfo, DThread* aCurrentThread) + { + LOG_MSG("DRM_DebugChannel::HandleInvalidOpCodeException()"); + + TInt err = KErrNone; + + TUint32 inst = KArmBreakPoint; + TInt instSize = 4; + + // change these for thumb mode + TUint32 regValue; + err = ReadKernelRegisterValue(aCurrentThread, STATUS_REGISTER, regValue); + if(err != KErrNone) + { + LOG_MSG2("DRM_DebugChannel::HandleInvalidOpCodeException - Non-zero error code discarded: %d", err); + } + + if (regValue & ECpuThumb) + { + inst = KThumbBreakPoint; + instSize = 2; + } + + TUint32 instruction = 0; + err = Kern::ThreadRawRead(aCurrentThread, (TUint32 *)aEventInfo.iRmdArmExcInfo.iR15, (TUint8 *)&instruction, instSize); + + if (KErrNone != err) + LOG_MSG2("Error reading instruction at currentpc: %d", err); + + if (!memcompare((TUint8 *)&inst, instSize, (TUint8 *)&instruction, instSize)) + { + TInt err = DoSuspendThread(aCurrentThread); + if(! ((KErrNone == err) || (KErrAlreadyExists == err)) ) + { + LOG_MSG2("DRM_DebugChannel::HandleInvalidOpCodeException() Thread with id 0x%08x could not be suspended.", aCurrentThread->iId); + return EFalse; + } + + // the exception was a breakpoint instruction. see if we have a breakpoint at that address + TBreakEntry* breakEntry = NULL; + do + { + breakEntry = iBreakManager->GetNextBreak(breakEntry); + if (breakEntry && ((breakEntry->iThreadSpecific && breakEntry->iId == aEventInfo.iThreadId) || (!breakEntry->iThreadSpecific && breakEntry->iId == aEventInfo.iProcessId)) && breakEntry->iAddress == aEventInfo.iRmdArmExcInfo.iR15) + { + LOG_MSG2("Breakpoint with Id %d has been hit", breakEntry->iBreakId); + + TBreakEntry tempBreakEntry = *breakEntry; + + //change the event type to breakpoint type + aEventInfo.iEventType = breakEntry->iThreadSpecific ? EEventsBreakPoint : EEventsProcessBreakPoint; + + // enable any breakpoints we had to disable for this thread + err = iBreakManager->DoEnableDisabledBreak(aEventInfo.iThreadId); + if (KErrNone != err) + LOG_MSG2("Error %d enabling disabled breakpoints", err); + + // see if this is a temp breakpoint + if (iBreakManager->IsTemporaryBreak(*breakEntry)) + { + // this was a temp breakpoint, so we need to clear it now + err = iBreakManager->DoClearBreak(breakEntry->iBreakId); + if (KErrNone != err) + LOG_MSG2("Error %d clearing temp breakpoint", err); + + // Find out how many steps remain to be done + + // reduce the number of steps to complete by 1 + tempBreakEntry.iNumSteps--; + + LOG_MSG2("There are %d steps remaining\n", tempBreakEntry.iNumSteps); + + // New. If we have not finished do all the steps, continue stepping and don't notify event + if (tempBreakEntry.iNumSteps) + { + LOG_MSG("Continuing stepping...not telling the agent yet\n"); + err = DoStepRange(aCurrentThread, aEventInfo.iRmdArmExcInfo.iR15, aEventInfo.iRmdArmExcInfo.iR15, ETrue, tempBreakEntry.iResumeOnceOutOfRange /*EFalse*/, tempBreakEntry.iNumSteps, ETrue); + if (err != KErrNone) + { + LOG_EVENT_MSG("Failed to continue stepping\n"); + + // what do we do? might as well stop here and tell the user + NotifyAgentsFromEventPid(aEventInfo); + + return ETrue; + } + + // continue as though no event occured. No need to suspend/resume anything... + LOG_MSG("Continuing to step\n"); + return ETrue; + } + + // Is this a case where we just want to continue? + if (tempBreakEntry.iResumeOnceOutOfRange) + { + LOG_MSG("PC is out of range, continuing thread"); + DoResumeThread(aCurrentThread); + + return ETrue; + } + } + + // if the breakpoint is thread specific, make sure it's the right thread + // if not, just continue the thread. take special care if it's the debugger + // thread. if it hits a regular breakpoint, we NEVER want to stop at it. if + // it hits a temp breakpoint, we're probably just stepping past a real breakpoint + // and we do need to handle it. + TBool needToResume = (tempBreakEntry.iThreadSpecific && tempBreakEntry.iId != aEventInfo.iThreadId) || + (!tempBreakEntry.iThreadSpecific && tempBreakEntry.iId != aEventInfo.iProcessId); + + if (needToResume) + { + LOG_MSG("breakpoint does not match threadId, calling DoResumeThread"); + err = DoResumeThread(aCurrentThread); + if (KErrNone != err) + LOG_MSG2("Error in DoResumeThread: %d", err); + + return EFalse; + } + + //normal user break point, just notify the event + break; + } + } while(breakEntry); + } + + NotifyAgentsFromEventPid(aEventInfo); + + return (aEventInfo.iEventType == EEventsBreakPoint) || (aEventInfo.iEventType == EEventsProcessBreakPoint); + } + +// +// DRM_DebugChannel::SetBreak +// +TInt DRM_DebugChannel::SetBreak(TSetBreakInfo* aBreakInfo) + { + LOG_MSG("DRM_DebugChannel::SetBreak()"); + + TInt err = KErrNone; + + if (!aBreakInfo) + { + LOG_MSG("DRM_DebugChannel::SetBreak() was passed a NULL argument"); + return KErrArgument; + } + + //User side memory is not accessible directly + TSetBreakInfo info; + err = Kern::ThreadRawRead(iClientThread, aBreakInfo, (TUint8*)&info, sizeof(TSetBreakInfo)); + if (err != KErrNone) + { + LOG_MSG("DRM_DebugChannel::SetBreak() was passed a bad argument"); + return err; + } + + DProcess* process = NULL; + NKern::ThreadEnterCS(); + if(info.iThreadSpecific) + { + DThread* thread = DebugUtils::OpenThreadHandle(info.iId); + if(!thread) + { + LOG_MSG2("DRM_DebugChannel::SetBreak() Thread with id 0x%08x not found", info.iId); + } + else + { + process = DebugUtils::OpenProcessHandle(thread->iOwningProcess->iId); + thread->Close(NULL); + } + } + else + { + process = DebugUtils::OpenProcessHandle(info.iId); + if(!process) + { + LOG_MSG2("DRM_DebugChannel::SetBreak() Process with id 0x%08x not found", info.iId); + } + } + + if (process == NULL) + { + NKern::ThreadLeaveCS(); + return KErrNotFound; + } + + TBool found = EFalse; + for(TInt i=0; iiId == iDebugProcessList[i].iId) + { + found = ETrue; + } + } + + if(!found) + { + DCodeSeg* codeSeg = process->iCodeSeg; + if (!codeSeg) + { + LOG_MSG2("DRM_DebugChannel::SetBreak() Code seg for process with id 0x%08x not found", process->iId); + err = KErrNotFound; + } + + TModuleMemoryInfo memoryInfo; + if (!err) + { + err = codeSeg->GetMemoryInfo(memoryInfo, process); + if (err != KErrNone) + { + LOG_MSG2("DRM_DebugChannel::SetBreak() Error getting memory info for process with id 0x%08x", process->iId); + } + } + + if (!err) + { + //add this process to the list of processes that we are debugging + TProcessInfo processInfo(process->iId, memoryInfo.iCodeBase, memoryInfo.iCodeSize, memoryInfo.iInitialisedDataBase); + iDebugProcessList.Append(processInfo); + } + } + + process->Close(NULL); + NKern::ThreadLeaveCS(); + + if (!info.iBreakId) //first check if the iId address is valid + return KErrArgument; + + if (err == KErrNone) + { + TInt32 iBreakId; + + err = iBreakManager->DoSetBreak(iBreakId, info.iId, info.iThreadSpecific, info.iAddress, info.iMode ); + + if (err == KErrNone) + { + err = Kern::ThreadRawWrite(iClientThread, (TUint8 *)info.iBreakId, &iBreakId, sizeof(TInt32), iClientThread); + } + } + + return err; + } + +// +// DRM_DebugChannel::StepRange +// +TInt DRM_DebugChannel::StepRange(DThread* aThread, TRM_DebugStepInfo* aStepInfo) + { + LOG_MSG("DRM_DebugChannel::StepRange()"); + + TInt err = KErrNone; + + if (!aStepInfo) + return KErrArgument; + + TRM_DebugStepInfo info(0, 0, 0); + err = Kern::ThreadRawRead(iClientThread, aStepInfo, (TUint8*)&info, sizeof(TRM_DebugStepInfo)); + + if (err != KErrNone) + return err; + + err = DoStepRange(aThread, info.iStartAddress, info.iStopAddress, info.iStepInto, EFalse, ETrue); + + return err; + } + +/** +Read memory from a target thread and return the data to the client. If the +memory block has breakpoints in it then the correct values are placed in the +returned data + +@param aThread pointer to thread whose memory space the memory is to be read from +@param aMemoryInfo information about what memory to read + +@return KErrNone if memory read successfully, + KErrArgument if aMemoryInfo is not initialised correctly, + KErrNoMemory if a temporary buffer could not be allocated, + KErrBadHandle if aThread is invalid, + or another of the system wide error codes +*/ +TInt DRM_DebugChannel::ReadMemory(DThread* aThread, TRM_DebugMemoryInfo* aMemoryInfo) + { + LOG_MSG("DRM_DebugChannel::ReadMemory()"); + + TInt err = KErrNone; + + if (!aMemoryInfo) + return KErrArgument; + + TRM_DebugMemoryInfo info(0, 0, 0); + err = Kern::ThreadRawRead(iClientThread, aMemoryInfo, (TUint8*)&info, sizeof(TRM_DebugMemoryInfo)); + if (err != KErrNone) + { + LOG_MSG2("DRM_DebugChannel::ReadMemory returning error %d after Kern::ThreadRawRead()", err); + return err; + } + + if (!info.iData) + return KErrArgument; + + NKern::ThreadEnterCS(); + TUint8 *data = (TUint8*)Kern::Alloc(info.iLength); + NKern::ThreadLeaveCS(); + if (!data) + { + return KErrNoMemory; + } + + TPtr8 dataDes(data, info.iLength); + + err = DoReadMemory(aThread, info.iAddress, info.iLength, dataDes); + if (err == KErrNone) + { + err = Kern::ThreadDesWrite(iClientThread, info.iData, dataDes, 0, KChunkShiftBy0, iClientThread); + if (err) + { + LOG_MSG2("DRM_DebugChannel::ReadMemory - Kern::ThreadDesWrite() returned error %d", err); + } + } + + NKern::ThreadEnterCS(); + Kern::Free(data); + NKern::ThreadLeaveCS(); + + return err; + } + +/** +Attempt to write memory to aThread's address space + +@param aThread thread to whose address space memory is to be written +@param aMemoryInfo memory info object representing the data to write + +@return KErrNone if memory written successfully, + KErrNoMemory if memory could not be allocated + KErrArgument if aMemoryInfo is NULL, if aMemoryInfo.iData is NULL, + if aMemoryInfo.iLength is greater than than the length of the passed + in descrptor + KErrBadHandle if aThread is invalid, + or another of the system wide error codes +*/ +TInt DRM_DebugChannel::WriteMemory(DThread* aThread, TRM_DebugMemoryInfo* aMemoryInfo) + { + LOG_MSG("DRM_DebugChannel::WriteMemory()"); + + TInt err = KErrNone; + + if (!aMemoryInfo) + return KErrArgument; + + TRM_DebugMemoryInfo info(0, 0, 0); + err = Kern::ThreadRawRead(iClientThread, aMemoryInfo, (TUint8*)&info, sizeof(TRM_DebugMemoryInfo)); + if (err != KErrNone) + return err; + + if (!info.iData) + return KErrArgument; + + NKern::ThreadEnterCS(); + TUint8 *data = (TUint8*)Kern::Alloc(info.iLength); + NKern::ThreadLeaveCS(); + if (!data) + { + return KErrNoMemory; + } + + TPtr8 dataDes(data, info.iLength); + + err = Kern::ThreadDesRead(iClientThread, info.iData, dataDes, 0); + if (err == KErrNone) + { + err = DoWriteMemory(aThread, info.iAddress, info.iLength, dataDes); + } + + NKern::ThreadEnterCS(); + Kern::Free(data); + NKern::ThreadLeaveCS(); + + return err; + } + +// +// DRM_DebugChannel::ReadRegisters +// +TInt DRM_DebugChannel::ReadRegistersLegacy(DThread* aThread, TRM_DebugRegisterInfo* aRegisterInfo) + { + LOG_MSG("DRM_DebugChannel::ReadRegistersLegacy()"); + + TInt err = KErrNone; + + if (!aRegisterInfo) + return KErrArgument; + + TRM_DebugRegisterInfo info(0, 0, 0); + err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInfo)); + if (err != KErrNone) + return err; + + if (!info.iValues) + return KErrArgument; + + TUint length = (info.iLastRegister - info.iFirstRegister + 1) * 4; + + NKern::ThreadEnterCS(); + TUint8 *values = (TUint8*)Kern::Alloc(length); + NKern::ThreadLeaveCS(); + if (!values) + { + return KErrNoMemory; + } + + TPtr8 valuesDes(values, length); + + err = DoReadRegisters(aThread, info.iFirstRegister, info.iLastRegister, valuesDes); + if (err == KErrNone) + { + err = Kern::ThreadDesWrite(iClientThread, info.iValues, valuesDes, 0, KChunkShiftBy0, iClientThread); + } + + NKern::ThreadEnterCS(); + Kern::Free(values); + NKern::ThreadLeaveCS(); + + return err; + } + +/** +Get listing information. + +@param aListInformation pointer to a TListInformation object containing the + user specified listings information + +@return KErrNone on success, + KErrTooBig if the kernel's data is too big to fit in the passed buffer, + KErrArgument if aListInformation is NULL, + or one of the other system-wide error codes +*/ +TInt DRM_DebugChannel::GetList(TListInformation* aListInformation) const + { + LOG_MSG("DRM_DebugChannel::GetList()"); + + TInt err = KErrNone; + + if(aListInformation == NULL) + { + return KErrArgument; + } + + //read DSS' data into local structure + TListInformation info; + err = Kern::ThreadRawRead(iClientThread, aListInformation, (TUint8*)&info, sizeof(TListInformation)); + if(err != KErrNone) + { + return err; + } + + //check arguments + TPtr8 buffer(NULL, 0); + err = AllocAndReadDes(iClientThread, *info.iBuffer, buffer); + if(err != KErrNone) + { + //need to free the buffer if it was allocated + if(err != KErrNoMemory) + { + NKern::ThreadEnterCS(); + Kern::Free((TAny*)buffer.Ptr()); + NKern::ThreadLeaveCS(); + } + return err; + } + + //get the list + TUint32 dataSize = 0; + TListManager manager; + err = KErrArgument; + switch(info.iType) + { + case EXipLibraries: + if(Debug::EScopeGlobal == info.iListScope) + { + err = manager.GetXipLibrariesList(buffer, dataSize); + } + break; + + case EThreads: + if(Debug::EScopeGlobal == info.iListScope) + { + err = manager.GetGlobalThreadList(buffer, dataSize); + } + else if(Debug::EScopeProcessSpecific == info.iListScope) + { + err = manager.GetThreadListForProcess(buffer, dataSize, info.iTargetId); + } + else if(Debug::EScopeThreadSpecific == info.iListScope) + { + err = manager.GetThreadListForThread(buffer, dataSize, info.iTargetId); + } + break; + + case EProcesses: + if(Debug::EScopeGlobal == info.iListScope) + { + err = manager.GetProcessList(buffer, dataSize); + } + break; + + case ECodeSegs: + if(Debug::EScopeGlobal == info.iListScope) + { + err = manager.GetGlobalCodeSegList(buffer, dataSize); + } + else if(Debug::EScopeProcessSpecific == info.iListScope) + { + err = manager.GetCodeSegListForProcess(buffer, dataSize, info.iTargetId); + } + else if(Debug::EScopeThreadSpecific == info.iListScope) + { + err = manager.GetCodeSegListForThread(buffer, dataSize, info.iTargetId); + } + break; + + default: + err = KErrNotSupported; + } + + if(err == KErrNone) + { + //if no error then write the buffer back + err = Kern::ThreadDesWrite(iClientThread, info.iBuffer, buffer, 0, KChunkShiftBy0, iClientThread); + } + + //write back the size of the data regardless of any error + TInt writeErr = Kern::ThreadRawWrite(iClientThread, info.iDataSize, (TUint8*)&dataSize, sizeof(TUint32), iClientThread); + if(writeErr != KErrNone) + { + //if there was an error writing the size return that error instead + err = writeErr; + } + + //free the buffer + NKern::ThreadEnterCS(); + Kern::Free((TAny*)buffer.Ptr()); + NKern::ThreadLeaveCS(); + + return err; + } + +/** +Read registers and store register data in aRegisterInfo + +@param aThread thread to read registers from +@param aRegisterInfo structure specifying which registers to read and providing + descriptors to write the register data into + +@return KErrNone if registers were read successfully. Note that this does not + mean that all the registers could be read, the + aRegisterInfo.iRegisterFlags array should be checked as to whether each + individual register could be read, + KErrArgument if aRegisterInfo is NULL, or if any of the pointers that + are members of aRegisterInfo are NULL, if an unknown register is + specified or if the passed in register values buffer is too small + KErrNoMemory if there is insufficient memory, + KErrDied, if the thread with thread ID aThreadId is dead +*/ +TInt DRM_DebugChannel::ReadRegisters(DThread* aThread, TRM_DebugRegisterInformation* aRegisterInfo) const + { + LOG_MSG("DRM_DebugChannel::ReadRegisters()"); + + TInt err = KErrNone; + + if (!aRegisterInfo) + return KErrArgument; + + TRM_DebugRegisterInformation info; + err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInformation)); + if (err != KErrNone) + return err; + + if ((!info.iRegisterIds) || (!info.iRegisterValues) || (!info.iRegisterFlags)) + return KErrArgument; + + //read ids from client thread + TPtr8 ids(NULL, 0); + err = AllocAndReadDes(iClientThread, *info.iRegisterIds, ids); + if(err != KErrNone) + { + if(err == KErrNoMemory) + { + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + NKern::ThreadLeaveCS(); + } + return err; + } + + //read values from client thread + TPtr8 values(NULL, 0); + err = AllocAndReadDes(iClientThread, *info.iRegisterValues, values, EFalse); + if(err != KErrNone) + { + if(err == KErrNoMemory) + { + NKern::ThreadEnterCS(); + Kern::Free((TAny*)values.Ptr()); + NKern::ThreadLeaveCS(); + } + + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + NKern::ThreadLeaveCS(); + return err; + } + + //read flags from client thread + TPtr8 flags(NULL, 0); + err = AllocAndReadDes(iClientThread, *info.iRegisterFlags, flags, EFalse); + if(err != KErrNone) + { + if(err == KErrNoMemory) + { + NKern::ThreadEnterCS(); + Kern::Free((TAny*)flags.Ptr()); + NKern::ThreadLeaveCS(); + } + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + Kern::Free((TAny*)values.Ptr()); + NKern::ThreadLeaveCS(); + return err; + } + + err = DoReadRegisters(aThread, ids, values, flags); + if (err == KErrNone) + { + err = Kern::ThreadDesWrite(iClientThread, info.iRegisterValues, values, 0, KChunkShiftBy0, iClientThread); + if(err == KErrNone) + { + err = Kern::ThreadDesWrite(iClientThread, info.iRegisterFlags, flags, 0, KChunkShiftBy0, iClientThread); + } + } + + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + Kern::Free((TAny*)values.Ptr()); + Kern::Free((TAny*)flags.Ptr()); + NKern::ThreadLeaveCS(); + + return err; + } + +/** +@deprecated use DRM_DebugChannel::WriteRegisters(DThread* aThread, TRM_DebugRegisterInformation* aRegisterInfo) instead +*/ +TInt DRM_DebugChannel::WriteRegistersLegacy(DThread* aThread, const TRM_DebugRegisterInfo* aRegisterInfo) + { + LOG_MSG("DRM_DebugChannel::WriteRegistersLegacy()"); + + TInt err = KErrNone; + + if (!aRegisterInfo) + return KErrArgument; + + TRM_DebugRegisterInfo info(0, 0, 0); + err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInfo)); + if (err != KErrNone) + return err; + + if (!info.iValues) + return KErrArgument; + + TUint length = (info.iLastRegister - info.iFirstRegister + 1) * 4; + + NKern::ThreadEnterCS(); + TUint8 *values = (TUint8*)Kern::Alloc(length); + NKern::ThreadLeaveCS(); + if (!values) + { + return KErrNoMemory; + } + + TPtr8 valuesDes(values, length); + + err = Kern::ThreadDesRead(iClientThread, info.iValues, valuesDes, 0); + if (err == KErrNone) + { + err = DoWriteRegisters(aThread, info.iFirstRegister, info.iLastRegister, valuesDes); + } + + NKern::ThreadEnterCS(); + Kern::Free(values); + NKern::ThreadLeaveCS(); + + return err; + } + +/** +Write registers and store flags data in aRegisterInfo + +@param aThread thread to write registers to +@param aRegisterInfo structure specifying which registers to write and providing + descriptors to write the register flags data into + +@return KErrNone if registers were written successfully. Note that this does not + mean that all the registers could be written, the flags array + should be checked as to whether each individual register could be read, + KErrArgument if aRegisterInfo is NULL, or if any of the pointers that + are members of aRegisterInfo are NULL, if an unknown register is + specified or if the passed in register values buffer is too small, or + if aThread is NULL, + KErrGeneral if there was a problem initialising the register set, + KErrNoMemory if there is insufficient memory, + KErrDied, if the thread with thread ID aThreadId is dead +*/ +TInt DRM_DebugChannel::WriteRegisters(DThread* aThread, TRM_DebugRegisterInformation* aRegisterInfo) const + { + LOG_MSG("DRM_DebugChannel::WriteRegisters()"); + + TInt err = KErrNone; + + if (!aRegisterInfo) + return KErrArgument; + + TRM_DebugRegisterInformation info; + err = Kern::ThreadRawRead(iClientThread, aRegisterInfo, (TUint8*)&info, sizeof(TRM_DebugRegisterInformation)); + if (err != KErrNone) + return err; + + if ((!info.iRegisterIds) || (!info.iRegisterValues) ||(!info.iRegisterFlags)) + return KErrArgument; + + //read ids from client thread + TPtr8 ids(NULL, 0); + err = AllocAndReadDes(iClientThread, *info.iRegisterIds, ids); + if(err != KErrNone) + { + if(err == KErrNoMemory) + { + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + NKern::ThreadLeaveCS(); + } + return err; + } + + //read values from client thread + TPtr8 values(NULL, 0); + err = AllocAndReadDes(iClientThread, *info.iRegisterValues, values); + if(err != KErrNone) + { + if(err == KErrNoMemory) + { + NKern::ThreadEnterCS(); + Kern::Free((TAny*)values.Ptr()); + NKern::ThreadLeaveCS(); + } + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + NKern::ThreadLeaveCS(); + return err; + } + + //read flags from client thread + TPtr8 flags(NULL, 0); + err = AllocAndReadDes(iClientThread, *info.iRegisterFlags, flags, EFalse); + if(err != KErrNone) + { + if(err == KErrNoMemory) + { + NKern::ThreadEnterCS(); + Kern::Free((TAny*)flags.Ptr()); + NKern::ThreadLeaveCS(); + } + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + Kern::Free((TAny*)values.Ptr()); + NKern::ThreadLeaveCS(); + return err; + } + + err = DoWriteRegisters(aThread, ids, values, flags); + if(err == KErrNone) + { + err = Kern::ThreadDesWrite(iClientThread, info.iRegisterFlags, flags, 0, KChunkShiftBy0, iClientThread); + } + + NKern::ThreadEnterCS(); + Kern::Free((TAny*)ids.Ptr()); + Kern::Free((TAny*)values.Ptr()); + Kern::Free((TAny*)flags.Ptr()); + NKern::ThreadLeaveCS(); + + return err; + } + +/** +Suspends execution of the specified thread. + +@param aThread thread to resume + +@return KErrNone if there were no problems or KErrArgument if aThread is NULL +*/ +TInt DRM_DebugChannel::DoSuspendThread(DThread *aThread) + { + LOG_MSG("DRM_DebugChannel::DoSuspendThread()"); + + if (!aThread) + { + LOG_MSG("Invalid dthread object"); + return KErrArgument; + } + + return TheDProcessTracker.SuspendThread(aThread); + } + +/** +Resumes execution of the specified thread. + +@param aThread thread to resume + +@return KErrNone if there were no problems, KErrArgument if aThread is NULL + or an error value returned from DoStepRange() +*/ +TInt DRM_DebugChannel::DoResumeThread(DThread *aThread) + { + if (!aThread) + return KErrArgument; + + // get the current PC + TUint32 currentPC; + TInt err = ReadKernelRegisterValue(aThread, PC_REGISTER, currentPC); + if(err != KErrNone) + { + LOG_MSG2("DRM_DebugChannel::DoResumeThread : Read PC reg error %d.", err); + // Set to this value because 0 is dangerous since structures are usually 0-initialised, + // and could thus lead to a false positive in tmp break loop below + currentPC = 0xFFFFFFFF; + } + else + { + LOG_MSG2("DRM_DebugChannel::DoResumeThread(), pc=0x%x", currentPC); + } + + // if there is a breakpoint at the current PC, we need to single step past it + TBreakEntry* breakEntry = NULL; + do + { + breakEntry = iBreakManager->GetNextBreak(breakEntry); + if(breakEntry && !iBreakManager->IsTemporaryBreak(*breakEntry)) + { + if (breakEntry->iAddress == currentPC) + { + LOG_MSG2("DRM_DebugChannel::DoResumeThread : > DoStepRange(pc+1)=0x%x, resume once out of range", currentPC+1 ); + return DoStepRange(aThread, currentPC, currentPC+1, ETrue, 1, ETrue); + } + } + } while(breakEntry); + + return TheDProcessTracker.ResumeThread(aThread); + } + +// +// DRM_DebugChannel::DoStepRange +// +TInt DRM_DebugChannel::DoStepRange(DThread *aThread, const TUint32 aStartAddress, const TUint32 aStopAddress, TBool aStepInto, TBool aResumeOnceOutOfRange, const TUint32 aNumSteps, TBool aUserRequest) + { + LOG_MSG("DRM_DebugChannel::DoStepRange()"); + + if (!aThread) + return KErrArgument; + + + TUint32 startAddress = (aStartAddress & 0x1) ? aStartAddress + 1 : aStartAddress; + TUint32 stopAddress = (aStopAddress & 0x1) ? aStopAddress + 1 : aStopAddress;; + + // don't allow the user to step in the excluded ROM region. this could be called + // internally however. for example, the the special breakpoints we set to handle + // panics, exceptions, and library loaded events are in the user library, and we + // will need to step past the breakpoint before continuing the thread. + //if (aUserRequest && (startAddress >= iExcludedROMAddressStart) && (startAddress < iExcludedROMAddressEnd)) + //{ + // return KErrNotSupported; + //} + + // set the temp breakpoint, and disable the breakpoint at the current PC if necessary + // if its not a user request, and we are just trying to resume from a breakpoint, + // then we don't need to check for stubs. The last parameter aUserRequest tells + // ModifyBreaksForStep to check for stubs or not. In some cases, the check for stubs + // is true even if its not a user request.For example, this is true in cases where + // we are doing a step range and the instruction in the range modified PC. + // in this case, DoStepRange will be called from the exception handler where + // we need to check for the stubs for the valid behavior. So truly, we don't need to check + // for stubs only when resuming from a breakpoint. + ReturnIfError(iStepper->ModifyBreaksForStep(aThread, startAddress, stopAddress, aResumeOnceOutOfRange, aUserRequest, aNumSteps)); + + LOG_MSG("DRM_DebugChannel::DoStepRange() - resuming thread\n"); + + return TheDProcessTracker.ResumeThread(aThread); + } + +/** +Read memory from the specified addres into the aData descriptor. If there is a +breakpoint set in the region of memory returned then the correct data value is +inserted into the descriptor + +@param aThread pointer to thread whose address space memory is to be read from +@param aAddress address to start reading memory from +@param aLength length of memory block to read +@param aData descriptor to read memory into + +@return KErrNone if memory read successfully, + KErrNotSupported if reading from the rom section is not supported, + KErrBadHandle if aThread is invalid, + or one of the other system wide error codes +*/ +TInt DRM_DebugChannel::DoReadMemory(const DThread *aThread, const TUint32 aAddress, const TUint32 aLength, TDes8 &aData) const + { + LOG_MSG("DRM_DebugChannel::DoReadMemory()"); + + // make sure the parameters are valid + if (aLength > aData.MaxSize()) + return KErrArgument; + + TInt err = KErrNone; + + // trap exceptions in case the address is invalid + XTRAPD(r, XT_DEFAULT, err = TryToReadMemory(aThread, (TAny *)aAddress, (TAny *)aData.Ptr(), aLength)); + + err = (KErrNone == r) ? err : r; + + if (KErrNone == err) + { + aData.SetLength(aLength); + + TPtr8 data((TUint8 *)aData.Ptr(), aLength, aLength); + + // if we have any breakpoints in this range, put the actual instruction in the buffer + TBreakEntry* breakEntry = NULL; + do + { + breakEntry = iBreakManager->GetNextBreak(breakEntry); + if(breakEntry && !iBreakManager->IsTemporaryBreak(*breakEntry)) + { + if ((breakEntry->iAddress >= aAddress) && (breakEntry->iAddress < (aAddress + aLength))) + { + TInt instSize; + + switch(breakEntry->iMode) + { + case EArmMode: + instSize = 4; + break; + + case EThumbMode: + instSize = 2; + break; + + case EThumb2EEMode: + default: + LOG_MSG("DRM_DebugChannel::DoReadMemory() cannot fixup breakpoints with unsupported architecture"); + return KErrNotSupported; + } + memcpy((TAny*)&data[breakEntry->iAddress - aAddress], (TAny *)breakEntry->iInstruction.Ptr(), instSize); + } + } + } while(breakEntry); + } + + return err; + } + +/** +Attempt to write memory to aThread's address space + +@param aThread thread to whose address space memory is to be written +@param aAddress memory location to write memory to +@param aLength number of bytes of data to write +@param aData descriptor containing memory to write + +@return KErrNone if memory written successfully, + KErrArgument if aLength is greater than than the length of the aData + KErrBadHandle if aThread is invalid, + or another of the system wide error codes +*/ +TInt DRM_DebugChannel::DoWriteMemory(DThread *aThread, const TUint32 aAddress, const TUint32 aLength, TDes8 &aData) + { + LOG_MSG("DRM_DebugChannel::DoWriteMemory()"); + + // make sure the parameters are valid + if (aLength > aData.Length()) + return KErrArgument; + + TInt err = KErrNone; + + // trap exceptions in case the address is invalid + XTRAPD(r, XT_DEFAULT, err = TryToWriteMemory(aThread, (TAny *)aAddress, (TAny *)aData.Ptr(), aLength)); + + err = (KErrNone == r) ? err : r; + + // reset any breakpoints we may have just overwritten + if (KErrNone == err) + { + TPtr8 data((TUint8 *)aData.Ptr(), aLength, aLength); + + TBreakEntry* breakEntry = NULL; + do + { + breakEntry = iBreakManager->GetNextBreak(breakEntry); + if(breakEntry && !iBreakManager->IsTemporaryBreak(*breakEntry)) + { + if ((breakEntry->iAddress >= aAddress) && (breakEntry->iAddress < (aAddress + aLength))) + { + // default to arm mode + TUint32 inst; + TInt instSize; + + switch (breakEntry->iMode) + { + case EArmMode: + inst = KArmBreakPoint; + instSize = 4; + break; + + case EThumbMode: + inst = KThumbBreakPoint; + instSize = 2; + break; + + case EThumb2EEMode: + default: + LOG_MSG("DRM_DebugChannel::DoWriteMemory() cannot fixup breakpoints of unsupported architecture type"); + + return KErrNotSupported; + } + + breakEntry->iInstruction.Copy(&data[breakEntry->iAddress - aAddress], instSize); + memcpy((TAny*)breakEntry->iAddress, (TAny *)&inst, instSize); + } + } + + } while(breakEntry); + } + return err; + } + +// +// DRM_DebugChannel::DoReadRegisters +// +TInt DRM_DebugChannel::DoReadRegisters(DThread *aThread, const TInt16 aFirstRegister, const TInt16 aLastRegister, TDes8 &aValues) + { + LOG_EVENT_MSG("DRM_DebugChannel::DoReadRegisters()"); + + // make sure the parameters are valid + if (!aThread || (aFirstRegister < 0) || (aLastRegister >= (TInt16)(sizeof(TArmRegSet)/sizeof(TArmReg)))) + return KErrArgument; + + // make sure the descriptor is big enough to hold the requested data + if ((TInt)((aLastRegister - aFirstRegister + 1) * sizeof(TArmReg)) > (aValues.MaxSize())) + return KErrArgument; + + TArmRegSet regSet; + TUint32 unused; + + NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, unused); + + LOG_MSG2( "DRM_DebugChannel::DoReadRegistersLegacy() : unused = 0x%X\n", unused ); + + TArmReg *reg = ®Set.iR0; + + if (!reg) + return KErrGeneral; + + for (TInt16 i = aFirstRegister; i <= aLastRegister; i++) + aValues.Append((TUint8 *)®[i], sizeof(TArmReg)); + + return KErrNone; +} + +/** + @prototype + + Experimental function for determining whether a thread is suspended. + + @param aThread thread to check if suspended + + @return ETrue if the thread is suspended, EFalse if it isn't or does not exist + */ +TBool DRM_DebugChannel::CheckSuspended(const DThread *aThread) const + { + if(!aThread) + { + return EFalse; + } + + if( (aThread->iNThread.iCsCount>0) && (aThread->iNThread.iCsFunction>0) ) + { + return ETrue; + } + + if(aThread->iNThread.iSuspendCount > 0) + { + return ETrue; + } + return EFalse; + } + +/** +Read registers and store register values in aRegisterValues and the flags +indicating which registers could be read in aRegisterFlags + +@param aThread thread to read registers from +@param aRegisterIds array containing register IDs to read +@param aRegisterValues array to store register values in +@param aRegisterFlags array to store flags in + +@return KErrNone if registers were read successfully. Note that this does not + mean that all the registers could be read, the aRegisterFlags array + should be checked as to whether each individual register could be read, + KErrArgument if aThread is NULL, if an unknown register is specified in + aRegisterValues or if aRegisterValues is too small + KErrGeneral if there was a problem initialising the register set +*/ +TInt DRM_DebugChannel::DoReadRegisters(DThread *aThread, const TDesC8 &aRegisterIds, TDes8 &aRegisterValues, TDes8& aRegisterFlags) const + { + LOG_MSG("DRM_DebugChannel::DoReadRegisters()"); + + // make sure the parameters are valid + if (!aThread) + return KErrArgument; + + //Need to revisit this to determine whether there is a way to validate this +#if 0 + if ( !CheckSuspended(aThread) ) + { + LOG_MSG2("DRM_DebugChannel::DoReadRegisters() thread with id 0x%08x is not suspended", aThread->iId); + return KErrInUse; + } +#endif + + //set lengths of output descriptors to 0 prior to filling + aRegisterValues.SetLength(0); + aRegisterFlags.SetLength(0); + + TArmRegSet regSet; + TUint32 flags; + + NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, flags); + + LOG_MSG2( "DRM_DebugChannel::DoReadRegisters() : flags = 0x%X\n", flags ); + + TArmReg *regPtr = ®Set.iR0; + + if (!regPtr) + return KErrGeneral; + + TUint numberOfRegisters = aRegisterIds.Length() / sizeof(TRegisterInfo); + + //iterate through registers setting the relevant aFlags value + for(TUint i=0; i aRegisterValues.MaxLength()) + { + //writing this value would cause overflow so exit + return KErrArgument; + } + aRegisterValues.SetLength(aRegisterValues.Length() + registerTag.iSize); + } + else + { + if(registerTag.iSize == sizeof(TArmReg)) + { + if(GetFlagAtOffset(flags, armReg)) + { + //set flag as valid + aRegisterFlags.Append(EValid); + } + else + { + // Even though the flag is invalid, we can return the value of the register + // and let the user decide what to do + aRegisterFlags.Append(EInValid); + } + + if(aRegisterValues.Length() + sizeof(TArmReg) > aRegisterValues.MaxLength()) + { + //writing this value would cause overflow so exit + return KErrArgument; + } + //write value into register into regSet + aRegisterValues.Append((TUint8 *)®Ptr[armReg], registerTag.iSize); + } + else + { + //currently all kernel supported registers are 4 bytes so + //return EBadSize. Would need updating if/when other register + //value sizes are supported + aRegisterFlags.Append(EBadSize); + aRegisterValues.SetLength(aRegisterValues.Length() + registerTag.iSize); + } + } + } + return KErrNone; + } + +// +// DRM_DebugChannel::DoWriteRegisters +// +TInt DRM_DebugChannel::DoWriteRegisters(DThread *aThread, const TInt16 aFirstRegister, const TInt16 aLastRegister, TDesC8 &aValues) + { + LOG_MSG("DRM_DebugChannel::DoWriteRegisters()"); + + // make sure the parameters are valid + if (!aThread || (aFirstRegister < 0) || (aLastRegister >= (TInt16)(sizeof(TArmRegSet)/sizeof(TArmReg)))) + return KErrArgument; + + // make sure the descriptor is big enough to hold the data to write + if ((TInt)((aLastRegister - aFirstRegister + 1) * sizeof(TArmReg)) > (aValues.Length())) + return KErrArgument; + + TArmRegSet regSet; + TUint32 unused; + + NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, unused); + + TArmReg *reg = ®Set.iR0; + + for (TInt16 i = aFirstRegister; i <= aLastRegister; i++) + reg[i] = *(TUint32 *)&aValues[(i-aFirstRegister)*sizeof(TArmReg)]; + + NKern::ThreadSetUserContext(&aThread->iNThread, ®Set); + + return KErrNone; + } + +/** +Write registers and store flags indicating which registers could be read in +aRegisterFlags + +@param aThread thread to write registers to +@param aRegisterIds array containing register IDs to write +@param aRegisterValues array containing register values to write +@param aRegisterFlags array to store flags in + +@return KErrNone if registers were written successfully. Note that this does not + mean that all the registers could be written, the aRegisterFlags array + should be checked as to whether each individual register could be read, + KErrArgument if aThread is NULL, if the buffer passed in as + aRegisterValue is too small, or if an unknown register is requested, + KErrGeneral if there was a problem initialising the register set +*/ +TInt DRM_DebugChannel::DoWriteRegisters(DThread *aThread, const TDesC8 &aRegisterIds, TDesC8 &aRegisterValues, TDes8 &aRegisterFlags) const + { + LOG_MSG("DRM_DebugChannel::DoWriteRegisters()"); + + // make sure the parameters are valid + if (!aThread) + return KErrArgument; + + + //get register values from kernel + TArmRegSet regSet; + TUint32 flags; + NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, flags); + + //set lengths of output descriptors to 0 prior to filling + aRegisterFlags.SetLength(0); + + //pointer to first kernel register + TArmReg *regPtr = ®Set.iR0; + + if (!regPtr) + return KErrGeneral; + + //calculate number of registers + TUint numberOfRegisters = aRegisterIds.Length() / sizeof(TRegisterInfo); + + //iterate through registers setting the relevant aRegisterFlags value and + //setting the necessary value in regSet ready to write to kernel + for(TUint i=0, offset = 0; i aRegisterValues.Length()) + { + //getting this value would cause overflow so exit + return KErrArgument; + } + //write value into register into regSet + regPtr[armReg] = *(TUint32 *)&aRegisterValues[offset]; + } + else + { + //currently all kernel supported registers are 4 bytes so + //return EBadSize. Would need updating if/when other register + //value sizes are supported + aRegisterFlags.Append(EBadSize); + } + + } + else + { + //set flag as invalid as register value couldn't be read + aRegisterFlags.Append(EInValid); + } + offset+=registerTag.iSize; + } + + //write the input data into the registers + NKern::ThreadSetUserContext(&aThread->iNThread, ®Set); + + //return normally + return KErrNone; + } + +// +// DRM_DebugChannel::DoSecurityCheck +// +TBool DRM_DebugChannel::DoSecurityCheck() + { + LOG_MSG("DRM_DebugChannel::DoSecurityCheck"); + DProcess* clientProcess = iClientThread->iOwningProcess; + if (clientProcess) + { + SSecurityInfo secureInfo = clientProcess->iS; + + LOG_MSG2("DoSecurityCheck - client secure id is 0x%08x",secureInfo.iSecureId); + + // Ensure we really are communicating with the Debug Security Server + if (secureInfo.iSecureId == KUidDebugSecurityServer.iUid ) + { + return ETrue; + } + } + return EFalse; + } + +/** +Attempt to read memory from aThread's address space + +@param aThread thread from whose address space memory is to be read +@param aSrc pointer to memory location to read memory from +@param aDest pointer to memory location to write memory to +@param aLength number of bytes of data to read + +@return KErrNone if memory read successfully, + or another of the system wide error codes +*/ +TInt DRM_DebugChannel::TryToReadMemory(const DThread *aThread, const TAny *aSrc, TAny *aDest, const TUint32 aLength) const + { + LOG_MSG("DRM_DebugChannel::TryToReadMemory()"); + + // make sure the parameters are valid + if (!aThread) + return KErrArgument; + + //Need to revisit this to determine whether there is a way to validate this +#if 0 + //check that the thread is suspended before reading the memory + if ( !CheckSuspended(aThread) ) + { + LOG_MSG2("DRM_DebugChannel::TryToReadMemory() thread with id 0x%08x is not suspended", aThread->iId); + return KErrInUse; + } +#endif + + LOG_MSG2("Using Kern::ThreadRawRead to read memory at address %x", aSrc); + return Kern::ThreadRawRead((DThread *)aThread, aSrc, aDest, aLength); + } + +/** +Attempt to write memory to aThread's address space + +@param aThread thread to whose address space memory is to be written +@param aDest pointer to memory location to write memory to +@param aSrc pointer to memory location to read memory from +@param aLength number of bytes of data to write + +@return KErrNone if memory written successfully, or another of the system wide + error codes +*/ +TInt DRM_DebugChannel::TryToWriteMemory(const DThread *aThread, TAny *aDest, const TAny *aSrc, const TUint32 aLength) + { + LOG_MSG("DRM_DebugChannel::TryToWriteMemory()"); + + + LOG_MSG2("Using Kern::ThreadRawWrite to write memory at address %x", (TUint32)aDest); + return Kern::ThreadRawWrite((DThread *)aThread, aDest, aSrc, aLength, iClientThread); + } + +/** +@deprecated use DRM_DebugChannel::ReadKernelRegisterValue(DThread *aThread, const TArmReg aKernelRegisterId, T4ByteRegisterValue &aValue) instead +*/ +TInt32 DRM_DebugChannel::ReadRegister(DThread *aThread, TInt aNum) + { + LOG_MSG("DRM_DebugChannel::ReadRegister()"); + + if (!aThread || (aNum < 0) || (aNum >= (TInt16)(sizeof(TArmRegSet)/sizeof(TArmReg)))) + { + LOG_MSG2("Invalid register number (%d) passed to ReadRegister", aNum); + return 0; + } + + TArmRegSet regSet; + TUint32 unused; + + NKern::ThreadGetUserContext(&aThread->iNThread, ®Set, unused); + + TArmReg *reg = ®Set.iR0; + + return ((TUint32 *)reg)[aNum]; + } + +/** +Given a TArmReg register ID, read the value of the register. The register value +will be stored in aValue if the register could be read. + +@param aThread thread to read register from +@param aKernelRegisterId ID of register to read from +@param aValue value read from register + +@return KErrNone if value was successfully stored in aValue, + KErrNotSupported if aKernelRegister is not supported by the debug + security server, + or a return value from DRM_DebugChannel::ReadDebugRegisterValue() +*/ +TInt32 DRM_DebugChannel::ReadKernelRegisterValue(DThread *aThread, const TArmReg aKernelRegisterId, T4ByteRegisterValue &aValue) const + { + //get register ID as a TRegisterInfo ID + TRegisterInfo regId; + TInt err = GetDebugRegisterId(aKernelRegisterId, regId); + if(err != KErrNone) + return err; + + //get the value for the register + err = ReadDebugRegisterValue(aThread, regId, aValue); + return err; + } + +/** +Given a TRegisterInfo register ID, read the value of this register. The +register value will be stored in aValue if the register could be read. + +@param aThread thread to read register from +@param aDebugRegisterId ID of register to read from +@param aValue value read from register + +@return KErrNone if value was successfully stored in aValue, + TRegisterFlag::EInValid if value could not be read from the register, + TRegisterFlag::ENotSupported if the register is not supported, + KErrNoMemory if temporary memory could not be allocated, + or a return value from DRM_DebugChannel::DoReadRegisters +*/ +TInt32 DRM_DebugChannel::ReadDebugRegisterValue(DThread *aThread, const TRegisterInfo aDebugRegisterId, T4ByteRegisterValue &aValue) const + { + //allocate temporary buffers to store data + NKern::ThreadEnterCS(); + TUint8* id = (TUint8*)Kern::Alloc(sizeof(TRegisterInfo)); + NKern::ThreadLeaveCS(); + if(id == NULL) + { + return KErrNoMemory; + } + + TPtr8 idPtr(id, sizeof(TRegisterInfo)); + + NKern::ThreadEnterCS(); + TUint8* value = (TUint8*)Kern::Alloc(sizeof(T4ByteRegisterValue)); + NKern::ThreadLeaveCS(); + if(value == NULL) + { + return KErrNoMemory; + } + TPtr8 valuePtr(value, sizeof(T4ByteRegisterValue)); + + NKern::ThreadEnterCS(); + TUint8* flag = (TUint8*)Kern::Alloc(sizeof(TUint8)); + NKern::ThreadLeaveCS(); + if(flag == NULL) + { + return KErrNoMemory; + } + TPtr8 flagPtr(flag, sizeof(TUint8)); + + //store register id in buffer + idPtr.Append((TUint8*)&aDebugRegisterId, sizeof(TRegisterInfo)); + + //read registers + TInt err = DoReadRegisters(aThread, idPtr, valuePtr, flagPtr); + if(err == KErrNone) + { + if(*flag == EValid) + { + //register could be read so store value + aValue = *(T4ByteRegisterValue*)value; + } + else + { + //register couldn't be read for some reason + err = *flag; + } + } + + //free memory + NKern::ThreadEnterCS(); + Kern::Free(id); + Kern::Free(value); + Kern::Free(flag); + NKern::ThreadLeaveCS(); + + return err; + } + +// +// DRM_DebugChannel::NotifyAgentsFromEventPid +// +TBool DRM_DebugChannel::NotifyAgentsFromEventPid(const TDriverEventInfo& aEventInfo) + { + + // Look for the relevant DTargetProcess + // We can find out the relevant process id from aEventInfo + TUint32 pid = aEventInfo.iProcessId; + +#ifdef _DEBUG + if (aEventInfo.iEventType != EEventsUserTrace) + { + LOG_MSG3(" NotifyAgentsFromEventPid - pid = 0x%x, evType=%d", pid, aEventInfo.iEventType); + } +#endif + + //opening handle to process + DProcess* targetProcess = DebugUtils::OpenProcessHandle(pid); + + if(!targetProcess) + { + LOG_EVENT_MSG("process does not exist!"); + return EFalse; + } + + // Are we debugging this process - decide based on iFileName + DCodeSeg* p = targetProcess->iCodeSeg; + DTargetProcess* foundProcess; + if (p) + { + foundProcess = TheDProcessTracker.FindProcess(*(p->iFileName)); + } + else + { + // special case: may not have a code seg in some cases. in which case we tell everyone! + if (targetProcess->iName) + { + // copy the name of the process + foundProcess = TheDProcessTracker.FindProcess(*(targetProcess->iName)); + } + else + { + foundProcess = NULL; + } + } + + //close the handle + targetProcess->Close(NULL); + + if (foundProcess) + { + foundProcess->NotifyEvent(aEventInfo); + return ETrue; + } + else + { + // Check if there's an attach-to-all handler + DDebugAgent* agent = TheDProcessTracker.GetCurrentAgentAttachedToAll(); + if (agent) + { + LOG_EVENT_MSG2(" NotifyAgentsFromEventPid - found attach all agent 0x%lx", agent->Id()); + agent->NotifyEvent(aEventInfo); + return ETrue; + } + } + + // we are not debugging this process + return EFalse; + } + +DECLARE_STANDARD_LDD() + { + return new DRM_DebugDriverFactory; + } + +/** +Helper function + +Allocates memory in current thread with a max length the same as aSrcDes. If +aReadFromClient is true (as it is by default) then the data from aSrdDes is +copied into the allocated aDestDes buffer. + +Use of this function should be followed at a later time by a call such as +Kern::Free(aDestDes.Ptr()) + +@param aThread pointer to thread to read data from +@param aSrcDes descriptor in aThread to read data from +@param aDestDes location to read data to. Memory is allocated at this location, + if memory is already allocated at this location then the function will + return KErrArgument +@param aReadFromClient if false then data is not actually read from the + client, the memory is simply allocated +@param aOffest offset into aSrcDes to start reading from. Default is 0. + +@return KErrNone if there were no problems, + KErrArgument if aDestDes.Ptr() != NULL or aSrcDes has max length 0, + KErrNoMemory if could not allocate memory, + or one of the other system wide error codes +*/ +TInt DRM_DebugChannel::AllocAndReadDes(DThread *aThread, const TDesC8& aSrcDes, TPtr8& aDestDes, const TBool aReadFromClient, const TUint aOffset) const + { + + //check thread is not null + if(!aThread) + { + return KErrArgument; + } + + //check aDestDes is empty + if(aDestDes.Ptr() != NULL) + { + return KErrArgument; + } + + //get the source descriptor's max length and exit if 0 + TUint srcMaxLength = Kern::ThreadGetDesMaxLength(aThread, &aSrcDes); + if(srcMaxLength == 0) + { + return KErrNone; + } + + //allocate memory and return if none available + NKern::ThreadEnterCS(); + TUint8 *destPtr = (TUint8*)Kern::Alloc(srcMaxLength); + NKern::ThreadLeaveCS(); + if (!destPtr) + { + return KErrNoMemory; + } + + //point the TPtr8 at the target memory + aDestDes.Set(destPtr, srcMaxLength, srcMaxLength); + + if(aReadFromClient) + { + //read data from the client thread and return status code + return Kern::ThreadDesRead(aThread, &aSrcDes, aDestDes, aOffset); + } + else + { + return KErrNone; + } + } + +/** +Helper function to extract a TRegisterInfo value from a descriptor containing +binary data. + +@param aRegisterIds descriptor containing register IDs +@param aOffset offset in bytes into the descriptor to start reading data from. + If this value is not a multiple of sizeof(TRegisterInfo) then a + KErrArgument error is returned. +@param aValue will contain the returned value + +@return KErrNone if aValue was set correctly, KErrArgument if bad arguments + were passed in +*/ +TInt DRM_DebugChannel::GetTRegisterInfo(const TDesC8 &aRegisterIds, const TUint aIndex, TRegisterInfo &aValue) const + { + TUint length = aRegisterIds.Length(); + + TUint size = sizeof(TRegisterInfo); + + //check that not trying to read past end of descriptor + if((aIndex + 1) * size > length) + return KErrArgument; + + //get pointer to descriptor's data + const TUint8 *dataPtr = aRegisterIds.Ptr(); + const TRegisterInfo *registerId = reinterpret_cast(dataPtr + (aIndex * size)); + + aValue = *registerId; + + return KErrNone; + } + +/** +Helper function to get the kernel register ID of the TRegisterInfo defined register. + +@param aDebugRegister the debug register ID to return the kernel ID for +@param aKernelRegister corresponding value of register aDebugRegister + +@return KErrNone if translation occurred without problems + KErrNotSupported if aDebugRegister is not supported by the kernel +*/ +TInt DRM_DebugChannel::GetKernelRegisterId(const TRegisterInfo aDebugRegister, TArmReg& aKernelRegister) const + { + if(Register::IsCoreReg(aDebugRegister)) + { + TUint id = Register::GetCoreRegId(aDebugRegister); + //first 17 registers match the first 17 kernel registers + if(id < 17) + { + aKernelRegister = id; + } + else + { + return KErrNotSupported; + } + } + else if(Register::IsCoproReg(aDebugRegister)) + { + TUint32 crn = Register::GetCRn(aDebugRegister); + TUint32 crm = Register::GetCRm(aDebugRegister); + TUint32 opcode1 = Register::GetOpcode1(aDebugRegister); + TUint32 opcode2 = Register::GetOpcode2(aDebugRegister); + TUint32 coproNum = Register::GetCoproNum(aDebugRegister); + + //each coprocessor register has potentially different characteristics + //so need to identify each individually + + //this is the DACR, the ARM ARM specifies that the CRn and the + //Opcodes are not relevant, section B3-24, point 3.7.3 + if((coproNum == 15) && (crm == 3)) + { + aKernelRegister = EArmDacr; + } + else + { + return KErrNotSupported; + } + } + else // might be supported at a later date + { + return KErrNotSupported; + } + + return KErrNone; + } + +/** +Helper function to get the debug register ID of the kernel defined register. + +@param aKernelRegister the kernel register ID to return the debug ID for +@param aDebugRegister corresponding value of register aKernelRegister + +@return KErrNone if translation occured without problems + KErrNotSupported if aKernelRegister is not supported by the debug + security server +*/ +TInt DRM_DebugChannel::GetDebugRegisterId(const TArmReg aKernelRegister, TRegisterInfo &aDebugRegister) const + { + + // registers 0 - 15 and the CPSR share the same values as with the debug enums + if(aKernelRegister < 17) + { + TUint32 id = aKernelRegister; + aDebugRegister = id << 8; + } + //the DACR value is special and corresponds to EDF_Register_DACR + else if(aKernelRegister == EArmDacr) + { + aDebugRegister = 0x00300f01; + } + // must be an unsupported register, return an error as such + else + { + return KErrNotSupported; + } + + //found a supported register so return KErrNone + return KErrNone; + } + +/** +Helper function to find out whether the aIndex flag is set. This is equivalent +to the aIndex bit of aFlags being non-zero. + +@param aFlags set of flags +@param aIndex offset into aFlags to get flag from + +@return ETrue if bit is set, EFalse if not +*/ +TBool DRM_DebugChannel::GetFlagAtOffset(const TUint32 aFlags, const TArmReg aIndex) const + { + return aFlags & (1<= KMaxPath) + { + return KErrArgument; + } + + // Allocate space to store the target process name in a kernel-side TPtr8 + NKern::ThreadEnterCS(); + TUint8* buffer = (TUint8*)Kern::AllocZ(length); + NKern::ThreadLeaveCS(); + if (buffer==NULL) + { + // Out of memory + return KErrNoMemory; + } + + // A temporary descriptor to store the target process name + TPtr8 targetProcessName(buffer,length,length); + + // Read the user-side data into targetProcessName + TInt err = Kern::ThreadDesRead(iClientThread,a1,targetProcessName,0,KChunkShiftBy0); + if (err != KErrNone) + { + // Could not read the user-side descriptor containing the target process name + NKern::ThreadEnterCS(); + Kern::Free(buffer); + NKern::ThreadLeaveCS(); + + return err; + } + + // Obtain the Debug Agent Id + TUint64 debugAgentId = 0; + + err = Kern::ThreadRawRead(iClientThread,a2,&debugAgentId,sizeof(debugAgentId)); + if (err != KErrNone) + { + // Something bad happened so free the memory and return + NKern::ThreadEnterCS(); + Kern::Free(buffer); + NKern::ThreadLeaveCS(); + return err; + } + + // Add the target process to our list of tracked processes + err = TheDProcessTracker.AttachProcess(targetProcessName, debugAgentId); + + // Free the kernel-side memory containing targetProcessName data + NKern::ThreadEnterCS(); + Kern::Free(buffer); + NKern::ThreadLeaveCS(); + + return err; + } + +/* Register the detachment of a debug agent to a process to be debugged. + * + * @param - a1 TDes8 target process name in a1 + * @param a2 - &TUint64 - Debug Agent Id + * + * @return - KErrNone if successful. KErrArgument if the filepath is not a valid size. + * KErrOutOfMemory if there is insufficient memory. Or one of the other system wide error codes + * if appropriate. + */ +TInt DRM_DebugChannel::DetachProcess(TAny* a1, TAny* a2) + { + TInt length = Kern::ThreadGetDesLength(iClientThread, a1); + if (length < 0) + { + return length; + } + + if (length < 1 || length >= KMaxPath) + { + return KErrArgument; + } + + HBuf* targetProcessName = HBuf::New(length); + if (targetProcessName == NULL) + { + // Out of memory + return KErrNoMemory; + } + + // Read the user-side data into buf + TInt err = Kern::ThreadDesRead(iClientThread,a1,*targetProcessName,0,KChunkShiftBy0); + if (err != KErrNone) + { + // Something bad happened so free the memory and return + delete targetProcessName; + return err; + } + + // Obtain the AgentId + TUint64 debugAgentId = 0; + + err = Kern::ThreadRawRead(iClientThread,a2,&debugAgentId,sizeof(debugAgentId)); + if (err == KErrNone) + { + // Remove the process from our list of tracked processes + err = TheDProcessTracker.DetachProcess(*targetProcessName, debugAgentId); + } + + // Free the kernel-side memory containing targetProcessName data + delete targetProcessName; + return err; + } + +/* Register the detachment of a debug agent from all processes being debugged. + * + * @param - a1 - &TUint64 Debug Agent Id. + * @return - KErrNone if successful. One of the system-wide error codes otherwise. + */ +TInt DRM_DebugChannel::DetachAgent(TAny* a1, TAny* a2) + { + // Obtain the AgentId + TUint64 debugAgentId = 0; + + TInt err = Kern::ThreadRawRead(iClientThread,a1,&debugAgentId,sizeof(debugAgentId)); + if (err != KErrNone) + { + return err; + } + + // Remove the process from our list of tracked processes + return TheDProcessTracker.DetachAgent(debugAgentId); + } + +/* Set the action associated with a particular kernel event for a given agent and target process + * + * @param - a1 TDes8 target process name in a1 + * @param - a2 &TRM_DebugEventActionInfo + * @return - KErrNone if successful. KErrArgument if the filepath is an invalid size. Or one of + * the other system wide error codes if appropriate. + */ +TInt DRM_DebugChannel::SetEventAction(TAny* a1, TAny* a2) + { + // Validate the supplied TDes8 target process name in a1 + TInt length, maxLength; + TUint8* aPtr; + + TInt err = Kern::ThreadGetDesInfo(iClientThread,\ + a1,\ + length,\ + maxLength,\ + aPtr,\ + EFalse); + if (err != KErrNone) + { + return err; + } + + if (length < 1 || length >= KMaxPath) + { + return KErrArgument; + } + + if (maxLength < 1 || maxLength >= KMaxPath) + { + return KErrArgument; + } + + // Allocate space to store the target process name in a kernelspace TPtr8 + NKern::ThreadEnterCS(); + TUint8* buffer = (TUint8*)Kern::AllocZ(length); + NKern::ThreadLeaveCS(); + if (buffer==NULL) + { + // Out of memory + return KErrNoMemory; + } + TPtr8 targetProcessName(buffer,length,length); + + // Read the user-side data into targetProcessName + err = Kern::ThreadDesRead(iClientThread,a1,targetProcessName,0,KChunkShiftBy0); + if (err != KErrNone) + { + // Something bad happened so free the memory and return + NKern::ThreadEnterCS(); + Kern::Free(buffer); + NKern::ThreadLeaveCS(); + + return err; + } + + // Read the Event and Action from the user-side + TRM_DebugEventActionInfo info(0,0,0); + + err = Kern::ThreadRawRead(iClientThread, a2, &info, sizeof(info)); + if (err != KErrNone) + { + // Could not read event action data from the user-side + + // Free memory used for targetProcessName + NKern::ThreadEnterCS(); + Kern::Free(buffer); + NKern::ThreadLeaveCS(); + + return err; + } + + DDebugAgent* debugAgent = TheDProcessTracker.FindAgentForProcessAndId( targetProcessName, info.iAgentId ); + if (debugAgent != NULL) + { + // Set the event action + err = debugAgent->SetEventAction((TEventType)info.iEvent,(TKernelEventAction)info.iAction); + } + else + { + // Bad agent means there is no tracking agent + LOG_MSG2("Cannot locate debug agent with pid 0x%0xlx",info.iAgentId); + err = KErrNotFound; + } + + // Free memory used for targetProcessName + NKern::ThreadEnterCS(); + Kern::Free(buffer); + NKern::ThreadLeaveCS(); + + return err; + + } + +TInt DRM_DebugChannel::Step(const TUint32 aThreadId, const TUint32 aNumSteps) + { + LOG_MSG3("DRM_DebugChannel::Step(aThreadId = 0x%08x, aNumSteps = 0x%08x)\n",aThreadId,aNumSteps); + + DThread* thread = DebugUtils::OpenThreadHandle(aThreadId); + + if (thread == NULL) + { + // The thread terminated before we could open it. + LOG_MSG2("DRM_DebugChannel::Step - Could not open thread %u", aThreadId); + + return KErrArgument; + } + + // We simply repeat this for desired number of steps + TInt err = KErrNone; + + // Need to step from the current location for 'n' steps + TUint32 startAddress; + + // We always step from the current PC. + err = ReadKernelRegisterValue(thread, PC_REGISTER, startAddress); + if(err != KErrNone) + { + LOG_MSG2("DRM_DebugChannel::Step - Could not read the PC: %d", err); + + // Close the handle + thread->Close(NULL); + + return err; + } + + err = DoStepRange(thread, startAddress, startAddress, ETrue, EFalse, aNumSteps, ETrue); + + if (err != KErrNone) + { + // There was a problem, return straightaway + LOG_MSG("DRM_DebugChannel::Step - failed to step"); + } + + // Close the handle + thread->Close(NULL); + + return err; + } + +TInt DRM_DebugChannel::KillProcess(const TUint32 aProcessId, const TInt aReason) + { + LOG_MSG3("DRM_DebugChannel::KillProcess(aProcessId = 0x%08x, aReason = 0x%08x)\n",aProcessId,aReason); + + DProcess* process = DebugUtils::OpenProcessHandle(aProcessId); + + if (process == NULL) + { + // The process terminated before we could open it to kill it ourselves. + LOG_MSG2("DRM_DebugChannel::KillProcess - Could not open process %u", aProcessId); + + return KErrArgument; + } + + TInt err = KErrNone; + + DebugSupport::TerminateProcess(process,aReason); + + // Close the handle + process->Close(NULL); + + return err; + } + +/* Security critical - this checks whether the specified process is debuggable or not + * + * @param aProcessId - The process id of the process to check + * @return KErrNone if debuggable, KErrPermissionDenied if not debuggable. + */ +TInt DRM_DebugChannel::IsDebuggable(const TUint32 aProcessId) + { + /* In order to ensure that only processes which are debuggable + * can be debugged, this function enables the security server + * to read the DProcess.iDebugAttributes field and ensure + * the process was created from a debuggable executable. + */ + LOG_MSG2("DRM_DebugChannel::IsDebuggable(aProcessId 0x%08x)\n",aProcessId); + + TInt err = KErrPermissionDenied; + + DProcess* process = DebugUtils::OpenProcessHandle(aProcessId); + if (process) + { + if (process->iDebugAttributes & TProcessCreateInfo::EDebugAllowed) + { + // Yes this process exists and is debuggable + err = KErrNone; + } + process->Close(NULL); + } + + if (err == KErrNone) + { + LOG_MSG2("DRM_DebugChannel::IsDebuggable(aProcessId 0x%08x) - Yes it is debuggable\n",aProcessId); + } + + return err; + }