kerneltest/e32test/debug/d_context.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/debug/d_context.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,569 @@
+// Copyright (c) 2003-2009 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:
+// e32test\debug\d_context.cpp
+// Test LDD exercising hardware/software exception hooks 
+// and get/set user context APIs.
+// 
+//
+
+#include "platform.h"
+#include <kernel/kern_priv.h>
+#include "kern_test.h"
+#include "d_context.h"
+
+_LIT(KClientPanicCat, "D_CONTEXT");
+
+extern TUint32 SpinInKernel(TBool);
+
+DThread* ThreadFromId(TUint aId)
+	{
+	// This is risky because the thread could die on us an the DThread* become invalid.
+	// We are relying on this never happening in our test code.
+	NKern::ThreadEnterCS(); 
+	DObjectCon& threads=*Kern::Containers()[EThread];
+	threads.Wait(); 
+	DThread* thread = Kern::ThreadFromId(aId);
+	threads.Signal();
+	NKern::ThreadLeaveCS(); 
+	return thread;
+	}
+
+void ModifyContext(TArmRegSet& aContext)
+	{
+	TArmReg* end = (TArmReg*)(&aContext+1);
+	for (TArmReg* p = (TArmReg*)&aContext; p<end; ++p)
+		*p = ~*p;
+	}
+
+void DumpContext(TArmRegSet& aContext)
+	{
+	Kern::Printf("  r0 =%08x r1 =%08x r2 =%08x r3 =%08x",aContext.iR0,aContext.iR1,aContext.iR2,aContext.iR3);
+	Kern::Printf("  r4 =%08x r5 =%08x r6 =%08x r7 =%08x",aContext.iR4,aContext.iR5,aContext.iR6,aContext.iR7);
+	Kern::Printf("  r8 =%08x r9 =%08x r10=%08x r11=%08x",aContext.iR8,aContext.iR9,aContext.iR10,aContext.iR11);
+	Kern::Printf("  r12=%08x r13=%08x r14=%08x r15=%08x",aContext.iR12,aContext.iR13,aContext.iR14,aContext.iR15);
+	Kern::Printf("  cpsr=%08x dacr=%08x",aContext.iFlags, aContext.iDacr);
+	}
+
+inline TBool IsRegisterAvailable(TInt aReg, TUint32 aMask)
+	{
+	return aMask & (1<<aReg);
+	}
+
+TInt CheckSetContext(const TArmRegSet& aSetContext, const TArmRegSet& aContextAfterSet, TUint32 aAvailMask)
+	{
+	Kern::Printf("Checking all available registers have been modified (0%08x)", aAvailMask);
+
+	const TArmReg* pSet = (const TArmReg*)&aSetContext;
+	const TArmReg* pAfterSet = (const TArmReg*)&aContextAfterSet;
+
+	for (int i=0; i<=EArmPc; ++i)
+		{
+		if (pAfterSet[i] == 0 && IsRegisterAvailable(i, aAvailMask) && pSet[i] != 0)
+			{
+			Kern::Printf("Register %d not set (expected %08x actual %08x)", i, pSet[i], pAfterSet[i]);
+			return KErrCorrupt;
+			}
+		if (pAfterSet[i] != 0 && ! IsRegisterAvailable(i, aAvailMask))
+			{
+			Kern::Printf("Register %d incorrectly set (expected %08x actual %08x)", i, 0, pAfterSet[i]);
+			return KErrCorrupt;
+			}
+		}
+
+	return KErrNone;
+	}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DEventHandler : public DKernelEventHandler
+	{
+public:
+	DEventHandler();
+	TInt Create(DLogicalDevice* aDevice);
+	~DEventHandler();
+	void Cancel();
+private:
+	static TUint EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis);
+	TUint HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2);
+	TBool HandleException(TRequestStatus*& aStatusPtr, TClientRequest*& aRequestPtr);
+	void HandleThreadDeath();
+	void Cleanup();
+private:
+	DThread* iClientThread;
+	DMutex* iLock; // serialise calls to handler
+public:
+	DLogicalDevice* iDevice;	// open reference to LDD for avoiding lifetime issues
+	// software exception fields
+	TExcType iSwExcLastType;
+	TInt* iSwExcCounterPtr;
+	TRequestStatus* iSwExcStatusPtr;
+	TClientRequest* iClientRequestSwExc;
+	// hardware exception fields
+	TRequestStatus* iHwExcStatusPtr;
+	TClientRequest* iClientRequestHwExc;
+	// fields used for both hardware and software exceptions
+	TUint iExcThreadId;
+	TAny* iExcContextPtr;
+	TBool iExcKillThread;
+	// thread death event fields
+	TUint iDeathThreadId;
+	TRequestStatus* iDeathStatusPtr;
+	TAny* iDeathContextPtr;
+	TClientRequest* iClientRequestDeath;
+	};
+
+
+DEventHandler::DEventHandler() 
+	: 	DKernelEventHandler(EventHandler, this) 
+	{
+	}
+
+
+TInt DEventHandler::Create(DLogicalDevice* aDevice)
+	{
+	TInt r;
+	r = aDevice->Open();
+	if (r != KErrNone)
+		goto error;
+	iDevice = aDevice;
+	iClientThread = &Kern::CurrentThread();
+	r = Kern::CreateClientRequest(iClientRequestSwExc);
+	if (r != KErrNone)
+		goto error;
+	r = Kern::CreateClientRequest(iClientRequestHwExc);
+	if (r != KErrNone)
+		goto error;
+	r = Kern::CreateClientRequest(iClientRequestDeath);
+	if (r != KErrNone)
+		goto error;
+	r = Kern::MutexCreate(iLock, _L("EventHandlerLock"), KMutexOrdDebug);
+	if (r != KErrNone)
+		goto error;
+	return Add();
+error:
+	Cleanup();
+	return r;
+	}
+
+void DEventHandler::Cleanup()
+	{
+	if (iLock)
+		iLock->Close(NULL);
+	if (iDevice)
+		iDevice->Close(NULL);
+	if (iClientRequestSwExc)
+		{
+		Kern::DestroyClientRequest(iClientRequestSwExc);
+		iClientRequestSwExc = NULL;
+		}
+	if (iClientRequestHwExc)
+		{
+		Kern::DestroyClientRequest(iClientRequestHwExc);
+		iClientRequestHwExc = NULL;
+		}
+	if (iClientRequestDeath)
+		{
+		Kern::DestroyClientRequest(iClientRequestDeath);
+		iClientRequestDeath = NULL;
+		}
+	}
+
+DEventHandler::~DEventHandler()
+	{
+	Cleanup();
+	}
+
+
+void DEventHandler::Cancel()
+	{
+	Kern::MutexWait(*iLock);
+	if (iHwExcStatusPtr)
+		{
+		Kern::QueueRequestComplete(iClientThread, iClientRequestHwExc, KErrCancel);
+		iHwExcStatusPtr = NULL;
+		}
+	if (iSwExcStatusPtr)
+		{
+		Kern::QueueRequestComplete(iClientThread, iClientRequestSwExc, KErrCancel);
+		iSwExcStatusPtr = NULL;
+		}
+	Kern::MutexSignal(*iLock);
+	}
+
+
+TUint DEventHandler::EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis)
+	{
+	return ((DEventHandler*)aThis)->HandleEvent(aEvent, a1, a2);
+	}
+
+
+// called in thread CS
+TUint DEventHandler::HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2)
+	{
+	// Ensure handler always called in thread critical section
+	NThread& nt = Kern::CurrentThread().iNThread;
+	__ASSERT_ALWAYS(nt.iCsCount != 0, (NKern::Lock(),*(TInt*)0xfaece5=0));
+
+	TUint r = DKernelEventHandler::ERunNext;
+
+	switch (aType)
+		{
+	case EEventSwExc:
+		// HACK, EVIL UNSAFE MEMORY ACCESS FOLLOWS...
+		TInt counter;
+		// folowing will kill system if memory is bad (because we're in a critical section)
+		umemget32(&counter, iSwExcCounterPtr, sizeof(TInt*));
+		++counter;
+		umemput32(iSwExcCounterPtr, &counter, sizeof(TInt));
+
+		Kern::MutexWait(*iLock);
+		iSwExcLastType = (TExcType)(TInt)a1;
+		if (iSwExcStatusPtr)
+			HandleException(iSwExcStatusPtr, iClientRequestSwExc);
+		Kern::MutexSignal(*iLock);
+		break;
+	case EEventHwExc:
+		Kern::MutexWait(*iLock);
+		if (iHwExcStatusPtr)
+			if (HandleException(iHwExcStatusPtr, iClientRequestHwExc))
+				r |= (TUint)EExcHandled;
+		Kern::MutexSignal(*iLock);
+		break;
+	case EEventKillThread:
+		Kern::MutexWait(*iLock);
+		HandleThreadDeath();
+		Kern::MutexSignal(*iLock);
+		break;
+	default:
+		// NO-OP
+		break;
+		}
+	
+	return r;
+	}
+
+
+// called in thread CS
+TBool DEventHandler::HandleException(TRequestStatus*& aStatusPtr, TClientRequest*& aRequestPtr)
+	{
+	DThread& t = Kern::CurrentThread();
+	TBool handled = EFalse;
+
+	if (iExcThreadId == t.iId)
+		{
+		Kern::Printf("Trapped exception");
+		TArmRegSet context1;
+		TUint32 availmask;
+		NKern::ThreadGetUserContext(&t.iNThread, &context1, availmask);
+		XTRAPD(r, XT_DEFAULT, umemput(iExcContextPtr, &context1, sizeof(context1)));
+
+		if (r == KErrNone)
+			{
+			if (iExcKillThread)
+				{
+				// We must preserve PC for software exceptions because execution
+				// goes back user-side and only then is the thread panicked.
+				TArmReg r15usr = context1.iR15;
+				ModifyContext(context1);
+				context1.iR15 = r15usr;
+				NKern::ThreadSetUserContext(&t.iNThread, &context1);
+
+				TArmRegSet context2;
+				memclr(&context2, sizeof context2);
+				NKern::ThreadGetUserContext(&t.iNThread, &context2, availmask);
+				r = CheckSetContext(context1, context2, availmask);
+				}
+			else
+				{
+				Kern::ThreadSuspend(t, 1);
+				handled = ETrue;
+				}
+			}
+		Kern::QueueRequestComplete(iClientThread, aRequestPtr, r);
+		aStatusPtr = NULL;
+		}
+	return handled;
+	}
+
+
+// called in thread CS
+void DEventHandler::HandleThreadDeath()
+	{
+	DThread& t = Kern::CurrentThread();
+	if (iDeathStatusPtr && iDeathThreadId == t.iId)
+		{
+		Kern::Printf("Trapping thread death: %O", &t);
+		TArmRegSet context;
+		TUint32 unused;
+		NKern::ThreadGetUserContext(&t.iNThread, &context, unused);
+		XTRAPD(r, XT_DEFAULT, umemput(iDeathContextPtr, &context, sizeof(context)));
+		Kern::QueueRequestComplete(iClientThread, iClientRequestDeath, r);
+		iDeathStatusPtr = NULL;
+		}
+	}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DTestChannel : public DLogicalChannelBase
+	{
+public:
+	virtual ~DTestChannel();
+protected:
+	// from DLogicalChannelBase
+	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
+	virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
+private:
+	TInt SetAndGetBackContext(TUint aId, TAny* aContext);
+	TInt SetAndGetFullContext(TUint aId, TAny* aContext);
+	void GetContext(TUint aId, TAny* aContext);
+	void GetKernelContext(TUint aId, TAny* aContext);
+	void AddUserCallback(TUint aId, TUserCallbackState aCallback);
+private:
+	DEventHandler* iHandler;
+	};
+
+
+// called in thread critical section
+TInt DTestChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
+	{
+	return KErrNone;
+	}
+
+// called in thread critical section
+DTestChannel::~DTestChannel()
+	{
+	if (iHandler)
+		{
+		iHandler->Cancel();
+		iHandler->Close();
+		}
+	}
+
+TInt DTestChannel::Request(TInt aFunction, TAny* a1, TAny* a2)
+	{
+	RContextLdd::TTrapInfo info;
+	DThread* pT;
+	TInt r = KErrNone;
+	switch (aFunction)
+		{
+	case RContextLdd::EHook:
+		NKern::ThreadEnterCS();
+		iHandler = new DEventHandler;
+		if (!iHandler)
+			r = KErrNoMemory;
+		else
+			{
+			r = iHandler->Create(iDevice);
+			iHandler->iSwExcCounterPtr = (TInt*)a1;
+			}
+		NKern::ThreadLeaveCS();
+		break;
+	case RContextLdd::EGetLastExc:
+		r = iHandler->iSwExcLastType;
+		break;
+	case RContextLdd::ETrapNextSwExc:
+	case RContextLdd::ETrapNextHwExc:
+		umemget(&info, a1, sizeof(info));
+		if (aFunction == RContextLdd::ETrapNextHwExc)
+			{
+			__ASSERT_ALWAYS(iHandler->iSwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+			iHandler->iHwExcStatusPtr = info.iStatusPtr;
+			r = iHandler->iClientRequestHwExc->SetStatus(iHandler->iHwExcStatusPtr);
+			__ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+			}
+		else
+			{
+			__ASSERT_ALWAYS(iHandler->iHwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+			iHandler->iSwExcStatusPtr = info.iStatusPtr;
+			r = iHandler->iClientRequestSwExc->SetStatus(iHandler->iSwExcStatusPtr);
+			__ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+			}
+		iHandler->iExcThreadId = info.iThreadId;
+		iHandler->iExcContextPtr = info.iContextPtr;
+		iHandler->iExcKillThread = info.iKillThread;
+		break;
+	case RContextLdd::ETrapNextDeath:
+		umemget(&info, a1, sizeof(info));
+		iHandler->iDeathThreadId = info.iThreadId;
+		iHandler->iDeathContextPtr = info.iContextPtr;
+		iHandler->iDeathStatusPtr = info.iStatusPtr;
+		r = iHandler->iClientRequestDeath->SetStatus(iHandler->iDeathStatusPtr);
+		__ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+		break;
+	case RContextLdd::EGetContext:
+		GetContext((TUint)a1, a2);
+		break;
+	case RContextLdd::ESetGetContext:
+		r = SetAndGetBackContext((TUint)a1, a2);
+		break;
+	case RContextLdd::ESetGetFullContext:
+		r = SetAndGetFullContext((TUint)a1, a2);
+		break;
+	case RContextLdd::EGetKernelContext:
+		GetKernelContext((TUint)a1, a2);
+		break;
+	case RContextLdd::ESpinInKernel:
+		r = SpinInKernel((TBool)a1);
+		break;
+	case RContextLdd::EAddUserCallback:
+		AddUserCallback((TUint)a1, (TUserCallbackState)(TUint)a2);
+		break;
+	case RContextLdd::EResumeTrappedThread:
+		pT = ThreadFromId((TUint)a1);
+		Kern::ThreadResume(*pT);
+		break;
+	default:
+		Kern::PanicCurrentThread(KClientPanicCat, __LINE__);
+		break;
+		}
+	return r;
+	}
+
+
+TInt DTestChannel::SetAndGetBackContext(TUint aId, TAny* aContext)
+	{
+	DThread* pT = ThreadFromId(aId);
+	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+
+	// The following code assumes the inspected thread does not run between the
+	// set context and get context call.
+
+	TArmRegSet context1;
+	umemget(&context1, aContext, sizeof context1);
+	NKern::ThreadSetUserContext(&pT->iNThread, &context1);
+
+	TArmRegSet context2;
+	memclr(&context2, sizeof context2);
+	TUint32 availmask;
+	NKern::ThreadGetUserContext(&pT->iNThread, &context2, availmask);
+	umemput(aContext, &context2, sizeof context2);
+
+	return CheckSetContext(context1, context2, availmask);
+	}
+
+#ifdef __SMP__
+class NKTest
+	{
+public:
+	static TBool IsDead(NThreadBase* aT)
+		{ return aT->iWaitState.ThreadIsDead(); }
+	};
+#endif
+
+TInt DTestChannel::SetAndGetFullContext(TUint aId, TAny* aContext)
+	{
+	DThread* pT = ThreadFromId(aId);
+	__ASSERT_ALWAYS(pT != NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+	TBool dead;
+#ifdef __SMP__
+	dead = NKTest::IsDead(&pT->iNThread);
+#else
+	dead = pT->iNThread.iSpare1 == NThreadBase::EDead;
+#endif
+	__ASSERT_ALWAYS(dead, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+
+	TArmFullContext contextData;
+	umemget(&contextData, aContext, sizeof contextData);
+	NKern::ThreadSetUserContext(&pT->iNThread, &contextData.iUserContext);
+
+	NKern::ThreadGetUserContext(&pT->iNThread, &contextData.iUserContext, contextData.iUserAvail);
+	NKern::ThreadGetSystemContext(&pT->iNThread, &contextData.iSystemContext, contextData.iSystemAvail);
+
+	umemput(aContext, &contextData, sizeof contextData);
+
+	return KErrNone;
+	}
+
+void DTestChannel::GetContext(TUint aId, TAny* aContext)
+	{
+	DThread* pT = ThreadFromId(aId);
+	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+
+	TArmRegSet context;
+	memclr(&context, sizeof context);
+	TUint32 unused;
+	NKern::ThreadGetUserContext(&pT->iNThread, &context, unused);
+	umemput(aContext, &context, sizeof context);
+	}
+
+void DTestChannel::GetKernelContext(TUint aId, TAny* aContext)
+	{
+	DThread* pT = ThreadFromId(aId);
+	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+
+	TArmRegSet context;
+	memclr(&context, sizeof context);
+	TUint32 unused;
+	NKern::ThreadGetSystemContext(&pT->iNThread, &context, unused);
+	umemput(aContext, &context, sizeof context);
+	}
+
+void DTestChannel::AddUserCallback(TUint aId, TUserCallbackState aCallback)
+	{
+	DThread* pT = ThreadFromId(aId);
+	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
+
+	switch (aCallback)
+		{
+	case ESpinningCallback:
+		KernTest::Test(KernTest::EUserModeCallbackSpin, pT);
+		break;
+	case ESleepingCallback:
+		KernTest::Test(KernTest::EUserModeCallbackSleep, pT);
+		break;
+	default:
+		Kern::PanicCurrentThread(KClientPanicCat, __LINE__);
+		}
+	}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class DTestFactory : public DLogicalDevice
+	{
+public:
+	DTestFactory();
+	// from DLogicalDevice
+	virtual TInt Install();
+	virtual void GetCaps(TDes8& aDes) const;
+	virtual TInt Create(DLogicalChannelBase*& aChannel);
+	};
+
+DTestFactory::DTestFactory()
+    {
+    iVersion = RContextLdd::Version();
+    // iParseMask = 0; // no unit, no info, no PDD
+    // iUnitsMask = 0; // only one thing
+    }
+
+TInt DTestFactory::Create(DLogicalChannelBase*& aChannel)
+    {
+	aChannel=new DTestChannel;
+	return aChannel ? KErrNone : KErrNoMemory;
+    }
+
+TInt DTestFactory::Install()
+    {
+    return SetName(&KTestLddName);
+    }
+
+void DTestFactory::GetCaps(TDes8& /*aDes*/) const
+    {
+    }
+
+//////////////////////////////////////////////////////////////////////////////
+
+DECLARE_STANDARD_LDD()
+	{
+    return new DTestFactory;
+	}