Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// 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;
}