sl@0: // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\nkern\arm\vectors.cia sl@0: // sl@0: // sl@0: sl@0: #define __INCLUDE_NTHREADBASE_DEFINES__ sl@0: #include sl@0: #include sl@0: sl@0: void FastMutexNestAttempt(); sl@0: void FastMutexSignalError(); sl@0: extern "C" void ExcFault(TAny*); sl@0: sl@0: #ifdef __CPU_HAS_MMU sl@0: #define __USE_CP15_FAULT_INFO__ sl@0: #endif sl@0: sl@0: #ifdef _DEBUG sl@0: #define __CHECK_LOCK_STATE__ sl@0: #endif sl@0: sl@0: //#define __FAULT_ON_FIQ__ sl@0: sl@0: #ifdef __CHECK_LOCK_STATE__ sl@0: // Check that the kernel is unlocked, no fast mutexes are held and that the thread is not in a sl@0: // critical section. Called when returning to user mode sl@0: __NAKED__ void CheckLockState() sl@0: { sl@0: asm("stmfd sp!, {r14}"); sl@0: asm("ldr r12, __TheScheduler "); sl@0: asm("ldr r14, [r12, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked)); sl@0: asm("cmp r14, #0 "); sl@0: asm("movne r12, #0xdd000000 "); sl@0: asm("strne r12, [r12, #1] "); sl@0: asm("ldr r12, [r12, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread)); sl@0: asm("ldr r14, [r12, #%a0]" : : "i" _FOFF(NThread,iHeldFastMutex)); sl@0: asm("cmp r14, #0 "); sl@0: __CPOPRET(eq, ""); sl@0: asm("badLockState: "); sl@0: asm("mov r12, #0xd7 "); sl@0: asm("msr cpsr, r12 "); sl@0: asm("mov r12, #0xdd000000 "); sl@0: asm("str r12, [r12, #3] "); sl@0: } sl@0: #endif sl@0: sl@0: __ASSERT_COMPILE(EUserModeCallbackRun == 0); sl@0: sl@0: __NAKED__ void CallUserModeCallbacks() sl@0: { sl@0: // called with interrupts disabled sl@0: // preserves r0 and r1 in additional to usual registers sl@0: // leaves current thread in r2 sl@0: // the vast majority of times this is called with zero or one callback pending sl@0: sl@0: asm(".global callUserModeCallbacks "); sl@0: asm("callUserModeCallbacks: "); sl@0: sl@0: asm("ldr ip, __TheScheduler "); sl@0: asm("ldr r2, [ip, #%a0]" : : "i" _FOFF(TScheduler, iCurrentThread)); sl@0: sl@0: asm("callUserModeCallbacks2: "); sl@0: sl@0: USER_MEMORY_GUARD_ASSERT_ON(ip); sl@0: sl@0: #ifdef __CHECK_LOCK_STATE__ sl@0: asm("ldr ip, [r2, #%a0]" : : "i" _FOFF(NThread,iCsCount)); sl@0: asm("cmp ip, #0 "); sl@0: asm("bne badLockState "); sl@0: #endif sl@0: sl@0: asm("ldr ip, [r2, #%a0]" : : "i" _FOFF(NThread, iUserModeCallbacks)); sl@0: asm("teq ip, #0"); sl@0: asm("bne 1f"); sl@0: __JUMP(,lr); sl@0: sl@0: asm("1: "); sl@0: asm("stmfd sp!, {r0-r2, r4-r11, lr}"); sl@0: asm("movs r4, r3"); sl@0: // if r3 != 0 it is the user context type to set the thread to sl@0: asm("strneb r3, [r2, #%a0]" : : "i" _FOFF(NThread, iUserContextType)); sl@0: sl@0: // Remove first callback and enter critical section - we can just set iCsCount to 1 as we are sl@0: // guaranteed not be be in a critical section already sl@0: asm("ldmia ip, {r1, r3} "); // HARDCODED: TUserModeCallback layout sl@0: asm("mov r0, #1"); sl@0: asm("str r0, [r2, #%a0]" : : "i" _FOFF(NThread, iCsCount)); sl@0: asm("str r1, [r2, #%a0]" : : "i" _FOFF(NThread,iUserModeCallbacks)); sl@0: sl@0: // Re-enable interrupts and call callback sl@0: SET_MODE(r0, MODE_SVC, INTS_ALL_ON); sl@0: asm("mov r1, #%a0 " : : "i" ((TInt)KUserModeCallbackUnqueued)); sl@0: asm("str r1, [ip, #%a0]" : : "i" _FOFF(TUserModeCallback, iNext)); sl@0: asm("mov r0, ip"); sl@0: asm("mov r1, #0 "); // 0 == EUserModeCallbackRun sl@0: __JUMPL(3); sl@0: sl@0: SET_MODE(r0, MODE_SVC, INTS_ALL_OFF); sl@0: sl@0: asm("movs r3, r4"); sl@0: // Leave critical section, avoid calling NKern::ThreadLeaveCS unless we have to sl@0: asm("ldmfd sp!, {r0-r2, r4-r11, lr}"); sl@0: // reset user context type to undefined if r3 != 0 sl@0: asm("mov ip, #%a0" : : "i" (NThread::EContextUndefined)); sl@0: asm("strneb ip, [r2, #%a0]" : : "i" _FOFF(NThread, iUserContextType)); sl@0: asm("ldr ip, [r2, #%a0]" : : "i" _FOFF(NThread, iCsFunction)); sl@0: asm("teq ip, #0"); sl@0: asm("streq ip, [r2, #%a0]" : : "i" _FOFF(NThread, iCsCount)); sl@0: asm("beq callUserModeCallbacks2 "); sl@0: sl@0: asm("leaveCS:"); sl@0: asm("sub sp, sp, #48 "); sl@0: SET_MODE(r0, MODE_SVC, INTS_ALL_ON); sl@0: asm("bl " CSM_ZN5NKern13ThreadLeaveCSEv); sl@0: SET_MODE(r0, MODE_SVC, INTS_ALL_OFF); sl@0: asm("ldmfd sp!, {r0-r2, r4-r11, lr}"); sl@0: asm("b callUserModeCallbacks2 "); sl@0: } sl@0: sl@0: /*************************************************************************** sl@0: * SWI Handler sl@0: ***************************************************************************/ sl@0: sl@0: extern "C" __NAKED__ void __ArmVectorSwi() sl@0: { sl@0: // IRQs disabled, FIQs enabled here sl@0: asm("ldr r12, [lr, #-4] "); // get SWI opcode sl@0: asm("stmfd sp!, {r11, lr} "); // save return address, r11 for 8 byte align sl@0: USER_MEMORY_GUARD_ON_IF_MODE_USR(r11); sl@0: asm("ldr r11, __TheScheduler "); sl@0: asm("adr lr, fast_swi_exit "); sl@0: asm("movs r12, r12, lsl #9 "); // 512*SWI number into r12 sl@0: asm("bcc slow_swi "); // bit 23=0 for slow/unprot sl@0: asm("ldr r1, [r11, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread)); sl@0: asm("beq wait_for_any_request "); // special case for Exec::WaitForAnyRequest sl@0: #ifdef __CPU_ARM_HAS_CPS sl@0: asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(NThread,iFastExecTable)); sl@0: CPSIDIF; // all interrupts off sl@0: asm("ldr r3, [r2], r12, lsr #7 "); // r3=limit, r2->dispatch table entry sl@0: asm("ldr r2, [r2] "); // r2->kernel function sl@0: asm("cmp r3, r12, lsr #9 "); // r3-SWI number sl@0: __JUMP(hi, r2); // if SWI number valid, call kernel function sl@0: #else sl@0: SET_INTS(r2, MODE_SVC, INTS_ALL_OFF); sl@0: asm("ldr r2, [r1, #%a0]" : : "i" _FOFF(NThread,iFastExecTable)); sl@0: asm("ldr r3, [r2], r12, lsr #7 "); // r3=limit, r2->dispatch table entry sl@0: asm("cmp r3, r12, lsr #9 "); // r3-SWI number sl@0: asm("ldrhi pc, [r2] "); // if SWI number valid, call kernel function sl@0: #endif sl@0: asm("mvn r12, #0 "); // put invalid SWI number into r12 sl@0: asm("b slow_swi "); // go through slow SWI routine to call invalid SWI handler sl@0: sl@0: asm("fast_swi_exit: "); sl@0: #ifdef __CHECK_LOCK_STATE__ sl@0: asm("mrs r12, spsr "); sl@0: asm("tst r12, #0x0f "); sl@0: asm("bleq " CSM_Z14CheckLockStatev); sl@0: #endif sl@0: USER_MEMORY_GUARD_OFF_IF_MODE_USR(r11); sl@0: ERRATUM_353494_MODE_CHANGE(,r11); sl@0: asm("ldmfd sp!, {r11, pc}^ "); // return and restore cpsr sl@0: sl@0: sl@0: asm("slow_swi: "); // IRQs off, FIQs on here sl@0: asm("stmfd sp!, {r3-r10} "); // save nonvolatile registers, r3 for 8 byte align sl@0: asm("ldr r9, [r11, #%a0]" : : "i" _FOFF(TScheduler,iCurrentThread)); // r9->current thread sl@0: SET_INTS(lr, MODE_SVC, INTS_ALL_ON); // all interrupts on sl@0: asm("mov r10, r11 "); // r10->scheduler sl@0: asm("ldr r4, [r9, #%a0]" : : "i" _FOFF(NThread,iSlowExecTable)); sl@0: asm("mrs r11, spsr "); // spsr_svc into r11 sl@0: asm("adr lr, slow_swi_exit "); sl@0: asm("add r6, r4, r12, lsr #6 "); // r6->dispatch table entry sl@0: asm("ldr r5, [r4, #-12] "); // r5=limit sl@0: SET_INTS_1(r7, MODE_SVC, INTS_ALL_OFF); sl@0: asm("cmp r5, r12, lsr #9 "); // r5-SWI number sl@0: asm("ldmhiia r6, {r5,r6} "); // if SWI number OK, flags into r5, function addr into r6 sl@0: asm("ldrls pc, [r4, #-8] "); // if SWI number invalid, call invalid handler sl@0: asm("tst r5, #%a0" : : "i" ((TInt)KExecFlagExtraArgMask)); // extra arguments needed? sl@0: asm("addne r2, sp, #4 "); // if so, point r2 at saved registers on stack sl@0: asm("tst r5, #%a0" : : "i" ((TInt)KExecFlagClaim)); // claim system lock? sl@0: asm("beq slow_swi_no_wait "); // skip if not sl@0: sl@0: SET_INTS_2(r7, MODE_SVC, INTS_ALL_OFF); // interrupts off sl@0: #ifdef _DEBUG sl@0: asm("ldr r12, [r9, #%a0]" : : "i" _FOFF(NThread,iHeldFastMutex)); sl@0: asm("cmp r12, #0 "); sl@0: asm("bne " CSM_Z20FastMutexNestAttemptv); // debug check that current thread doesn't already hold a fast mutex sl@0: #endif sl@0: asm("ldr r12, [r10, #%a0]!" : : "i" _FOFF(TScheduler,iLock.iHoldingThread)); // r12=iLock.iHoldingThread sl@0: SET_INTS_1(r7, MODE_SVC, INTS_ALL_ON); sl@0: asm("cmp r12, #0 "); // is system lock already held? sl@0: asm("bne ss_fast_mutex_held "); // branch if it is sl@0: asm("ss_fast_mutex_obtained: "); sl@0: asm("str r10, [r9, #%a0]" : : "i" _FOFF(NThread,iHeldFastMutex)); // current thread->iHeldFastMutex=&iLock sl@0: asm("str r9, [r10], #-%a0" : : "i" _FOFF(TScheduler,iLock)); // iLock.iHoldingThread=current thread, r10->scheduler sl@0: #ifdef BTRACE_FAST_MUTEX sl@0: asm("ldrb r12, [r10,#%a0]" : : "i" (_FOFF(TScheduler,iFastMutexFilter))); sl@0: asm("cmp r12, #0"); sl@0: asm("bne syslock_trace_wait"); sl@0: asm("syslock_trace_wait_done:"); sl@0: #endif sl@0: SET_INTS_2(r7, MODE_SVC, INTS_ALL_ON); // all interrupts on sl@0: sl@0: asm("slow_swi_no_wait: "); sl@0: asm("tst r5, #%a0" : : "i" ((TInt)KExecFlagPreprocess)); // preprocess (handle lookup)? can use r4, r7, r8, r12, r0 sl@0: asm("mov lr, pc "); sl@0: asm("ldrne pc, [r4, #-4] "); // call preprocess handler if required sl@0: asm("mov lr, pc "); sl@0: __JUMP(,r6); // call exec function, preserve r5,r11 if release syslock not required sl@0: // preserve r5,r9,r10,r11 if release required sl@0: asm("tst r5, #%a0" : : "i" ((TInt)KExecFlagRelease)); // release system lock? sl@0: asm("beq slow_swi_exit "); // skip if not sl@0: sl@0: SET_INTS(r12, MODE_SVC, INTS_ALL_OFF); // disable interrupts sl@0: #ifdef _DEBUG sl@0: asm("add r8, r10, #%a0" : : "i" _FOFF(TScheduler,iLock)); sl@0: asm("ldr r12, [r9, #%a0]" : : "i" _FOFF(NThread,iHeldFastMutex)); sl@0: asm("cmp r12, r8 "); sl@0: asm("bne " CSM_Z20FastMutexSignalErrorv); // debug check that current thread holds system lock sl@0: #endif sl@0: #ifdef BTRACE_FAST_MUTEX sl@0: asm("ldrb r12, [r10,#%a0]" : : "i" (_FOFF(TScheduler,iFastMutexFilter))); sl@0: asm("cmp r12, #0"); sl@0: asm("bne syslock_trace_signal"); sl@0: asm("syslock_trace_signal_done:"); sl@0: #endif sl@0: asm("mov r12, #0 "); sl@0: asm("str r12, [r10, #%a0]" : : "i" _FOFF(TScheduler,iLock.iHoldingThread)); // iLock.iHoldingThread=NULL sl@0: asm("str r12, [r9, #%a0]" : : "i" _FOFF(NThread,iHeldFastMutex)); // current thread->iHeldFastMutex=NULL sl@0: asm("ldr r3, [r10, #%a0]" : : "i" _FOFF(TScheduler,iLock.iWaiting)); // r3=iLock.iWaiting sl@0: asm("str r12, [r10, #%a0]" : : "i" _FOFF(TScheduler,iLock.iWaiting)); // iLock.iWaiting=0 sl@0: SET_INTS_1(r8, MODE_SVC, INTS_ALL_ON); sl@0: asm("cmp r3, #0 "); // check waiting flag sl@0: asm("bne ss_signal_check "); // branch if set sl@0: asm("ss_signal_done: "); sl@0: SET_INTS_2(r8, MODE_SVC, INTS_ALL_ON); // otherwise reenable interrupts sl@0: sl@0: asm("slow_swi_exit: "); sl@0: #ifdef __CHECK_LOCK_STATE__ sl@0: asm("tst r11, #0x0f "); sl@0: asm("bleq " CSM_Z14CheckLockStatev); sl@0: #endif sl@0: SET_INTS(r12, MODE_SVC, INTS_ALL_OFF); // disable interrupts sl@0: asm("msr spsr, r11 "); // restore spsr_svc sl@0: asm("tst r11, #0x0f "); sl@0: asm("mov r3, #0 "); sl@0: #if defined(__CPU_CORTEX_A9__) && !defined(__CPU_ARM_A9_ERRATUM_571622_FIXED) sl@0: asm("nop "); // ARM Cortex-A9 MPCore erratum 571622 workaround sl@0: // Insert nops so branch doesn't occur in 2nd or 3rd position after a msr spsr sl@0: #endif sl@0: asm("bleq callUserModeCallbacks "); // call user-mode callbacks sl@0: USER_MEMORY_GUARD_OFF_IF_MODE_USR(r11); sl@0: ERRATUM_353494_MODE_CHANGE(,r11); sl@0: asm("ldmfd sp!, {r3-r11,pc}^ "); // return from EXEC function sl@0: sl@0: sl@0: // Come here if we need to wait for the system lock sl@0: // r9->current thread, r10=&iLock, r12=iLock.iHoldingThread sl@0: asm("ss_fast_mutex_held: "); sl@0: asm("mov r8, #1 "); sl@0: asm("str r8, [r10, #%a0]" : : "i" (_FOFF(TScheduler,iKernCSLocked)-_FOFF(TScheduler,iLock))); // lock the kernel sl@0: SET_INTS_2(r7, MODE_SVC, INTS_ALL_ON); // enable interrupts sl@0: asm("str r8, [r10, #4] "); // iWaiting=1 sl@0: asm("str r10, [r9, #%a0]" : : "i" _FOFF(NThread,iWaitFastMutex)); // current thread->iWaitFastMutex=&iLock sl@0: asm("stmfd sp!, {r0-r3} "); // save exec call arguments sl@0: asm("mov r0, r12 "); // parameter for YieldTo sl@0: ASM_DEBUG1(NKFMWaitYield,r0); sl@0: asm("bl " CSM_ZN10TScheduler7YieldToEP11NThreadBase); // yield to the mutex holding thread sl@0: // will not return until the mutex is free sl@0: // on return r0=Scheduler,r1=0,r2!=0,r3=current thread, kernel unlocked, interrupts disabled sl@0: asm("str r1, [r9, #%a0]" : : "i" _FOFF(NThread,iWaitFastMutex)); // iWaitFastMutex=NULL sl@0: asm("ldmfd sp!, {r0-r3} "); // retrieve exec call arguments sl@0: asm("b ss_fast_mutex_obtained "); // branch back to main code path sl@0: sl@0: // Come here if we need to reschedule after releasing the system lock sl@0: // kernel unlocked, interrupts enabled, r0 contains return value from Exec call sl@0: // r9->current thread, r10=&TheScheduler, r3=1, r8=0x13 sl@0: asm("ss_signal_check: "); sl@0: asm("str r3, [r10, #%a0]" : : "i" _FOFF(TScheduler,iKernCSLocked)); // lock the kernel (assumes iWaiting always 0 or 1) sl@0: SET_INTS_2(r8, MODE_SVC, INTS_ALL_ON); // reenable interrupts sl@0: asm("strb r3, [r10, #%a0]" : : "i" _FOFF(TScheduler,iRescheduleNeededFlag)); sl@0: asm("ldr r3, [r9, #%a0]" : : "i" _FOFF(NThread,iCsFunction)); // r3=current thread->iCsFunction sl@0: asm("ldr r2, [r9, #%a0]" : : "i" _FOFF(NThread,iCsCount)); // r2=current thread->iCsCount sl@0: asm("mov r4, r0 "); // save return value sl@0: asm("cmp r3, #0 "); // outstanding CS function? sl@0: asm("beq 2f "); // branch if not sl@0: asm("cmp r2, #0 "); // iCsCount!=0 ? sl@0: asm("moveq r0, r9 "); // if iCsCount=0, DoCsFunction() sl@0: asm("bleq " CSM_ZN11NThreadBase12DoCsFunctionEv); sl@0: asm("2: "); sl@0: asm("bl " CSM_ZN10TScheduler10RescheduleEv); // reschedule to allow waiting thread in sl@0: asm("mov r0, r4 "); // recover return value sl@0: asm("b ss_signal_done "); // branch back to main code path sl@0: sl@0: #ifdef BTRACE_FAST_MUTEX sl@0: asm("syslock_trace_wait:"); sl@0: asm("ldr r12, [sp,#9*4]"); // r12 = return address from SWI sl@0: asm("mov r8, r3"); // save r3 sl@0: asm("stmdb sp!,{r0-r2,r12}"); // 4th item on stack is PC value for trace sl@0: asm("ldr r0, fmwait_trace_header"); sl@0: asm("mov r2, r9"); // current thread sl@0: asm("add r3, r10, #%a0" : : "i" _FOFF(TScheduler,iLock)); sl@0: asm("mov lr, pc"); sl@0: asm("ldr pc, [r10, #%a0]" : : "i" _FOFF(TScheduler,iBTraceHandler)); sl@0: asm("ldmia sp!,{r0-r2,r12}"); sl@0: asm("mov r3, r8"); // restore r3 sl@0: asm("b syslock_trace_wait_done"); sl@0: sl@0: asm("syslock_trace_signal:"); sl@0: asm("ldr r12, [sp,#9*4]"); // r12 = return address from SWI sl@0: asm("stmdb sp!,{r0-r2,r12}"); // 4th item on stack is PC value for trace sl@0: asm("ldr r0, fmsignal_trace_header"); sl@0: asm("mov r2, r9"); // current thread sl@0: asm("add r3, r10, #%a0" : : "i" _FOFF(TScheduler,iLock)); sl@0: asm("mov lr, pc"); sl@0: asm("ldr pc, [r10, #%a0]" : : "i" _FOFF(TScheduler,iBTraceHandler)); sl@0: asm("ldmia sp!,{r0-r2,r12}"); sl@0: asm("b syslock_trace_signal_done"); sl@0: sl@0: asm("fmsignal_trace_header:"); sl@0: asm(".word %a0" : : "i" ((TInt)(16<