kernel/eka/kernel/arm/ckernel.cpp
changeset 43 96e5fb8b040d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/kernel/arm/ckernel.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,503 @@
+// 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\arm\ckernel.cpp
+// 
+//
+
+#include <arm_mem.h>
+#include <arm_vfp.h>
+
+#define iMState		iWaitLink.iSpare1
+#define iExiting	iWaitLink.iSpare2
+
+GLREF_C void __ArmVectorReset();
+GLREF_C void __ArmVectorUndef();
+GLREF_C void __ArmVectorSwi();
+GLREF_C void __ArmVectorAbortPrefetch();
+GLREF_C void __ArmVectorAbortData();
+GLREF_C void __ArmVectorReserved();
+GLREF_C void __ArmVectorIrq();
+GLREF_C void __ArmVectorFiq();
+
+extern "C" void ExcFault(TAny* aExcInfo);
+
+/********************************************
+ * Thread
+ ********************************************/
+
+DArmPlatThread::~DArmPlatThread()
+	{
+	DThread::Destruct();
+	}
+
+TInt DArmPlatThread::Context(TDes8& aDes)
+	{
+	TArmRegSet& s=*(TArmRegSet*)aDes.Ptr();
+	aDes.SetLength(sizeof(s));
+	TInt r=KErrNone;
+	if (iThreadType!=EThreadUser || this==TheCurrentThread)
+		r=KErrAccessDenied;
+	else if (iExiting)
+		r=KErrDied;
+	else
+		{
+		TUint32 unused;
+		NKern::ThreadGetUserContext(&iNThread,&s,unused);
+		}
+	return r;
+	}
+
+DProcess* P::NewProcess()
+	{
+	return new DArmPlatProcess;
+	}
+
+#ifdef __CPU_HAS_VFP
+#ifdef __VFP_V3
+const TInt KVfpContextSize = (1 + (32 * 2)) * 4;	// FPSCR + 32 dword registers
+#else
+const TInt KVfpContextSize = (3 + (16 * 2)) * 4;	// FPSCR + FPINST + FPINST2 + 16 dword registers
+#endif
+#endif
+TInt DArmPlatProcess::GetNewThread(DThread*& aThread, SThreadCreateInfo& aInfo)
+//
+// Create a new DArmPlatThread object
+// If aThread=NULL on entry, allocate it on the kernel heap,
+// otherwise do in-place construction.
+//
+	{
+	DArmPlatThread* pT=(DArmPlatThread*)aThread;
+	if (!pT)
+		{
+		TInt size = sizeof(DArmPlatThread);
+#ifdef __CPU_HAS_VFP
+		size += KVfpContextSize;
+#endif
+		__KTRACE_OPT(KTHREAD,Kern::Printf("GetNewThread size=%04x",size));
+		pT = (DArmPlatThread*)Kern::AllocZ(size);
+		}
+	if (!pT)
+		return KErrNoMemory;
+	new (pT) DArmPlatThread;
+	aThread = pT;
+	pT->iOwningProcess=this;
+
+#ifdef __CPU_HAS_VFP
+	pT->iNThread.iExtraContext = (TUint8*)pT + sizeof(DArmPlatThread);
+	*(TUint32*)(pT->iNThread.iExtraContext) = Arm::VfpDefaultFpScr;
+	// Inherit parent VFP FPSCR value if applicable
+	if ((TInt)aInfo.iType != (TInt)EThreadInitial)
+		{
+		if (pT->iOwningProcess == Kern::CurrentThread().iOwningProcess)
+			{
+			if (Arm::FpExc() & VFP_FPEXC_EN)
+				{
+				*(TUint32*)(pT->iNThread.iExtraContext) = Arm::FpScr() & VFP_FPSCR_MODE_MASK;
+				}
+			}
+		}
+#endif
+
+	return KErrNone;
+	}
+
+extern void DumpExcInfo(TArmExcInfo& a);
+void DumpExcInfoX(TArmExcInfo& a)
+	{
+	DumpExcInfo(a);
+	NThread* nthread = NCurrentThread();
+	if (nthread == NULL)
+		Kern::Printf("No current thread");
+	else
+		{
+		DThread* thread = Kern::NThreadToDThread(NCurrentThread());
+		if (thread)
+			{
+			TFullName thread_name;
+			thread->TraceAppendFullName(thread_name, EFalse);
+			Kern::Printf("Thread full name=%S", &thread_name);
+			Kern::Printf("Thread ID=%d, KernCSLocked=%d",TheCurrentThread->iId,NKern::KernelLocked());
+			}
+		else
+			Kern::Printf("Thread N/A, KernCSLocked=%d",NKern::KernelLocked());
+		}
+	}
+
+extern void DumpFullRegSet(SFullArmRegSet& a);
+extern void GetUndefinedInstruction(TArmExcInfo* /*aContext*/);
+extern void PushExcInfoOnUserStack(TArmExcInfo*, TInt);
+extern "C" {
+extern SFullArmRegSet DefaultRegSet;
+}
+
+#ifdef __CPU_HAS_VFP
+GLREF_C TInt HandleVFPOperation(TAny* aPtr);
+#endif
+
+void Exc::Dispatch(TAny* aPtr, NThread*)
+	{
+#ifdef __CPU_ARM_ABORT_MODEL_UPDATED
+#error Processors implementing the 'Base Register Updated' Abort Model are no longer supported
+#endif
+
+	TArmExcInfo* pR=(TArmExcInfo*)aPtr;
+	TInt mode=pR->iCpsr & EMaskMode;
+
+	TBool faultHandled = EFalse;
+
+#ifdef __DEMAND_PAGING__
+	faultHandled |= M::DemandPagingFault(aPtr) == KErrNone;
+#endif
+
+	if(!faultHandled)
+		faultHandled |= M::RamDefragFault(aPtr) == KErrNone;
+
+	if(faultHandled)
+		{
+#ifdef __ATOMIC64_USE_SLOW_EXEC__
+		if (mode==ESvcMode && pR->iExcCode==EArmExceptionDataAbort && IsMagicAtomic64(pR->iR15))
+			{
+			// Magic atomic instruction so return to next instruction to stop any 
+			// writes to memory being executed and ensure interrupts are enabled.
+			pR->iR15 += 4;
+			pR->iCpsr &= ~KAllInterruptsMask;
+			}
+#endif
+		return;
+		}
+
+	if (mode==ESvcMode && pR->iExcCode==EArmExceptionDataAbort && IsMagic(pR->iR15))
+		{
+		// skip instruction that caused the exception, set the zero flag and place faulted address in r12
+		__KTRACE_OPT(KPANIC,DumpExcInfoX(*pR));
+		pR->iR15 += 4;
+		pR->iCpsr |= ECpuZf;
+		pR->iR12 = pR->iFaultAddress;
+		return;
+		}
+
+	DThread* pT=TheCurrentThread;
+	TExcTrap* xt=pT->iExcTrap;
+	if (xt)
+		{
+		__KTRACE_OPT(KPANIC,DumpExcInfoX(*pR));
+		// current thread wishes to handle exceptions
+		(*xt->iHandler)(xt,pT,aPtr);
+		}
+
+	if (NKern::HeldFastMutex())	// thread held fast mutex when exception occurred
+		Exc::Fault(aPtr);
+
+#ifdef __CPU_HAS_VFP
+	if (pR->iExcCode==EArmExceptionUndefinedOpcode)
+		{
+		// Get the undefined instruction
+		GetUndefinedInstruction(pR);
+
+		const TUint32 opcode = pR->iFaultAddress;
+		TInt cpnum = -1;
+
+#ifdef __SUPPORT_THUMB_INTERWORKING
+		if (!(pR->iCpsr & ECpuThumb)) {
+#endif
+		// check for coprocessor instructions
+		//		10987654321098765432109876543210
+		// CDP:	cond1110op1 CRn CRd cp_#op20CRm
+		// LDC: cond110PUNW1Rn  CRd cp_#offset
+		// STC: cond110PUNW0Rn  CRd cp_#offset
+		// MRC: cond1110op11CRn Rd  cp_#op21CRm
+		// MCR: cond1110op10CRn Rd  cp_#op21CRm
+		// ext:	cond11000x0xRn  CRd cp_#offset
+		//CDP2:	11111110xxxxxxxxxxxxxxxxxxx0xxxx
+		//LDC2: 1111110xxxx1xxxxxxxxxxxxxxxxxxxx
+		//STC2: 1111110xxxx0xxxxxxxxxxxxxxxxxxxx
+		//MRC2:	11111110xxx1xxxxxxxxxxxxxxx1xxxx
+		//MCR2:	11111110xxx0xxxxxxxxxxxxxxx1xxxx
+		//MRRC: cond11100101Rn  Rd  cp_#opc CRm
+		//MCRR: cond11100100Rn  Rd  cp_#opc CRm
+		//
+		//NEON data processing:
+		//      1111001xxxxxxxxxxxxxxxxxxxxxxxxx
+		//NEON element/structure load/store:
+		//      11110100xxx0xxxxxxxxxxxxxxxxxxxx
+		//
+		// Coprocessor instructions have 2nd hex digit (bits 24-27) C,D,E.
+		// The coprocessor number is in bits 8-11.
+		// NEON instructions have 1st hex digits F2, F3, or F4x where x is even
+		// No coprocessor number, route to cp 10
+
+		TUint32 hex2 = (opcode>>24) & 0x0f;
+
+		if (hex2==0xc || hex2==0xd || hex2==0xe)
+			cpnum=(opcode>>8)&0x0f;
+#ifdef __CPU_ARMV7
+		else if ((opcode>>28)==0xf && (hex2==2 || hex2==3 || (hex2==4 && ((opcode>>20)&1)==0)))
+			cpnum=VFP_CPID_S;
+#endif
+
+#ifdef __SUPPORT_THUMB_INTERWORKING
+		}
+#endif
+#ifdef __CPU_ARMV7
+	else
+		{
+		// Check for coprocessor instructions (thumb mode, so only first halfword)
+		//			5432109876543210 5432109876543210
+		//   CDP:	11101110op1 CRn  CRd cp_#op20CRm
+		//   LDC:	1110110PUNW1Rn   CRd cp_#offset
+		//   STC:	1110110PUNW0Rn   CRd cp_#offset
+		//   MRC:	11101110op11CRn  Rd  cp_#op21CRm
+		//   MCR:	11101110op10CRn  Rd  cp_#op21CRm
+		//	CDP2:	11111110xxxxxxxx xxxxxxxxxx0xxxxx
+		//	LDC2:	1111110xxxx1xxxx xxxxxxxxxxxxxxxx
+		//	STC2:	1111110xxxx0xxxx xxxxxxxxxxxxxxxx
+		//	MRC2:	11111110xxx1xxxx xxxxxxxxxx1xxxxx
+		//	MCR2:	11111110xxx0xxxx xxxxxxxxxx1xxxxx
+		//  MRRC:	111011100101Rn   Rd  cp_#opc CRm
+		//  MCRR:	111011100100Rn   Rd  cp_#opc CRm
+		//
+		// Operations starting 1111 are not currently valid for VFP/NEON
+		// but are handled here in case of future development or 
+		// alternative coprocessors
+		//
+		// NEON data processing:
+		//			111x1111xxxxxxxx xxxxxxxxxxxxxxxx
+		// NEON element/structure load/store:
+		//			11111001xxx0xxxx xxxxxxxxxxxxxxxx
+		//
+		// Coprocessor instructions have first hex digit E or F
+		// and second C, D or E
+		// The coprocessor number is in bits 8-11 of the second halfword
+		// NEON instructions have first 2 hex digits EF, FF or F9
+		// No coprocessor number, route to cp 10
+
+		const TUint32 hex12 = opcode >> 8;
+
+		if ((hex12 & 0xe0) == 0xe0)
+			{
+			const TUint32 hex2 = hex12 & 0xf;
+			if (hex2 == 0xc || hex2 == 0xd || hex2 == 0xe)
+				{
+				TArmExcInfo nextInstruction = *pR;
+				nextInstruction.iR15 += 2;
+				GetUndefinedInstruction(&nextInstruction);
+				cpnum = (nextInstruction.iFaultAddress >> 8) & 0x0f;
+				}
+			else
+				{
+				if (hex12 == 0xef || hex12 == 0xf9 || hex12 == 0xff)
+					cpnum = VFP_CPID_S;
+				}
+			}
+		}
+#endif // __CPU_ARMV7
+
+		if (cpnum >= 0)
+			{
+			__KTRACE_OPT(KEVENT,Kern::Printf("VFP Instruction %08x", opcode));
+			TInt r = HandleVFPOperation(pR);
+			if (r==KErrNone)
+				return;
+			__KTRACE_OPT(KEVENT,Kern::Printf("VFP Instruction returned %d", r));
+			}
+		}
+#endif // __CPU_HAS_VFP
+
+	NKern::LockSystem();
+	if (pT->iThreadType==EThreadUser && mode==EUserMode)
+		{
+		TExcType type=(TExcType)12;
+		if (pT->IsExceptionHandled(type))
+			{
+			pT->iFlags |= KThreadFlagLastChance;
+			NKern::UnlockSystem();
+
+			// tweak context to call exception handler
+			PushExcInfoOnUserStack(pR, type);
+			pR->iR15 = (TUint32)pT->iOwningProcess->iReentryPoint;
+			pR->iR4 = KModuleEntryReasonException;
+#ifdef __SUPPORT_THUMB_INTERWORKING
+			pR->iCpsr &= ~ECpuThumb;
+#endif
+			return;
+			}
+		}
+	// Fault system before attempting to signal kernel event handler as going to
+	// crash anyway so no point trying to handle the event.  Also, stops
+	// D_EXC hanging system on crash of critical/permanent thread.
+    if (pT->iFlags & (KThreadFlagSystemCritical|KThreadFlagSystemPermanent))
+		Exc::Fault(aPtr);
+	NKern::UnlockSystem();	
+
+	TUint m = DKernelEventHandler::Dispatch(EEventHwExc, pR, NULL);
+	if (m & (TUint)DKernelEventHandler::EExcHandled)
+		return;
+
+//	__KTRACE_OPT(KPANIC,DumpExcInfoX(*pR));
+	DumpExcInfoX(*pR);
+
+	// panic current thread
+	K::PanicKernExec(ECausedException);
+	}
+
+EXPORT_C void Exc::Fault(TAny* aExcInfo)
+	{
+#ifdef __SMP__
+	TSubScheduler* ss = &SubScheduler();
+	if (!ss)
+		ss = &TheSubSchedulers[0];
+	ss->i_ExcInfo = aExcInfo;
+	SFullArmRegSet* a = (SFullArmRegSet*)ss->i_Regs;
+	if (!a)
+		a = &DefaultRegSet;
+#else
+	TheScheduler.i_ExcInfo = aExcInfo;
+	SFullArmRegSet* a = (SFullArmRegSet*)TheScheduler.i_Regs;
+#endif
+	if (aExcInfo)
+		{
+		Arm::SaveState(*a);
+		Arm::UpdateState(*a, *(TArmExcInfo*)aExcInfo);
+		}
+	TExcInfo e;
+	e.iCodeAddress = (TAny*)a->iN.iR15;
+	e.iDataAddress = (TAny*)a->iB[0].iDFAR;
+	e.iExtraData = (TInt)a->iB[0].iDFSR;
+//	__KTRACE_OPT(KPANIC,DumpExcInfoX(*pR));
+	DumpFullRegSet(*a);
+	TheSuperPage().iKernelExcId = a->iExcCode;
+	TheSuperPage().iKernelExcInfo = e;
+	Kern::Fault("Exception", K::ESystemException);
+	}
+
+extern "C" void ExcFault(TAny* aExcInfo)
+	{
+	Exc::Fault(aExcInfo);
+	}
+
+void DArmPlatThread::DoExit2()
+	{
+#ifdef __CPU_HAS_VFP
+	for (TInt cpu = 0; cpu < NKern::NumberOfCpus(); cpu++)
+		{
+		// Ensure that if this thread object is re-used then it gets a fresh context
+		if (Arm::VfpThread[cpu] == &iNThread)
+			Arm::VfpThread[cpu] = NULL;
+#ifndef __SMP__
+		Arm::ModifyFpExc(VFP_FPEXC_EX | VFP_FPEXC_EN, 0); // Disable VFP here for unicore
+#endif
+		}
+#endif
+	}
+
+
+/** Sets the function used to handle bounces from VFP hardware.
+
+Used by a VFP coprocessor support kernel extension to register its
+bounce handler.
+
+@publishedPartner
+@released
+ */
+EXPORT_C void Arm::SetVfpBounceHandler(TVfpBounceHandler aHandler)
+	{
+	Arm::VfpBounceHandler = aHandler;
+	}
+
+
+/** Sets the default value of FPSCR in the VFP hardware.
+
+Used by a VFP coprocessor support kernel extension to enable a
+better default mode than RunFast.
+
+@publishedPartner
+@released
+ */
+EXPORT_C void Arm::SetVfpDefaultFpScr(TUint32 aFpScr)
+	{
+#ifdef __CPU_HAS_VFP
+	VfpDefaultFpScr = aFpScr;
+#endif
+	}
+
+
+#ifdef __CPU_HAS_VFP
+
+#ifndef __SMP__
+extern void DoSaveVFP(void*);
+#endif
+extern void DoRestoreVFP(const void*);
+
+GLDEF_C TInt HandleVFPOperation(TAny* aPtr)
+	{
+	NThread* pC = NCurrentThread();
+
+	if (Arm::FpExc() & VFP_FPEXC_EN)
+		{
+		// Coprocessor already enabled so it must be a real exception
+		if (Arm::VfpBounceHandler)
+			return Arm::VfpBounceHandler((TArmExcInfo*)aPtr);
+		else
+			return KErrGeneral;	
+		}
+
+	NKern::Lock();
+
+	// Enable access for this thread, clear any exceptional condition
+	TUint32 oldFpExc = Arm::ModifyFpExc(VFP_FPEXC_EX, VFP_FPEXC_EN);
+
+#ifndef __SMP__
+	if (Arm::VfpThread[0] != pC)
+		{
+		// Only for unicore - SMP explicitly saves the current context and disables VFP
+		// when a thread is descheduled in case it runs on a different core next time
+		if (Arm::VfpThread[0])
+			{
+			DoSaveVFP(Arm::VfpThread[0]->iExtraContext);
+			Arm::VfpThread[0]->ModifyFpExc(VFP_FPEXC_EN, 0); // Take access away from previous thread
+			}
+		DoRestoreVFP(pC->iExtraContext);	// Restore this thread's context
+		Arm::VfpThread[0] = pC;
+		}
+#else
+	const TInt currentCpu = NKern::CurrentCpu();
+	if (Arm::VfpThread[currentCpu] != pC)
+		{
+		DoRestoreVFP(pC->iExtraContext);	// Restore this thread's context
+		Arm::VfpThread[currentCpu] = pC;
+		for (TInt cpu = 0; cpu < NKern::NumberOfCpus(); cpu++)
+			{
+			if (cpu != currentCpu)
+				{
+				TUint32 pCcopy = (TUint32)pC;
+				__e32_atomic_cas_rlx32(&Arm::VfpThread[cpu], &pCcopy, NULL);
+				}
+			}
+		}
+#endif
+
+	// Put FPEXC back how it was in case there was a pending exception, but keep enable bit on
+	Arm::SetFpExc(oldFpExc | VFP_FPEXC_EN);
+	NKern::Unlock();
+
+	return KErrNone;
+	}
+#endif // __CPU_HAS_VFP
+
+#ifdef _DEBUG
+extern "C" void __FaultIpcClientNotNull()
+	{
+	K::Fault(K::EIpcClientNotNull);
+	}
+#endif