// Copyright (c) 2007-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\x86\cexec.cia
// 
//

#include <x86_mem.h>
#include <u32std.h>
#include <kernel/cache.h>

GLREF_C TInt CalcKernelHeapUsed();
void GetLatencyValues(TInt aMode, TInt& aCount, TInt* aDest);
void KernMsgTest();
void InvalidExecHandler();
void InvalidFastExec();
void PreprocessHandler();

void InvalidExecHandler();

#define __GEN_KERNEL_EXEC_CODE__

#include "execs.h"

// These must be defined here because the inline assembler syntax can't
// cope with symbols or expressions involving scope resolution (::).
// Therefore, each such value should be turned into a simple constant ...

const TLinAddr K_PanicCurrentThread = (TLinAddr)&K::PanicCurrentThread;
const TLinAddr K_MsgInfo = (TLinAddr)&K::MsgInfo;
const TLinAddr NKern_CurrentThread = (TLinAddr)&NKern::CurrentThread;

#ifdef __X86GCC__

const TUint32 RMessage2_EDisConnect = RMessage2::EDisConnect;
const TUint32 RMessageK_AlignMask = (RMessageK::KMessageSize-1);
const TUint32 offset_DThread_iNThread = _FOFF(DThread,iNThread);
const TUint32 offset_RMessageK_iNext = _FOFF(RMessageK,iServerLink.iNext);
const TUint32 offset_RMessageK_iPrev = _FOFF(RMessageK,iServerLink.iPrev);
const TUint32 offset_SMsgInfo_iBase = _FOFF(K::SMsgInfo,iBase);
const TUint32 offset_SMsgInfo_iMaxSize = _FOFF(K::SMsgInfo,iMaxSize);
const TUint32 sizeof_RMessageK = sizeof(RMessageK);

#else

// With non-GCC for immediate values, including offsets, we must use the
// following enum trick, as otherwise the compiler allocates storage for
// the consts and the code emitted uses the address of this storage rather
// than the value of the constant.  Grrr.

enum
	{
	RMessage2_EDisConnect = RMessage2::EDisConnect,
	RMessageK_AlignMask = (RMessageK::KMessageSize-1),
	offset_DThread_iNThread = _FOFF(DThread,iNThread),
	offset_RMessageK_iNext = _FOFF(RMessageK,iServerLink.iNext),
	offset_RMessageK_iPrev = _FOFF(RMessageK,iServerLink.iPrev),
	offset_SMsgInfo_iBase = _FOFF(K::SMsgInfo,iBase),
	offset_SMsgInfo_iMaxSize = _FOFF(K::SMsgInfo,iMaxSize),
	sizeof_RMessageK = sizeof(RMessageK),
	};

#endif


/***********************************************************************************
* User-side executive handlers
***********************************************************************************/

__NAKED__ void InvalidFastExec()
	{
	asm("sti");
	asm("call %a0": :"i" (&InvalidExecHandler));
	}


/***************************************************************************
* Look up a handle in the current thread or process handles array
* If handle invalid, panic the current thread
* A handle is:
*		Bits 0-14	index
*		Bit  15		no-close flag (ignored here)
*		Bits 16-29	instance value
*		Bit  30		thread-local flag
*		Bit  31		special handle flag
* On entry:
*		EBX = attribute flags for this call
*				(bits 0-4 indicate the type of object referenced)
*				XXX means that only 0x1F valid, but RObjectIx::At uses 0x3F ???
*		EDI points to current NThread
*		Executive call arguments in the usual place on the stack
*					i.e. [ESP+4] onwards - handle is at [ESP+4]
* Return:
*		EAX, ECX, EDX, EDI modified, other registers preserved
*		[ESP+4] replaced with object pointer
*		K::PanicCurrentThread(EBadHandle) if not a valid handle
*
* System lock is held on entry and on return.
***************************************************************************/
__NAKED__ void PreprocessHandler()
	{
	asm("mov eax, [esp+4]");										// get the handle
	asm("mov ecx, %0": :"i" (offset_DThread_iNThread));
	asm("sub edi, ecx");											// EDI points to DThread
	// HARDCODED(TObjectType) - all (and only) IPC types are assumed to have this bit set!
	asm("test bl, 0x20");											// IPC handle?
	asm("jnz lookup_message_handle");
	asm("test eax, eax");				// check for special handle
	asm("js lookup_special");			// branch if special

	asm("test eax, 0x40000000");		// check for thread-relative handle
	asm("jnz handle_local");			// branch if it is
	asm("mov ecx, [edi+%0]": : "i"_FOFF(DThread,iOwningProcess));
	asm("lea ecx, [ecx+%0]": : "i"_FOFF(DProcess,iHandles));
	asm("jmp got_ix_ptr");

	asm("handle_local:");
	asm("lea ecx, [edi+%0]": : "i"_FOFF(DThread,iHandles));

	asm("got_ix_ptr:");
#ifdef __HANDLES_USE_RW_SPIN_LOCK__
	XXX TODO need to acquire the read lock
	// (__HANDLES_USE_RW_SPIN_LOCK__ is not supported or even finished)
#else
	// System lock held on entry, so nothing to do here
#endif
	asm("mov edx, eax");
	asm("and edx, 0x00007fff");			// edx=handle index
	asm("cmp edx, [ecx+%0]": : "i"_FOFF(RObjectIx,iCount));	// check against count
	asm("jae lookup_handle_bad");		// if >= count, invalid

	asm("shl edx, 3");					// sizeof iSlots[0] == 8
	asm("add edx, [ecx+%0]": : "i"_FOFF(RObjectIx,iSlots));	// edx points to record for this handle
	asm("shr eax, 16");					// eax=instance value with thread-local and special bits
	asm("mov ecx, [edx]");				// ecx=attrib:type:instance
	asm("mov edx, [edx+4]");			// edx points to DObject specified by handle

	// check the instance value in the incoming handle against the value in the slot
	asm("xor ax, cx");
	asm("and eax, 0x3fff");
	asm("jnz lookup_handle_bad");

	// check that we have the right type of object
	asm("test bl, 0x1f");				// if zero, any type of object accepted
										// should be the same constant as in "and al" line below
	asm("jz handle_ok");

	asm("shr ecx, 14");
	asm("mov al, bl");
	asm("xor al, cl");
	asm("and al, 0x1f");				// this should be 0x3f, but function header says
										// it's only lower 5 bits of ebx
	asm("jnz lookup_handle_bad");

	asm("handle_ok:");
	asm("and edx, 0xFFFFFFFC");			// clear bottom two bits in pointer [EObjRObjMask]
	asm("jz lookup_handle_bad");		// => value was < 4, i.e. a reserved slot, not a valid DObject pointer
#ifdef __HANDLES_USE_RW_SPIN_LOCK__
	XXX TODO must take a reference to the object (call edx->Open(), must == KErrNone)
		if handle lookup only protected by RW spin lock
#endif
	asm("mov [esp+4], edx");			// handle OK so replace handle with DObject pointer
	asm("ret");

	asm("lookup_special:");
	asm("xor ecx, ecx");
	asm("mov cl, bl");
	asm("and cl, 0x1f");											// ecx=required UID or zero if none specified
	asm("cmp cl, 2");
	asm("ja lookup_handle_bad_no_unlock");			// special handle can only refer to thread or process
#ifdef	__OBSOLETE_V1_IPC_SUPPORT__
	asm("cmp eax, 0x88000000");
	asm("jb lookup_thread_pseudo");									// jump if IPC client thread pseudo handle
#endif
	asm("add eax, 0x8000");
	asm("jnc lookup_handle_bad_no_unlock");			// must be >= FFFF8000
	asm("je lookup_special_1");			// branch if must be process
	asm("dec eax");						// test for current thread handle
	asm("je current_thread_handle");
	asm("test cl, cl");
	asm("jnz lookup_handle_bad_no_unlock");
	asm("inc eax");
	asm("lookup_special_1:");
	asm("test eax, eax");
	asm("jnz lookup_handle_bad_no_unlock");
	asm("mov edi, [edi+%0]": : "i" _FOFF(DThread,iOwningProcess));
	asm("current_thread_handle:");
	asm("mov [esp+4], edi");										// handle OK so replace handle with DObject pointer
	asm("ret");

#ifdef	__OBSOLETE_V1_IPC_SUPPORT__
	asm("lookup_thread_pseudo:");
	asm("cmp cl, 1");												// must be thread or unspecified
	asm("ja lookup_handle_bad_no_unlock");
	asm("mov ecx, %0" : : "i" (K_MsgInfo));							// ecx->msg chunk info
	asm("add ax, ax");												// shift off no-close bit
	asm("add eax, eax");											// shift off top bit, eax = alleged message offset
	asm("test eax, %0" : : "i" (RMessageK_AlignMask));				// check alignment
	asm("jne bad_message_handle");									// reject if misaligned
	asm("lea edx, [eax+%0]" : : "i" (sizeof_RMessageK));			// edx = alleged message offset + sizeof RMessageK
	asm("cmp edx, [ecx+%0]" : : "i" (offset_SMsgInfo_iMaxSize));
	asm("ja lookup_handle_bad_no_unlock");											// reject if offset+size > msg chunk max size
	asm("add eax, [ecx+%0]" : : "i" (offset_SMsgInfo_iBase));		// eax = message address
	asm("jmp lookup_message_handle2");
#endif

	asm("lookup_message_handle:");
	asm("mov ecx, %0" : : "i" (K_MsgInfo));							// ecx->msg chunk info
	asm("mov edx, eax");											// edx = handle
	asm("sub edx, [ecx+%0]" : : "i" (offset_SMsgInfo_iBase));		// edx = handle - kernel msg chunk base
	asm("jb bad_message_handle");									// reject if offset is negative
	asm("test edx, %0" : : "i" (RMessageK_AlignMask));				// check alignment
	asm("jne bad_message_handle");									// reject if misaligned
	asm("add edx, %0": : "i" (sizeof_RMessageK));
	asm("cmp edx, [ecx+%0]" : : "i" (offset_SMsgInfo_iMaxSize));
	asm("ja bad_message_handle");									// reject if offset+size > msg chunk max size
#ifdef	__OBSOLETE_V1_IPC_SUPPORT__
	asm("lookup_message_handle2:");
#endif
	asm("lea edx, bad_message_handle");								// if exception occurs, handle is bad
	asm("mov [edi+%0], edx": : "i" _FOFF(DX86PlatThread,iMagicExcHandler));
	asm("mov edx, [eax+%0]": : "i" (offset_RMessageK_iNext));		// edx should be complement of message address
	asm("mov ecx, [eax+%0]": : "i" (offset_RMessageK_iPrev));		// ecx should be complement of server process address
	asm("xor edx, eax");											// should be 0xffffffff
	asm("xor ecx, [edi+%0]": : "i" _FOFF(DThread,iOwningProcess));	// should be 0xffffffff
	asm("and ecx, edx");											// should be 0xffffffff
	asm("mov dword ptr [edi+%0], 0": : "i" _FOFF(DX86PlatThread,iMagicExcHandler));
	asm("inc ecx");													// this should be zero
	asm("jne bad_message_handle");									// if not, bad
	asm("cmp bl, %0": : "i" (EIpcMessageD));						// allow disconnect?
	asm("je lookup_msg_ok");
	asm("mov ecx, [eax+%0]": : "i" _FOFF(RMessageK,iFunction));
	asm("cmp ecx, %0": : "i" (RMessage2_EDisConnect));				// check function
	asm("je bad_message_handle");									// disconnect is not allowed
	asm("cmp bl, %0": : "i" (EIpcMessage));											// message or client?
	asm("je lookup_msg_ok");										// if message, finished
	asm("mov edx, [eax+%0]": : "i" _FOFF(RMessageK,iClient));
	asm("mov [esp+4], edx");										// if client, replace first arg with client ptr
	asm("lookup_msg_ok:");
	asm("ret");

	// XXX	we only take the handle-reading spin lock if we're not dealing with a
	//		special handle (same as in the ARM implementation).  In that case, we
	//		shouldn't release the spinlock when they decide that the handle they've
	//		been given is bad.
	asm("lookup_handle_bad:");
#ifdef __HANDLES_USE_RW_SPIN_LOCK__
	XXX TODO release the read lock
#else
	// System lock held on entry and exit, so don't release here
#endif
	asm("lookup_handle_bad_no_unlock:");
	// K::PanicCurrentThread(EBadHandle) - x86gcc can't handle "mov dword ptr [esp], %a0" : : "i" (EBadHandle)
	// EBadHandle is 0
	asm("mov dword ptr [esp], %0": :"i" (EBadHandle));
	asm("call %a0": :"i"(K_PanicCurrentThread));

	asm("bad_message_handle:");

	// ((DX86PlatThread*)TheCurrentThread)->iMagicExcHandler = 0;
	asm("call %a0" : : "i" (NKern_CurrentThread));
	asm("sub eax, %0": :"i" (offset_DThread_iNThread));
	asm("mov dword ptr [eax+%0], 0": :"i" _FOFF(DX86PlatThread,iMagicExcHandler));

	// K::PanicCurrentThread(EBadMessageHandle);
	asm("mov dword ptr [esp], %0": :"i" (EBadMessageHandle));
	asm("call %a0": :"i" (K_PanicCurrentThread));
	}

static __NAKED__ TInt Kern__HalFunction()
	{
	asm("mov eax, %0": : "i" (EExecHalFunction));
	asm("push ebx");
	asm("push esi");
	asm("mov ecx, [esp+12]");
	asm("mov edx, [esp+16]");
	asm("mov ebx, [esp+20]");
	asm("mov esi, [esp+24]");
	asm("int 0x21");
	asm("pop esi");
	asm("pop ebx");
	asm("ret");
	}

EXPORT_C __NAKED__ TInt Kern::HalFunction(TInt /*aGroup*/, TInt /*aFunction*/, TAny* /*a1*/, TAny* /*a2*/, TInt aDeviceNumber)
//
// Execute a HAL function
// This must be done as an exec to get the correct permissions when calling from supervisor mode.
//
	{
	asm("mov eax, [esp+20]");
	asm("shl eax, 16");
	asm("or [esp+4], eax");
	asm("jmp %a0" : : "i" (&Kern__HalFunction));
	}

EXPORT_C __NAKED__ TInt Kern::HalFunction(TInt /*aGroup*/, TInt /*aFunction*/, TAny* /*a1*/, TAny* /*a2*/)
//
// Execute a HAL function
// This must be done as an exec to get the correct permissions when calling from supervisor mode.
//
	{
	asm("jmp %a0": :"i" (&Kern__HalFunction));
	}

