// 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);
	}
