kernel/eka/kernel/sipc.cpp
changeset 9 96e5fb8b040d
child 11 329ab0095843
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/kernel/sipc.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,2110 @@
+// Copyright (c) 1994-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:
+// e32\kernel\sipc.cpp
+// 
+//
+
+#include <kernel/kern_priv.h>
+#include "execs.h"
+#include "memmodel.h"
+
+#define iMState		iWaitLink.iSpare1
+
+const TInt KMaxMsgLimit=512;
+
+extern "C" void __SendDiscMsg(DSession* aSession);
+
+/********************************************
+ * User side message formats
+ ********************************************/
+
+// User side message version
+// This must match the layout of class RMessage2
+class RMessageU2
+	{
+public:
+	inline RMessageU2(const RMessageK& a);
+public:
+	// from RMessagePtr2
+	TInt iHandle;
+	// from RMessage2
+	TInt iFunction;
+	TInt iArgs[KMaxMessageArguments];
+	TUint32 iSpare1;
+	const TAny* iSessionPtr;
+#if		0
+	// not used or copied by IPC, so not required ...
+	mutable TInt iFlags;
+	TInt iSpare3;
+#endif
+	};
+
+inline RMessageU2::RMessageU2(const RMessageK& a)
+	{
+	iHandle = (TInt)&a;
+	iFunction = a.iFunction;
+	if (iFunction == RMessage2::EDisConnect)
+		{
+		iArgs[0] = 0;
+		iArgs[1] = 0;
+		iArgs[2] = 0;
+		iArgs[3] = 0;
+		}
+	else
+		{
+		iArgs[0] = a.Arg(0);
+		iArgs[1] = a.Arg(1);
+		iArgs[2] = a.Arg(2);
+		iArgs[3] = a.Arg(3);
+		}
+	iSpare1 = 0;
+	iSessionPtr = a.iSession->iSessionCookie;
+#ifdef KIPC
+	if (KDebugNum(KIPC))
+		Kern::Printf("RMessageU2(%08X): %08X %08X; %08X->%08X",
+			&a, iHandle, iFunction, a.iSession, iSessionPtr);
+#endif //KIPC
+	}
+
+TServerMessage::TServerMessage() :
+	TClientRequest(CallbackFunc)
+	{
+	}
+
+#ifndef __CLIENT_REQUEST_MACHINE_CODED__
+
+void TServerMessage::CallbackFunc(TAny* aData, TUserModeCallbackReason aReason)
+	{
+	TServerMessage* req = (TServerMessage*)aData;
+	if (aReason == EUserModeCallbackRun && req->iResult == KErrNone)
+		{
+		RMessageU2 userData(*req->iMessageData);
+		K::USafeWrite(req->iMessagePtr, &userData, sizeof(userData));
+		}
+	TClientRequest::CallbackFunc(aData, aReason);
+	}
+
+#endif
+
+RMessageK::RMessageK() :
+	TClientRequest(CallbackFunc),
+   	iAccessCount(0)
+	{
+	}
+
+void RMessageK::CallbackFunc(TAny* aData, TUserModeCallbackReason aReason)
+	{
+	RMessageK* msg = (RMessageK*)aData;
+
+	TBool ok = ETrue;
+	if (aReason == EUserModeCallbackRun)
+		{
+		// Write back updated descriptor lengths
+		TInt flags = msg->iMsgArgs.AllDesWritten();
+		for (TInt i = 0 ; flags != 0 ; ++i, flags >>= 1)
+			{
+			if (flags & 1)
+				{
+				const TDesHeader& des = msg->Descriptor(i);
+				TAny* exc = K::USafeWrite(msg->Ptr(i), &des.TypeAndLength(), sizeof(TUint32));
+				if (exc != NULL)
+					ok = EFalse;	
+				if (ok && des.Type() == EBufCPtr)
+					{
+					TInt len = des.Length();
+					TUint8* pL = (TUint8*)(des.DataPtr() - sizeof(TDesC));
+					exc = K::USafeWrite(pL, &len, sizeof(len));
+					if (exc != NULL)
+						ok = EFalse;	
+					}
+				}
+			}
+		}
+	
+	TClientRequest::CallbackFunc(aData, aReason);
+
+	NKern::LockSystem();
+	__NK_ASSERT_DEBUG(msg->IsCompleting());
+	msg->CloseRef();
+	NKern::UnlockSystem();
+
+	if (!ok)
+		{
+		NKern::ThreadLeaveCS();
+		K::PanicKernExec(EBadIpcDescriptor);
+		}
+	}
+
+void RMessageK::OpenRef()
+	{ 
+	// iAccess count will probably never be more than 2?
+	__NK_ASSERT_DEBUG(iAccessCount < 0xfe);
+	__e32_atomic_add_ord8(&iAccessCount, 1);
+	}
+
+void RMessageK::CloseRef()
+	{
+	__ASSERT_SYSTEM_LOCK;
+	if (__e32_atomic_add_ord8(&iAccessCount, (TUint8)-1) == 1)
+	   	Free(); 
+	}
+
+#if		0
+// Just to exercise the chunk-adjustment code and check that we
+// manage the system lock and thread critical sections correctly.
+// Enter and return with system unlocked and caller in a critical section
+static void JiggleMsgChunk()
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "RMessageK::JiggleMsgChunk");
+	DChunk* msgChunk = K::MsgInfo.iChunk;
+	if (msgChunk)
+		{
+		__ASSERT_DEBUG((TUint)msgChunk->Size() == K::MsgInfo.iCurrSize, K::Fault(K::EMsgFreeBadPool));
+		msgChunk->Adjust(K::MsgInfo.iCurrSize+Kern::RoundToPageSize(1));
+		msgChunk->Adjust(K::MsgInfo.iCurrSize);
+		__ASSERT_DEBUG((TUint)msgChunk->Size() == K::MsgInfo.iCurrSize, K::Fault(K::EMsgFreeBadPool));
+		}
+	}
+
+	// We must drop the system lock before calling JiggleMsgChunk(),
+	// but as our caller may be holding resources we have to enter a
+	// critical section before doing so. Note that K::ThreadEnterCS()
+	// (as opposed to NKern::ThreadEnterCS()) both enters a critical
+	// section AND then releases the lock; likewise K::ThreadLeaveCS()
+	// reclaims the system lock before leaving the critical section,
+	// so there is no window for the thread to be killed while holding
+	// allocated resources ...
+	K::ThreadEnterCS();
+	JiggleMsgChunk();
+	K::ThreadLeaveCS();
+#endif
+
+#ifdef	_DEBUG
+// Check consistency of the specified list
+// Enter and return with system locked
+static TBool MessagePoolIntact(RMessageK* p = K::MsgInfo.iNextMessage, TInt count = K::MsgInfo.iFreeMessageCount)
+{
+	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED, "DSession::MessagePoolIntact");
+	TInt k = 0;
+	while (p)
+		{
+		k += 1;
+		p = (RMessageK*)p->iSessionLink.iNext;
+		}
+
+	return (k == count);
+}
+
+// Ditto, but enter and return with system unlocked
+static TBool MessagePoolIntactUnlocked()
+	{
+	NKern::LockSystem();
+	TInt r = MessagePoolIntact();
+	NKern::UnlockSystem();
+	return r;
+	}
+#endif
+
+#ifndef __MESSAGE_MACHINE_CODED_2__
+EXPORT_C RMessageK* RMessageK::MessageK(TInt aMsgHandle)
+//
+// Validates a message handle and converts it into a pointer.
+// Enter and leave with system locked.
+//
+	{
+	RMessageK* m = MessageK(aMsgHandle, TheCurrentThread);
+	if (m == NULL || m->iFunction == RMessage2::EDisConnect)
+		K::PanicCurrentThread(EBadMessageHandle);
+	return m;
+	}
+
+RMessageK* RMessageK::MessageK(TInt aMsgHandle, DThread* aThread)
+//
+// Validates a message handle and converts it into a pointer.
+// Enter and leave with system locked.
+//
+	{
+	RMessageK& m = *(RMessageK*)aMsgHandle;
+	SDblQueLink lnk;
+
+	// Handle must point into kernel message chunk, and be correctly aligned
+	TInt offset = (TUint8*)&m - K::MsgInfo.iBase;
+	if (offset < 0 || offset+sizeof(RMessageK) > (TUint)K::MsgInfo.iMaxSize)
+		return NULL;
+	if (offset & (KMessageSize-1))
+		return NULL;
+
+	// Message must be readable, in the correct state and owned by the right process
+	if (Kern::SafeRead(&m.iServerLink, &lnk, sizeof(lnk)) != NULL
+	||  TUint32(lnk.iNext) != ~TUint32(&m)
+	||	TUint32(lnk.iPrev) != ~TUint32(aThread->iOwningProcess))
+		return NULL;
+
+	return &m;
+	}
+#endif
+
+// Claim one page of memory, carve it up into message blocks,
+// and add them to the global pool
+// Enter and return with system lock, may be temporarily released though
+TInt RMessageK::ExpandMessagePool()
+	{
+	// KMessageSize must have been defined as a power of two at least as big as an RMessageK
+	__ASSERT_COMPILE((KMessageSize & (KMessageSize-1)) == 0);
+	__ASSERT_COMPILE(KMessageSize >= sizeof(RMessageK));
+
+	// This function can be called during the creation of the very first thread, which is
+	// before the msg chunk has been created (the first thread will own the chunk, so must
+	// be created first). In this situation we cannot expand the pool, and so just return.
+	// This first thread is thus unique in not having a preallocated message for synchronous
+	// use; but it's destined to become the idle thread and as such never makes use of IPC.
+	// So that's alright then :)
+	DChunk* msgChunk = K::MsgInfo.iChunk;
+	if (!msgChunk)
+		return KErrNotReady;
+
+	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED, "DSession::ExpandMessagePool");
+	__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+
+	RMessageK* first = NULL;
+	RMessageK* m = NULL;
+	TInt r = KErrNone;
+	TInt k = 0;
+
+	// We must drop the system lock before calling DChunk::Adjust().
+	// This DMutex ensures single-threading while adjusting the pool.
+	// Note that the system lock may be dropped and reacquired here ...
+	TUint oldSize = K::MsgInfo.iCurrSize;
+	NKern::ThreadEnterCS();
+	K::MsgInfo.iMsgChunkLock->Wait();
+	NKern::UnlockSystem();
+
+	// Someone may have got in first while we were waiting for the DMutex.
+	// So we only expand the chunk if its size is unchanged from above;
+	// otherwise we just do nothing and let the caller retry if necessary
+	if (K::MsgInfo.iCurrSize == oldSize)
+		{
+		__ASSERT_DEBUG((TUint)msgChunk->Size() == K::MsgInfo.iCurrSize, K::Fault(K::EMsgFreeBadPool));
+		__KTRACE_OPT(KSERVER,Kern::Printf("ExpandMessagePool(): MsgChunk %08X, base %08X, size %08X, max %08X",
+			msgChunk, K::MsgInfo.iBase, K::MsgInfo.iCurrSize, K::MsgInfo.iMaxSize));
+
+		r = msgChunk->Adjust(K::MsgInfo.iCurrSize+Kern::RoundToPageSize(KMessageSize));
+		__KTRACE_OPT(KSERVER,Kern::Printf("ExpandMessagePool(): Adjust returns %d, size now %08X", r, msgChunk->Size()));
+
+		if (r == KErrNone)
+			{
+			// Turn the allocated memory info a list of messages
+			// This can (and should) be done without the system lock
+			TUint8* p = K::MsgInfo.iBase + K::MsgInfo.iCurrSize;
+			K::MsgInfo.iCurrSize = (TUint)msgChunk->Size();
+			TUint8* top = K::MsgInfo.iBase + K::MsgInfo.iCurrSize;
+			__ASSERT_DEBUG(p < top, K::Fault(K::EMsgFreeBadPool));
+			memclr(p, top-p);
+
+			for (first = (RMessageK*)p, k = 0; p < top; ++k)
+				{
+				m = (RMessageK*)p;
+				p += KMessageSize;
+				new (m) RMessageK;
+				m->iMsgType = EGlobal;
+				m->iSessionLink.iNext = (SDblQueLink*)p;
+				}
+
+			__ASSERT_DEBUG(p == top, K::Fault(K::EMsgFreeBadPool));
+			__ASSERT_DEBUG((TAny*)(m+1) <= top, K::Fault(K::EMsgFreeBadPool));
+			}
+		}
+
+	// Reacquire system lock before adding the new messages to the pool
+	// Note that K::MsgInfo.iNextMessage may no longer be NULL
+	NKern::LockSystem();
+
+	if (first)
+		{
+		// We expanded the pool; m is the last allocated message
+		m->iSessionLink.iNext = (SDblQueLink*)K::MsgInfo.iNextMessage;
+		K::MsgInfo.iNextMessage = first;
+		K::MsgInfo.iFreeMessageCount += k;
+		}
+
+	// Now we can release the pool mutex; unfortunately this also releases
+	// the system lock, so we have to acquire it again before returning.
+	// This makes it possible -- though very unlikely -- that after adding
+	// messages to the pool, they *all* get claimed by other threads in the
+	// window between dropping the system lock and reacquiring it, so that
+	// our caller finds the pool still empty.  In such cases, it's up to
+	// the caller to retry ...
+	K::MsgInfo.iMsgChunkLock->Signal();
+	NKern::LockSystem();
+	NKern::ThreadLeaveCS();
+	return r;
+	}
+
+// Claim a chain of aCount RMessageK objects from the global pool,
+// and set their type to aType (e.g. ESession or ESync)
+// Enter and return with system unlocked and caller in a critical section
+RMessageK* RMessageK::ClaimMessagePool(enum TMsgType aType, TInt aCount, DSession *aSession)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "RMessageK::ClaimMessagePool");
+
+	RMessageK* chain = NULL;
+	TInt wanted = aCount;
+
+	// In order not to hold the system lock for too long at one time,
+	// we release it between iterations of this loop.
+#define	KMaxMessagesInOneGo		16
+	for (NKern::LockSystem(); ; NKern::FlashSystem())
+		{
+		TInt n = Min(wanted, KMaxMessagesInOneGo);
+		while (K::MsgInfo.iFreeMessageCount < n)
+			if (ExpandMessagePool() != KErrNone)
+				goto fail;
+
+		__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+		RMessageK* first = K::MsgInfo.iNextMessage;
+		RMessageK* last = first;
+		RMessageK* p = first;
+
+		// Count out n message blocks
+		__ASSERT_DEBUG(K::MsgInfo.iFreeMessageCount >= n, K::Fault(K::EMsgFreeBadPool));
+		for (TInt k = 0; k < n; ++k)
+			{
+			last = p;
+			p->iSession = aSession;
+			p->iMsgType = (TUint8)aType;
+			p = (RMessageK*)p->iSessionLink.iNext;
+			}
+
+		// Add them to the chain we're building
+		if (chain)
+			last->iSessionLink.iNext = (SDblQueLink*)chain;
+		else
+			last->iSessionLink.iNext = NULL;
+		chain = first;
+
+		// Synchronise the global pool
+		K::MsgInfo.iNextMessage = p;
+		K::MsgInfo.iFreeMessageCount -= n;
+		__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+		if ((wanted -= n) == 0)
+			break;
+		}
+
+	__ASSERT_DEBUG(MessagePoolIntact(chain, aCount), K::Fault(K::EMsgFreeBadPool));
+	NKern::UnlockSystem();
+
+	// Return this chain of message blocks
+	return chain;
+
+fail:
+	__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+	NKern::UnlockSystem();
+	if (chain)
+		chain->ReleaseMessagePool(aType, KMaxTInt);
+	return NULL;
+	}
+
+// Release a chain of at most aMax RMessageK objects back to the
+// global pool, resetting their type to EGlobal in the process
+// Enter and return with system unlocked and caller in a critical section
+void RMessageK::ReleaseMessagePool(enum TMsgType aType, TInt aMax)
+	{
+	CHECK_PRECONDITIONS(MASK_THREAD_CRITICAL, "RMessageK::ReleaseMessagePool");
+	__ASSERT_DEBUG(MessagePoolIntactUnlocked(), K::Fault(K::EMsgFreeBadPool));
+
+	RMessageK* next = this;
+	RMessageK* p;
+	TInt k = 0;
+
+	do
+		{
+		p = next;
+		next = (RMessageK*)p->iSessionLink.iNext;
+		__ASSERT_DEBUG(p->iMsgType == aType, K::Fault(K::EMsgFreeBadPool));
+		p->iMsgType = EGlobal;
+		}
+	while (++k < aMax && next != NULL);
+
+	// The linked list may not be NULL-terminated -- in DEBUG builds, the link
+	// may be deliberately set to an invalid value.  Here, we assert that if
+	// exit was reached because the count reached zero, rather than because we
+	// reached a NULL link, then the next link was in fact just such a value.
+#ifndef	KILL_LINK_VALUE
+#define KILL_LINK_VALUE ((SDblQueLink*)0xdfdfdfdf)
+#endif
+	__ASSERT_DEBUG((next == NULL || (k == aMax && next == (TAny*)KILL_LINK_VALUE)), K::Fault(K::EMsgFreeBadPool));
+	__ASSERT_DEBUG((k == aMax || aMax == KMaxTInt), K::Fault(K::EMsgFreeBadPool));
+
+	// At the end of the chain
+	NKern::LockSystem();
+	p->iSessionLink.iNext = (SDblQueLink*)K::MsgInfo.iNextMessage;
+	K::MsgInfo.iNextMessage = this;
+	K::MsgInfo.iFreeMessageCount += k;
+	__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+	NKern::UnlockSystem();
+	(void)aType;				// Suppress compiler whinge in non-DEBUG build
+	}
+
+// Get a free message from the global pool. Expand the global pool if
+// it's empty; but return NULL if that fails to add more messages.
+// 
+// Enter and return with system lock, may be temporarily released though
+RMessageK* RMessageK::GetNextFreeMessage(DSession* aSession)
+	{
+	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED, "RMessageK::GetNextFreeMessage");
+
+	__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+	RMessageK* volatile* p = &K::MsgInfo.iNextMessage;
+	RMessageK* m;
+
+	// If there are no free messages, expand the global pool. Keep trying
+	// until we successfully get a message or fail with an error.
+	while ((m = *p) == NULL)
+		if (ExpandMessagePool() != KErrNone)
+			return NULL;
+
+	*p = (RMessageK*)m->iSessionLink.iNext;
+	m->iSessionLink.iNext = NULL;
+	m->iSession = aSession;
+	K::MsgInfo.iFreeMessageCount -= 1;
+
+	__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+	__KTRACE_OPT(KSERVER,Kern::Printf("RMessageK::GetNextFreeMessage returns %08x", m));
+	return m;
+	}
+
+// Enter and return with system unlocked.
+void RMessageK::TMsgArgs::ReadDesHeaders(const TInt aArgsPtr[KMaxMessageArguments+1])
+	{
+	// Copy the args into the structure; the extra word contains the IPC flags
+	TInt i;
+	for (i = 0; i < KMaxMessageArguments; ++i)
+		iArgs[i] = *aArgsPtr++;
+	iArgFlags = *aArgsPtr & KAllIpcFlagsMask;
+
+	TInt descFlags = AllDescriptorFlags();
+	for (i = 0; descFlags != 0; ++i, descFlags >>= TIpcArgs::KBitsPerType)
+		if (descFlags & TIpcArgs::EFlagDes)
+			{
+			// Read descriptor header in current process.  May take page fault
+			TInt r = K::USafeReadAndParseDesHeader((TAny*)iArgs[i], iDesInfo[i]);
+			if (r != KErrNone)
+				{
+				// If the descriptor is bad (unreadable or unknown type), clear
+				// the parameter type and mark the descriptor header as invalid
+				SetArgUndefined(i);
+				iDesInfo[i].Unset();
+				}
+			}
+	}
+
+// Enter and return with system unlocked and caller in a critical section
+void UnpinMessageArguments(RMessageK::TPinArray *aPinArray)
+	{
+	for (TInt i = 0; i < KMaxMessageArguments; ++i)
+		{
+		if (aPinArray->iPinPtrs[i])
+			{
+			// This will unpin the pinned memory and set iPinPtrs[i] to NULL.
+			M::DestroyVirtualPinObject(aPinArray->iPinPtrs[i]);
+			}
+		__NK_ASSERT_DEBUG(!aPinArray->iPinPtrs[i]);
+		}
+	}
+
+// Enter and return with system lock, may be temporarily released though
+TInt RMessageK::PinDescriptors(DSession* aSession, TBool aPinningServer)
+	{
+	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED, "RMessageK::PinDescriptors");
+	__NK_ASSERT_DEBUG(!iPinArray);				// Free messages should always have this nulled.
+
+	// If none of the args are descriptors, there's obviously nothing to do
+	TUint descFlags = iMsgArgs.AllDescriptorFlags();
+	if (!descFlags)
+		return KErrNone;
+
+	// If neither the server nor the client have requested pinning, there's nothing to do
+	TUint argPinFlags = aPinningServer ? ~0u : iMsgArgs.AllPinFlags();
+	if (!argPinFlags)
+		return KErrNone;
+
+	// The session can only ever belong to one server so ok to release system lock
+	// during pinning as need to pin won't change unless the session is closed.
+	// Need a reference on the session, though, in case it goes away.
+	aSession->TotalAccessInc();
+	NKern::ThreadEnterCS();
+	NKern::UnlockSystem();
+	TInt r = KErrNone;
+
+	for (TInt i = 0; descFlags != 0; ++i, argPinFlags >>= 1, descFlags >>= TIpcArgs::KBitsPerType)
+ 		{
+		__NK_ASSERT_DEBUG(i < KMaxMessageArguments);	// Should stop after max args processed.
+
+		// Is this arg a descriptor that should be pinned?
+		if ((descFlags & TIpcArgs::EFlagDes) && (argPinFlags & 1))
+			{
+			TDesHeader& desInfo = Descriptor(i);
+
+			// Pin the max length for modifiable descriptors, but only
+			// the current length for non-modifiable descriptors.
+			TUint pinLength = desInfo.IsWriteable() ? desInfo.MaxLength() : desInfo.Length();
+			if (pinLength)
+				{
+				if (!iPinArray)
+					{
+					iPinArray = new TPinArray;
+					if (!iPinArray)
+						{
+						r = KErrNoMemory;
+						break;
+						}
+					}
+
+				// This will only create and pin if the descriptor data is paged.
+				// An out-of-memory error here means we fail the whole operation.
+				r = Kern::CreateAndPinVirtualMemory(iPinArray->iPinPtrs[i], desInfo.DataPtr(), pinLength);
+				if (r == KErrNoMemory)
+					break;
+				if (r != KErrNone)
+					{
+					// For any other error, clear the parameter type and mark the
+					// descriptor header as invalid, so the server will see a problem
+					// on access, but then suppress the error so we can continue ...
+					iMsgArgs.SetArgUndefined(i);
+					desInfo.Unset();
+					r = KErrNone;
+					}
+				}
+			}
+		}
+
+	if (r == KErrNoMemory)
+		{
+		// Failed to pin everything so clean up any pin objects created.
+		// This will also unpin any pinned memory.
+		if (iPinArray)
+			{
+			UnpinMessageArguments(iPinArray);
+			delete iPinArray;
+			iPinArray = NULL;
+			}
+		}
+
+	NKern::LockSystem();
+
+	// Remove the access on the session.
+	if (aSession->TotalAccessDec() == DObject::EObjectDeleted)
+		{// This was the last access on the session and it has been deleted so 
+		// don't access any of its members.
+		r = KErrDisconnected;
+		}
+	NKern::ThreadLeaveCS();
+	return r;
+	}
+
+// Free a single message back to the pool it belongs in
+// Enter and leave with system lock, may be temporarily released though
+void RMessageK::Free()
+	{
+	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED, "RMessageK::Free");
+	__KTRACE_OPT(KSERVER, Kern::Printf("RMessageK(%08x)::Free(), type %d", this, iMsgType));
+	__ASSERT_SYSTEM_LOCK;
+	__NK_ASSERT_DEBUG(!IsFree());
+	__NK_ASSERT_DEBUG(State()==EFree); // TClientRequest should be free
+	__NK_ASSERT_DEBUG(iAccessCount==0);
+
+	DThread* closeThread = 0;
+	if (iSession)
+		{
+		--iSession->iMsgCount;
+		iSessionLink.Deque();
+		if (--iClient->iIpcCount == 0x80000000u)
+			closeThread = iClient;
+		}
+
+	// take ownership of any pin objects...
+	TPinArray* pinArray = iPinArray;
+	iPinArray = NULL;
+
+	// free the message...
+	SetFree();
+	switch (iMsgType)
+		{
+		default:
+		case EDisc:
+			// The Disconnect message is owned by the session and should never be freed
+			K::Fault(K::EMsgFreeBadPool);
+			break;
+
+		case ESync:
+			// The Synchronous message is owned by the thread, so we don't actually free it
+			break;
+
+		case ESession:
+			if (iSession)
+				{
+				// Put this message back on the session's freelist
+				iSessionLink.iNext = (SDblQueLink*)iSession->iNextFreeMessage;
+				iSession->iNextFreeMessage = this;
+				break;
+				}
+			// Session has gone away; return message to global pool instead
+			iMsgType = EGlobal;
+			/*FALLTHRU*/
+		case EGlobal:
+			__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+			// Put this message back on the global freelist
+			iSessionLink.iNext = (SDblQueLink*)K::MsgInfo.iNextMessage;
+			K::MsgInfo.iNextMessage = this;
+			K::MsgInfo.iFreeMessageCount += 1;
+			__ASSERT_DEBUG(MessagePoolIntact(), K::Fault(K::EMsgFreeBadPool));
+			break;
+		}
+
+	if (closeThread)
+		{
+		K::ThreadEnterCS();
+		closeThread->AsyncClose();
+		K::ThreadLeaveCS();
+		}
+
+	if (pinArray)
+		{
+		// unpin any pinned descriptors and free the pin objects...
+		K::ThreadEnterCS();
+		UnpinMessageArguments(pinArray);
+		delete pinArray;
+		K::ThreadLeaveCS();
+		}
+	}
+
+EXPORT_C DThread* RMessageK::Thread() const
+	{
+	return iClient;
+	}
+
+
+/********************************************
+ * Server control block
+ ********************************************/
+DServer::DServer()
+	{
+	}
+
+TInt DServer::Create()
+	{
+	iMessage = new TServerMessage;
+	return iMessage ? KErrNone : KErrNoMemory;
+	}
+
+DServer::~DServer()
+//
+// Destructor
+// Note that no-one else will try to access this server in here, since
+// the access count will now be zero.
+//
+	{
+	__KTRACE_OPT(KSERVER,Kern::Printf("DServer %O Destruct", this));
+	__ASSERT_ALWAYS(iSessionQ.IsEmpty(), K::Fault(K::EServerDestructSessionsRemain));
+	__ASSERT_ALWAYS(iDeliveredQ.IsEmpty(), K::Fault(K::EServerDestructMessagesRemain));
+	if (iMessage)
+		iMessage->Close();
+	Kern::SafeClose((DObject*&)iOwningThread,NULL);
+	}
+
+TInt DServer::Close(TAny*)
+	{
+	__KTRACE_OPT(KSERVER,Kern::Printf("DServer %O Close AC=%d", this, AccessCount()));
+	TInt r = Dec();
+	if (r==1)
+		{
+		if (iOwningThread)
+			{
+			Kern::QueueRequestComplete(iOwningThread, iMessage, KErrCancel);
+			NKern::LockSystem();
+			for (; !iSessionQ.IsEmpty(); NKern::FlashSystem())
+				{
+				DSession* s = _LOFF(iSessionQ.First(), DSession, iServerLink);
+				s->Detach(KErrServerTerminated);
+				}
+			__ASSERT_ALWAYS(iDeliveredQ.IsEmpty(), K::Fault(K::EServerCloseLeftoverMsg));
+#ifdef BTRACE_CLIENT_SERVER
+			BTrace4(BTrace::EClientServer,BTrace::EServerDestroy,this);
+#endif
+			NKern::UnlockSystem();
+			}
+		K::ObjDelete(this);
+		return EObjectDeleted;
+		}
+	return 0;
+	}
+
+#ifndef __MESSAGE_MACHINE_CODED__
+void DServer::Receive(TRequestStatus& aStatus, TAny* aMessage)
+//
+// Receive a message asynchronously.
+// Enter and leave with system locked
+//
+	{
+
+	if (iMessage->SetStatus(&aStatus) != KErrNone)
+		K::PanicCurrentThread(EMesAlreadyPending);
+	iMessage->iMessagePtr = aMessage;
+	if (!iDeliveredQ.IsEmpty())
+		{
+		RMessageK* m = _LOFF(iDeliveredQ.First()->Deque(), RMessageK, iServerLink);
+		Accept(m);
+		}
+	}
+#endif
+
+void DServer::Cancel()
+//
+// Cancel a message receive request.
+// Enter and leave with system locked
+//
+	{
+	Kern::QueueRequestComplete(iOwningThread, iMessage, KErrCancel);
+	}
+
+#ifndef __MESSAGE_MACHINE_CODED__
+void DServer::Accept(RMessageK* aMsg)
+//
+// Accepts a message, assumes one is pending.
+// Enter and leave with system locked
+//
+	{
+	// set the ACCEPTED state
+	aMsg->SetAccepted(iOwningThread->iOwningProcess);
+	if (iOwningThread->iMState==DThread::EDead)
+		return;	// server has already died
+#ifdef BTRACE_CLIENT_SERVER
+	BTrace4(BTrace::EClientServer,BTrace::EMessageReceive,aMsg);
+#endif
+	iMessage->iMessageData = aMsg;
+#ifdef KIPC
+	if (KDebugNum(KIPC))
+		{
+		TInt f = aMsg->iFunction;
+		if (f == RMessage2::EDisConnect)
+			Kern::Printf("MsgAcD: %O->%O", aMsg->iSession, this);
+		else
+			Kern::Printf("MsgAc: M:%d %O->%O", f, aMsg->iClient, this);
+		}
+#endif //KIPC
+	Kern::QueueRequestComplete(iOwningThread, iMessage, KErrNone);
+	}
+
+void DServer::Deliver(RMessageK* aMsg)
+//
+// Delivers a message to the server.
+// Enter and leave with system locked
+//
+	{
+	if (iMessage->IsReady())
+		Accept(aMsg);
+	else
+		aMsg->SetDelivered(iDeliveredQ);
+	}
+#endif //__MESSAGE_MACHINE_CODED__
+
+TInt DServer::RequestUserHandle(DThread* aThread, TOwnerType aType)
+	{
+	(void)aType;
+	return (aThread->iOwningProcess==iOwningThread->iOwningProcess) ? KErrNone : KErrPermissionDenied;
+	}
+
+TInt ExecHandler::ServerCreateWithOptions(const TDesC8* aName, TInt aMode, TInt aRole, TInt aOpts)
+//
+// Create a server belonging to the current thread. UNPROTECTED exec call.
+//
+	{
+	DThread* t = TheCurrentThread;
+	TInt r = KErrNone;
+	TKName n;
+	if (aName)
+		Kern::KUDesGet(n, *aName);
+	__KTRACE_OPT(KEXEC, Kern::Printf("Exec::ServerCreate %S", &n));
+	TInt nameLen = n.Length();
+	if (nameLen && n[0] == KProtectedServerNamePrefix && !Kern::CurrentThreadHasCapability(ECapabilityProtServ, __PLATSEC_DIAGNOSTIC_STRING("Attempt to create a server with a '!' as the first character of the name")))
+		K::UnlockedPlatformSecurityPanic();
+
+	// Fully decode & validate the arguments before allocating the server object
+	switch (aMode)
+		{
+		case EIpcSession_Unsharable:
+		case EIpcSession_Sharable:
+		case EIpcSession_GlobalSharable:
+			break;
+		default:
+			r = KErrArgument;
+			break;
+		}
+
+	switch (aRole)
+		{
+		case EServerRole_Default:
+			aRole = EServerRole_Standalone;
+			break;
+		case EServerRole_Standalone:
+		case EServerRole_Master:
+		case EServerRole_Slave:
+			break;
+		default:
+			r = KErrArgument;
+			break;
+		}
+
+	TUint pinMode = (aOpts & EServerOpt_PinClientDescriptorsMask);
+	aOpts &= ~EServerOpt_PinClientDescriptorsMask;
+	switch (pinMode)
+		{
+		case EServerOpt_PinClientDescriptorsDisable:
+			pinMode = EFalse;
+			break;
+		case EServerOpt_PinClientDescriptorsEnable:
+			pinMode = ETrue;
+			break;
+		case EServerOpt_PinClientDescriptorsDefault:
+			pinMode = EFalse;
+			if (K::MemModelAttributes & EMemModelAttrDataPaging)
+				if (!(t->iOwningProcess->iAttributes & DProcess::EDataPaged))
+				{
+				// If the platform supports data-paging, and the server process
+				// *isn't* data-paged, then enable descriptor pinning by default
+#if		0
+				// Worried about the impact of this causing new failure modes in
+				// existing code - for now we are not going to turn on pinning by
+				// default for unpaged servers.
+				pinMode = ETrue;
+#endif
+				}
+			break;
+		default:
+			r = KErrArgument;
+			break;
+		}
+
+	if (aOpts)
+		r = KErrArgument;
+	if (r != KErrNone)
+		return r;	// An invalid server mode, role or option was specified.
+
+	r = KErrNoMemory;
+	NKern::ThreadEnterCS();
+	DServer *pS = new DServer;
+	if (pS)
+		{
+		r = pS->Create();
+		if (r == KErrNone)
+			{
+			t->Open();
+			pS->iOwningThread = t;
+			pS->iSessionType = (TUint8)aMode;
+			pS->iServerRole = (TUint8)aRole;
+			pS->iPinClientDescriptors = (TUint8)pinMode;
+			if (nameLen)
+				r = pS->SetName(&n);
+#ifdef BTRACE_CLIENT_SERVER
+			BTraceN(BTrace::EClientServer, BTrace::EServerCreate, pS, pS->iOwningThread, n.Ptr(), n.Size());
+#endif
+			}
+		if (r == KErrNone)
+			{
+			r = K::AddObject(pS, EServer);
+			if (r == KErrNone)
+				r = K::MakeHandle(nameLen ? EOwnerThread : EOwnerProcess, pS);
+			}
+		if (r < KErrNone)
+			pS->Close(NULL);
+		}
+	NKern::ThreadLeaveCS();
+	__KTRACE_OPT(KEXEC, Kern::Printf("Exec::ServerCreate returns %d", r));
+	return r;
+	}
+
+TInt ExecHandler::ServerCreate(const TDesC8* aName, TInt aMode)
+	{
+	return ServerCreateWithOptions(aName, aMode, EServerRole_Default, 0);
+	}
+
+void DServer::BTracePrime(TInt aCategory)
+	{
+#ifdef BTRACE_CLIENT_SERVER
+	if (aCategory == BTrace::EClientServer || aCategory == -1)
+		{
+		TKName nameBuf;
+		Name(nameBuf);
+		BTraceN(BTrace::EClientServer, BTrace::EServerCreate, this, iOwningThread, nameBuf.Ptr(), nameBuf.Size());
+		}
+#endif
+	}
+
+
+/********************************************
+ * Session control block
+ ********************************************/
+DSession::DSession()
+	: iTotalAccessCount(1), iMsgLimit(KMaxMsgLimit)
+	{
+	}
+
+DSession::~DSession()
+//
+// Destroy
+//
+	{
+	__KTRACE_OPT(KSERVER,Kern::Printf("DSession::Destruct"));
+
+	// DObject access count and total access count should both be zero
+	__ASSERT_ALWAYS(AccessCount()==0 && iTotalAccessCount==0, K::Fault(K::ESessionDestructStillRef));
+
+	// session should already have been unlinked from server
+	__ASSERT_ALWAYS(!iServer, K::Fault(K::ESessionDestruct));
+
+	// there should be no messages outstanding on this session
+	__ASSERT_ALWAYS(iMsgCount==0, K::Fault(K::ESessionDestructMsgCount));
+	__ASSERT_ALWAYS(iMsgQ.IsEmpty(), K::Fault(K::ESessionDestructMsgQ));
+
+	if (iDisconnectMsgPtr)
+		{
+		__KTRACE_OPT(KSERVER,Kern::Printf("DSession::~DSession(%08X) releasing disconnect message at %08X", this, iDisconnectMsgPtr));
+		iDisconnectMsgPtr->ReleaseMessagePool(RMessageK::EDisc, 1);
+		}
+	iDisconnectMsgPtr = NULL;
+	if (iNextFreeMessage)
+		{
+		__KTRACE_OPT(KSERVER,Kern::Printf("DSession::~DSession(%08X) releasing session pool at %08X", this, iNextFreeMessage));
+		__ASSERT_DEBUG(iHasSessionPool, K::Fault(K::ESessionDestructMsgCount));
+		__ASSERT_DEBUG(iPoolSize > 0, K::Fault(K::ESessionDestructMsgCount));
+		iNextFreeMessage->ReleaseMessagePool(RMessageK::ESession, iPoolSize);
+		}
+	iNextFreeMessage = NULL;
+	}
+
+
+/**
+Decrement the total access count, deleting the session object if this is the last
+reference on the session.
+
+Note - This may release the system lock temporarily.
+
+@return 0 if the session still exists, EObjectDeleted if the session has been deleted.
+
+@pre Enter with system locked
+@post Leave with system locked
+*/
+TInt DSession::TotalAccessDec()
+	{
+	__ASSERT_SYSTEM_LOCK;
+	TInt r = 0;
+	if (--iTotalAccessCount == 0)
+		{// The session must have been closed.
+		__NK_ASSERT_DEBUG(AccessCount()==0);
+		NKern::UnlockSystem();
+		K::ObjDelete(this);
+		r = EObjectDeleted;
+		NKern::LockSystem();
+		}
+	return r;
+	}
+
+/**
+As DSession::TotalAccessDec() except it returns with the system lock released.
+
+@pre Enter with system locked
+@post Leave with system unlocked
+*/
+TInt DSession::TotalAccessDecRel()
+	{
+	__ASSERT_SYSTEM_LOCK;
+	TInt r = 0;
+	TUint tac = --iTotalAccessCount;
+	NKern::UnlockSystem();
+
+	if (tac == 0)
+		{// The session must have been closed.
+		__NK_ASSERT_DEBUG(AccessCount()==0);
+		K::ObjDelete(this);
+		r = EObjectDeleted;
+		}
+	return r;
+	}
+
+
+void DSession::Transfer(DServer* aServer, RMessageK* aMsg)
+//
+// Enter and leave with system locked
+//
+	{
+	(void)aMsg;	// avoid non-DEBUG whinge about unused parameter
+	__KTRACE_OPT(KSERVER, Kern::Printf("Session %O Transfer From %O To %O, msg %08x",
+		this, iServer, aServer, aMsg));
+
+	// The current server must be a master, and the new one a designated slave
+	if (iServer->iServerRole != EServerRole_Master)
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+	if (aServer->iServerRole != EServerRole_Slave)
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+
+	// Both servers must be part of the same process
+	if (iServer->iOwningThread->iOwningProcess != aServer->iOwningThread->iOwningProcess)
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+
+	// Transfers must not involve moribund servers
+	if (iServer->IsClosing() || aServer->IsClosing())
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+	if (iServer->iOwningThread->iMState == DThread::EDead)
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+	if (aServer->iOwningThread->iMState == DThread::EDead)
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+
+	// Unlink from the current server, and link to the new one
+	iServerLink.Deque();
+	iServer = aServer;
+	iServer->iSessionQ.Add(&iServerLink);
+
+	// The message queue must contain at least the Connect message
+	__ASSERT_DEBUG(!iMsgQ.IsEmpty(),
+					K::Fault(K::EMsgFreeBadPool));
+
+	RMessageK* connectMsg = _LOFF(iMsgQ.First(), RMessageK, iSessionLink);
+	// RMessageK* lastMsg = _LOFF(iMsgQ.Last(), RMessageK, iSessionLink);
+
+	__ASSERT_DEBUG(connectMsg == aMsg,
+					K::Fault(K::EKernelMsgNotAccepted));
+	__ASSERT_DEBUG(connectMsg->iFunction == RMessage2::EConnect,
+					K::Fault(K::EKernelMsgNotAccepted));
+	__ASSERT_DEBUG(connectMsg->IsAccepted(),
+					K::Fault(K::EKernelMsgNotAccepted));
+
+	// Transfer all pending messages except the first (which is the Connect)
+	SDblQueLink* p;
+	TInt msgCount = 0;
+	for (RMessageK* m = connectMsg; (p = m->iSessionLink.iNext) != &iMsgQ.iA; )
+		{
+		msgCount += 1;
+		m = _LOFF(p, RMessageK, iSessionLink);
+		__ASSERT_DEBUG(!m->IsAccepted(),
+						K::Fault(K::EMessageAlreadyPending));
+		if (m->IsDelivered())
+			m->iServerLink.Deque();
+		iServer->Deliver(m);
+		}
+
+	// Transfer the disconnect message too?
+	if (iDisconnectMsgPtr->IsDelivered())
+		{
+		__ASSERT_DEBUG(!iDisconnectMsgPtr->IsAccepted(),
+						K::Fault(K::EMessageAlreadyPending));
+		iDisconnectMsgPtr->iServerLink.Deque();
+		__SendDiscMsg(this);
+		}
+	}
+
+// Detach a session from the server
+// Called either when server terminates or when server completes disconnect message
+// Enter and leave with system locked
+//
+void DSession::Detach(TInt aReason)
+	{
+	__KTRACE_OPT(KSERVER,Kern::Printf("Session %O Detach From %O reason %d", this, iServer, aReason));
+	iServer = NULL;
+	iServerLink.Deque();
+
+	if (iDisconnectMsgPtr->IsDelivered())
+		iDisconnectMsgPtr->iServerLink.Deque();
+	iDisconnectMsgPtr->SetFree();
+
+	for (; !iMsgQ.IsEmpty(); NKern::LockSystem())
+		{
+		--iMsgCount;
+		RMessageK* m = _LOFF(iMsgQ.First()->Deque(), RMessageK, iSessionLink);
+		__ASSERT_ALWAYS(m->iSession == this, K::Fault(K::EInvalidSessionAccessCount));
+		m->iSession = NULL;		// message now detached from session
+		if (m->iMsgType == RMessageK::ESession)
+			iPoolSize -= 1;		// one less message in the pool
+
+		// Note whether session connect message has been discarded
+		if (m->iFunction == RMessage2::EConnect)
+			iConnectMsgPtr = NULL;
+		if (m->IsDelivered())
+			m->iServerLink.Deque();
+
+		DThread* t = m->iClient;
+		TUint32 c = --t->iIpcCount;
+
+		if(m->IsDelivered() || m->IsAccepted())
+			{
+			if (!IsClosing())
+				{
+				m->SetCompleting();
+				Kern::QueueRequestComplete(t, m, aReason);
+				}
+			else
+				{
+				m->Reset();
+				m->CloseRef();
+				}
+			}
+
+		NKern::UnlockSystem();
+		if (c == 0x80000000u)
+			t->AsyncClose();
+		}
+
+	// Output total access count here before the session object may potentially 
+	// be deleted.  Give the value after the detach has completed.
+	__KTRACE_OPT(KSERVER,Kern::Printf("TAC: %d", iTotalAccessCount - 1));
+#ifdef BTRACE_CLIENT_SERVER
+	BTrace8(BTrace::EClientServer,BTrace::ESessionDetach,this,aReason);
+#endif
+	// This may temporarily release the system lock if it deletes the session.
+	// Don't access any members after this call as the session may be deleted.
+	TotalAccessDec();
+	}
+
+void DSession::CloseFromDisconnect()
+//
+// Called when server completes the disconnect message
+// Enter and leave with system locked
+//
+	{
+	__KTRACE_OPT(KIPC,Kern::Printf("MsgCoD: %O->%O",TheCurrentThread,this));
+	__KTRACE_OPT(KTHREAD,Kern::Printf("DSession::CloseFromDisconnect AC:%d TAC:%d", AccessCount(), iTotalAccessCount));
+	__ASSERT_ALWAYS(AccessCount()==0, K::Fault(K::EInvalidSessionAccessCount));
+
+	// there should not be any DELIVERED messages outstanding
+	// there may be outstanding ACCEPTED messages
+	// no other thread can access the session here
+
+	NKern::ThreadEnterCS();
+	iDisconnectMsgPtr->SetFree();
+	if (iServer && !iServer->IsClosing())
+		Detach(KErrSessionClosed);
+	// otherwise server is closing, so let that handle it
+	NKern::ThreadLeaveCS();
+	}
+
+TInt DSession::Close(TAny*)
+//
+// Session close called from client side.
+// Enter and leave with system unlocked.
+//
+	{
+	__KTRACE_OPT(KTHREAD,Kern::Printf("DSession::Close %O AC:%d TAC:%d", this, AccessCount(), iTotalAccessCount));
+	if (Dec() == 1)
+		{
+		// Last client access has been closed. Only the server may now influence this session.
+		// Thus no more messages can be added to the session's queue.
+
+		NKern::LockSystem();
+
+		if (!iDisconnectMsgPtr)
+			{
+			// Allocation of disconnect message failed during session create; there's no cleanup to do
+			}
+		else if (!iDisconnectMsgPtr->IsFree())
+			{
+			// Disconnect message has already been sent and is pending completion by the server
+			}
+		else if (iSessionCookie)
+			{
+			// Deliver a disconnect message now if a user-side session has already been created
+			__SendDiscMsg(this);
+			}
+		else if (!iConnectMsgPtr)
+			{
+			// Deliver a disconnect message now if there is no connect message outstanding
+			__SendDiscMsg(this);
+			}
+		else if (iConnectMsgPtr->IsDelivered())
+			{
+			// We'll remove the pending message, to prevent it from being accepted and creating
+			// an orphan session object. Then, completing the connect message will result in a
+			// disconnect message being sent, as this is equivalent to the case where a connect
+			// message was accepted but then subsequently rejected.
+			iConnectMsgPtr->iServerLink.Deque();
+			iConnectMsgPtr->SetCompleting();
+			ExecHandler::MessageComplete(iConnectMsgPtr, KErrSessionClosed);
+			}
+		else
+			{
+			// There's an accepted connect message, but no user session (yet). However
+			// one could still be created, so delay sending the disconnect message.
+			// It will be sent from SetSessionPtr() or MessageComplete() instead.
+			}
+
+		// Remove client contribution to the total access count.  This may 
+		// delete the object so don't access any of its members after this call.
+		// This will release the system lock.
+		return TotalAccessDecRel();
+		}
+
+	return 0;
+	}
+
+// Enter and return with system unlocked and caller in a critical section
+TInt DSession::New(DSession*& aS, TInt aMsgSlots, TInt aMode)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("DSession::New MS:%d Mode %d", aMsgSlots, aMode));
+	if (aMsgSlots<-1 || aMsgSlots>KMaxMsgLimit)
+		return KErrArgument;
+	if ( (TUint)aMode > (TUint)EIpcSession_GlobalSharable)
+		return KErrArgument;
+	aS = new DSession;
+	if (!aS)
+		return KErrNoMemory;
+
+	RMessageK* mp = RMessageK::ClaimMessagePool(RMessageK::EDisc, 1, aS);
+	__KTRACE_OPT(KSERVER,Kern::Printf("DSession::New(%08X) claimed disconnect message at %08X", aS, mp));
+	if (!mp)
+		return KErrNoMemory;
+	mp->iFunction = RMessage2::EDisConnect;
+	aS->iDisconnectMsgPtr = mp;
+
+	aS->iSessionType = (TUint8)aMode;
+	if (aMsgSlots == 0)
+		{
+		aS->iHasSessionPool = 1;			// pretend it has its own pool
+		aS->iNextFreeMessage = NULL;		// but never any free messages
+		}
+	else if (aMsgSlots > 0)
+		{
+		aS->iHasSessionPool = 1;			// remember that it has its own pool
+		aS->iNextFreeMessage = RMessageK::ClaimMessagePool(RMessageK::ESession, aMsgSlots, aS);
+		__KTRACE_OPT(KSERVER,Kern::Printf("DSession::New(%08X) claimed %d messages at %08X", aS, aMsgSlots, aS->iNextFreeMessage));
+		if (!aS->iNextFreeMessage)
+			return KErrNoMemory;
+		aS->iPoolSize = aMsgSlots;			// remember that it has its own pool
+		}
+	return KErrNone;
+	}
+
+// Add a new session to a server
+// Enter with system locked, return with system unlocked
+TInt DSession::Add(DServer* aSvr, const TSecurityPolicy* aSecurityPolicy)
+	{
+	__KTRACE_OPT(KSERVER,Kern::Printf("Session %O Add to server %O", this, aSvr));
+	if (aSvr->IsClosing())
+		{
+		NKern::UnlockSystem();
+		return KErrServerTerminated;
+		}
+	if (aSecurityPolicy && !aSecurityPolicy->CheckPolicy(aSvr->iOwningThread,__PLATSEC_DIAGNOSTIC_STRING("Checked during RSessionBase::CreateSession")))
+		{
+		NKern::UnlockSystem();
+		return KErrPermissionDenied;
+		}
+	iSvrSessionType = aSvr->iSessionType;
+	if (iSessionType > iSvrSessionType)
+		{
+		NKern::UnlockSystem();
+		return iSvrSessionType ? KErrPermissionDenied : KErrAccessDenied;
+		}
+	iServer = aSvr;
+	aSvr->iSessionQ.Add(&iServerLink);		// Add it to the server
+	TotalAccessInc();						// give the server an access on this session
+
+	DObject* owner = NULL;
+	if (iSvrSessionType == EIpcSession_Unsharable)
+		owner = TheCurrentThread;
+	else if (iSvrSessionType == EIpcSession_Sharable)
+		owner = TheCurrentThread->iOwningProcess;
+
+#ifdef BTRACE_CLIENT_SERVER
+	BTraceContext12(BTrace::EClientServer, BTrace::ESessionAttach, this, iServer, owner);
+#endif
+	NKern::UnlockSystem();					// server could be deleted after this line
+
+	if (owner)
+		SetOwner(owner);
+	if (iSessionType == EIpcSession_GlobalSharable)
+		SetProtection(EProtected);
+	return KErrNone;
+	}
+
+TInt DSession::MakeHandle()
+	{
+	TInt r = K::AddObject(this, ESession);
+	if (r==KErrNone)
+		{
+		TOwnerType htype = iSessionType == EIpcSession_Unsharable ? EOwnerThread : EOwnerProcess;
+		r = K::MakeHandle(htype, this);
+		}
+	return r;
+	}
+
+TInt DSession::RequestUserHandle(DThread* aThread, TOwnerType aType)
+	{
+	if (iSvrSessionType == EIpcSession_Unsharable)
+		return (aThread==Owner() && aType==EOwnerThread) ? KErrNone : KErrPermissionDenied;
+	if (iSvrSessionType == EIpcSession_Sharable)
+		return (aThread->iOwningProcess==Owner()) ? KErrNone : KErrPermissionDenied;
+	return KErrNone;
+	}
+
+TInt ExecHandler::SessionCreate(const TDesC& aServer, TInt aMsgSlots, const TSecurityPolicy* aSecurityPolicy, TInt aMode)
+//
+// Create a new session. UNPROTECTED exec call.
+//
+	{
+	TKName n;
+	TKName svrName;
+	DServer* svr = NULL;
+	DObjectCon& servers = *K::Containers[EServer];
+	Kern::KUDesGet(n, aServer);
+
+	TSecurityPolicy policy;
+	if(aSecurityPolicy)
+		{
+		kumemget32(&policy,aSecurityPolicy,sizeof(policy));
+		if(!policy.Validate())
+			return KErrArgument;
+		aSecurityPolicy=&policy;
+		}
+
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionCreate %S MS:%d Mode %d", &n, aMsgSlots, aMode));
+	NKern::ThreadEnterCS();
+	DSession* s=NULL;
+	TInt r = DSession::New(s, aMsgSlots, aMode);
+	if (r==KErrNone)
+		{
+		servers.Wait();
+		TFindHandle fh;
+		r = servers.FindByName(fh, n, svrName);
+		if (r==KErrNone)
+			{
+			svr = (DServer*)servers.At(fh);	// can't return NULL since we just found the server
+			NKern::LockSystem();
+			r = s->Add(svr, aSecurityPolicy);
+			}
+		servers.Signal();
+		}
+	if (r==KErrNone)
+		r = s->MakeHandle();
+	if (r<KErrNone && s)
+		s->Close(NULL);
+	NKern::ThreadLeaveCS();
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionCreate returns %d",r));
+	if (r==KErrAccessDenied)
+		K::PanicKernExec(EUnsharableSession);
+	return r;
+	}
+
+TInt ExecHandler::SessionCreateFromHandle(TInt aSvrHandle, TInt aMsgSlots, const TSecurityPolicy* aSecurityPolicy, TInt aMode)
+//
+// Create a new session to the given server.
+// Enter and return with system unlocked.
+//
+	{
+	TSecurityPolicy policy;
+	if(aSecurityPolicy)
+		{
+		kumemget32(&policy,aSecurityPolicy,sizeof(policy));
+		if(!policy.Validate())
+			return KErrArgument;
+		aSecurityPolicy=&policy;
+		}
+
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionCreate %08x MS:%d Mode %d", aSvrHandle, aMsgSlots, aMode));
+	NKern::ThreadEnterCS();
+	DSession* s;
+	DThread* t = TheCurrentThread;
+	TInt r = DSession::New(s, aMsgSlots, aMode);
+	if (r==KErrNone)
+		{
+		NKern::LockSystem();
+		DServer* svr = (DServer*)t->ObjectFromHandle(aSvrHandle, EServer);
+		if (svr)
+			r = s->Add(svr, aSecurityPolicy);
+		else
+			{
+			r = KErrBadHandle;
+			NKern::UnlockSystem();
+			}
+		}
+	if (r==KErrNone)
+		r = s->MakeHandle();
+	if (r<KErrNone && s)
+		s->Close(NULL);
+	NKern::ThreadLeaveCS();
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionCreate returns %d",r));
+	if (r==KErrAccessDenied)
+		K::PanicKernExec(EUnsharableSession);
+	if (r==KErrBadHandle)
+		K::PanicKernExec(EBadHandle);
+	return r;
+	}
+
+// If this session doesn't have its own pool, get a free message from the
+// global pool. This will attempt to expand the pool if necessary, and may
+// temporarily release the system lock.
+//
+// If this session does have its own message pool, get a free message from
+// it; if there are none, just return NULL, without attempting to expand it
+// or releasing the system lock.
+// 
+// Enter and return with system lock, may be temporarily released though
+RMessageK* DSession::GetNextFreeMessage()
+	{
+	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED, "DSession::GetNextFreeMessage");
+
+	if (!iHasSessionPool)
+		return RMessageK::GetNextFreeMessage(this);
+
+	RMessageK* volatile* p = &iNextFreeMessage;
+	RMessageK* m = *p;
+	if (m)
+		{
+		*p = (RMessageK*)m->iSessionLink.iNext;
+		m->iSessionLink.iNext = NULL;
+		m->iSession = this;
+		}
+	__KTRACE_OPT(KSERVER,Kern::Printf("DSession::GetNextFreeMessage returns %08x", m));
+	return m;
+	}
+
+TInt DSession::Send(TInt aHandle, TInt aFunction, const TInt* aPtr, TRequestStatus* aStatus)
+//
+// Send a message to a server. Assumes aStatus
+// has already been set to KRequestPending.
+// Enter and return with system unlocked.
+//
+	{
+	if (aFunction == RMessage2::EDisConnect)
+		return KErrArgument;
+
+	RMessageK::TMsgArgs msgArgs;
+	if (aPtr)
+		msgArgs.ReadDesHeaders(aPtr);
+
+	NKern::LockSystem();
+	DSession* session = (DSession*)K::ObjectFromHandle(aHandle, ESession);
+	RMessageK* m = session->GetNextFreeMessage();
+	if (!m)
+		{
+		NKern::UnlockSystem();
+		return KErrServerBusy;
+		}
+	__ASSERT_DEBUG(m->IsFree(), K::Fault(K::EMessageNotFree));
+	return session->Send(m, aFunction, aPtr ? &msgArgs : NULL, aStatus);
+	}
+
+TInt DSession::SendSync(TInt aHandle, TInt aFunction, const TInt* aPtr, TRequestStatus* aStatus)
+//
+// Send a SYNCHRONOUS message to a server using current thread's
+// dedicated synchronous message. Assumes aStatus has already been set to KRequestPending.
+// Enter and return with system unlocked.
+//
+	{
+	if (aFunction == RMessage2::EDisConnect)
+		return KErrArgument;
+
+	RMessageK::TMsgArgs msgArgs;
+	if (aPtr)
+		msgArgs.ReadDesHeaders(aPtr);
+
+	NKern::LockSystem();
+	DSession* session = (DSession*)K::ObjectFromHandle(aHandle, ESession);
+	RMessageK* m = TheCurrentThread->iSyncMsgPtr;
+	__ASSERT_ALWAYS(m->IsFree(), K::PanicCurrentThread(ESyncMsgSentTwice));
+	return session->Send(m, aFunction, aPtr ? &msgArgs : NULL, aStatus);
+	}
+
+TInt DSession::Send(RMessageK* aMsg, TInt aFunction, const RMessageK::TMsgArgs* aArgs, TRequestStatus* aStatus)
+//
+// Send a message to a server.
+// Enter with system locked, return with system unlocked.
+//
+	{
+	TInt panicReason = KErrNone;
+	TInt r = KErrNone;
+
+	__NK_ASSERT_DEBUG(aMsg->IsFree());
+
+	aMsg->OpenRef();
+	aMsg->SetInitialising();
+	aMsg->iSession = this;
+	aMsg->iClient = TheCurrentThread;
+	TheCurrentThread->iIpcCount += 1;
+	iMsgQ.Add(&aMsg->iSessionLink);
+	if (++iMsgCount > iMsgLimit)
+		{
+		r = KErrOverflow;
+		goto error;
+		}
+
+	// Check server is still alive ...
+	if (!iServer || iServer->IsClosing())
+		{
+		r = KErrServerTerminated;
+		goto error;
+		}
+
+	if (aFunction == RMessage2::EConnect)
+		{
+		// Only one connect message is allowed at once
+		if (iConnectMsgPtr)
+			{
+			panicReason = ERequestAlreadyPending;
+			goto error;
+			}
+
+		// Further connect messages not allowed after successful session creation
+		if (iSessionCookie)
+			{
+			panicReason = ESessionAlreadyConnected;
+			goto error;
+			}
+
+		// Keep track of connect message
+		iConnectMsgPtr = aMsg;
+		}
+
+	// Set message contents
+	aMsg->iFunction = aFunction;
+	if (aArgs == NULL)
+		{
+		aMsg->iMsgArgs.iArgFlags = 0;
+		aMsg->iMsgArgs.iArgs[0] = 0;
+		aMsg->iMsgArgs.iArgs[1] = 0;
+		aMsg->iMsgArgs.iArgs[2] = 0;
+		aMsg->iMsgArgs.iArgs[3] = 0;
+		}
+	else
+		{
+		aMsg->iMsgArgs = *aArgs;
+
+		// Attempt to pin the descriptors if required.  This may release the system lock temporarily
+		r = aMsg->PinDescriptors(this, iServer->iPinClientDescriptors);
+		if (r != KErrNone)
+			goto error;
+
+		if (!iServer || iServer->IsClosing())
+			{
+			// Server went away while we weren't holding the system lock
+			r = KErrServerTerminated;
+			goto error;
+			}
+		}
+
+	// don't release system lock again after here until message is deliver/accepted
+	// else session may have become detached
+
+#ifdef BTRACE_CLIENT_SERVER
+	BTraceContext12(BTrace::EClientServer,BTrace::EMessageSend,aMsg,aFunction,this);
+#endif
+
+	// NB: aStatus is NULL for blind messages
+	r = aMsg->SetStatus(aStatus);
+	__ASSERT_DEBUG(r == KErrNone, K::Fault(K::EMessageInUse));
+	iServer->Deliver(aMsg);
+	NKern::UnlockSystem();
+	return r;
+
+error:
+	aMsg->CloseRef();
+	if (panicReason)
+		K::PanicCurrentThread(panicReason);		// doesn't return!
+	NKern::UnlockSystem();
+	return r;
+	}
+
+void DSession::BTracePrime(TInt aCategory)
+	{
+#ifdef BTRACE_CLIENT_SERVER
+	if (aCategory == BTrace::EClientServer || aCategory == -1)
+		{
+		BTrace12(BTrace::EClientServer, BTrace::ESessionAttach, this, iServer, iOwner);
+		}
+#endif
+	}
+
+
+void ExecHandler::SetSessionPtr(RMessageK* aMsg, const TAny* aSessionCookie)
+//
+// Enter with system locked, return with system unlocked
+//
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SetSessionPtr"));
+
+	// Session cookie must not be set to NULL
+	if (!aSessionCookie)
+		K::PanicCurrentThread(ESessionNullCookie);
+
+	// Session cookie must be set from the connect message
+	if (aMsg->iFunction != RMessage2::EConnect)
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+
+	DSession* pS = aMsg->iSession;
+
+	// Must not attempt to set the cookie more than once
+	if (pS->iSessionCookie)
+		K::PanicCurrentThread(ESessionCookieAlreadySet);
+
+	pS->iSessionCookie = aSessionCookie;
+
+	if (pS->IsClosing() && pS->iDisconnectMsgPtr->IsFree())
+		{
+		// NB: pS->iServer is set to NULL during CloseFromDisconnect(), called
+		// when a disconnect message has been completed (and hence is free again),
+		// so this cannot send the disconnect message again.
+		__SendDiscMsg(pS);
+		}
+	NKern::UnlockSystem();
+	}
+
+void ExecHandler::TransferSession(RMessageK* aMsg, TInt aHandle)
+//
+// Enter and return with system locked
+//
+	{
+	__KTRACE_OPT(KEXEC, Kern::Printf("Exec::TransferSession"));
+
+	// Session transfer must done using the Connect message
+	if (aMsg->iFunction != RMessage2::EConnect)
+		K::PanicCurrentThread(ESessionInvalidCookieMsg);
+
+	// Session cookie must already have been set
+	DSession* pSession = aMsg->iSession;
+	if (pSession->iSessionCookie == NULL)
+		K::PanicCurrentThread(ESessionNullCookie);
+
+	// Find the new server and transfer the session to it
+	DServer* pServer = (DServer*)K::ObjectFromHandle(aHandle, EServer);
+	pSession->Transfer(pServer, aMsg);
+
+	// This call also completes the original message with no error
+	ExecHandler::MessageComplete(aMsg, KErrNone);
+	}
+
+void ExecHandler::ServerReceive(DServer* aServer, TRequestStatus& aStatus, TAny* aMsg)
+//
+// Enter and leave with system locked
+//
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::ServerReceive"));
+	aServer->Receive(aStatus, aMsg);
+	}
+
+void ExecHandler::ServerCancel(DServer* aServer)
+//
+// Enter and leave with system locked
+//
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::ServerCancel"));
+	aServer->Cancel();
+	}
+
+TInt ExecHandler::SessionSend(TInt aHandle, TInt aFunction, TAny* aPtr, TRequestStatus* aStatus)
+//
+// Enter with system locked, return with system unlocked.
+//
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionSend"));
+	return DSession::Send(aHandle, aFunction, (const TInt*)aPtr, aStatus);
+	}
+
+TInt ExecHandler::SessionSendSync(TInt aHandle, TInt aFunction, TAny* aPtr, TRequestStatus* aStatus)
+//
+// Enter with system locked, return with system unlocked.
+//
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionSendSync"));
+	return DSession::SendSync(aHandle, aFunction, (const TInt*)aPtr, aStatus);
+	}
+
+#ifndef __MESSAGE_MACHINE_CODED__
+void ExecHandler::MessageComplete(RMessageK* aMsg, TInt aReason)
+//
+// Enter and leave with system locked.
+//
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageComplete"));
+	RMessageK& m = *aMsg;
+
+#ifdef BTRACE_CLIENT_SERVER
+	BTraceContext8(BTrace::EClientServer,BTrace::EMessageComplete,aMsg,aReason);
+#endif
+
+	DSession* s = m.iSession;
+
+	// First check for disconnect message
+	if (m.iFunction == RMessage2::EDisConnect)
+		{
+		s->CloseFromDisconnect();
+		return;
+		}
+
+	// Note whether session connect message has completed
+	if (m.iFunction == RMessage2::EConnect)
+		s->iConnectMsgPtr = NULL;
+
+	__KTRACE_OPT(KIPC,Kern::Printf("MsgCo: M:%d r:%d %O->%O", m.iFunction, aReason, TheCurrentThread, m.iClient));
+	if (!s->IsClosing())
+		{
+		m.SetCompleting();
+		Kern::QueueRequestComplete(m.iClient, &m, aReason);
+		}
+	else
+		{
+		// Pending disconnect, is it a connect message?
+		if(m.iFunction == RMessage2::EConnect)
+			{
+			// If a session has been created, then a disconnect message should already have been sent
+			__ASSERT_DEBUG(!s->iSessionCookie || !s->iServer || !s->iDisconnectMsgPtr->IsFree(),
+							K::Fault(K::EMsgCompleteDiscNotSent));
+
+			// if no server-side session object to clean up, send disconnect message
+			// anyway to prevent message lifetime issues for the server for any other
+			// messages on this unconnected session the server may have accepted
+			if (!s->iSessionCookie)
+				__SendDiscMsg(s);
+			}
+		m.Reset();
+		m.CloseRef();   // Return message to appropriate pool
+		}
+	}
+#else
+#ifdef _DEBUG
+extern "C" void __FaultBadMsgPool()
+	{
+	K::Fault(K::EMsgFreeBadPool);
+	}
+
+extern "C" void __FaultMsgNotFree()
+	{
+	K::Fault(K::EMessageNotFree);
+	}
+
+extern "C" void __FaultMsgInUse()
+	{
+	K::Fault(K::EMessageInUse);
+	}
+
+extern "C" void __FaultMsgCompleteDiscNotSent()
+	{
+	K::Fault(K::EMsgCompleteDiscNotSent);
+	}
+#endif
+
+extern "C" void __PanicSyncMsgSentTwice()
+	{
+	K::PanicCurrentThread(ESyncMsgSentTwice);
+	}
+
+extern "C" void __PanicMesAlreadyPending()
+	{
+	K::PanicCurrentThread(EMesAlreadyPending);
+	}
+
+#endif
+
+//
+// Enter and leave with system locked
+//
+extern "C" void __SendDiscMsg(DSession* aSession)
+	{
+	DServer* server = aSession->iServer;
+	if (server && !server->IsClosing())
+		{
+		// Send the preallocated disconnect message, as the session is still attached
+		// and the server isn't closing so it won't detach the session itself.
+		RMessageK* disc = aSession->iDisconnectMsgPtr;
+		__ASSERT_DEBUG(disc->iSession == aSession && disc->IsFree(),
+						K::Fault(K::EMsgCompleteDiscNotSent));
+
+#ifdef BTRACE_CLIENT_SERVER
+		BTraceContext12(BTrace::EClientServer,BTrace::EMessageSend,disc,disc->iFunction,aSession);
+#endif
+		disc->iSession = aSession;
+		server->Deliver(disc);
+		}
+	}
+
+TInt ExecHandler::SessionShare(TInt& aHandle, TInt aMode)
+//
+// Mutate a single-threaded session into a multi-threaded session
+//
+	{
+	if ( (TUint)aMode > (TUint)EIpcSession_GlobalSharable || aMode == EIpcSession_Unsharable)
+		return KErrArgument;
+	TInt oldHandle;
+	TInt newHandle = 0;
+	kumemget32(&oldHandle, &aHandle, sizeof(TInt));
+	NKern::LockSystem();
+	DSession* s = (DSession*)K::ObjectFromHandle(oldHandle, ESession);
+	s->CheckedOpen();
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionShare %O server %O mode %08x", s, s->iServer, aMode));
+	TInt r = KErrNone;
+	TUint ct = s->iSessionType;
+	if (s->iSvrSessionType == EIpcSession_Unsharable)
+		{
+		r = EUnsharableSession;
+		goto end;
+		}
+	if ((TUint)s->iSvrSessionType < (TUint)aMode)
+		{
+		r = KErrPermissionDenied;
+		goto end;
+		}
+	if (aMode == EIpcSession_GlobalSharable)
+		s->SetProtection(DObject::EProtected);
+	if (ct >= (TUint)aMode)
+		goto end;	// nothing to do
+	s->iSessionType = (TUint8)aMode;
+	if (ct >= EIpcSession_Sharable)
+		goto end;	// nothing more to do
+	K::ThreadEnterCS();
+	r = K::MakeHandle(EOwnerProcess, s);
+	if (r>=0)
+		{
+		newHandle = r;
+		r = KErrNone;
+		K::HandleClose(oldHandle);
+		if (Kern::KUSafeWrite(&aHandle, &newHandle, sizeof(newHandle)))	// don't let thread die before updating handle
+			r = ECausedException;
+		}
+	if (r==KErrNone)
+		{
+		NKern::ThreadLeaveCS();
+		__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionShare returns %d",r));
+		return r;
+		}
+	goto end2;
+end:
+	K::ThreadEnterCS();
+end2:
+	s->Close(NULL);
+	NKern::ThreadLeaveCS();
+	if (r>0)
+		K::PanicKernExec(r);
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::SessionShare returns %d",r));
+	return r;
+	}
+
+#ifndef __MESSAGE_MACHINE_CODED_2__
+void ExecHandler::MessageConstructFromPtr(RMessageK* aMsgK, TAny* aMsgU)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageConstructFromPtr"));
+	RMessageU2 umsg(*aMsgK);
+	NKern::UnlockSystem();
+	kumemput32(aMsgU, &umsg, sizeof(umsg));
+	}
+#endif
+
+TInt ExecHandler::MessageGetDesLength(RMessageK* aMsg, TInt aParam)
+	{
+	__KTRACE_OPT(KEXEC, Kern::Printf("Exec::MessageGetDesLength"));
+	if (TUint(aParam) >= TUint(KMaxMessageArguments))
+		return KErrArgument;
+	if (!aMsg->IsDescriptor(aParam))
+		return KErrBadDescriptor;
+	return aMsg->DesLength(aParam);
+	}
+
+TInt ExecHandler::MessageGetDesMaxLength(RMessageK* aMsg, TInt aParam)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageGetDesMaxLength"));
+	if (TUint(aParam) >= TUint(KMaxMessageArguments))
+		return KErrArgument;
+	if (!aMsg->IsDescriptor(aParam))
+		return KErrBadDescriptor;
+	return aMsg->DesMaxLength(aParam);
+	}
+
+const TRequestStatus* ExecHandler::MessageClientStatus(RMessageK* aMsg)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageClientStatus"));
+	if (aMsg->iMsgType == RMessageK::ESync)
+		return NULL;
+	return aMsg->StatusPtr();
+	}
+
+#ifndef __REMOVE_PLATSEC_DIAGNOSTICS__
+#define __PSIF(msg, diag)	PlatSec::ProcessIsolationIPCFail(msg, diag)
+#else //__REMOVE_PLATSEC_DIAGNOSTICS__
+#define __PSIF(msg, diag)	PlatSec::EmitDiagnostic()
+#endif // !__REMOVE_PLATSEC_DIAGNOSTICS__
+
+TInt ExecHandler::MessageIpcCopy(RMessageK* aMsg, TInt aParam, SIpcCopyInfo& aInfo, TInt aOffset)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageIpcCopy flags=%08x",aInfo.iFlags));
+	if (TUint(aParam) >= TUint(KMaxMessageArguments))
+		return KErrArgument;
+	if (!aMsg->IsDescriptor(aParam) || !aMsg->Descriptor(aParam).IsSet())
+		{
+		__PSIF(aMsg, __PLATSEC_DIAGNOSTIC_STRING("Server attempted to use RMessagePtr2::Read/Write on a non descriptor message argument"));
+		return KErrBadDescriptor;
+		}
+
+	TInt argType = aMsg->ArgType(aParam);
+	if ((aInfo.iFlags & KIpcDirWrite) && (argType & TIpcArgs::EFlagConst))
+		if (__PSIF(aMsg, __PLATSEC_DIAGNOSTIC_STRING("Server attempted to use RMessagePtr2::Write on a const client descriptor argument")))
+			return KErrBadDescriptor;
+	if ((aInfo.iFlags & KChunkShiftBy1) && !(argType & TIpcArgs::EFlag16Bit))
+		if (__PSIF(aMsg, __PLATSEC_DIAGNOSTIC_STRING("Server attempted to use 16bit RMessagePtr2::Read/Write on a 8 bit client descriptor argument")))
+			return KErrBadDescriptor;
+	if ((argType & TIpcArgs::EFlag16Bit) && !(aInfo.iFlags & KChunkShiftBy1))
+		if (__PSIF(aMsg, __PLATSEC_DIAGNOSTIC_STRING("Server attempted to use 8bit RMessagePtr2::Read/Write on a 16 bit client descriptor argument")))
+			return KErrBadDescriptor;
+
+	DThread& t = *TheCurrentThread;
+	t.iTempMsg = aMsg;
+	aMsg->OpenRef();
+
+#ifndef __MEMMODEL_FLEXIBLE__
+	NKern::FlashSystem();
+#else
+	NKern::UnlockSystem();
+#endif
+
+	TInt r = KErrNone;
+	DThread* pT = aMsg->iClient;
+	TInt mode = (aInfo.iFlags & KChunkShiftBy1) | KCheckLocalAddress | KDoNotUpdateDesLength;	// assume called from user mode
+
+	if ((aInfo.iFlags & KIpcDirWrite) == 0)
+		{
+		r = pT->DoDesRead(aMsg->Descriptor(aParam), aInfo.iLocalPtr, aInfo.iLocalLen, aOffset, mode);
+		}
+	else
+		{
+		TAny* ptr = aMsg->Ptr(aParam);
+		r = pT->DoDesWrite(ptr, aMsg->Descriptor(aParam), aInfo.iLocalPtr, aInfo.iLocalLen, aOffset, mode, NULL);
+		if (r >= 0)
+			{
+			// Update cached flags + length word for all matching descriptors and set written bit
+			TInt descFlags = aMsg->iMsgArgs.AllDescriptorFlags();
+			for (TInt i = 0; descFlags != 0; ++i, descFlags >>= TIpcArgs::KBitsPerType)
+				if ((descFlags & TIpcArgs::EFlagDes) && aMsg->Ptr(i) == ptr)
+					{
+					aMsg->Descriptor(i).SetTypeAndLength(r);
+					aMsg->iMsgArgs.SetDesWritten(i);
+					}
+			r = KErrNone;
+			}
+		}
+
+#ifndef __MEMMODEL_FLEXIBLE__
+#else
+	NKern::LockSystem();
+#endif
+
+	__NK_ASSERT_DEBUG(aMsg->IsAccepted() || aMsg->IsCompleting());
+	t.iTempMsg = NULL;
+	aMsg->CloseRef();
+	return r;
+	}
+
+TInt ExecHandler::MessageClient(DThread* aClient, TOwnerType aType)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageClient"));
+	NKern::ThreadEnterCS();
+	TInt r=aClient->Open();
+	NKern::UnlockSystem();
+	if (r==KErrNone)
+		{
+		r=K::MakeHandle(aType,aClient);
+		if (r<KErrNone)
+			aClient->Close(NULL);
+		}
+	NKern::ThreadLeaveCS();
+	return r;
+	}
+
+TInt ExecHandler::MessageSetProcessPriority(DThread* aClient, TProcessPriority aPriority)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageSetProcessPriority"));
+	DProcess* pP = aClient->iOwningProcess;
+	if (pP->iFlags & KProcessFlagPriorityControl)
+		if (aPriority==EPriorityBackground || aPriority==EPriorityForeground)
+			{
+			ExecHandler::ProcessSetPriority(pP,aPriority);
+			return KErrNone;
+			}
+	NKern::UnlockSystem();
+	return KErrPermissionDenied;
+	}
+
+void GetCategory(TDes& aDest, const TDesC& aSrc); // in server.cpp
+
+void ExecHandler::MessageKill(TInt aHandle, TExitType aType, TInt aReason, const TDesC* aCategory)
+	{
+	TBuf<KMaxExitCategoryName> cat;
+	if (aType==EExitPanic && aCategory)
+		GetCategory(cat,*aCategory);
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageKill %d,%d,%lS",aType,aReason,&cat));
+	K::CheckKernelUnlocked();
+	NKern::LockSystem();
+	RMessageK* pM = RMessageK::MessageK(aHandle);
+	DThread* pT = pM->iClient;
+	pT->Die(aType,aReason,cat);		// releases system lock
+	}
+
+TInt ExecHandler::MessageOpenObject(RMessageK* aMsg, TObjectType aObjType, TInt aParam, TOwnerType aOwnerType)
+	{
+	__KTRACE_OPT(KEXEC,Kern::Printf("Exec::MessageOpenObject"));
+	NKern::ThreadEnterCS();
+	DObject* pO; 
+	pO=0;   // To shut up the compiler
+	TInt r;
+	if ( TUint(aParam)>=TUint(KMaxMessageArguments) )
+		{
+		r = KErrArgument;
+		goto done;
+		}
+	r = KErrBadHandle;
+	if ( aMsg->ArgType(aParam)!=TIpcArgs::EHandle )
+		goto done;
+	pO=aMsg->iClient->ObjectFromHandle(aMsg->Arg(aParam),aObjType);
+	if (pO)
+		if(pO->Protection()!=DObject::ELocal)
+			r=pO->Open();
+done:
+	NKern::UnlockSystem();
+	if (r==KErrNone)
+		{
+		r=K::MakeHandle(aOwnerType,pO);	// this will add to process if necessary
+		if (r<KErrNone)
+			pO->Close(NULL);	// can't have been added to process so NULL
+		}
+	NKern::ThreadLeaveCS();
+	return r;
+	}
+
+void ExecHandler::MessageCompleteWithHandle(RMessageK* aMsg, TInt aHandle)
+//
+// Enter and leave with system locked.
+//
+	{
+	DObject* pO = K::ObjectFromHandle(aHandle);
+	pO->CheckedOpen();
+	DThread* pT=aMsg->iClient;
+	TInt r = pT->Open();
+	K::ThreadEnterCS();
+	TInt h;
+	if(r==KErrNone)
+		{
+		// always EOwnerThreads so it doesn't leak if client thread dies
+		r = pT->MakeHandleAndOpen(EOwnerThread,pO,h);
+		if (r==KErrNone)
+			r = h;
+		pT->Close(NULL);
+		}
+	pO->Close(NULL);
+	K::ThreadLeaveCS();
+	RMessageK::MessageK((TInt)aMsg);
+	ExecHandler::MessageComplete(aMsg,r);
+	}