proposed fix for 340 [GCCE] improper use of __asm
and 341 Enabling support for gcce in cia files
// 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
}