os/kernelhwsrv/kerneltest/e32test/debug/d_context.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // e32test\debug\d_context.cpp
    15 // Test LDD exercising hardware/software exception hooks 
    16 // and get/set user context APIs.
    17 // 
    18 //
    19 
    20 #include "platform.h"
    21 #include <kernel/kern_priv.h>
    22 #include "kern_test.h"
    23 #include "d_context.h"
    24 
    25 _LIT(KClientPanicCat, "D_CONTEXT");
    26 
    27 extern TUint32 SpinInKernel(TBool);
    28 
    29 DThread* ThreadFromId(TUint aId)
    30 	{
    31 	// This is risky because the thread could die on us an the DThread* become invalid.
    32 	// We are relying on this never happening in our test code.
    33 	NKern::ThreadEnterCS(); 
    34 	DObjectCon& threads=*Kern::Containers()[EThread];
    35 	threads.Wait(); 
    36 	DThread* thread = Kern::ThreadFromId(aId);
    37 	threads.Signal();
    38 	NKern::ThreadLeaveCS(); 
    39 	return thread;
    40 	}
    41 
    42 void ModifyContext(TArmRegSet& aContext)
    43 	{
    44 	TArmReg* end = (TArmReg*)(&aContext+1);
    45 	for (TArmReg* p = (TArmReg*)&aContext; p<end; ++p)
    46 		*p = ~*p;
    47 	}
    48 
    49 void DumpContext(TArmRegSet& aContext)
    50 	{
    51 	Kern::Printf("  r0 =%08x r1 =%08x r2 =%08x r3 =%08x",aContext.iR0,aContext.iR1,aContext.iR2,aContext.iR3);
    52 	Kern::Printf("  r4 =%08x r5 =%08x r6 =%08x r7 =%08x",aContext.iR4,aContext.iR5,aContext.iR6,aContext.iR7);
    53 	Kern::Printf("  r8 =%08x r9 =%08x r10=%08x r11=%08x",aContext.iR8,aContext.iR9,aContext.iR10,aContext.iR11);
    54 	Kern::Printf("  r12=%08x r13=%08x r14=%08x r15=%08x",aContext.iR12,aContext.iR13,aContext.iR14,aContext.iR15);
    55 	Kern::Printf("  cpsr=%08x dacr=%08x",aContext.iFlags, aContext.iDacr);
    56 	}
    57 
    58 inline TBool IsRegisterAvailable(TInt aReg, TUint32 aMask)
    59 	{
    60 	return aMask & (1<<aReg);
    61 	}
    62 
    63 TInt CheckSetContext(const TArmRegSet& aSetContext, const TArmRegSet& aContextAfterSet, TUint32 aAvailMask)
    64 	{
    65 	Kern::Printf("Checking all available registers have been modified (0%08x)", aAvailMask);
    66 
    67 	const TArmReg* pSet = (const TArmReg*)&aSetContext;
    68 	const TArmReg* pAfterSet = (const TArmReg*)&aContextAfterSet;
    69 
    70 	for (int i=0; i<=EArmPc; ++i)
    71 		{
    72 		if (pAfterSet[i] == 0 && IsRegisterAvailable(i, aAvailMask) && pSet[i] != 0)
    73 			{
    74 			Kern::Printf("Register %d not set (expected %08x actual %08x)", i, pSet[i], pAfterSet[i]);
    75 			return KErrCorrupt;
    76 			}
    77 		if (pAfterSet[i] != 0 && ! IsRegisterAvailable(i, aAvailMask))
    78 			{
    79 			Kern::Printf("Register %d incorrectly set (expected %08x actual %08x)", i, 0, pAfterSet[i]);
    80 			return KErrCorrupt;
    81 			}
    82 		}
    83 
    84 	return KErrNone;
    85 	}
    86 
    87 //////////////////////////////////////////////////////////////////////////////
    88 
    89 class DEventHandler : public DKernelEventHandler
    90 	{
    91 public:
    92 	DEventHandler();
    93 	TInt Create(DLogicalDevice* aDevice);
    94 	~DEventHandler();
    95 	void Cancel();
    96 private:
    97 	static TUint EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis);
    98 	TUint HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2);
    99 	TBool HandleException(TRequestStatus*& aStatusPtr, TClientRequest*& aRequestPtr);
   100 	void HandleThreadDeath();
   101 	void Cleanup();
   102 private:
   103 	DThread* iClientThread;
   104 	DMutex* iLock; // serialise calls to handler
   105 public:
   106 	DLogicalDevice* iDevice;	// open reference to LDD for avoiding lifetime issues
   107 	// software exception fields
   108 	TExcType iSwExcLastType;
   109 	TInt* iSwExcCounterPtr;
   110 	TRequestStatus* iSwExcStatusPtr;
   111 	TClientRequest* iClientRequestSwExc;
   112 	// hardware exception fields
   113 	TRequestStatus* iHwExcStatusPtr;
   114 	TClientRequest* iClientRequestHwExc;
   115 	// fields used for both hardware and software exceptions
   116 	TUint iExcThreadId;
   117 	TAny* iExcContextPtr;
   118 	TBool iExcKillThread;
   119 	// thread death event fields
   120 	TUint iDeathThreadId;
   121 	TRequestStatus* iDeathStatusPtr;
   122 	TAny* iDeathContextPtr;
   123 	TClientRequest* iClientRequestDeath;
   124 	};
   125 
   126 
   127 DEventHandler::DEventHandler() 
   128 	: 	DKernelEventHandler(EventHandler, this) 
   129 	{
   130 	}
   131 
   132 
   133 TInt DEventHandler::Create(DLogicalDevice* aDevice)
   134 	{
   135 	TInt r;
   136 	r = aDevice->Open();
   137 	if (r != KErrNone)
   138 		goto error;
   139 	iDevice = aDevice;
   140 	iClientThread = &Kern::CurrentThread();
   141 	r = Kern::CreateClientRequest(iClientRequestSwExc);
   142 	if (r != KErrNone)
   143 		goto error;
   144 	r = Kern::CreateClientRequest(iClientRequestHwExc);
   145 	if (r != KErrNone)
   146 		goto error;
   147 	r = Kern::CreateClientRequest(iClientRequestDeath);
   148 	if (r != KErrNone)
   149 		goto error;
   150 	r = Kern::MutexCreate(iLock, _L("EventHandlerLock"), KMutexOrdDebug);
   151 	if (r != KErrNone)
   152 		goto error;
   153 	return Add();
   154 error:
   155 	Cleanup();
   156 	return r;
   157 	}
   158 
   159 void DEventHandler::Cleanup()
   160 	{
   161 	if (iLock)
   162 		iLock->Close(NULL);
   163 	if (iDevice)
   164 		iDevice->Close(NULL);
   165 	if (iClientRequestSwExc)
   166 		{
   167 		Kern::DestroyClientRequest(iClientRequestSwExc);
   168 		iClientRequestSwExc = NULL;
   169 		}
   170 	if (iClientRequestHwExc)
   171 		{
   172 		Kern::DestroyClientRequest(iClientRequestHwExc);
   173 		iClientRequestHwExc = NULL;
   174 		}
   175 	if (iClientRequestDeath)
   176 		{
   177 		Kern::DestroyClientRequest(iClientRequestDeath);
   178 		iClientRequestDeath = NULL;
   179 		}
   180 	}
   181 
   182 DEventHandler::~DEventHandler()
   183 	{
   184 	Cleanup();
   185 	}
   186 
   187 
   188 void DEventHandler::Cancel()
   189 	{
   190 	Kern::MutexWait(*iLock);
   191 	if (iHwExcStatusPtr)
   192 		{
   193 		Kern::QueueRequestComplete(iClientThread, iClientRequestHwExc, KErrCancel);
   194 		iHwExcStatusPtr = NULL;
   195 		}
   196 	if (iSwExcStatusPtr)
   197 		{
   198 		Kern::QueueRequestComplete(iClientThread, iClientRequestSwExc, KErrCancel);
   199 		iSwExcStatusPtr = NULL;
   200 		}
   201 	Kern::MutexSignal(*iLock);
   202 	}
   203 
   204 
   205 TUint DEventHandler::EventHandler(TKernelEvent aEvent, TAny* a1, TAny* a2, TAny* aThis)
   206 	{
   207 	return ((DEventHandler*)aThis)->HandleEvent(aEvent, a1, a2);
   208 	}
   209 
   210 
   211 // called in thread CS
   212 TUint DEventHandler::HandleEvent(TKernelEvent aType, TAny* a1, TAny* a2)
   213 	{
   214 	// Ensure handler always called in thread critical section
   215 	NThread& nt = Kern::CurrentThread().iNThread;
   216 	__ASSERT_ALWAYS(nt.iCsCount != 0, (NKern::Lock(),*(TInt*)0xfaece5=0));
   217 
   218 	TUint r = DKernelEventHandler::ERunNext;
   219 
   220 	switch (aType)
   221 		{
   222 	case EEventSwExc:
   223 		// HACK, EVIL UNSAFE MEMORY ACCESS FOLLOWS...
   224 		TInt counter;
   225 		// folowing will kill system if memory is bad (because we're in a critical section)
   226 		umemget32(&counter, iSwExcCounterPtr, sizeof(TInt*));
   227 		++counter;
   228 		umemput32(iSwExcCounterPtr, &counter, sizeof(TInt));
   229 
   230 		Kern::MutexWait(*iLock);
   231 		iSwExcLastType = (TExcType)(TInt)a1;
   232 		if (iSwExcStatusPtr)
   233 			HandleException(iSwExcStatusPtr, iClientRequestSwExc);
   234 		Kern::MutexSignal(*iLock);
   235 		break;
   236 	case EEventHwExc:
   237 		Kern::MutexWait(*iLock);
   238 		if (iHwExcStatusPtr)
   239 			if (HandleException(iHwExcStatusPtr, iClientRequestHwExc))
   240 				r |= (TUint)EExcHandled;
   241 		Kern::MutexSignal(*iLock);
   242 		break;
   243 	case EEventKillThread:
   244 		Kern::MutexWait(*iLock);
   245 		HandleThreadDeath();
   246 		Kern::MutexSignal(*iLock);
   247 		break;
   248 	default:
   249 		// NO-OP
   250 		break;
   251 		}
   252 	
   253 	return r;
   254 	}
   255 
   256 
   257 // called in thread CS
   258 TBool DEventHandler::HandleException(TRequestStatus*& aStatusPtr, TClientRequest*& aRequestPtr)
   259 	{
   260 	DThread& t = Kern::CurrentThread();
   261 	TBool handled = EFalse;
   262 
   263 	if (iExcThreadId == t.iId)
   264 		{
   265 		Kern::Printf("Trapped exception");
   266 		TArmRegSet context1;
   267 		TUint32 availmask;
   268 		NKern::ThreadGetUserContext(&t.iNThread, &context1, availmask);
   269 		XTRAPD(r, XT_DEFAULT, umemput(iExcContextPtr, &context1, sizeof(context1)));
   270 
   271 		if (r == KErrNone)
   272 			{
   273 			if (iExcKillThread)
   274 				{
   275 				// We must preserve PC for software exceptions because execution
   276 				// goes back user-side and only then is the thread panicked.
   277 				TArmReg r15usr = context1.iR15;
   278 				ModifyContext(context1);
   279 				context1.iR15 = r15usr;
   280 				NKern::ThreadSetUserContext(&t.iNThread, &context1);
   281 
   282 				TArmRegSet context2;
   283 				memclr(&context2, sizeof context2);
   284 				NKern::ThreadGetUserContext(&t.iNThread, &context2, availmask);
   285 				r = CheckSetContext(context1, context2, availmask);
   286 				}
   287 			else
   288 				{
   289 				Kern::ThreadSuspend(t, 1);
   290 				handled = ETrue;
   291 				}
   292 			}
   293 		Kern::QueueRequestComplete(iClientThread, aRequestPtr, r);
   294 		aStatusPtr = NULL;
   295 		}
   296 	return handled;
   297 	}
   298 
   299 
   300 // called in thread CS
   301 void DEventHandler::HandleThreadDeath()
   302 	{
   303 	DThread& t = Kern::CurrentThread();
   304 	if (iDeathStatusPtr && iDeathThreadId == t.iId)
   305 		{
   306 		Kern::Printf("Trapping thread death: %O", &t);
   307 		TArmRegSet context;
   308 		TUint32 unused;
   309 		NKern::ThreadGetUserContext(&t.iNThread, &context, unused);
   310 		XTRAPD(r, XT_DEFAULT, umemput(iDeathContextPtr, &context, sizeof(context)));
   311 		Kern::QueueRequestComplete(iClientThread, iClientRequestDeath, r);
   312 		iDeathStatusPtr = NULL;
   313 		}
   314 	}
   315 
   316 //////////////////////////////////////////////////////////////////////////////
   317 
   318 class DTestChannel : public DLogicalChannelBase
   319 	{
   320 public:
   321 	virtual ~DTestChannel();
   322 protected:
   323 	// from DLogicalChannelBase
   324 	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
   325 	virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);
   326 private:
   327 	TInt SetAndGetBackContext(TUint aId, TAny* aContext);
   328 	TInt SetAndGetFullContext(TUint aId, TAny* aContext);
   329 	void GetContext(TUint aId, TAny* aContext);
   330 	void GetKernelContext(TUint aId, TAny* aContext);
   331 	void AddUserCallback(TUint aId, TUserCallbackState aCallback);
   332 private:
   333 	DEventHandler* iHandler;
   334 	};
   335 
   336 
   337 // called in thread critical section
   338 TInt DTestChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& /*aVer*/)
   339 	{
   340 	return KErrNone;
   341 	}
   342 
   343 // called in thread critical section
   344 DTestChannel::~DTestChannel()
   345 	{
   346 	if (iHandler)
   347 		{
   348 		iHandler->Cancel();
   349 		iHandler->Close();
   350 		}
   351 	}
   352 
   353 TInt DTestChannel::Request(TInt aFunction, TAny* a1, TAny* a2)
   354 	{
   355 	RContextLdd::TTrapInfo info;
   356 	DThread* pT;
   357 	TInt r = KErrNone;
   358 	switch (aFunction)
   359 		{
   360 	case RContextLdd::EHook:
   361 		NKern::ThreadEnterCS();
   362 		iHandler = new DEventHandler;
   363 		if (!iHandler)
   364 			r = KErrNoMemory;
   365 		else
   366 			{
   367 			r = iHandler->Create(iDevice);
   368 			iHandler->iSwExcCounterPtr = (TInt*)a1;
   369 			}
   370 		NKern::ThreadLeaveCS();
   371 		break;
   372 	case RContextLdd::EGetLastExc:
   373 		r = iHandler->iSwExcLastType;
   374 		break;
   375 	case RContextLdd::ETrapNextSwExc:
   376 	case RContextLdd::ETrapNextHwExc:
   377 		umemget(&info, a1, sizeof(info));
   378 		if (aFunction == RContextLdd::ETrapNextHwExc)
   379 			{
   380 			__ASSERT_ALWAYS(iHandler->iSwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   381 			iHandler->iHwExcStatusPtr = info.iStatusPtr;
   382 			r = iHandler->iClientRequestHwExc->SetStatus(iHandler->iHwExcStatusPtr);
   383 			__ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   384 			}
   385 		else
   386 			{
   387 			__ASSERT_ALWAYS(iHandler->iHwExcStatusPtr==NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   388 			iHandler->iSwExcStatusPtr = info.iStatusPtr;
   389 			r = iHandler->iClientRequestSwExc->SetStatus(iHandler->iSwExcStatusPtr);
   390 			__ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   391 			}
   392 		iHandler->iExcThreadId = info.iThreadId;
   393 		iHandler->iExcContextPtr = info.iContextPtr;
   394 		iHandler->iExcKillThread = info.iKillThread;
   395 		break;
   396 	case RContextLdd::ETrapNextDeath:
   397 		umemget(&info, a1, sizeof(info));
   398 		iHandler->iDeathThreadId = info.iThreadId;
   399 		iHandler->iDeathContextPtr = info.iContextPtr;
   400 		iHandler->iDeathStatusPtr = info.iStatusPtr;
   401 		r = iHandler->iClientRequestDeath->SetStatus(iHandler->iDeathStatusPtr);
   402 		__ASSERT_ALWAYS(r==KErrNone, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   403 		break;
   404 	case RContextLdd::EGetContext:
   405 		GetContext((TUint)a1, a2);
   406 		break;
   407 	case RContextLdd::ESetGetContext:
   408 		r = SetAndGetBackContext((TUint)a1, a2);
   409 		break;
   410 	case RContextLdd::ESetGetFullContext:
   411 		r = SetAndGetFullContext((TUint)a1, a2);
   412 		break;
   413 	case RContextLdd::EGetKernelContext:
   414 		GetKernelContext((TUint)a1, a2);
   415 		break;
   416 	case RContextLdd::ESpinInKernel:
   417 		r = SpinInKernel((TBool)a1);
   418 		break;
   419 	case RContextLdd::EAddUserCallback:
   420 		AddUserCallback((TUint)a1, (TUserCallbackState)(TUint)a2);
   421 		break;
   422 	case RContextLdd::EResumeTrappedThread:
   423 		pT = ThreadFromId((TUint)a1);
   424 		Kern::ThreadResume(*pT);
   425 		break;
   426 	default:
   427 		Kern::PanicCurrentThread(KClientPanicCat, __LINE__);
   428 		break;
   429 		}
   430 	return r;
   431 	}
   432 
   433 
   434 TInt DTestChannel::SetAndGetBackContext(TUint aId, TAny* aContext)
   435 	{
   436 	DThread* pT = ThreadFromId(aId);
   437 	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   438 
   439 	// The following code assumes the inspected thread does not run between the
   440 	// set context and get context call.
   441 
   442 	TArmRegSet context1;
   443 	umemget(&context1, aContext, sizeof context1);
   444 	NKern::ThreadSetUserContext(&pT->iNThread, &context1);
   445 
   446 	TArmRegSet context2;
   447 	memclr(&context2, sizeof context2);
   448 	TUint32 availmask;
   449 	NKern::ThreadGetUserContext(&pT->iNThread, &context2, availmask);
   450 	umemput(aContext, &context2, sizeof context2);
   451 
   452 	return CheckSetContext(context1, context2, availmask);
   453 	}
   454 
   455 #ifdef __SMP__
   456 class NKTest
   457 	{
   458 public:
   459 	static TBool IsDead(NThreadBase* aT)
   460 		{ return aT->iWaitState.ThreadIsDead(); }
   461 	};
   462 #endif
   463 
   464 TInt DTestChannel::SetAndGetFullContext(TUint aId, TAny* aContext)
   465 	{
   466 	DThread* pT = ThreadFromId(aId);
   467 	__ASSERT_ALWAYS(pT != NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   468 	TBool dead;
   469 #ifdef __SMP__
   470 	dead = NKTest::IsDead(&pT->iNThread);
   471 #else
   472 	dead = pT->iNThread.iSpare1 == NThreadBase::EDead;
   473 #endif
   474 	__ASSERT_ALWAYS(dead, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   475 
   476 	TArmFullContext contextData;
   477 	umemget(&contextData, aContext, sizeof contextData);
   478 	NKern::ThreadSetUserContext(&pT->iNThread, &contextData.iUserContext);
   479 
   480 	NKern::ThreadGetUserContext(&pT->iNThread, &contextData.iUserContext, contextData.iUserAvail);
   481 	NKern::ThreadGetSystemContext(&pT->iNThread, &contextData.iSystemContext, contextData.iSystemAvail);
   482 
   483 	umemput(aContext, &contextData, sizeof contextData);
   484 
   485 	return KErrNone;
   486 	}
   487 
   488 void DTestChannel::GetContext(TUint aId, TAny* aContext)
   489 	{
   490 	DThread* pT = ThreadFromId(aId);
   491 	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   492 
   493 	TArmRegSet context;
   494 	memclr(&context, sizeof context);
   495 	TUint32 unused;
   496 	NKern::ThreadGetUserContext(&pT->iNThread, &context, unused);
   497 	umemput(aContext, &context, sizeof context);
   498 	}
   499 
   500 void DTestChannel::GetKernelContext(TUint aId, TAny* aContext)
   501 	{
   502 	DThread* pT = ThreadFromId(aId);
   503 	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   504 
   505 	TArmRegSet context;
   506 	memclr(&context, sizeof context);
   507 	TUint32 unused;
   508 	NKern::ThreadGetSystemContext(&pT->iNThread, &context, unused);
   509 	umemput(aContext, &context, sizeof context);
   510 	}
   511 
   512 void DTestChannel::AddUserCallback(TUint aId, TUserCallbackState aCallback)
   513 	{
   514 	DThread* pT = ThreadFromId(aId);
   515 	__ASSERT_ALWAYS(pT!=NULL, Kern::PanicCurrentThread(KClientPanicCat, __LINE__));
   516 
   517 	switch (aCallback)
   518 		{
   519 	case ESpinningCallback:
   520 		KernTest::Test(KernTest::EUserModeCallbackSpin, pT);
   521 		break;
   522 	case ESleepingCallback:
   523 		KernTest::Test(KernTest::EUserModeCallbackSleep, pT);
   524 		break;
   525 	default:
   526 		Kern::PanicCurrentThread(KClientPanicCat, __LINE__);
   527 		}
   528 	}
   529 
   530 //////////////////////////////////////////////////////////////////////////////
   531 
   532 class DTestFactory : public DLogicalDevice
   533 	{
   534 public:
   535 	DTestFactory();
   536 	// from DLogicalDevice
   537 	virtual TInt Install();
   538 	virtual void GetCaps(TDes8& aDes) const;
   539 	virtual TInt Create(DLogicalChannelBase*& aChannel);
   540 	};
   541 
   542 DTestFactory::DTestFactory()
   543     {
   544     iVersion = RContextLdd::Version();
   545     // iParseMask = 0; // no unit, no info, no PDD
   546     // iUnitsMask = 0; // only one thing
   547     }
   548 
   549 TInt DTestFactory::Create(DLogicalChannelBase*& aChannel)
   550     {
   551 	aChannel=new DTestChannel;
   552 	return aChannel ? KErrNone : KErrNoMemory;
   553     }
   554 
   555 TInt DTestFactory::Install()
   556     {
   557     return SetName(&KTestLddName);
   558     }
   559 
   560 void DTestFactory::GetCaps(TDes8& /*aDes*/) const
   561     {
   562     }
   563 
   564 //////////////////////////////////////////////////////////////////////////////
   565 
   566 DECLARE_STANDARD_LDD()
   567 	{
   568     return new DTestFactory;
   569 	}