// 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\x86\cutils.cpp
// 
//

//#define __DBG_MON_FAULT__
//#define __RAM_LOADED_CODE__
//#define __EARLY_DEBUG__

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

#ifdef __SMP__
#include <apic.h>
TSpinLock DbgSpinLock(TSpinLock::EOrderBTrace);
#endif


TInt A::VariantHal(TInt aFunction, TAny* a1, TAny* a2)
	{
	return X86::TheAsic->VariantHal(aFunction,a1,a2);
	}

#ifdef __EARLY_DEBUG__
void DebugOutputChar(TUint aChar);
#else
static void DebugOutputChar(TUint aChar)
//
// Transmit one character from the UART (for debugging only)
//
	{
	if (X86::TheAsic)
		X86::TheAsic->DebugOutput(aChar);
	}
#endif

void A::DebugPrint(const TText* aPtr, TInt aLen, TBool aNewLine)
//
// Send the string to kernel trace port (UART)
//
	{
	const TText* pE=aPtr+aLen;
	if (aPtr!=pE)
		{
#ifdef __SMP__
		TInt irq = DbgSpinLock.LockIrqSave();
		if (aNewLine)
			{
			DebugOutputChar(NKern::CurrentCpu()+0x30);
			DebugOutputChar(0x3a);
			}
#endif
		do
			DebugOutputChar(*aPtr++);
		while(aPtr<pE);

		if (aNewLine)
			{
			DebugOutputChar(0x0d);
			DebugOutputChar(0x0a);
			}
#ifdef __SMP__
		DbgSpinLock.UnlockIrqRestore(irq);
#endif
		}
	}

EXPORT_C Asic* Arch::TheAsic()
	{
	return X86::TheAsic;
	}

TPtr8 A::MachineConfiguration()
	{
	return X86::TheAsic->MachineConfiguration();
	}

TInt A::SystemTimeInSecondsFrom2000(TInt& aTime)
	{
	return X86::TheAsic->SystemTimeInSecondsFrom2000(aTime);
	}

TInt A::SetSystemTimeInSecondsFrom2000(TInt aTime)
	{
	return X86::TheAsic->SetSystemTimeInSecondsFrom2000(aTime);
	}


extern "C" void NKCrashHandler(TInt aPhase, const TAny* a0, TInt a1)
	{
	if (aPhase==0)
		{
		Cache::AtomicSyncMemory();
		return;
		}
#ifdef __SMP__
	SFullX86RegSet* regs = &(((SCpuData*)SubScheduler().i_Tss)->iRegs);
#else
	SFullX86RegSet* regs = &X86_Regs;
#endif
	if (a1 != K::ESystemException)
		TheSuperPage().iKernelExcInfo.iCodeAddress = (TAny*)regs->iEip;
	K::DoFault(a0,a1);
	}


void A::StartCrashDebugger(const TAny* a0, TInt a1)
	{
#ifdef __SMP__
	SFullX86RegSet* regs = &(((SCpuData*)SubScheduler().i_Tss)->iRegs);
#else
	SFullX86RegSet* regs = &X86_Regs;
#endif
	regs->iFaultCategory = (TAny*)a0;
	regs->iFaultReason = a1;

	typedef void (*PVFVP)(TAny*);
	PVFVP f = (PVFVP)PP::MonitorEntryPoint[0];
	if (f)
		(*f)(regs);
	FOREVER;
	}


/** 
Restart the system. 
On hardware targets this calls the Restart Vector in the ROM Header.
Note, aMode is set to zero when this function is used by Kern::Fault()

@param aMode Argument passed to the restart routine. The meaning of this value
depends on the bootstrap implementation. 		
*/
EXPORT_C void Kern::Restart(TInt aMode)
	{
	TLinAddr f = (TLinAddr)&TheRomHeader().iRestartVector;
#ifdef __SMP__
	NKern::DisableAllInterrupts();
	TInt i;
	for (i=0; i<KMaxCpus; ++i)
		{
		TheSubSchedulers[i].i_CrashState = (TAny*)f;
		TheSubSchedulers[i].i_ExcInfo = (TAny*)aMode;
		}
	write_apic_reg(ICRH, 0);
	write_apic_reg(ICRL, 0xC4400);	// send NMI to all processors other than this one - will call vector
#ifdef __GCC32__
	asm("int 2");
#else
	_asm int 2						// fake NMI on this processor
#endif
	for(;;) {}
#else
	(*(void (*)(TInt))f)(aMode);
#endif
	}


TInt K::FloatingPointTypes(TUint32& aTypes)
	{
	aTypes = EFpTypeNone;
	return KErrNotSupported;
	}
	

extern "C" EXPORT_C void pagecpy(TAny* aTrg, const TAny* aSrc)
	{
	memcpy(aTrg, aSrc, KPageSize);
	}

extern "C" void fastcpy(TAny* aTrg, const TAny* aSrc, TUint8 aPower)
	{
	memcpy(aTrg, aSrc, 1<<aPower);
	}

#ifdef __MEMMODEL_MULTIPLE__
#include <mmubase.h>
TInt MmuBase::MoveKernelStackPage(DChunk* /*aChunk*/, TUint32 /*aOffset*/, TPhysAddr /*aOld*/, TPhysAddr& /*aNew*/, TUint /*aBlockZoneId*/, TBool /*aBlockRest*/)
	{
	NKern::UnlockSystem();
	return KErrNotSupported;
	}
#endif

TInt K::FloatingPointSystemId(TUint32& /*aSysId*/)
	{
	return KErrNotSupported;
	}
