diff -r 000000000000 -r a41df078684a kernel/eka/nkern/arm/ncutilf.cia --- /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 +#include +#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 + } +