diff -r 000000000000 -r a41df078684a kerneltest/e32test/debug/d_context.cpp --- /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 +#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; pOpen(); + 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; + }