sl@0: // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32test\debug\d_context.cpp sl@0: // Test LDD exercising hardware/software exception hooks sl@0: // and get/set user context APIs. sl@0: // sl@0: // sl@0: sl@0: #include "platform.h" sl@0: #include sl@0: #include "kern_test.h" sl@0: #include "d_context.h" sl@0: sl@0: _LIT(KClientPanicCat, "D_CONTEXT"); sl@0: sl@0: extern TUint32 SpinInKernel(TBool); sl@0: sl@0: DThread* ThreadFromId(TUint aId) sl@0: { sl@0: // This is risky because the thread could die on us an the DThread* become invalid. sl@0: // We are relying on this never happening in our test code. sl@0: NKern::ThreadEnterCS(); sl@0: DObjectCon& threads=*Kern::Containers()[EThread]; sl@0: threads.Wait(); sl@0: DThread* thread = Kern::ThreadFromId(aId); sl@0: threads.Signal(); sl@0: NKern::ThreadLeaveCS(); sl@0: return thread; sl@0: } sl@0: sl@0: void ModifyContext(TArmRegSet& aContext) sl@0: { sl@0: TArmReg* end = (TArmReg*)(&aContext+1); sl@0: for (TArmReg* p = (TArmReg*)&aContext; pOpen(); sl@0: if (r != KErrNone) sl@0: goto error; sl@0: iDevice = aDevice; sl@0: iClientThread = &Kern::CurrentThread(); sl@0: r = Kern::CreateClientRequest(iClientRequestSwExc); sl@0: if (r != KErrNone) sl@0: goto error; sl@0: r = Kern::CreateClientRequest(iClientRequestHwExc); sl@0: if (r != KErrNone) sl@0: goto error; sl@0: r = Kern::CreateClientRequest(iClientRequestDeath); sl@0: if (r != KErrNone) sl@0: goto error; sl@0: r = Kern::MutexCreate(iLock, _L("EventHandlerLock"), KMutexOrdDebug); sl@0: if (r != KErrNone) sl@0: goto error; sl@0: return Add(); sl@0: error: sl@0: Cleanup(); sl@0: return r; sl@0: } sl@0: sl@0: void DEventHandler::Cleanup() sl@0: { sl@0: if (iLock) sl@0: iLock->Close(NULL); sl@0: if (iDevice) sl@0: iDevice->Close(NULL); sl@0: if (iClientRequestSwExc) sl@0: { sl@0: Kern::DestroyClientRequest(iClientRequestSwExc); sl@0: iClientRequestSwExc = NULL; sl@0: } sl@0: if (iClientRequestHwExc) sl@0: { sl@0: Kern::DestroyClientRequest(iClientRequestHwExc); sl@0: iClientRequestHwExc = NULL; sl@0: } sl@0: if (iClientRequestDeath) sl@0: { sl@0: Kern::DestroyClientRequest(iClientRequestDeath); sl@0: iClientRequestDeath = NULL; sl@0: } sl@0: } sl@0: sl@0: DEventHandler::~DEventHandler() sl@0: { sl@0: Cleanup(); sl@0: } sl@0: sl@0: sl@0: void DEventHandler::Cancel() sl@0: { sl@0: Kern::MutexWait(*iLock); sl@0: if (iHwExcStatusPtr) sl@0: { sl@0: Kern::QueueRequestComplete(iClientThread, iClientRequestHwExc, KErrCancel); sl@0: iHwExcStatusPtr = NULL; sl@0: } sl@0: if (iSwExcStatusPtr) sl@0: { sl@0: Kern::QueueRequestComplete(iClientThread, iClientRequestSwExc, KErrCancel); sl@0: iSwExcStatusPtr = NULL; sl@0: } sl@0: Kern::MutexSignal(*iLock); sl@0: } sl@0: sl@0: sl@0: TUint DEventHandler::EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis) sl@0: { sl@0: return ((DEventHandler*)aThis)->HandleEvent(aEvent, a1, a2); sl@0: } sl@0: sl@0: sl@0: // called in thread CS sl@0: TUint DEventHandler::HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2) sl@0: { sl@0: // Ensure handler always called in thread critical section sl@0: NThread& nt = Kern::CurrentThread().iNThread; sl@0: __ASSERT_ALWAYS(nt.iCsCount != 0, (NKern::Lock(),*(TInt*)0xfaece5=0)); sl@0: sl@0: TUint r = DKernelEventHandler::ERunNext; sl@0: sl@0: switch (aType) sl@0: { sl@0: case EEventSwExc: sl@0: // HACK, EVIL UNSAFE MEMORY ACCESS FOLLOWS... sl@0: TInt counter; sl@0: // folowing will kill system if memory is bad (because we're in a critical section) sl@0: umemget32(&counter, iSwExcCounterPtr, sizeof(TInt*)); sl@0: ++counter; sl@0: umemput32(iSwExcCounterPtr, &counter, sizeof(TInt)); sl@0: sl@0: Kern::MutexWait(*iLock); sl@0: iSwExcLastType = (TExcType)(TInt)a1; sl@0: if (iSwExcStatusPtr) sl@0: HandleException(iSwExcStatusPtr, iClientRequestSwExc); sl@0: Kern::MutexSignal(*iLock); sl@0: break; sl@0: case EEventHwExc: sl@0: Kern::MutexWait(*iLock); sl@0: if (iHwExcStatusPtr) sl@0: if (HandleException(iHwExcStatusPtr, iClientRequestHwExc)) sl@0: r |= (TUint)EExcHandled; sl@0: Kern::MutexSignal(*iLock); sl@0: break; sl@0: case EEventKillThread: sl@0: Kern::MutexWait(*iLock); sl@0: HandleThreadDeath(); sl@0: Kern::MutexSignal(*iLock); sl@0: break; sl@0: default: sl@0: // NO-OP sl@0: break; sl@0: } sl@0: sl@0: return r; sl@0: } sl@0: sl@0: sl@0: // called in thread CS sl@0: TBool DEventHandler::HandleException(TRequestStatus*& aStatusPtr, TClientRequest*& aRequestPtr) sl@0: { sl@0: DThread& t = Kern::CurrentThread(); sl@0: TBool handled = EFalse; sl@0: sl@0: if (iExcThreadId == t.iId) sl@0: { sl@0: Kern::Printf("Trapped exception"); sl@0: TArmRegSet context1; sl@0: TUint32 availmask; sl@0: NKern::ThreadGetUserContext(&t.iNThread, &context1, availmask); sl@0: XTRAPD(r, XT_DEFAULT, umemput(iExcContextPtr, &context1, sizeof(context1))); sl@0: sl@0: if (r == KErrNone) sl@0: { sl@0: if (iExcKillThread) sl@0: { sl@0: // We must preserve PC for software exceptions because execution sl@0: // goes back user-side and only then is the thread panicked. sl@0: TArmReg r15usr = context1.iR15; sl@0: ModifyContext(context1); sl@0: context1.iR15 = r15usr; sl@0: NKern::ThreadSetUserContext(&t.iNThread, &context1); sl@0: sl@0: TArmRegSet context2; sl@0: memclr(&context2, sizeof context2); sl@0: NKern::ThreadGetUserContext(&t.iNThread, &context2, availmask); sl@0: r = CheckSetContext(context1, context2, availmask); sl@0: } sl@0: else sl@0: { sl@0: Kern::ThreadSuspend(t, 1); sl@0: handled = ETrue; sl@0: } sl@0: } sl@0: Kern::QueueRequestComplete(iClientThread, aRequestPtr, r); sl@0: aStatusPtr = NULL; sl@0: } sl@0: return handled; sl@0: } sl@0: sl@0: sl@0: // called in thread CS sl@0: void DEventHandler::HandleThreadDeath() sl@0: { sl@0: DThread& t = Kern::CurrentThread(); sl@0: if (iDeathStatusPtr && iDeathThreadId == t.iId) sl@0: { sl@0: Kern::Printf("Trapping thread death: %O", &t); sl@0: TArmRegSet context; sl@0: TUint32 unused; sl@0: NKern::ThreadGetUserContext(&t.iNThread, &context, unused); sl@0: XTRAPD(r, XT_DEFAULT, umemput(iDeathContextPtr, &context, sizeof(context))); sl@0: Kern::QueueRequestComplete(iClientThread, iClientRequestDeath, r); sl@0: iDeathStatusPtr = NULL; sl@0: } sl@0: } sl@0: sl@0: ////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: class DTestChannel : public DLogicalChannelBase sl@0: { sl@0: public: sl@0: virtual ~DTestChannel(); sl@0: protected: sl@0: // from DLogicalChannelBase sl@0: virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer); sl@0: virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2); sl@0: private: sl@0: TInt SetAndGetBackContext(TUint aId, TAny* aContext); sl@0: TInt SetAndGetFullContext(TUint aId, TAny* aContext); sl@0: void GetContext(TUint aId, TAny* aContext); sl@0: void GetKernelContext(TUint aId, TAny* aContext); sl@0: void AddUserCallback(TUint aId, TUserCallbackState aCallback); sl@0: private: sl@0: DEventHandler* iHandler; sl@0: }; sl@0: sl@0: sl@0: // called in thread critical section sl@0: TInt DTestChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/) sl@0: { sl@0: return KErrNone; sl@0: } sl@0: sl@0: // called in thread critical section sl@0: DTestChannel::~DTestChannel() sl@0: { sl@0: if (iHandler) sl@0: { sl@0: iHandler->Cancel(); sl@0: iHandler->Close(); sl@0: } sl@0: } sl@0: sl@0: TInt DTestChannel::Request(TInt aFunction, TAny* a1, TAny* a2) sl@0: { sl@0: RContextLdd::TTrapInfo info; sl@0: DThread* pT; sl@0: TInt r = KErrNone; sl@0: switch (aFunction) sl@0: { sl@0: case RContextLdd::EHook: sl@0: NKern::ThreadEnterCS(); sl@0: iHandler = new DEventHandler; sl@0: if (!iHandler) sl@0: r = KErrNoMemory; sl@0: else sl@0: { sl@0: r = iHandler->Create(iDevice); sl@0: iHandler->iSwExcCounterPtr = (TInt*)a1; sl@0: } sl@0: NKern::ThreadLeaveCS(); sl@0: break; sl@0: case RContextLdd::EGetLastExc: sl@0: r = iHandler->iSwExcLastType; sl@0: break; sl@0: case RContextLdd::ETrapNextSwExc: sl@0: case RContextLdd::ETrapNextHwExc: sl@0: umemget(&info, a1, sizeof(info)); sl@0: if (aFunction == RContextLdd::ETrapNextHwExc) sl@0: { sl@0: __ASSERT_ALWAYS(iHandler->iSwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: iHandler->iHwExcStatusPtr = info.iStatusPtr; sl@0: r = iHandler->iClientRequestHwExc->SetStatus(iHandler->iHwExcStatusPtr); sl@0: __ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: } sl@0: else sl@0: { sl@0: __ASSERT_ALWAYS(iHandler->iHwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: iHandler->iSwExcStatusPtr = info.iStatusPtr; sl@0: r = iHandler->iClientRequestSwExc->SetStatus(iHandler->iSwExcStatusPtr); sl@0: __ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: } sl@0: iHandler->iExcThreadId = info.iThreadId; sl@0: iHandler->iExcContextPtr = info.iContextPtr; sl@0: iHandler->iExcKillThread = info.iKillThread; sl@0: break; sl@0: case RContextLdd::ETrapNextDeath: sl@0: umemget(&info, a1, sizeof(info)); sl@0: iHandler->iDeathThreadId = info.iThreadId; sl@0: iHandler->iDeathContextPtr = info.iContextPtr; sl@0: iHandler->iDeathStatusPtr = info.iStatusPtr; sl@0: r = iHandler->iClientRequestDeath->SetStatus(iHandler->iDeathStatusPtr); sl@0: __ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: break; sl@0: case RContextLdd::EGetContext: sl@0: GetContext((TUint)a1, a2); sl@0: break; sl@0: case RContextLdd::ESetGetContext: sl@0: r = SetAndGetBackContext((TUint)a1, a2); sl@0: break; sl@0: case RContextLdd::ESetGetFullContext: sl@0: r = SetAndGetFullContext((TUint)a1, a2); sl@0: break; sl@0: case RContextLdd::EGetKernelContext: sl@0: GetKernelContext((TUint)a1, a2); sl@0: break; sl@0: case RContextLdd::ESpinInKernel: sl@0: r = SpinInKernel((TBool)a1); sl@0: break; sl@0: case RContextLdd::EAddUserCallback: sl@0: AddUserCallback((TUint)a1, (TUserCallbackState)(TUint)a2); sl@0: break; sl@0: case RContextLdd::EResumeTrappedThread: sl@0: pT = ThreadFromId((TUint)a1); sl@0: Kern::ThreadResume(*pT); sl@0: break; sl@0: default: sl@0: Kern::PanicCurrentThread(KClientPanicCat, __LINE__); sl@0: break; sl@0: } sl@0: return r; sl@0: } sl@0: sl@0: sl@0: TInt DTestChannel::SetAndGetBackContext(TUint aId, TAny* aContext) sl@0: { sl@0: DThread* pT = ThreadFromId(aId); sl@0: __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: sl@0: // The following code assumes the inspected thread does not run between the sl@0: // set context and get context call. sl@0: sl@0: TArmRegSet context1; sl@0: umemget(&context1, aContext, sizeof context1); sl@0: NKern::ThreadSetUserContext(&pT->iNThread, &context1); sl@0: sl@0: TArmRegSet context2; sl@0: memclr(&context2, sizeof context2); sl@0: TUint32 availmask; sl@0: NKern::ThreadGetUserContext(&pT->iNThread, &context2, availmask); sl@0: umemput(aContext, &context2, sizeof context2); sl@0: sl@0: return CheckSetContext(context1, context2, availmask); sl@0: } sl@0: sl@0: #ifdef __SMP__ sl@0: class NKTest sl@0: { sl@0: public: sl@0: static TBool IsDead(NThreadBase* aT) sl@0: { return aT->iWaitState.ThreadIsDead(); } sl@0: }; sl@0: #endif sl@0: sl@0: TInt DTestChannel::SetAndGetFullContext(TUint aId, TAny* aContext) sl@0: { sl@0: DThread* pT = ThreadFromId(aId); sl@0: __ASSERT_ALWAYS(pT != NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: TBool dead; sl@0: #ifdef __SMP__ sl@0: dead = NKTest::IsDead(&pT->iNThread); sl@0: #else sl@0: dead = pT->iNThread.iSpare1 == NThreadBase::EDead; sl@0: #endif sl@0: __ASSERT_ALWAYS(dead, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: sl@0: TArmFullContext contextData; sl@0: umemget(&contextData, aContext, sizeof contextData); sl@0: NKern::ThreadSetUserContext(&pT->iNThread, &contextData.iUserContext); sl@0: sl@0: NKern::ThreadGetUserContext(&pT->iNThread, &contextData.iUserContext, contextData.iUserAvail); sl@0: NKern::ThreadGetSystemContext(&pT->iNThread, &contextData.iSystemContext, contextData.iSystemAvail); sl@0: sl@0: umemput(aContext, &contextData, sizeof contextData); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: void DTestChannel::GetContext(TUint aId, TAny* aContext) sl@0: { sl@0: DThread* pT = ThreadFromId(aId); sl@0: __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: sl@0: TArmRegSet context; sl@0: memclr(&context, sizeof context); sl@0: TUint32 unused; sl@0: NKern::ThreadGetUserContext(&pT->iNThread, &context, unused); sl@0: umemput(aContext, &context, sizeof context); sl@0: } sl@0: sl@0: void DTestChannel::GetKernelContext(TUint aId, TAny* aContext) sl@0: { sl@0: DThread* pT = ThreadFromId(aId); sl@0: __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: sl@0: TArmRegSet context; sl@0: memclr(&context, sizeof context); sl@0: TUint32 unused; sl@0: NKern::ThreadGetSystemContext(&pT->iNThread, &context, unused); sl@0: umemput(aContext, &context, sizeof context); sl@0: } sl@0: sl@0: void DTestChannel::AddUserCallback(TUint aId, TUserCallbackState aCallback) sl@0: { sl@0: DThread* pT = ThreadFromId(aId); sl@0: __ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__)); sl@0: sl@0: switch (aCallback) sl@0: { sl@0: case ESpinningCallback: sl@0: KernTest::Test(KernTest::EUserModeCallbackSpin, pT); sl@0: break; sl@0: case ESleepingCallback: sl@0: KernTest::Test(KernTest::EUserModeCallbackSleep, pT); sl@0: break; sl@0: default: sl@0: Kern::PanicCurrentThread(KClientPanicCat, __LINE__); sl@0: } sl@0: } sl@0: sl@0: ////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: class DTestFactory : public DLogicalDevice sl@0: { sl@0: public: sl@0: DTestFactory(); sl@0: // from DLogicalDevice sl@0: virtual TInt Install(); sl@0: virtual void GetCaps(TDes8& aDes) const; sl@0: virtual TInt Create(DLogicalChannelBase*& aChannel); sl@0: }; sl@0: sl@0: DTestFactory::DTestFactory() sl@0: { sl@0: iVersion = RContextLdd::Version(); sl@0: // iParseMask = 0; // no unit, no info, no PDD sl@0: // iUnitsMask = 0; // only one thing sl@0: } sl@0: sl@0: TInt DTestFactory::Create(DLogicalChannelBase*& aChannel) sl@0: { sl@0: aChannel=new DTestChannel; sl@0: return aChannel ? KErrNone : KErrNoMemory; sl@0: } sl@0: sl@0: TInt DTestFactory::Install() sl@0: { sl@0: return SetName(&KTestLddName); sl@0: } sl@0: sl@0: void DTestFactory::GetCaps(TDes8& /*aDes*/) const sl@0: { sl@0: } sl@0: sl@0: ////////////////////////////////////////////////////////////////////////////// sl@0: sl@0: DECLARE_STANDARD_LDD() sl@0: { sl@0: return new DTestFactory; sl@0: }