diff -r 000000000000 -r a41df078684a kernel/eka/nkernsmp/arm/ncthrd.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/nkernsmp/arm/ncthrd.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1141 @@ +// Copyright (c) 2008-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\nkernsmp\arm\ncthrd.cpp +// +// + +// NThreadBase member data +#define __INCLUDE_NTHREADBASE_DEFINES__ + +#define __INCLUDE_REG_OFFSETS__ +#include +#include +#include +#include +#include + +const TInt KNThreadMinStackSize = 0x100; // needs to be enough for interrupt + reschedule stack + +// Called by a thread when it first runs +extern "C" void __StartThread(); + +// Initialise CPU registers +extern void initialiseState(TInt aCpu, TSubScheduler* aSS); + +extern "C" void ExcFault(TAny*); + +extern TUint32 __mpid(); +extern void InitAPTimestamp(SNThreadCreateInfo& aInfo); + +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 cpu = -1; + new (this) NThread; + if (aInitial) + { + cpu = __e32_atomic_add_ord32(&TheScheduler.iNumCpus, 1); + aInfo.iCpuAffinity = cpu; + // OK since we can't migrate yet + TSubScheduler& ss = TheSubSchedulers[cpu]; + ss.iCurrentThread = this; + iRunCount64 = UI64LIT(1); + __KTRACE_OPT(KBOOT,DEBUGPRINT("Init: cpu=%d ss=%08x", cpu, &ss)); + if (cpu) + { + initialiseState(cpu,&ss); + + ArmLocalTimer& T = LOCAL_TIMER; + T.iWatchdogDisable = E_ArmTmrWDD_1; + T.iWatchdogDisable = E_ArmTmrWDD_2; + T.iTimerCtrl = 0; + T.iTimerIntStatus = E_ArmTmrIntStatus_Event; + T.iWatchdogCtrl = 0; + T.iWatchdogIntStatus = E_ArmTmrIntStatus_Event; + + NIrq::HwInit2AP(); + T.iTimerCtrl = E_ArmTmrCtrl_IntEn | E_ArmTmrCtrl_Reload | E_ArmTmrCtrl_Enable; + + __e32_atomic_ior_ord32(&TheScheduler.iActiveCpus1, 1<iExcCode = SThreadExcStack::EStub; + tss->iR15 = 0; + tss->iCPSR = 0; + sp = (TLinAddr)tss; + sp -= (TLinAddr)aInfo.iParameterBlockSize; + wordmove((TAny*)sp, aInfo.iParameterBlock, aInfo.iParameterBlockSize); + pb = (TUint32)sp; + tss->iPBlock = sp; + } + SThreadInitStack* tis = (SThreadInitStack*)sp; + --tis; + memclr(tis, sizeof(SThreadInitStack)); + iSavedSP = (TLinAddr)tis; +#ifdef __CPU_HAS_VFP + tis->iR.iFpExc = VFP_FPEXC_THRD_INIT; +#endif + tis->iR.iCar = Arm::DefaultCoprocessorAccess; + tis->iR.iDacr = Arm::DefaultDomainAccess; + tis->iR.iSpsrSvc = MODE_SVC; + tis->iR.iSPRschdFlg = TLinAddr(&tis->iX) | 1; + tis->iR.iR15 = (TUint32)&__StartThread; + + tis->iX.iR0 = pb; + tis->iX.iR4 = (TUint32)this; + tis->iX.iR11 = stack_top; + tis->iX.iExcCode = SThreadExcStack::EInit; + tis->iX.iR15 = (TUint32)aInfo.iFunction; + tis->iX.iCPSR = MODE_SVC; + } + else + { + NKern::EnableAllInterrupts(); + + // start local timer + ArmLocalTimer& T = LOCAL_TIMER; + T.iTimerCtrl = E_ArmTmrCtrl_IntEn | E_ArmTmrCtrl_Reload | E_ArmTmrCtrl_Enable; + + // synchronize AP's timestamp with BP's + if (cpu>0) + InitAPTimestamp(aInfo); + } +#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() + { + } + +/** 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() + { + } + + +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); + + TInt irq = NKern::DisableAllInterrupts(); + TSubScheduler& ss = SubScheduler(); + NThreadBase* ct = ss.iCurrentThread; + TInt inc = TInt(ss.i_IrqNestCount); + TInt cpu = ss.iCpuNum; + TInt klc = ss.iKernLockCount; + NKern::RestoreInterrupts(irq); + DEBUGPRINT("Thread %T, CPU %d, KLCount=%d, IrqNest=%d", ct, cpu, klc, inc); + } + +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, \ + 0, \ + 0 \ + } + +#define CONTEXT_ELEMENT_EXCEPTION(reg) \ + { \ + TArmContextElement::EOffsetFromStackTop, \ + ((sizeof(SThreadExcStack)-_FOFF(SThreadExcStack,reg))>>2), \ + 0, \ + 0 \ + } + +#define CONTEXT_ELEMENT_RESCHED(reg) \ + { \ + TArmContextElement::EOffsetFromSp, \ + (_FOFF(SThreadReschedStack,reg)>>2), \ + 0, \ + 0 \ + } + +#define CONTEXT_ELEMENT_RESCHED_SP() \ + { \ + TArmContextElement::EOffsetFromSpBic3, \ + (_FOFF(SThreadReschedStack,iSPRschdFlg)>>2), \ + 0, \ + 0 \ + } + +#define CONTEXT_ELEMENT_RESCHED_SP_PLUS(offset) \ + { \ + TArmContextElement::EOffsetFromSpBic3_1, \ + (_FOFF(SThreadReschedStack,iSPRschdFlg)>>2), \ + (offset), \ + 0 \ + } + +#define CONTEXT_ELEMENT_RESCHED_SP_OFFSET(offset) \ + { \ + TArmContextElement::EOffsetFromSpBic3_2, \ + (_FOFF(SThreadReschedStack,iSPRschdFlg)>>2), \ + (offset), \ + 0 \ + } + +#define CONTEXT_ELEMENT_RESCHED_IRQ(reg) \ + { \ + TArmContextElement::EOffsetFromSpBic3_2, \ + (_FOFF(SThreadReschedStack,iSPRschdFlg)>>2), \ + ((_FOFF(SThreadIrqStack,reg)-sizeof(SThreadReschedStack))>>2), \ + 0 \ + } + +#define CONTEXT_ELEMENT_RESCHED_INIT(reg) \ + { \ + TArmContextElement::EOffsetFromSpBic3_2, \ + (_FOFF(SThreadReschedStack,iSPRschdFlg)>>2), \ + ((_FOFF(SThreadInitStack,reg)-sizeof(SThreadReschedStack))>>2), \ + 0 \ + } + + +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(iR13usr), + CONTEXT_ELEMENT_EXCEPTION(iR14usr), + 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_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(iR13usr), + CONTEXT_ELEMENT_EXCEPTION(iR14usr), + CONTEXT_ELEMENT_EXCEPTION(iR15), + CONTEXT_ELEMENT_EXCEPTION(iCPSR), + 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_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(iR13usr), + CONTEXT_ELEMENT_EXCEPTION(iR14usr), + CONTEXT_ELEMENT_EXCEPTION(iR15), + 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_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(iR13usr), + CONTEXT_ELEMENT_EXCEPTION(iR14usr), + CONTEXT_ELEMENT_EXCEPTION(iR15), + CONTEXT_ELEMENT_EXCEPTION(iCPSR), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +const TArmContextElement ContextTableExec[] = + { + 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(iR13usr), + CONTEXT_ELEMENT_EXCEPTION(iR14usr), + CONTEXT_ELEMENT_EXCEPTION(iR15), + CONTEXT_ELEMENT_EXCEPTION(iCPSR), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used to retrieve a thread's kernel side context at the point where +// Reschedule() returns. +// 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_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_RESCHED_SP(), // supervisor stack pointer before reschedule + CONTEXT_ELEMENT_UNDEFINED(0), // supervisor lr is unknown + CONTEXT_ELEMENT_RESCHED(iR15), // return address from reschedule + CONTEXT_ELEMENT_UNDEFINED(ESvcMode), // can't get flags so just use 'user mode' + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used to retrieve a thread's kernel side context at the point where +// NKern::Unlock() or NKern::PreemptionPoint() returns. +// Used for kernel threads. +const TArmContextElement ContextTableKernel1[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(4), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(8), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(12), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(16), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(20), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(24), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(28), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(32), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_RESCHED_SP_PLUS(40), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(36), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(36), + CONTEXT_ELEMENT_UNDEFINED(ESvcMode), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used to retrieve a thread's kernel side context at the point where +// NKern::FSWait() or NKern::WaitForAnyRequest() returns. +// Used for kernel threads. +const TArmContextElement ContextTableKernel2[] = + { + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(4), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(8), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(12), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(16), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(20), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(24), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(28), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(32), + CONTEXT_ELEMENT_UNDEFINED(0), + CONTEXT_ELEMENT_RESCHED_SP_PLUS(40), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(36), + CONTEXT_ELEMENT_RESCHED_SP_OFFSET(36), + CONTEXT_ELEMENT_UNDEFINED(ESvcMode), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used to retrieve a thread's kernel side context at the point where +// an interrupt taken in supervisor mode returns. +// Used for kernel threads. +const TArmContextElement ContextTableKernel3[] = + { + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR0), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR1), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR2), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR3), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR4), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR5), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR6), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR7), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR8), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR9), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR10), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR11), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR12), + CONTEXT_ELEMENT_RESCHED_SP_PLUS((sizeof(SThreadExcStack)+8)), + CONTEXT_ELEMENT_RESCHED_IRQ(iR14svc), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iR15), + CONTEXT_ELEMENT_RESCHED_IRQ(iX.iCPSR), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +// Table used to retrieve a thread's kernel side context at the point where +// Exec::WaitForAnyRequest() returns. +// Used for kernel threads. +const TArmContextElement ContextTableKernel4[] = + { + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR0), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR1), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR2), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR3), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR4), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR5), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR6), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR7), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR8), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR9), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR10), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR11), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR12), + CONTEXT_ELEMENT_RESCHED_SP_PLUS(sizeof(SThreadExcStack)), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR15), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iR15), + CONTEXT_ELEMENT_RESCHED_INIT(iX.iCPSR), + CONTEXT_ELEMENT_UNDEFINED(0), + }; + +const TArmContextElement* const ThreadUserContextTables[] = + { + ContextTableUndefined, // EContextNone + ContextTableException, // EContextException + ContextTableUndefined, // EContextUndefined + ContextTableUserInterrupt, // EContextUserInterrupt + ContextTableUndefined, // EContextUserInterruptDied (not used) + ContextTableSvsrInterrupt1, // EContextSvsrInterrupt1 + ContextTableUndefined, // EContextSvsrInterrupt1Died (not used) + ContextTableUndefined, // EContextSvsrInterrupt2 (not used) + ContextTableUndefined, // EContextSvsrInterrupt2Died (not used) + ContextTableWFAR, // EContextWFAR + ContextTableUndefined, // EContextWFARDied (not used) + ContextTableExec, // EContextExec + ContextTableKernel, // EContextKernel + ContextTableKernel1, // EContextKernel1 + ContextTableKernel2, // EContextKernel2 + ContextTableKernel3, // EContextKernel3 + ContextTableKernel4, // EContextKernel4 + 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]; + } + + +/** 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"); + + /* + The SMP nanokernel always saves R0-R12,R13usr,R14usr,ExcCode,PC,CPSR on any + entry to the kernel, so getting the user context is always the same. + The only possible problem is an FIQ occurring immediately after any other + exception, before the registers have been saved. In this case the registers + saved by the FIQ will be the ones observed and they will be correct except + that the CPSR value will indicate a mode other than USR, which can be used + to detect the condition. + */ + return EContextException; + } + + +// Enter and return with kernel locked +void NThread::GetUserContext(TArmRegSet& aContext, TUint32& aAvailRegistersMask) + { + NThread* pC = NCurrentThreadL(); + TSubScheduler* ss = 0; + if (pC != this) + { + AcqSLock(); + if (iWaitState.ThreadIsDead()) + { + RelSLock(); + aAvailRegistersMask = 0; + return; + } + if (iReady && iParent->iReady) + { + ss = TheSubSchedulers + (iParent->iReady & EReadyCpuMask); + ss->iReadyListLock.LockOnly(); + } + if (iCurrent) + { + // thread is actually running on another CPU + // interrupt that CPU and wait for it to enter interrupt mode + // this allows a snapshot of the thread user state to be observed + // and ensures the thread cannot return to user mode + send_resched_ipi_and_wait(iLastCpu); + } + } + SThreadExcStack* txs = (SThreadExcStack*)(TLinAddr(iStackBase) + TLinAddr(iStackSize)); + --txs; + if (txs->iExcCode <= SThreadExcStack::EInit) // if not, thread never entered user mode + { + aContext.iR0 = txs->iR0; + aContext.iR1 = txs->iR1; + aContext.iR2 = txs->iR2; + aContext.iR3 = txs->iR3; + aContext.iR4 = txs->iR4; + aContext.iR5 = txs->iR5; + aContext.iR6 = txs->iR6; + aContext.iR7 = txs->iR7; + aContext.iR8 = txs->iR8; + aContext.iR9 = txs->iR9; + aContext.iR10 = txs->iR10; + aContext.iR11 = txs->iR11; + aContext.iR12 = txs->iR12; + aContext.iR13 = txs->iR13usr; + aContext.iR14 = txs->iR14usr; + aContext.iR15 = txs->iR15; + aContext.iFlags = txs->iCPSR; + if ((aContext.iFlags & 0x1f) == 0x10) + aAvailRegistersMask = 0x1ffffu; // R0-R15,CPSR all valid + else + { + aContext.iFlags = 0x10; // account for FIQ in SVC case + aAvailRegistersMask = 0x0ffffu; // CPSR not valid + } + } + if (pC != this) + { + if (ss) + ss->iReadyListLock.UnlockOnly(); + RelSLock(); + } + } + +class TGetContextIPI : public TGenericIPI + { +public: + void Get(TInt aCpu, TArmRegSet& aContext, TUint32& aAvailRegistersMask); + static void Isr(TGenericIPI*); +public: + TArmRegSet* iContext; + TUint32* iAvailRegsMask; + }; + +extern "C" TLinAddr get_sp_svc(); +extern "C" TLinAddr get_lr_svc(); +extern "C" TInt get_kernel_context_type(TLinAddr /*aReschedReturn*/); + +void TGetContextIPI::Isr(TGenericIPI* aPtr) + { + TGetContextIPI& ipi = *(TGetContextIPI*)aPtr; + TArmRegSet& a = *ipi.iContext; + SThreadExcStack* txs = (SThreadExcStack*)get_sp_svc(); + a.iR0 = txs->iR0; + a.iR1 = txs->iR1; + a.iR2 = txs->iR2; + a.iR3 = txs->iR3; + a.iR4 = txs->iR4; + a.iR5 = txs->iR5; + a.iR6 = txs->iR6; + a.iR7 = txs->iR7; + a.iR8 = txs->iR8; + a.iR9 = txs->iR9; + a.iR10 = txs->iR10; + a.iR11 = txs->iR11; + a.iR12 = txs->iR12; + a.iR13 = TUint32(txs) + sizeof(SThreadExcStack); + a.iR14 = get_lr_svc(); + a.iR15 = txs->iR15; + a.iFlags = txs->iCPSR; + *ipi.iAvailRegsMask = 0x1ffffu; + } + +void TGetContextIPI::Get(TInt aCpu, TArmRegSet& aContext, TUint32& aAvailRegsMask) + { + iContext = &aContext; + iAvailRegsMask = &aAvailRegsMask; + Queue(&Isr, 1u<>=1, ++d) + { + if (aMask & 1) + *d = *s++; + } + } + +// Enter and return with kernel locked +void NThread::GetSystemContext(TArmRegSet& aContext, TUint32& aAvailRegsMask) + { + aAvailRegsMask = 0; + NThread* pC = NCurrentThreadL(); + __NK_ASSERT_ALWAYS(pC!=this); + TSubScheduler* ss = 0; + AcqSLock(); + if (iWaitState.ThreadIsDead()) + { + RelSLock(); + return; + } + if (iReady && iParent->iReady) + { + ss = TheSubSchedulers + (iParent->iReady & EReadyCpuMask); + ss->iReadyListLock.LockOnly(); + } + if (iCurrent) + { + // thread is actually running on another CPU + // use an interprocessor interrupt to get a snapshot of the state + TGetContextIPI ipi; + ipi.Get(iLastCpu, aContext, aAvailRegsMask); + } + else + { + // thread is not running and can't start + SThreadReschedStack* trs = (SThreadReschedStack*)iSavedSP; + TInt kct = get_kernel_context_type(trs->iR15); + __NK_ASSERT_ALWAYS(kct>=0); // couldn't match return address from reschedule + TLinAddr sp = trs->iSPRschdFlg &~ 3; + switch (kct) + { + case 0: // thread not yet started + case 5: // Exec::WaitForAnyRequest() + GetRegs(aContext, sp, 0x01fffu); + aContext.iR13 = sp + sizeof(SThreadExcStack); + GetRegs(aContext, sp+64, 0x18000u); + aAvailRegsMask =0x1bfffu; + break; + case 1: // unlock + case 2: // preemption point + case 3: // NKern::WaitForAnyRequest() or NKern::FSWait() + GetRegs(aContext, sp+4, 0x08ff0u); + aContext.iR14 = aContext.iR15; + aContext.iR13 = sp+40; + aAvailRegsMask =0x0eff0u; + break; + case 4: // IRQ/FIQ + GetRegs(aContext, sp+4, 0x04000u); + GetRegs(aContext, sp+8, 0x01fffu); + GetRegs(aContext, sp+64, 0x18000u); + aContext.iR13 = sp + sizeof(SThreadExcStack) + 8; + aAvailRegsMask =0x1ffffu; + break; + default: + __NK_ASSERT_ALWAYS(0); + } + } + if (ss) + ss->iReadyListLock.UnlockOnly(); + RelSLock(); + } + +// Enter and return with kernel locked +void NThread::SetUserContext(const TArmRegSet& aContext, TUint32& aRegMask) + { + NThread* pC = NCurrentThreadL(); + TSubScheduler* ss = 0; + if (pC != this) + { + AcqSLock(); + if (iWaitState.ThreadIsDead()) + { + RelSLock(); + aRegMask = 0; + return; + } + if (iReady && iParent->iReady) + { + ss = TheSubSchedulers + (iParent->iReady & EReadyCpuMask); + ss->iReadyListLock.LockOnly(); + } + if (iCurrent) + { + // thread is actually running on another CPU + // interrupt that CPU and wait for it to enter interrupt mode + // this allows a snapshot of the thread user state to be observed + // and ensures the thread cannot return to user mode + send_resched_ipi_and_wait(iLastCpu); + } + } + SThreadExcStack* txs = (SThreadExcStack*)(TLinAddr(iStackBase) + TLinAddr(iStackSize)); + --txs; + aRegMask &= 0x1ffffu; + if (txs->iExcCode <= SThreadExcStack::EInit) // if not, thread never entered user mode + { + if (aRegMask & 0x0001u) + txs->iR0 = aContext.iR0; + if (aRegMask & 0x0002u) + txs->iR1 = aContext.iR1; + if (aRegMask & 0x0004u) + txs->iR2 = aContext.iR2; + if (aRegMask & 0x0008u) + txs->iR3 = aContext.iR3; + if (aRegMask & 0x0010u) + txs->iR4 = aContext.iR4; + if (aRegMask & 0x0020u) + txs->iR5 = aContext.iR5; + if (aRegMask & 0x0040u) + txs->iR6 = aContext.iR6; + if (aRegMask & 0x0080u) + txs->iR7 = aContext.iR7; + if (aRegMask & 0x0100u) + txs->iR8 = aContext.iR8; + if (aRegMask & 0x0200u) + txs->iR9 = aContext.iR9; + if (aRegMask & 0x0400u) + txs->iR10 = aContext.iR10; + if (aRegMask & 0x0800u) + txs->iR11 = aContext.iR11; + if (aRegMask & 0x1000u) + txs->iR12 = aContext.iR12; + if (aRegMask & 0x2000u) + txs->iR13usr = aContext.iR13; + if (aRegMask & 0x4000u) + txs->iR14usr = aContext.iR14; + if (aRegMask & 0x8000u) + txs->iR15 = aContext.iR15; + // Assert that target thread is in USR mode, and update only the flags part of the PSR + __NK_ASSERT_ALWAYS((txs->iCPSR & 0x1f) == 0x10); + if (aRegMask & 0x10000u) + { + // NZCVQ.......GE3-0................ + const TUint32 writableFlags = 0xF80F0000; + txs->iCPSR &= ~writableFlags; + txs->iCPSR |= aContext.iFlags & writableFlags; + } + } + else + aRegMask = 0; + if (pC != this) + { + if (ss) + ss->iReadyListLock.UnlockOnly(); + RelSLock(); + } + } + +/** Get (subset of) user context of specified thread. + + The nanokernel does not systematically save all registers in the supervisor + stack on entry into privileged mode and the exact subset depends on why the + switch to privileged mode occured. So in general only a subset of the + register set is available. + + @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::ThreadGetUserContext(NThread* aThread, TAny* aContext, TUint32& aAvailRegistersMask) + { + CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadGetUserContext"); + TArmRegSet& a = *(TArmRegSet*)aContext; + memclr(aContext, sizeof(TArmRegSet)); + NKern::Lock(); + aThread->GetUserContext(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; + TUint32 mask = 0x1ffffu; + NKern::Lock(); + aThread->SetUserContext(a, mask); + NKern::Unlock(); + } + + +#ifdef __CPU_HAS_VFP +extern void VfpContextSave(void*); +#endif +/** Complete the saving of a thread's context + +This saves the VFP/NEON registers if necessary once we know that we are definitely +switching threads. + +@internalComponent +*/ +void NThread::CompleteContextSave() + { +#ifdef __CPU_HAS_VFP + if (Arm::VfpThread[NKern::CurrentCpu()] == this) + { + VfpContextSave(iExtraContext); // Disables VFP + } +#endif + } + + +extern "C" TInt HandleSpecialOpcode(TArmExcInfo* aContext, TInt aType) + { + TUint32 cpsr = aContext->iCpsr; + TUint32 mode = cpsr & 0x1f; + TUint32 opcode = aContext->iFaultStatus; + + // Coprocessor abort from CP15 or E7FFDEFF -> crash immediately + if ( (aType==15 && opcode!=0xee000f20) + || (aType==32 && opcode==0xe7ffdeff) + || (aType==33 && opcode==0xdeff) + ) + { + if (mode != 0x10) + ExcFault(aContext); // crash instruction in privileged mode + return 0; // crash instruction in user mode - handle normally + } + if ( (aType==15 && opcode==0xee000f20) + || (aType==32 && opcode==0xe7ffdefc) + || (aType==33 && opcode==0xdefc) + ) + { + // checkpoint + __KTRACE_OPT(KPANIC,DumpExcInfo(*aContext)); + if (aType==32) + aContext->iR15 += 4; + else + aContext->iR15 += 2; + return 1; + } + return 0; + } + +/** Return the total CPU time so far used by the specified thread. + + @return The total CPU time in units of 1/NKern::CpuTimeMeasFreq(). +*/ +EXPORT_C TUint64 NKern::ThreadCpuTime(NThread* aThread) + { + TSubScheduler* ss = 0; + NKern::Lock(); + aThread->AcqSLock(); + if (aThread->i_NThread_Initial) + ss = &TheSubSchedulers[aThread->iLastCpu]; + else if (aThread->iReady && aThread->iParent->iReady) + ss = &TheSubSchedulers[aThread->iParent->iReady & NSchedulable::EReadyCpuMask]; + if (ss) + ss->iReadyListLock.LockOnly(); + TUint64 t = aThread->iTotalCpuTime64; + if (aThread->iCurrent || (aThread->i_NThread_Initial && !ss->iCurrentThread)) + t += (NKern::Timestamp() - ss->iLastTimestamp64); + if (ss) + ss->iReadyListLock.UnlockOnly(); + aThread->RelSLock(); + NKern::Unlock(); + return t; + } + +TInt NKern::QueueUserModeCallback(NThreadBase* aThread, TUserModeCallback* aCallback) + { + __e32_memory_barrier(); + if (aCallback->iNext != KUserModeCallbackUnqueued) + return KErrInUse; + TInt result = KErrDied; + NKern::Lock(); + TUserModeCallback* listHead = aThread->iUserModeCallbacks; + do { + if (TLinAddr(listHead) & 3) + goto done; // thread exiting + aCallback->iNext = listHead; + } while (!__e32_atomic_cas_ord_ptr(&aThread->iUserModeCallbacks, &listHead, aCallback)); + result = KErrNone; + + if (!listHead) // if this isn't first callback someone else will have done this bit + { + /* + * If aThread is currently running on another CPU we need to send an IPI so + * that it will enter kernel mode and run the callback. + * The synchronization is tricky here. We want to check if the thread is + * running and if so on which core. We need to avoid any possibility of + * the thread entering user mode without having seen the callback, + * either because we thought it wasn't running so didn't send an IPI or + * because the thread migrated after we looked and we sent the IPI to + * the wrong processor. Sending a redundant IPI is not a problem (e.g. + * because the thread is running in kernel mode - which we can't tell - + * or because the thread stopped running after we looked) + * The following events are significant: + * Event A: Target thread writes to iCurrent when it starts running + * Event B: Target thread reads iUserModeCallbacks before entering user + * mode + * Event C: This thread writes to iUserModeCallbacks + * Event D: This thread reads iCurrent to check if aThread is running + * There is a DMB and DSB between A and B since A occurs with the ready + * list lock for the CPU involved or the thread lock for aThread held + * and this lock is released before B occurs. + * There is a DMB between C and D (part of __e32_atomic_cas_ord_ptr). + * Any observer which observes B must also have observed A. + * Any observer which observes D must also have observed C. + * If aThread observes B before C (i.e. enters user mode without running + * the callback) it must observe A before C and so it must also observe + * A before D (i.e. D reads the correct value for iCurrent). + */ + TInt current = aThread->iCurrent; + if (current) + { + TInt cpu = current & NSchedulable::EReadyCpuMask; + if (cpu != NKern::CurrentCpu()) + send_resched_ipi(cpu); + } + } +done: + NKern::Unlock(); + return result; + } +