diff -r 000000000000 -r a41df078684a kernel/eka/nkern/arm/ncthrd.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/nkern/arm/ncthrd.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1137 @@ +// 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\nkern\arm\ncthrd.cpp +// +// + +// NThreadBase member data +#define __INCLUDE_NTHREADBASE_DEFINES__ + +#define __INCLUDE_REG_OFFSETS__ +#include + +const TInt KNThreadMinStackSize = 0x100; // needs to be enough for interrupt + reschedule stack + +// Called by a thread when it first runs +extern void __StartThread(); + +// Called by a thread which has been forced to exit +// Interrupts off here, kernel unlocked +extern void __DoForcedExit(); + +void NThreadBase::SetEntry(NThreadFunction aFunc) + { + TUint32* sp=(TUint32*)iSavedSP; + sp[SP_R5]=(TUint32)aFunc; + } + +TInt NThread::Create(SNThreadCreateInfo& aInfo, TBool aInitial) + { + // Assert ParameterBlockSize is not negative and is a multiple of 8 bytes + __NK_ASSERT_ALWAYS((aInfo.iParameterBlockSize&0x80000007)==0); + + __NK_ASSERT_ALWAYS(aInfo.iStackBase && aInfo.iStackSize>=aInfo.iParameterBlockSize+KNThreadMinStackSize); + TInt r=NThreadBase::Create(aInfo,aInitial); + if (r!=KErrNone) + return r; + if (!aInitial) + { + TUint32* sp=(TUint32*)(iStackBase+iStackSize-aInfo.iParameterBlockSize); + TUint32 r6=(TUint32)aInfo.iParameterBlock; + if (aInfo.iParameterBlockSize) + { + wordmove(sp,aInfo.iParameterBlock,aInfo.iParameterBlockSize); + r6=(TUint32)sp; + } + *--sp=(TUint32)__StartThread; // PC + *--sp=0; // R11 + *--sp=0; // R10 + *--sp=0; // R9 + *--sp=0; // R8 + *--sp=0; // R7 + *--sp=r6; // R6 + *--sp=(TUint32)aInfo.iFunction; // R5 + *--sp=(TUint32)this; // R4 + *--sp=0x13; // SPSR_SVC + *--sp=0; // R14_USR + *--sp=0; // R13_USR +#ifdef __CPU_ARM_USE_DOMAINS + *--sp=Arm::DefaultDomainAccess; // DACR +#endif +#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG + *--sp=Arm::DefaultCoprocessorAccess; // CAR +#endif +#ifdef __CPU_HAS_VFP + *--sp=VFP_FPEXC_THRD_INIT; // FPEXC +#endif +#ifdef __CPU_HAS_CP15_THREAD_ID_REG + *--sp=0; // TID +#endif +#ifdef __CPU_SUPPORT_THUMB2EE + *--sp=0; // ThumbEE Base +#endif + iSavedSP=(TLinAddr)sp; + } + else + { +#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG +#ifdef __CPU_HAS_VFP +#ifdef __CPU_XSCALE__ + Arm::ModifyCar(0, 0x0c00); // enable CP10, CP11 +#else + Arm::ModifyCar(0, 0x00f00000); // full access to CP10, CP11 +#endif +#endif + Arm::DefaultCoprocessorAccess = Arm::Car(); +#endif + NKern::EnableAllInterrupts(); + } +#ifdef BTRACE_THREAD_IDENTIFICATION + BTrace4(BTrace::EThreadIdentification,BTrace::ENanoThreadCreate,this); +#endif + return KErrNone; + } + +/** Called from generic layer when thread is killed asynchronously. + + For ARM, save reason for last user->kernel switch (if any) so that user + context can be accessed from EDebugEventRemoveThread hook. Must be done + before forcing the thread to exit as this alters the saved return address + which is used to figure out where the context is saved. + + @pre kernel locked + @post kernel locked + */ + +void NThreadBase::OnKill() + { + if (iUserContextType != NThread::EContextNone) + { + NThread::TUserContextType t = ((NThread*)this)->UserContextType(); + switch (t) + { + case NThread::EContextUserInterrupt: + t = NThread::EContextUserInterruptDied; + break; + case NThread::EContextSvsrInterrupt1: + t = NThread::EContextSvsrInterrupt1Died; + break; + case NThread::EContextSvsrInterrupt2: + t = NThread::EContextSvsrInterrupt2Died; + break; + case NThread::EContextWFAR: + t = NThread::EContextWFARDied; + break; + default: + // NOP + break; + } + iUserContextType = t; + } + } + +/** Called from generic layer when thread exits. + + For ARM, save that if the thread terminates synchronously the last + user->kernel switch was an exec call. Do nothing if non-user thread or + reason already saved in OnKill(). + + @pre kernel locked + @post kernel locked + @see OnKill + */ + +void NThreadBase::OnExit() + { + CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED,"NThreadBase::OnExit"); + if (iUserContextType == NThread::EContextUndefined) + iUserContextType = NThread::EContextExec; + } + +void NThreadBase::ForceExit() + { + TUint32* sp=(TUint32*)iSavedSP; + sp[SP_PC]=(TUint32)__DoForcedExit; + } + +void DumpExcInfo(TArmExcInfo& a) + { + DEBUGPRINT("Exc %1d Cpsr=%08x FAR=%08x FSR=%08x",a.iExcCode,a.iCpsr,a.iFaultAddress,a.iFaultStatus); + DEBUGPRINT(" R0=%08x R1=%08x R2=%08x R3=%08x",a.iR0,a.iR1,a.iR2,a.iR3); + DEBUGPRINT(" R4=%08x R5=%08x R6=%08x R7=%08x",a.iR4,a.iR5,a.iR6,a.iR7); + DEBUGPRINT(" R8=%08x R9=%08x R10=%08x R11=%08x",a.iR8,a.iR9,a.iR10,a.iR11); + DEBUGPRINT("R12=%08x R13=%08x R14=%08x R15=%08x",a.iR12,a.iR13,a.iR14,a.iR15); + DEBUGPRINT("R13Svc=%08x R14Svc=%08x SpsrSvc=%08x",a.iR13Svc,a.iR14Svc,a.iSpsrSvc); + DEBUGPRINT("Thread %T, KernCSLocked=%d",TheScheduler.iCurrentThread,TheScheduler.iKernCSLocked); + } + +void DumpFullRegSet(SFullArmRegSet& a) + { + SNormalRegs& r = a.iN; + DEBUGPRINT("MODE_USR:"); + DEBUGPRINT(" R0=%08x R1=%08x R2=%08x R3=%08x", r.iR0, r.iR1, r.iR2, r.iR3); + DEBUGPRINT(" R4=%08x R5=%08x R6=%08x R7=%08x", r.iR4, r.iR5, r.iR6, r.iR7); + DEBUGPRINT(" R8=%08x R9=%08x R10=%08x R11=%08x", r.iR8, r.iR9, r.iR10, r.iR11); + DEBUGPRINT("R12=%08x R13=%08x R14=%08x R15=%08x", r.iR12, r.iR13, r.iR14, r.iR15); + DEBUGPRINT("CPSR=%08x", r.iFlags); + DEBUGPRINT("MODE_FIQ:"); + DEBUGPRINT(" R8=%08x R9=%08x R10=%08x R11=%08x", r.iR8Fiq, r.iR9Fiq, r.iR10Fiq, r.iR11Fiq); + DEBUGPRINT("R12=%08x R13=%08x R14=%08x SPSR=%08x", r.iR12Fiq, r.iR13Fiq, r.iR14Fiq, r.iSpsrFiq); + DEBUGPRINT("MODE_IRQ:"); + DEBUGPRINT("R13=%08x R14=%08x SPSR=%08x", r.iR13Irq, r.iR14Irq, r.iSpsrIrq); + DEBUGPRINT("MODE_SVC:"); + DEBUGPRINT("R13=%08x R14=%08x SPSR=%08x", r.iR13Svc, r.iR14Svc, r.iSpsrSvc); + DEBUGPRINT("MODE_ABT:"); + DEBUGPRINT("R13=%08x R14=%08x SPSR=%08x", r.iR13Abt, r.iR14Abt, r.iSpsrAbt); + DEBUGPRINT("MODE_UND:"); + DEBUGPRINT("R13=%08x R14=%08x SPSR=%08x", r.iR13Und, r.iR14Und, r.iSpsrUnd); +// DEBUGPRINT("MODE_MON:"); +// DEBUGPRINT("R13=%08x R14=%08x SPSR=%08x", r.iR13Mon, r.iR14Mon, r.iSpsrMon); + + SAuxiliaryRegs& aux = a.iA; + DEBUGPRINT("TEEHBR=%08x CPACR=%08x", aux.iTEEHBR, aux.iCPACR); + + SBankedRegs& b = a.iB[0]; + DEBUGPRINT(" SCTLR=%08x ACTLR=%08x PRRR=%08x NMRR=%08x", b.iSCTLR, b.iACTLR, b.iPRRR, b.iNMRR); + DEBUGPRINT(" DACR=%08x TTBR0=%08x TTBR1=%08x TTBCR=%08x", b.iDACR, b.iTTBR0, b.iTTBR1, b.iTTBCR); + DEBUGPRINT(" VBAR=%08x FCSEID=%08x CTXIDR=%08x", b.iVBAR, b.iFCSEIDR, b.iCTXIDR); + DEBUGPRINT("Thread ID RWRW=%08x RWRO=%08x RWNO=%08x", b.iRWRWTID, b.iRWROTID, b.iRWNOTID); + DEBUGPRINT(" DFSR=%08x DFAR=%08x IFSR=%08x IFAR=%08x", b.iDFSR, b.iDFAR, b.iIFSR, b.iIFAR); + DEBUGPRINT(" ADFSR=%08x AIFSR=%08x", b.iADFSR, b.iAIFSR); +#ifdef __CPU_HAS_VFP + DEBUGPRINT("FPEXC %08x", a.iMore[0]); +#endif + DEBUGPRINT("ExcCode %08x", a.iExcCode); + } + +#define CONTEXT_ELEMENT_UNDEFINED(val) \ + { \ + TArmContextElement::EUndefined, \ + val \ + } + +#define CONTEXT_ELEMENT_EXCEPTION(reg) \ + { \ + TArmContextElement::EOffsetFromStackTop, \ + (- (-sizeof(TArmExcInfo)+_FOFF(TArmExcInfo,reg)) )>>2 \ + } + +#define CONTEXT_ELEMENT_FROM_SP(offset) \ + { \ + TArmContextElement::EOffsetFromSp, \ + offset \ + } + +#define CONTEXT_ELEMENT_FROM_STACK_TOP(offset) \ + { \ + TArmContextElement::EOffsetFromStackTop, \ + offset \ + } + +#define CONTEXT_ELEMENT_SP_PLUS(offset) \ + { \ + TArmContextElement::ESpPlusOffset, \ + offset \ + } + +const TArmContextElement ContextTableException[] = + { + CONTEXT_ELEMENT_EXCEPTION(iR0), + CONTEXT_ELEMENT_EXCEPTION(iR1), + CONTEXT_ELEMENT_EXCEPTION(iR2), + CONTEXT_ELEMENT_EXCEPTION(iR3), + CONTEXT_ELEMENT_EXCEPTION(iR4), + CONTEXT_ELEMENT_EXCEPTION(iR5), + CONTEXT_ELEMENT_EXCEPTION(iR6), + CONTEXT_ELEMENT_EXCEPTION(iR7), + CONTEXT_ELEMENT_EXCEPTION(iR8), + CONTEXT_ELEMENT_EXCEPTION(iR9), + CONTEXT_ELEMENT_EXCEPTION(iR10), + CONTEXT_ELEMENT_EXCEPTION(iR11), + CONTEXT_ELEMENT_EXCEPTION(iR12), + CONTEXT_ELEMENT_EXCEPTION(iR13), + CONTEXT_ELEMENT_EXCEPTION(iR14), + CONTEXT_ELEMENT_EXCEPTION(iR15), + CONTEXT_ELEMENT_EXCEPTION(iCpsr), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +const TArmContextElement ContextTableUndefined[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(EUserMode), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for non dying threads which have been preempted by an interrupt +// while in user mode. + +const TArmContextElement ContextTableUserInterrupt[] = + { + CONTEXT_ELEMENT_FROM_STACK_TOP(6), + CONTEXT_ELEMENT_FROM_STACK_TOP(5), + CONTEXT_ELEMENT_FROM_STACK_TOP(4), + CONTEXT_ELEMENT_FROM_STACK_TOP(3), + CONTEXT_ELEMENT_FROM_SP(SP_R4), + CONTEXT_ELEMENT_FROM_SP(SP_R5), + CONTEXT_ELEMENT_FROM_SP(SP_R6), + CONTEXT_ELEMENT_FROM_SP(SP_R7), + CONTEXT_ELEMENT_FROM_SP(SP_R8), + CONTEXT_ELEMENT_FROM_SP(SP_R9), + CONTEXT_ELEMENT_FROM_SP(SP_R10), + CONTEXT_ELEMENT_FROM_SP(SP_R11), + CONTEXT_ELEMENT_FROM_STACK_TOP(2), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_FROM_STACK_TOP(8), // interrupted CPSR + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for threads which have been asynchronously killed after being +// preempted by interrupt while in user mode. + +const TArmContextElement ContextTableUserInterruptDied[] = + { + CONTEXT_ELEMENT_FROM_STACK_TOP(6), + CONTEXT_ELEMENT_FROM_STACK_TOP(5), + CONTEXT_ELEMENT_FROM_STACK_TOP(4), + CONTEXT_ELEMENT_FROM_STACK_TOP(3), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_STACK_TOP(2), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_FROM_STACK_TOP(8), // interrupted CPSR + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for threads which have been preempted by an interrupt while in +// supervisor mode in the SWI handler either before the return address was +// saved or after the registers were restored. + +const TArmContextElement ContextTableSvsrInterrupt1[] = + { + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+2), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+3), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+4), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+5), + CONTEXT_ELEMENT_FROM_SP(SP_R4), + CONTEXT_ELEMENT_FROM_SP(SP_R5), + CONTEXT_ELEMENT_FROM_SP(SP_R6), + CONTEXT_ELEMENT_FROM_SP(SP_R7), + CONTEXT_ELEMENT_FROM_SP(SP_R8), + CONTEXT_ELEMENT_FROM_SP(SP_R9), + CONTEXT_ELEMENT_FROM_SP(SP_R10), + CONTEXT_ELEMENT_FROM_SP(SP_R11), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+6), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+6), // r15 = r12 + CONTEXT_ELEMENT_UNDEFINED(EUserMode), // can't get flags so just use 'user mode' + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for threads which have been asynchronously killed while in the situation +// described above (see ContextTableSvsrInterrupt1). + +const TArmContextElement ContextTableSvsrInterrupt1Died[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(EUserMode), // can't get flags so just use 'user mode' + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for threads which have been preempted by an interrupt while in +// supervisor mode in the SWI handler after the return address was saved. + +const TArmContextElement ContextTableSvsrInterrupt2[] = + { + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+2), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+3), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+4), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+5), + CONTEXT_ELEMENT_FROM_SP(SP_R4), + CONTEXT_ELEMENT_FROM_SP(SP_R5), + CONTEXT_ELEMENT_FROM_SP(SP_R6), + CONTEXT_ELEMENT_FROM_SP(SP_R7), + CONTEXT_ELEMENT_FROM_SP(SP_R8), + CONTEXT_ELEMENT_FROM_SP(SP_R9), + CONTEXT_ELEMENT_FROM_SP(SP_R10), + CONTEXT_ELEMENT_FROM_STACK_TOP(2), + CONTEXT_ELEMENT_FROM_SP(SP_NEXT+USER_MEMORY_GUARD_SAVE_WORDS+6), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_UNDEFINED(EUserMode), // can't get flags so just use 'user mode' + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for threads which have been asynchronously killed while in the situation +// described above (see ContextTableSvsrInterrupt2). + +const TArmContextElement ContextTableSvsrInterrupt2Died[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_UNDEFINED(EUserMode), // can't get flags so just use 'user mode' + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for non-dying threads blocked on their request semaphore. + +const TArmContextElement ContextTableWFAR[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R4), + CONTEXT_ELEMENT_FROM_SP(SP_R5), + CONTEXT_ELEMENT_FROM_SP(SP_R6), + CONTEXT_ELEMENT_FROM_SP(SP_R7), + CONTEXT_ELEMENT_FROM_SP(SP_R8), + CONTEXT_ELEMENT_FROM_SP(SP_R9), + CONTEXT_ELEMENT_FROM_SP(SP_R10), + CONTEXT_ELEMENT_FROM_STACK_TOP(2), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_FROM_SP(SP_SPSR), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for threads killed asynchronously while blocked on their request +// semaphore. + +const TArmContextElement ContextTableWFARDied[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_FROM_SP(SP_SPSR), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +const TArmContextElement ContextTableExec[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_STACK_TOP(10), + CONTEXT_ELEMENT_FROM_STACK_TOP(9), + CONTEXT_ELEMENT_FROM_STACK_TOP(8), + CONTEXT_ELEMENT_FROM_STACK_TOP(7), + CONTEXT_ELEMENT_FROM_STACK_TOP(6), + CONTEXT_ELEMENT_FROM_STACK_TOP(5), + CONTEXT_ELEMENT_FROM_STACK_TOP(4), + CONTEXT_ELEMENT_FROM_STACK_TOP(3), + CONTEXT_ELEMENT_FROM_STACK_TOP(2), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_UNDEFINED(EUserMode), // can't get flags so just use 'user mode' + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used to retrieve a thread's kernel side context. +// Used for kernel threads. +const TArmContextElement ContextTableKernel[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R4), // r4 before reschedule + CONTEXT_ELEMENT_FROM_SP(SP_R5), // r5 before reschedule + CONTEXT_ELEMENT_FROM_SP(SP_R6), // r6 before reschedule + CONTEXT_ELEMENT_FROM_SP(SP_R7), // r7 before reschedule + CONTEXT_ELEMENT_FROM_SP(SP_R8), // r8 before reschedule + CONTEXT_ELEMENT_FROM_SP(SP_R9), // r9 before reschedule + CONTEXT_ELEMENT_FROM_SP(SP_R10), // r10 before reschedule + CONTEXT_ELEMENT_FROM_SP(SP_R11), // r11 before reschedule + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_SP_PLUS(SP_NEXT), // supervisor stack pointer before reschedule + CONTEXT_ELEMENT_UNDEFINED(0), // supervisor lr is unknown + CONTEXT_ELEMENT_FROM_SP(SP_PC), // return address from reschedule + CONTEXT_ELEMENT_UNDEFINED(ESvcMode), // can't get flags so just use 'supervisor mode' + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for non dying threads which are in a user callback while returning +// from having been preempted by an interrupt while in user mode. + +const TArmContextElement ContextTableUserIntrCallback[] = + { + CONTEXT_ELEMENT_FROM_STACK_TOP(6), + CONTEXT_ELEMENT_FROM_STACK_TOP(5), + CONTEXT_ELEMENT_FROM_STACK_TOP(4), + CONTEXT_ELEMENT_FROM_STACK_TOP(3), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+9), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+8), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+7), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+6), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+5), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+4), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+3), + CONTEXT_ELEMENT_FROM_STACK_TOP(8+USER_MEMORY_GUARD_SAVE_WORDS+2), + CONTEXT_ELEMENT_FROM_STACK_TOP(2), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_FROM_STACK_TOP(8), // interrupted CPSR + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used for non-dying threads which are in a user callback while returning +// from being blocked on their request semaphore. + +const TArmContextElement ContextTableWFARCallback[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_STACK_TOP(11), + CONTEXT_ELEMENT_FROM_STACK_TOP(10), + CONTEXT_ELEMENT_FROM_STACK_TOP(9), + CONTEXT_ELEMENT_FROM_STACK_TOP(8), + CONTEXT_ELEMENT_FROM_STACK_TOP(7), + CONTEXT_ELEMENT_FROM_STACK_TOP(6), + CONTEXT_ELEMENT_FROM_STACK_TOP(5), + CONTEXT_ELEMENT_FROM_STACK_TOP(2), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_FROM_SP(SP_R13U), + CONTEXT_ELEMENT_FROM_SP(SP_R14U), + CONTEXT_ELEMENT_FROM_STACK_TOP(1), + CONTEXT_ELEMENT_FROM_SP(SP_SPSR), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +const TArmContextElement* const ThreadUserContextTables[] = + { + ContextTableUndefined, // EContextNone + ContextTableException, + ContextTableUndefined, + ContextTableUserInterrupt, + ContextTableUserInterruptDied, + ContextTableSvsrInterrupt1, + ContextTableSvsrInterrupt1Died, + ContextTableSvsrInterrupt2, + ContextTableSvsrInterrupt2Died, + ContextTableWFAR, + ContextTableWFARDied, + ContextTableExec, + ContextTableKernel, + ContextTableUserIntrCallback, + ContextTableWFARCallback, + 0 // Null terminated + }; + +/** Return table of pointers to user context tables. + + Each user context table is an array of TArmContextElement objects, one per + ARM CPU register, in the order defined in TArmRegisters. + + The master table contains pointers to the user context tables in the order + defined in TUserContextType. There are as many user context tables as + scenarii leading a user thread to switch to privileged mode. + + Stop-mode debug agents should use this function to store the address of the + master table at a location known to the host debugger. Run-mode debug + agents are advised to use NKern::GetUserContext() and + NKern::SetUserContext() instead. + + @return A pointer to the master table. The master table is NULL + terminated. The master and user context tables are guaranteed to remain at + the same location for the lifetime of the OS execution so it is safe the + cache the returned address. + + @see UserContextType + @see TArmContextElement + @see TArmRegisters + @see TUserContextType + @see NKern::SetUserContext + @see NKern::GetUserContext + + @publishedPartner + */ +EXPORT_C const TArmContextElement* const* NThread::UserContextTables() + { + return &ThreadUserContextTables[0]; + } + + +#ifndef __USER_CONTEXT_TYPE_MACHINE_CODED__ +extern TBool RescheduledAfterInterrupt(TUint32 /*aAddr*/); + +/** 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 NThread::TUserContextType NThread::UserContextType() + { + CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED,"NThread::UserContextType"); + // Dying thread? use context saved earlier by kernel + if (iCsFunction == ECSExitInProgress) + return (TUserContextType)iUserContextType; + + // Check for EContextNone and EContextException + // Also EContextUserIntrCallback and EContextWFARCallback + if(iUserContextType<=EContextException || iUserContextType==EContextUserIntrCallback + || iUserContextType==EContextWFARCallback) + return (TUserContextType)iUserContextType; + + // Getting current thread context? must be in exec call as exception + // and dying thread cases were tested above. + if (this == NCurrentThread()) + return EContextExec; + + // Check what caused the thread to enter supervisor mode + TUint32* sst=(TUint32*)((TUint32)iStackBase+(TUint32)iStackSize); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + TInt n=sst-sp; // number of words on the supervisor stack + TUint32 resched_ret=sp[SP_PC]; // return address from reschedule + if (RescheduledAfterInterrupt(resched_ret)) + { + // thread was preempted due to an interrupt + // interrupt and reschedule will have pushed 20+EXTRA words onto the stack + if ((sp[SP_NEXT]&EMaskMode)==EUserMode) // interrupted mode = user? + return NThread::EContextUserInterrupt; + if (n<(30+EXTRA_WORDS)) // n<30 if interrupt occurred in exec call entry before r3-r10 saved + { // or after r3-r10 restored + if (n==(20+EXTRA_WORDS)) + { + // interrupt before return address, r11 were saved or after registers restored + return EContextSvsrInterrupt1; + } + else + { + // interrupt after return address, r11 saved + return EContextSvsrInterrupt2; + } + } + // thread was interrupted in supervisor mode + // return address and r3-r11 were saved + } + + // Transition to supervisor mode must have been due to a SWI + if (n==(15+EXTRA_WORDS)) + { + // thread must have blocked doing Exec::WaitForAnyRequest + return EContextWFAR; + } + + // Thread must have been in a SLOW or UNPROTECTED Exec call + return EContextExec; + } + +#endif // __USER_CONTEXT_TYPE_MACHINE_CODED__ + +// Enter and return with kernel locked +void NThread::GetContext(TArmRegSet& aContext, TUint32& aAvailRegistersMask, const TArmContextElement* aContextTable) + { + TUint32* sp = (TUint32*)iSavedSP; + TUint32* st = (TUint32*)((TUint32)iStackBase+(TUint32)iStackSize); + TArmReg* out = (TArmReg*)(&aContext); + TBool currentThread = (NCurrentThread() == this); + + aAvailRegistersMask = 0; + if (iNState == EDead) + {// This thread's stack may no longer exist so just exit. + return; + } + + // Copy available context into provided structure. + for (TInt i = 0; iGetUserContext(a, aAvailRegistersMask); + NKern::Unlock(); + } + +/** Get (subset of) system context of specified thread. + + @param aThread Thread to inspect. It can be the current thread or a + non-current one. + + @param aContext Pointer to TArmRegSet structure where the context is + copied. + + @param aAvailRegistersMask Bit mask telling which subset of the context is + available and has been copied to aContext (1: register available / 0: not + available). Bit 0 stands for register R0. + + @see TArmRegSet + @see ThreadSetUserContext + + @pre Call in a thread context. + @pre Interrupts must be enabled. + */ +EXPORT_C void NKern::ThreadGetSystemContext(NThread* aThread, TAny* aContext, TUint32& aAvailRegistersMask) + { + CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadGetSystemContext"); + TArmRegSet& a=*(TArmRegSet*)aContext; + memclr(aContext, sizeof(TArmRegSet)); + NKern::Lock(); + aThread->GetSystemContext(a, aAvailRegistersMask); + NKern::Unlock(); + } + +/** Set (subset of) user context of specified thread. + + @param aThread Thread to modify. It can be the current thread or a + non-current one. + + @param aContext Pointer to TArmRegSet structure containing the context + to set. The values of registers which aren't part of the context saved + on the supervisor stack are ignored. + + @see TArmRegSet + @see ThreadGetUserContext + + @pre Call in a thread context. + @pre Interrupts must be enabled. + */ +EXPORT_C void NKern::ThreadSetUserContext(NThread* aThread, TAny* aContext) + { + CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadSetUserContext"); + TArmRegSet& a=*(TArmRegSet*)aContext; + NKern::Lock(); + aThread->SetUserContext(a); + NKern::Unlock(); + } + +/** @internalComponent */ +void NKern::ThreadModifyUsp(NThread* aThread, TLinAddr aUsp) + { + NKern::Lock(); + aThread->ModifyUsp(aUsp); + NKern::Unlock(); + } + +#ifdef __CPU_ARM_USE_DOMAINS +TUint32 NThread::Dacr() + { + if (this==TheScheduler.iCurrentThread) + return Arm::Dacr(); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + TUint32 dacr=sp[SP_DACR]; + NKern::Unlock(); + return dacr; + } + +void NThread::SetDacr(TUint32 aDacr) + { + if (this==TheScheduler.iCurrentThread) + Arm::SetDacr(aDacr); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + sp[SP_DACR]=aDacr; + NKern::Unlock(); + } + +TUint32 NThread::ModifyDacr(TUint32 aClearMask, TUint32 aSetMask) + { + if (this==TheScheduler.iCurrentThread) + return Arm::ModifyDacr(aClearMask,aSetMask); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + TUint32 dacr=sp[SP_DACR]; + sp[SP_DACR]=(dacr&~aClearMask)|aSetMask; + NKern::Unlock(); + return dacr; + } +#endif + +#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG +void NThread::SetCar(TUint32 aCar) + { + if (this==TheScheduler.iCurrentThread) + Arm::SetCar(aCar); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + sp[SP_CAR]=aCar; + NKern::Unlock(); + } +#endif + + + +/** Get the saved coprocessor access register value for a thread + +@return The saved value of the CAR, 0 if CPU doesn't have CAR +@pre Don't call from ISR + +@publishedPartner +@released + */ +EXPORT_C TUint32 NThread::Car() + { + CHECK_PRECONDITIONS(MASK_NOT_ISR,"NThread::Car"); +#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG + if (this==TheScheduler.iCurrentThread) + return Arm::Car(); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + TUint32 car=sp[SP_CAR]; + NKern::Unlock(); + return car; +#else + return 0; +#endif + } + + + +/** Modify the saved coprocessor access register value for a thread + 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 saved value of the CAR, 0 if CPU doesn't have CAR +@pre Don't call from ISR + +@publishedPartner +@released + */ +EXPORT_C TUint32 NThread::ModifyCar(TUint32 aClearMask, TUint32 aSetMask) + { + CHECK_PRECONDITIONS(MASK_NOT_ISR,"NThread::ModifyCar"); +#ifdef __CPU_HAS_COPROCESSOR_ACCESS_REG + if (this==TheScheduler.iCurrentThread) + return Arm::ModifyCar(aClearMask,aSetMask); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + TUint32 car=sp[SP_CAR]; + sp[SP_CAR]=(car&~aClearMask)|aSetMask; + NKern::Unlock(); + return car; +#else + return 0; +#endif + } + +#ifdef __CPU_HAS_VFP +void NThread::SetFpExc(TUint32 aVal) + { + if (this==TheScheduler.iCurrentThread) + Arm::SetFpExc(aVal); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + sp[SP_FPEXC]=aVal; + NKern::Unlock(); + } +#endif + + + +/** Get the saved VFP FPEXC register value for a thread + +@return The saved value of FPEXC, 0 if VFP not present +@pre Don't call from ISR + +@publishedPartner +@released + */ +EXPORT_C TUint32 NThread::FpExc() + { + CHECK_PRECONDITIONS(MASK_NOT_ISR,"NThread::FpExc"); +#ifdef __CPU_HAS_VFP + if (this==TheScheduler.iCurrentThread) + return Arm::FpExc(); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + TUint32 r=sp[SP_FPEXC]; + NKern::Unlock(); + return r; +#else + return 0; +#endif + } + + + +/** Modify the saved VFP FPEXC register value for a thread + Does nothing if VFP not present + +@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 saved value of FPEXC, 0 if VFP not present +@pre Don't call from ISR + +@publishedPartner +@released + */ +EXPORT_C TUint32 NThread::ModifyFpExc(TUint32 aClearMask, TUint32 aSetMask) + { + CHECK_PRECONDITIONS(MASK_NOT_ISR,"NThread::ModifyFpExc"); +#ifdef __CPU_HAS_VFP + if (this==TheScheduler.iCurrentThread) + return Arm::ModifyFpExc(aClearMask,aSetMask); + NKern::Lock(); + TUint32* sp=(TUint32*)iSavedSP; // saved supervisor stack pointer + TUint32 r=sp[SP_FPEXC]; + sp[SP_FPEXC]=(r&~aClearMask)|aSetMask; + NKern::Unlock(); + return r; +#else + return 0; +#endif + } +