kernel/eka/nkern/arm/ncutilf.cia
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/nkern/arm/ncutilf.cia	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,507 @@
+// Copyright (c) 1998-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\nkern\arm\ncutilf.cia
+// 
+//
+
+#include <e32cia.h>
+#include <arm.h>
+#include "highrestimer.h"
+
+#ifdef __SCHEDULER_MACHINE_CODED__
+/** Signals the request semaphore of a nanothread.
+
+	This function is intended to be used by the EPOC layer and personality
+	layers.  Device drivers should use Kern::RequestComplete instead.
+
+	@param aThread Nanothread to signal.  Must be non NULL.
+
+	@see Kern::RequestComplete()
+
+	@pre Interrupts must be enabled.
+	@pre Do not call from an ISR.
+ */
+EXPORT_C __NAKED__ void NKern::ThreadRequestSignal(NThread* /*aThread*/)
+	{
+	ASM_CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR);
+
+	asm("ldr r2, __TheScheduler ");
+	asm("str lr, [sp, #-4]! ");
+	asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked));
+	asm("add r0, r0, #%a0" : : "i" _FOFF(NThread,iRequestSemaphore));
+	asm("add r3, r3, #1 ");
+	asm("str r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked));
+	asm("bl  " CSM_ZN14NFastSemaphore6SignalEv);	// alignment OK since target is also assembler
+	asm("ldr lr, [sp], #4 ");
+	asm("b  " CSM_ZN5NKern6UnlockEv);
+	}
+
+
+/** Atomically signals the request semaphore of a nanothread and a fast mutex.
+
+	This function is intended to be used by the EPOC layer and personality
+	layers.  Device drivers should use Kern::RequestComplete instead.
+
+	@param aThread Nanothread to signal.  Must be non NULL.
+	@param aMutex Fast mutex to signal.  If NULL, the system lock is signaled.
+
+	@see Kern::RequestComplete()
+
+	@pre Kernel must be unlocked.
+	@pre Call in a thread context.
+	@pre Interrupts must be enabled.
+ */
+EXPORT_C __NAKED__ void NKern::ThreadRequestSignal(NThread* /*aThread*/, NFastMutex* /*aMutex*/)
+	{
+	ASM_CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC);
+
+	asm("ldr r2, __TheScheduler ");
+	asm("cmp r1, #0 ");
+	asm("ldreq r1, __SystemLock ");
+	asm("ldr r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked));
+	asm("stmfd sp!, {r1,lr} ");
+	asm("add r0, r0, #%a0" : : "i" _FOFF(NThread,iRequestSemaphore));
+	asm("add r3, r3, #1 ");
+	asm("str r3, [r2, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked));
+	asm("bl  " CSM_ZN14NFastSemaphore6SignalEv);
+	asm("ldr r0, [sp], #4 ");
+	asm("bl  " CSM_ZN10NFastMutex6SignalEv);		// alignment OK since target is also assembler
+	asm("ldr lr, [sp], #4 ");
+	asm("b  " CSM_ZN5NKern6UnlockEv);
+
+	asm("__SystemLock: ");
+	asm(".word %a0" : : "i" ((TInt)&TheScheduler.iLock));
+	asm("__TheScheduler: ");
+	asm(".word TheScheduler ");
+	}
+#endif
+
+
+#ifndef __USER_CONTEXT_TYPE_MACHINE_CODED__
+// called by C++ version of NThread::UserContextType()
+__NAKED__ TBool RescheduledAfterInterrupt(TUint32 /*aAddr*/)
+	{
+	asm("ldr r1, __irq_resched_return ");
+	asm("cmp r0, r1 ");
+	asm("movne r0, #0 ");
+	__JUMP(,lr);
+	asm("__irq_resched_return: ");
+	asm(".word irq_resched_return ");
+	}
+
+#else
+
+/** Get a value which indicates where a thread's user mode context is stored.
+
+	@return A value that can be used as an index into the tables returned by
+	NThread::UserContextTables().
+
+	@pre any context
+	@pre kernel locked
+	@post kernel locked
+ 
+	@see UserContextTables
+	@publishedPartner
+ */
+EXPORT_C __NAKED__ NThread::TUserContextType NThread::UserContextType()
+	{
+	ASM_CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR);
+//
+// Optimisation note: It may be possible to coalesce the first and second
+// checks below by creating separate "EContextXxxDied" context types for each
+// possible way a thread can die and ordering these new types before
+// EContextException.
+//
+
+	// Dying thread? use context saved earlier by kernel
+
+	asm("ldr r3, [r0, #%a0]" : : "i" _FOFF(NThread,iCsFunction));
+	asm("ldrb r2, [r0, #%a0]" : : "i" _FOFF(NThread,iSpare3));   // r2 = iUserContextType
+	asm("mov r1, r0 ");    // r1 = this
+	asm("cmp r3, #%a0" : : "i" ((TInt)NThread::ECSExitInProgress));
+	asm("moveq r0, r2"); 
+	__JUMP(eq,lr);
+
+	// Exception or no user context?
+
+	asm("ldr r3, __TheScheduler");
+	asm("cmp r2, #%a0 " : : "i" ((TInt)NThread::EContextException));
+	asm("ldr r3, [r3, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread));
+	asm("movls r0, r2 ");  // Return EContextNone or EContextException
+	__JUMP(ls,lr);
+	asm("cmp r2, #%a0 " : : "i" ((TInt)NThread::EContextUserIntrCallback));
+	asm("blo 1f");
+	asm("cmp r2, #%a0 " : : "i" ((TInt)NThread::EContextWFARCallback));
+	asm("movls r0, r2 ");  // Return EContextUserIntrCallback or EContextWFARCallback
+	__JUMP(ls,lr);
+
+	// Getting current thread context? must be in exec call as exception
+	// and dying thread cases were tested above.
+
+	asm("1: ");
+	asm("cmp r3, r1");
+	asm("moveq r0, #%a0" : : "i" ((TInt)NThread::EContextExec));
+	__JUMP(eq,lr);
+
+	asm("ldr r0, [r1, #%a0]" : : "i" _FOFF(NThread,iStackBase));
+	asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(NThread,iStackSize));
+	asm("ldr r3, [r1, #%a0]" : : "i" _FOFF(NThread,iSavedSP));
+	asm("add r2, r2, r0");
+	asm("ldr r0, [r3, #%a0]" : : "i" (EXTRA_STACK_SPACE+11*4)); // get saved return address from reschedule
+	asm("ldr r12, __irq_resched_return ");
+	asm("sub r2, r2, r3");
+	asm("cmp r0, r12 ");
+	asm("beq preempted ");
+
+	// Transition to supervisor mode must have been due to a SWI
+
+	asm("not_preempted:");
+	asm("cmp r2, #%a0 " : : "i" ((TInt)(EXTRA_STACK_SPACE+15*4)));
+	asm("moveq r0, #%a0 " : : "i" ((TInt)NThread::EContextWFAR)); // thread must have blocked doing Exec::WaitForAnyRequest
+	asm("movne r0, #%a0 " : : "i" ((TInt)NThread::EContextExec)); // Thread must have been in a SLOW or UNPROTECTED Exec call
+	__JUMP(,lr);
+	
+	// thread was preempted due to an interrupt
+	// interrupt and reschedule will have pushed ? words + USER_MEMORY_GUARD_SAVE_WORDS + EXTRA_STACK_SPACE onto the stack
+
+	asm("preempted:");
+	asm("ldr r12, [r3, #%a0]" : : "i" (EXTRA_STACK_SPACE+USER_MEMORY_GUARD_SAVE_WORDS*4+12*4));  // first word on stack before reschedule
+	asm("mov r0, #%a0 " : : "i" ((TInt)NThread::EContextUserInterrupt));
+	asm("and r12, r12, #0x1f ");
+	asm("cmp r12, #0x10 ");   // interrupted mode = user?
+	__JUMP(eq,lr);
+
+	asm("cmp r2, #%a0 " : : "i" ((TInt)(EXTRA_STACK_SPACE+USER_MEMORY_GUARD_SAVE_WORDS*4+30*4)));
+	asm("bcs not_preempted "); 	// thread was interrupted in supervisor mode, return address and r4-r11 were saved
+
+	// interrupt occurred in exec call entry before r4-r11 saved
+	asm("cmp r2, #%a0 " : : "i" ((TInt)(EXTRA_STACK_SPACE+USER_MEMORY_GUARD_SAVE_WORDS*4+20*4)));
+	asm("moveq r0, #%a0 " : : "i" ((TInt)NThread::EContextSvsrInterrupt1)); // interrupt before return address was saved or after registers restored
+	asm("movne r0, #%a0 " : : "i" ((TInt)NThread::EContextSvsrInterrupt2)); // interrupt after return address saved
+	__JUMP(,lr);
+
+	asm("__irq_resched_return: ");
+	asm(".word irq_resched_return ");
+	}
+
+#endif  // __USER_CONTEXT_TYPE_MACHINE_CODED__
+
+__NAKED__ void Arm::GetUserSpAndLr(TAny*) 
+	{
+	asm("stmia r0, {r13, r14}^ ");
+	asm("mov r0, r0"); // NOP needed between stm^ and banked register access
+	__JUMP(,lr);
+	}
+
+__NAKED__ void Arm::SetUserSpAndLr(TAny*) 
+	{
+	asm("ldmia r0, {r13, r14}^ ");
+	asm("mov r0, r0"); // NOP needed between ldm^ and banked register access
+	__JUMP(,lr);
+	}
+
+#ifdef __CPU_ARM_USE_DOMAINS
+__NAKED__ TUint32 Arm::Dacr()
+	{
+	asm("mrc p15, 0, r0, c3, c0, 0 ");
+	__JUMP(,lr);
+	}
+
+__NAKED__ void Arm::SetDacr(TUint32)
+	{
+	asm("mcr p15, 0, r0, c3, c0, 0 ");
+	CPWAIT(,r0);
+	__JUMP(,lr);
+	}
+
+__NAKED__ TUint32 Arm::ModifyDacr(TUint32, TUint32)
+	{
+	asm("mrc p15, 0, r2, c3, c0, 0 ");
+	asm("bic r2, r2, r0 ");
+	asm("orr r2, r2, r1 ");
+	asm("mcr p15, 0, r2, c3, c0, 0 ");
+	CPWAIT(,r0);
+	asm("mov r0, r2 ");
+	__JUMP(,lr);
+	}
+#endif
+
+#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG
+__NAKED__ void Arm::SetCar(TUint32)
+	{
+	SET_CAR(,r0);
+	CPWAIT(,r0);
+	__JUMP(,lr);
+	}
+#endif
+
+
+
+/** Get the CPU's coprocessor access register value
+
+@return The value of the CAR, 0 if CPU doesn't have CAR
+
+@publishedPartner
+@released
+ */
+EXPORT_C __NAKED__ TUint32 Arm::Car()
+	{
+#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG
+	GET_CAR(,r0);
+#else
+	asm("mov r0, #0 ");
+#endif
+	__JUMP(,lr);
+	}
+
+
+
+/** Modify the CPU's coprocessor access register value
+	Does nothing if CPU does not have CAR.
+
+@param	aClearMask	Mask of bits to clear	(1 = clear this bit)
+@param	aSetMask	Mask of bits to set		(1 = set this bit)
+@return The original value of the CAR, 0 if CPU doesn't have CAR
+
+@publishedPartner
+@released
+ */
+EXPORT_C __NAKED__ TUint32 Arm::ModifyCar(TUint32 /*aClearMask*/, TUint32 /*aSetMask*/)
+	{
+#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG
+	GET_CAR(,r2);
+	asm("bic r0, r2, r0 ");
+	asm("orr r0, r0, r1 ");
+	SET_CAR(,r0);
+	CPWAIT(,r0);
+	asm("mov r0, r2 ");
+#else
+	asm("mov r0, #0 ");
+#endif
+	__JUMP(,lr);
+	}
+
+#ifdef __CPU_HAS_VFP
+__NAKED__ void Arm::SetFpExc(TUint32)
+	{
+#if defined(__CPU_ARM1136__) && !defined(__CPU_ARM1136_ERRATUM_351912_FIXED)
+// If we are about to enable VFP, disable dynamic branch prediction
+// If we are about to disable VFP, enable dynamic branch prediction if return stack prediction is enabled
+	asm("mrs r3, cpsr ");
+	CPSIDAIF;
+	asm("mrc p15, 0, r1, c1, c0, 1 ");
+	asm("tst r0, #%a0" : : "i" ((TInt)VFP_FPEXC_EN) );
+	asm("bic r1, r1, #2 ");				// clear DB bit (disable dynamic prediction)
+	asm("and r2, r1, #1 ");				// r2 bit 0 = RS bit (1 if return stack enabled)
+	asm("orreq r1, r1, r2, lsl #1 ");	// if VFP is being disabled set DB = RS
+	asm("mcr p15, 0, r1, c1, c0, 1 ");
+	asm("mcr p15, 0, r2, c7, c5, 6 ");	// flush BTAC
+	VFP_FMXR(,VFP_XREG_FPEXC,0);
+	asm("msr cpsr, r3 ");
+	__JUMP(,lr);
+#else
+	VFP_FMXR(,VFP_XREG_FPEXC,0);
+	__JUMP(,lr);
+#endif
+	}
+#endif
+
+
+
+/** Get the value of the VFP FPEXC register
+
+@return The value of FPEXC, 0 if there is no VFP
+
+@publishedPartner
+@released
+ */
+EXPORT_C __NAKED__ TUint32 Arm::FpExc()
+	{
+#ifdef __CPU_HAS_VFP
+	VFP_FMRX(,0,VFP_XREG_FPEXC);
+#else
+	asm("mov r0, #0 ");
+#endif
+	__JUMP(,lr);
+	}
+
+
+
+/** Modify the VFP FPEXC register
+	Does nothing if there is no VFP
+
+@param	aClearMask	Mask of bits to clear	(1 = clear this bit)
+@param	aSetMask	Mask of bits to set		(1 = set this bit)
+@return The original value of FPEXC, 0 if no VFP present
+
+@publishedPartner
+@released
+ */
+EXPORT_C __NAKED__ TUint32 Arm::ModifyFpExc(TUint32 /*aClearMask*/, TUint32 /*aSetMask*/)
+	{
+#ifdef __CPU_HAS_VFP
+	VFP_FMRX(,12,VFP_XREG_FPEXC);
+	asm("bic r0, r12, r0 ");
+	asm("orr r0, r0, r1 ");
+
+#if defined(__CPU_ARM1136__) && !defined(__CPU_ARM1136_ERRATUM_351912_FIXED)
+// If we are about to enable VFP, disable dynamic branch prediction
+// If we are about to disable VFP, enable dynamic branch prediction if return stack prediction is enabled
+	asm("mrs r3, cpsr ");
+	CPSIDAIF;
+	asm("mrc p15, 0, r1, c1, c0, 1 ");
+	asm("tst r0, #%a0" : : "i" ((TInt)VFP_FPEXC_EN) );
+	asm("bic r1, r1, #2 ");				// clear DB bit (disable dynamic prediction)
+	asm("and r2, r1, #1 ");				// r2 bit 0 = RS bit (1 if return stack enabled)
+	asm("orreq r1, r1, r2, lsl #1 ");	// if VFP is being disabled set DB = RS
+	asm("mcr p15, 0, r1, c1, c0, 1 ");
+	asm("mcr p15, 0, r2, c7, c5, 6 ");	// flush BTAC
+	VFP_FMXR(,VFP_XREG_FPEXC,0);
+	asm("msr cpsr, r3 ");
+#else
+	VFP_FMXR(,VFP_XREG_FPEXC,0);
+#endif	// erratum 351912
+
+	asm("mov r0, r12 ");
+#else	// no vfp
+	asm("mov r0, #0 ");
+#endif
+	__JUMP(,lr);
+	}
+
+/** Get the value of the VFP FPSCR register
+
+@return The value of FPSCR, 0 if there is no VFP
+
+@publishedPartner
+@released
+ */
+EXPORT_C __NAKED__ TUint32 Arm::FpScr()
+	{
+#ifdef __CPU_HAS_VFP
+	VFP_FMRX(,0,VFP_XREG_FPSCR);
+#else
+	asm("mov r0, #0 ");
+#endif
+	__JUMP(,lr);
+	}
+
+
+
+/** Modify the VFP FPSCR register
+	Does nothing if there is no VFP
+
+@param	aClearMask	Mask of bits to clear	(1 = clear this bit)
+@param	aSetMask	Mask of bits to set		(1 = set this bit)
+@return The original value of FPSCR, 0 if no VFP present
+
+@publishedPartner
+@released
+ */
+EXPORT_C __NAKED__ TUint32 Arm::ModifyFpScr(TUint32 /*aClearMask*/, TUint32 /*aSetMask*/)
+	{
+#ifdef __CPU_HAS_VFP
+	VFP_FMRX(,2,VFP_XREG_FPSCR);
+	asm("bic r0, r2, r0 ");
+	asm("orr r0, r0, r1 ");
+	VFP_FMXR(,VFP_XREG_FPSCR,0);
+	asm("mov r0, r2 ");
+#else
+	asm("mov r0, #0 ");
+#endif
+	__JUMP(,lr);
+	}
+
+
+/** Detect whether NEON is present
+
+@return ETrue if present, EFalse if not
+
+@internalTechnology
+@released
+ */
+#if defined(__CPU_HAS_VFP) && defined(__VFP_V3)
+__NAKED__ TBool Arm::NeonPresent()
+	{
+	asm("mov	r0, #0 ");										// Not present
+	VFP_FMRX(,	1,VFP_XREG_FPEXC);								// Save VFP state
+	asm("orr	r2, r1, #%a0" : : "i" ((TInt)VFP_FPEXC_EN));
+	VFP_FMXR(,	VFP_XREG_FPEXC,1);								// Enable VFP
+
+	VFP_FMRX(,	2,VFP_XREG_MVFR0);								// Read MVFR0
+	asm("tst	r2, #%a0" : : "i" ((TInt)VFP_MVFR0_ASIMD32));	// Check to see if all 32 Advanced SIMD registers are present
+	asm("beq	0f ");											// Skip ahead if not
+	GET_CAR(,	r2);
+	asm("tst	r2, #%a0" : : "i" ((TInt)VFP_CPACR_ASEDIS));	// Check to see if ASIMD is disabled
+	asm("bne	0f ");											// Skip ahead if so
+	asm("tst	r2, #%a0" : : "i" ((TInt)VFP_CPACR_D32DIS));	// Check to see if the upper 16 registers are disabled
+	asm("moveq	r0, #1" );										// If not then eport NEON present
+
+	asm("0: ");
+	VFP_FMXR(,VFP_XREG_FPEXC,1);								// Restore VFP state
+	__JUMP(,	lr);
+	}
+#endif
+
+
+#ifdef __CPU_HAS_MMU
+__NAKED__ TBool Arm::MmuActive()
+	{
+	asm("mrc p15, 0, r0, c1, c0, 0 ");
+	asm("and r0, r0, #1 ");
+	__JUMP(,lr);
+	}
+
+// Returns the content of Translate Table Base Register 0.
+// To get physical address of the level 1 table, on some platforms this must be orred with 0xffff8000 (to get rid of table walk cache attributes)
+__NAKED__ TUint32 Arm::MmuTTBR0()
+	{
+	asm("mrc p15, 0, r0, c2, c0, 0 ");
+	__JUMP(,lr);
+	}
+#endif
+
+
+
+/** Get the current value of the high performance counter.
+
+    If a high performance counter is not available, this uses the millisecond
+    tick count instead.
+*/
+#ifdef HAS_HIGH_RES_TIMER
+EXPORT_C __NAKED__ TUint32 NKern::FastCounter()
+	{
+	GET_HIGH_RES_TICK_COUNT(R0);
+	__JUMP(,lr);
+	}
+#else
+EXPORT_C TUint32 NKern::FastCounter()
+	{
+	return NTickCount();
+	}
+#endif
+
+
+
+/** Get the frequency of counter queried by NKern::FastCounter().
+*/
+EXPORT_C TInt NKern::FastCounterFrequency()
+	{
+#ifdef HAS_HIGH_RES_TIMER
+	return KHighResTimerFrequency;
+#else
+	return 1000000 / NKern::TickPeriod();
+#endif
+	}
+