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