debugsrv/runmodedebug/rmdriver/src/rm_debug_kerneldriver.cpp
branchRCL_3
changeset 20 ca8a1b6995f6
--- /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 <e32def.h>
+#include <e32def_private.h>
+#include <e32cmn.h>
+#include <e32cmn_private.h>
+#include <e32ldr.h>
+#include <u32std.h>
+#include <kernel/kernel.h>
+#include <kernel/kern_priv.h>
+#include <nk_trace.h>
+#include <arm.h>
+#include <kernel/cache.h>
+#include <platform.h>
+#include <nkern.h>
+#include <u32hal.h>
+#include <rm_debug_api.h>
+
+#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<SCodeSegEntry>* dynamicCode = &(process->iDynamicCode);
+
+		for (TInt j=0; j<dynamicCode->Count(); 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",
+			&currentThread->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; i<iDebugProcessList.Count(); i++)
+		{
+		if(process->iId == 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, &regSet, unused);
+
+	LOG_MSG2( "DRM_DebugChannel::DoReadRegistersLegacy() : unused = 0x%X\n", unused );
+
+	TArmReg *reg = &regSet.iR0;
+
+	if (!reg)
+		return KErrGeneral;
+
+	for (TInt16 i = aFirstRegister; i <= aLastRegister; i++)
+		aValues.Append((TUint8 *)&reg[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, &regSet, flags);
+
+	LOG_MSG2( "DRM_DebugChannel::DoReadRegisters() : flags = 0x%X\n", flags );
+
+	TArmReg *regPtr = &regSet.iR0;
+
+	if (!regPtr)
+		return KErrGeneral;
+
+	TUint numberOfRegisters = aRegisterIds.Length() / sizeof(TRegisterInfo);
+
+	//iterate through registers setting the relevant aFlags value
+	for(TUint i=0; i<numberOfRegisters; i++)
+		{
+		//get current register id
+		TRegisterInfo reg;
+		TInt err = GetTRegisterInfo(aRegisterIds, i, reg);
+		//exit with the error value if there was an error
+		if(err != KErrNone)
+			return err;
+
+		//if unknown register then exit as can't know how many bytes this entry will
+		//represent in aRegisterValues
+		TTag registerTag;
+		TDebugFunctionality::GetRegister(reg, registerTag);
+		if(registerTag.iValue == EAccessUnknown)
+			{
+			return KErrArgument;
+			}
+
+		//get the current register id as a kernel register
+		TArmReg armReg;
+		err = GetKernelRegisterId(reg, armReg);
+		if((err == KErrNotSupported) || (registerTag.iValue == EAccessNone) || (registerTag.iValue == EAccessWriteOnly))
+			{
+			//reading this register is not supported
+			aRegisterFlags.Append(ENotSupported);
+			//just skip over this entry in the values buffer
+			if(aRegisterValues.Length() + registerTag.iSize > 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 *)&regPtr[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, &regSet, unused);
+
+	TArmReg *reg = &regSet.iR0;
+
+	for (TInt16 i = aFirstRegister; i <= aLastRegister; i++)
+		reg[i] = *(TUint32 *)&aValues[(i-aFirstRegister)*sizeof(TArmReg)];
+
+	NKern::ThreadSetUserContext(&aThread->iNThread, &regSet);
+
+	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, &regSet, flags);
+
+	//set lengths of output descriptors to 0 prior to filling
+	aRegisterFlags.SetLength(0);
+
+	//pointer to first kernel register
+	TArmReg *regPtr = &regSet.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<numberOfRegisters; i++)
+		{
+		//get current register id
+		TRegisterInfo reg;
+		TInt err = GetTRegisterInfo(aRegisterIds, i, reg);
+		//exit with the error value if there was an error
+		if(err != KErrNone)
+			{
+			return err;
+			}
+
+		//if unknown register then exit as can't know how many bytes this entry will
+		//represent in aRegisterValues
+		TTag registerTag;
+		TDebugFunctionality::GetRegister(reg, registerTag);
+		if(registerTag.iValue == EAccessUnknown)
+			{
+			return KErrArgument;
+			}
+
+		//get the current register id as a kernel register
+		TArmReg armReg;
+		err = GetKernelRegisterId(reg, armReg);
+		if((err == KErrNotSupported) || (registerTag.iValue == EAccessNone) || (registerTag.iValue == EAccessReadOnly))
+			{
+			//writing to this register is not supported
+			aRegisterFlags.Append(ENotSupported);
+			}
+		else if(GetFlagAtOffset(flags, armReg))
+			{
+			if(registerTag.iSize == sizeof(TArmReg))
+				{
+				//set flag as valid
+				aRegisterFlags.Append(EValid);
+				if(offset + sizeof(TArmReg) > 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, &regSet);
+
+	//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, &regSet, unused);
+
+	TArmReg *reg = &regSet.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<const TRegisterInfo*>(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<<aIndex);
+	}
+
+/* Register the attachment of a debug agent to a process to be debugged
+ *
+ * @param a1 - TDes8 target process name
+ * @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::AttachProcess(TAny* a1, TAny* a2)
+	{
+	LOG_MSG("DRM_DebugChannel::AttachProcess()");
+
+	// Validate the supplied TDes8 target process name in a1
+	TInt length = Kern::ThreadGetDesLength(iClientThread, a1);
+	if (length < 0) return length;
+
+	// Check the processname is a valid size for a filepath
+	if (length < 1 || length >= 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;
+	}