kernel/eka/nkernsmp/x86/ncsched.cia
changeset 0 a41df078684a
child 90 947f0dc9f7a8
equal deleted inserted replaced
-1:000000000000 0:a41df078684a
       
     1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of the License "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // e32\nkernsmp\x86\ncsched.cia
       
    15 // 
       
    16 //
       
    17 
       
    18 #include <x86.h>
       
    19 #include <apic.h>
       
    20 
       
    21 // SubSchedulerLookupTable	: global data, type: TSubScheduler* [256];
       
    22 // BTraceLock				: global data, type: TSpinLock
       
    23 
       
    24 const TLinAddr TScheduler_Reschedule = (TLinAddr)&TScheduler::Reschedule;
       
    25 //const TLinAddr TheScheduler_iRescheduleNeededFlag = (TLinAddr)&TheScheduler.iRescheduleNeededFlag;
       
    26 const TLinAddr NKern_FastCounter = (TLinAddr)&NKern::FastCounter;
       
    27 const TLinAddr NKern_Lock = (TLinAddr)&NKern::Lock;
       
    28 const TLinAddr NKern_Unlock = (TLinAddr)&NKern::Unlock;
       
    29 const TLinAddr addressof_TheScheduler = (TLinAddr)&TheScheduler;
       
    30 const TUint32 new_thread_trace_header = ((8<<BTrace::ESizeIndex) + (BTrace::EContextIdPresent<<BTrace::EFlagsIndex*8) + (BTrace::ECpuUsage<<BTrace::ECategoryIndex*8) + (BTrace::ENewThreadContext<<BTrace::ESubCategoryIndex*8));
       
    31 
       
    32 extern "C" void __fastcall queue_dfcs(TSubScheduler* aS);
       
    33 extern "C" NThreadBase* __fastcall select_next_thread(TSubScheduler* aS);
       
    34 extern "C" void send_resched_ipis(TUint32 aMask);
       
    35 extern "C" void __fastcall do_forced_exit(NThreadBase* aT);
       
    36 extern "C" void NewThreadTrace(NThread* a);
       
    37 
       
    38 
       
    39 /***************************************************************************
       
    40 * Reschedule
       
    41 * Enter with:
       
    42 *		Kernel locked, interrupts enabled or disabled
       
    43 * Return with:
       
    44 *		Kernel unlocked, interrupts disabled
       
    45 *		EAX=0 if no reschedule occurred, 1 if it did
       
    46 *		ESI pointing to TSubScheduler for current CPU
       
    47 *		EDI pointing to current NThread
       
    48 ***************************************************************************/
       
    49 __NAKED__ void TScheduler::Reschedule()
       
    50 	{
       
    51 	asm("push 0 ");
       
    52 	asm("mov eax, ds:[%0]" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));	// OK since kernel locked
       
    53 	asm("mov edi, %0" : : "i" (addressof_TheScheduler));
       
    54 	asm("shr eax, 24 ");
       
    55 	asm("mov esi, [eax*4+%0]" : : "i" (&SubSchedulerLookupTable));
       
    56 	asm("cli ");
       
    57 	asm("start_resched: ");
       
    58 //	_asm cmp dword ptr [esi]TSubScheduler.iRescheduleNeededFlag, 10000h		VC6 ignores the "dword ptr"
       
    59 	asm("lea eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iRescheduleNeededFlag));
       
    60 	asm("cmp dword ptr [eax], 0x10000 ");
       
    61 	asm("jb short resched_no_dfcs ");
       
    62 	asm("mov ecx, esi ");
       
    63 	asm("call %a0" : : "i" (&queue_dfcs));
       
    64 	asm("resched_no_dfcs: ");
       
    65 	asm("cmp byte ptr [esi+%0], 0" : : "i" _FOFF(TSubScheduler,iRescheduleNeededFlag));
       
    66 	asm("jz resched_not_needed ");
       
    67 	asm("sti ");
       
    68 	asm("mov dword ptr [esp], 1 ");
       
    69 	asm("mov ebp, [esi+%0]" : : "i"_FOFF(TSubScheduler, iCurrentThread));		// EBP -> original thread
       
    70 	asm("mov eax, cr0");
       
    71 	asm("push eax");
       
    72 	asm("mov [ebp+%0], esp" : : "i" _FOFF(NThreadBase, iSavedSP));			// Save original thread stack pointer
       
    73 
       
    74 	// We must move to a temporary stack before selecting the next thread.
       
    75 	// This is because another CPU may begin executing this thread before the
       
    76 	// select_next_thread() function returns and our stack would then be
       
    77 	// corrupted. We use the stack belonging to this CPU's initial thread since
       
    78 	// we are guaranteed that will never run on another CPU.
       
    79 	asm("mov ecx, [esi+%0]" : : "i" _FOFF(TSubScheduler, iInitialThread));
       
    80 	asm("mov esp, [ecx+%0]" : : "i" _FOFF(NThreadBase, iSavedSP));
       
    81 
       
    82 	asm("select_thread:");
       
    83 	asm("mov ecx, esi ");
       
    84 	asm("call %a0" : : "i" (&select_next_thread));
       
    85 	asm("mov ebx, eax ");
       
    86 	asm("cmp ebx, 0 ");
       
    87 	asm("jz no_thread ");
       
    88 	asm("mov esp, [ebx+%0]" : : "i" _FOFF(NThreadBase, iSavedSP)); // move to new thread's stack
       
    89 
       
    90 #ifdef BTRACE_CPU_USAGE
       
    91 	asm("cmp byte ptr %a0, 0" : : "i" (&BTraceData.iFilter[4]));
       
    92 	asm("jz short no_trace ");
       
    93 	asm("push ebx ");
       
    94 	asm("call %a0" : : "i" (NewThreadTrace));
       
    95 	asm("pop ebx ");
       
    96 	asm("no_trace: ");
       
    97 #endif	// BTRACE_CPU_USAGE
       
    98 
       
    99 	asm("cmp ebp, ebx ");
       
   100 	asm("je same_thread ");
       
   101 	asm("mov eax, [ebx+%0]" : : "i" _FOFF(NThreadBase, iStackBase));
       
   102 	asm("add eax, [ebx+%0]" : : "i" _FOFF(NThreadBase, iStackSize));
       
   103 	asm("mov ecx, [esi+60+%0]" : : "i" _FOFF(TSubScheduler, iExtras));		// iExtras[15] points to TSS
       
   104 	asm("mov [ecx+%0], eax" : : "i" _FOFF(TX86Tss, iEsp0));					// set ESP0 to top of new thread supervisor stack
       
   105 
       
   106 	asm("test byte ptr [ebx+%0], 2" : : "i" _FOFF(NThreadBase,i_ThrdAttr));	// test for address space switch
       
   107 	asm("jz short resched_no_as_switch ");
       
   108 	asm("call [edi+%0]" : : "i" _FOFF(TScheduler, iProcessHandler));		// call handler with
       
   109 														// EBX=pointer to new thread, EDI->scheduler, ESI->subscheduler
       
   110 	asm("resched_no_as_switch: ");
       
   111 	asm("same_thread: ");
       
   112 	asm("pop eax ");
       
   113 	asm("mov cr0, eax ");
       
   114 	asm("cli ");
       
   115 //	asm("cmp dword ptr [esi]TSubScheduler.iRescheduleNeededFlag, 0	VC6 ignores the "dword ptr"
       
   116 	asm("lea eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iRescheduleNeededFlag));
       
   117 	asm("cmp dword ptr [eax], 0 ");
       
   118 	asm("jnz start_resched ");
       
   119 
       
   120 	asm("resched_not_needed: ");
       
   121 	asm("mov edi, [esi+%0]" : : "i" _FOFF(TSubScheduler, iCurrentThread));
       
   122 	asm("cmp dword ptr [edi+%0], -3" : : "i" _FOFF(NThreadBase, iCsFunction)); // ECSDivertPending
       
   123 	asm("je resched_thread_divert ");
       
   124 	asm("mov dword ptr [esi+%0], 0" : : "i" _FOFF(TSubScheduler, iKernLockCount));
       
   125 	asm("pop eax ");
       
   126 	asm("ret ");
       
   127 
       
   128 	asm("resched_thread_divert: ");
       
   129 	asm("push edi ");
       
   130 	asm("xor eax, eax ");
       
   131 	asm("lock xchg eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iReschedIPIs));
       
   132 	asm("test eax, eax ");
       
   133 	asm("jz short no_resched_ipis ");
       
   134 	asm("push eax ");
       
   135 	asm("call %a0" : : "i" (&send_resched_ipis));
       
   136 	asm("add esp, 4 ");
       
   137 	asm("no_resched_ipis: ");
       
   138 
       
   139 	asm("sti ");
       
   140 	asm("mov ecx, [esp+12] ");			// SThreadReschedStack iReason 0 not run 1 unlock 2 IRQ
       
   141 	asm("cmp ecx, 2 ");
       
   142 	asm("ja short rtd_unknown ");		// unknown - die
       
   143 	asm("shl ecx, 2 ");					// reason * 4
       
   144 	asm("mov eax, 0xa1a ");
       
   145 	asm("shr eax, cl ");
       
   146 	asm("and eax, 15 ");
       
   147 	asm("mov gs, [esp+eax*4+16] ");		// restore GS
       
   148 
       
   149 	asm("pop ecx ");					// exiting thread pointer
       
   150 	asm("call %a0" : : "i" (&do_forced_exit));
       
   151 	asm("int 0xff ");					// should never get here
       
   152 
       
   153 	asm("rtd_unknown: ");
       
   154 	asm("int 0xff ");					// should never get here
       
   155 
       
   156 
       
   157 	// There is no thread ready to run
       
   158 	asm("no_thread: ");
       
   159 	asm("cli ");
       
   160 	asm("xor eax, eax ");
       
   161 	asm("lock xchg eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iReschedIPIs));
       
   162 	asm("test eax, eax ");
       
   163 	asm("jz short no_resched_ipis2 ");
       
   164 	asm("push eax ");
       
   165 	asm("call %a0" : : "i" (&send_resched_ipis));
       
   166 	asm("add esp, 4 ");
       
   167 	asm("no_resched_ipis2: ");
       
   168 	asm("sti ");
       
   169 	asm("hlt ");
       
   170 	asm("no_thread2: ");
       
   171 //	_asm cmp dword ptr [esi]TSubScheduler.iRescheduleNeededFlag, 10000h		VC6 ignores the "dword ptr"
       
   172 	asm("lea eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iRescheduleNeededFlag));
       
   173 	asm("cmp dword ptr [eax], 0x10000 ");
       
   174 	asm("jb short no_thread ");
       
   175 	asm("mov ecx, esi ");
       
   176 	asm("call %a0" : : "i" (&queue_dfcs));
       
   177 	asm("cmp byte ptr [esi+%0], 0" : : "i" _FOFF(TSubScheduler, iRescheduleNeededFlag));
       
   178 	asm("jz short no_thread2 ");
       
   179 	asm("jmp select_thread ");
       
   180 	}
       
   181 
       
   182 
       
   183 /** Disable interrupts to the specified level
       
   184 
       
   185 If aLevel = 0 does not affect interrupt state
       
   186 If aLevel <>0 disables all maskable interrupts.
       
   187 
       
   188 @param	aLevel level to which to disable
       
   189 @return	Cookie to pass into RestoreInterrupts()
       
   190 */
       
   191 EXPORT_C __NAKED__ TInt NKern::DisableInterrupts(TInt /*aLevel*/)
       
   192 	{
       
   193 	asm("pushfd");
       
   194 	asm("mov ecx, [esp+4]");
       
   195 	asm("pop eax");
       
   196 	asm("and eax, 0x200");
       
   197 	asm("test ecx, ecx");
       
   198 	asm("jz disable_ints_0");
       
   199 	asm("cli");
       
   200 	asm("disable_ints_0:");
       
   201 	asm("ret");
       
   202 	}
       
   203 
       
   204 
       
   205 /** Disable all maskable interrupts
       
   206 
       
   207 @return	Cookie to pass into RestoreInterrupts()
       
   208 */
       
   209 EXPORT_C __NAKED__ TInt NKern::DisableAllInterrupts()
       
   210 	{
       
   211 	asm("pushfd");
       
   212 	asm("pop eax");
       
   213 	asm("and eax, 0x200");
       
   214 	asm("cli");
       
   215 	asm("ret");
       
   216 	}
       
   217 
       
   218 
       
   219 /** Restore interrupt mask to state preceding a DisableInterrupts() call
       
   220 
       
   221 @param	aLevel Cookie returned by Disable(All)Interrupts()
       
   222 */
       
   223 EXPORT_C __NAKED__ void NKern::RestoreInterrupts(TInt aLevel)
       
   224 	{
       
   225 	asm("test byte ptr [esp+5], 2");	// test saved I flag
       
   226 	asm("jz restore_irq_off");			// jump if clear
       
   227 	asm("sti");							// else reenable interrupts
       
   228 	asm("ret");
       
   229 	asm("restore_irq_off:");
       
   230 	asm("cli");
       
   231 	asm("ret");
       
   232 	}
       
   233 
       
   234 
       
   235 /** Enable all maskable interrupts
       
   236 
       
   237 @internalComponent
       
   238 */
       
   239 EXPORT_C __NAKED__ void NKern::EnableAllInterrupts()
       
   240 	{
       
   241 	asm("sti");
       
   242 	asm("ret");
       
   243 	}
       
   244 
       
   245 
       
   246 /**	Unlocks the kernel
       
   247 	Decrements iKernCSLocked; if it becomes zero and IDFCs or a reschedule are
       
   248 	pending, calls the scheduler to process them.
       
   249 
       
   250 	@pre	Thread or IDFC context. Don't call from ISRs.
       
   251  */
       
   252 EXPORT_C __NAKED__ void NKern::Unlock()
       
   253 	{
       
   254 	asm("mov eax, ds:[%0]" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID)); // OK since kernel locked
       
   255 	asm("shr eax, 24 ");
       
   256 	asm("push esi ");
       
   257 	asm("mov esi, [eax*4+%0]" : : "i" (&SubSchedulerLookupTable));
       
   258 #ifdef _DEBUG
       
   259 	asm("cmp dword ptr [esi+%0], 0" : : "i" _FOFF(TSubScheduler, iKernLockCount));
       
   260 	asm("jg short _dbg1 ");
       
   261 	asm("int 0xff ");
       
   262 	asm("_dbg1: ");
       
   263 #endif
       
   264 	asm("cli ");
       
   265 	asm("dec dword ptr [esi+%0]" : : "i" _FOFF(TSubScheduler, iKernLockCount));
       
   266 	asm("jnz short still_locked ");
       
   267 //	asm("cmp dword ptr [esi]TSubScheduler.iRescheduleNeededFlag, 0	VC6 ignores the "dword ptr"
       
   268 	asm("lea eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iRescheduleNeededFlag));
       
   269 	asm("cmp dword ptr [eax], 0 ");
       
   270 	asm("jz short no_resched ");
       
   271 
       
   272 	asm("mov dword ptr [esi+%0], 1" : : "i" _FOFF(TSubScheduler, iKernLockCount));
       
   273 	asm("push edi ");
       
   274 	asm("push ebp ");
       
   275 	asm("push ebx ");
       
   276 	asm("push gs ");
       
   277 	asm("push fs ");
       
   278 	asm("sti ");
       
   279 
       
   280 	// Reschedule - return with local interrupts disabled, iKernLockCount=0
       
   281 	asm("push 1 ");
       
   282 	asm("call %a0" : : "i" (TScheduler_Reschedule));
       
   283 	asm("add esp, 4 ");
       
   284 
       
   285 	asm("xor eax, eax ");
       
   286 	asm("lock xchg eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iReschedIPIs));
       
   287 	asm("test eax, eax ");
       
   288 	asm("jz short no_resched_ipis_ul ");
       
   289 
       
   290 	asm("unlock_do_resched_ipis: ");
       
   291 	asm("push eax ");
       
   292 	asm("call %a0" : : "i" (&send_resched_ipis));
       
   293 	asm("add esp, 4 ");
       
   294 
       
   295 	asm("no_resched_ipis_ul: ");
       
   296 	asm("pop fs ");
       
   297 	asm("pop gs ");
       
   298 	asm("pop ebx ");
       
   299 	asm("pop ebp ");
       
   300 	asm("pop edi ");
       
   301 
       
   302 	asm("still_locked: ");
       
   303 	asm("sti ");
       
   304 	asm("pop esi ");
       
   305 	asm("ret ");
       
   306 
       
   307 	asm("no_resched: ");
       
   308 	asm("xor eax, eax ");
       
   309 	asm("lock xchg eax, [esi+%0]" : : "i" _FOFF(TSubScheduler, iReschedIPIs));
       
   310 	asm("test eax, eax ");
       
   311 	asm("jz short still_locked ");
       
   312 	asm("push edi ");
       
   313 	asm("push ebp ");
       
   314 	asm("push ebx ");
       
   315 	asm("push gs ");
       
   316 	asm("push fs ");
       
   317 	asm("jmp short unlock_do_resched_ipis ");
       
   318 	}
       
   319 
       
   320 
       
   321 /**	Locks the kernel
       
   322 	Defer IDFCs and preemption
       
   323 
       
   324 	@pre	Thread or IDFC context. Don't call from ISRs.
       
   325  */
       
   326 EXPORT_C __NAKED__ void NKern::Lock()
       
   327 	{
       
   328 	asm("cli");		// stop thread migration between reading APIC ID and subscheduler stuff
       
   329 	asm("mov eax, ds:[%0]" : : "i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   330 	asm("shr eax, 24");
       
   331 	asm("mov ecx, [eax*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   332 	asm("inc dword ptr [ecx+%0]": : "i"_FOFF(TSubScheduler, iKernLockCount));
       
   333 	asm("sti");
       
   334 	asm("ret");
       
   335 	}
       
   336 
       
   337 
       
   338 /**	Locks the kernel and returns a pointer to the current thread
       
   339 	Defer IDFCs and preemption
       
   340 
       
   341 	@pre	Thread or IDFC context. Don't call from ISRs.
       
   342  */
       
   343 EXPORT_C __NAKED__ NThread* NKern::LockC()
       
   344 	{
       
   345 	asm("cli");		// stop thread migration between reading APIC ID and subscheduler stuff
       
   346 	asm("mov eax, ds:[%0]" : : "i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   347 	asm("shr eax, 24");
       
   348 	asm("mov ecx, [eax*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   349 	asm("inc dword ptr [ecx+%0]": : "i"_FOFF(TSubScheduler, iKernLockCount));
       
   350 	asm("mov eax, [ecx+%0]" : : "i"_FOFF(TSubScheduler, iCurrentThread));
       
   351 	asm("sti");
       
   352 	asm("ret");
       
   353 	}
       
   354 
       
   355 
       
   356 /**	Allows IDFCs and rescheduling if they are pending.
       
   357 	If IDFCs or a reschedule are pending and iKernCSLocked is exactly equal to 1
       
   358 	calls the scheduler to process the IDFCs and possibly reschedule.
       
   359 
       
   360 	@return	Nonzero if a reschedule actually occurred, zero if not.
       
   361 	@pre	Thread or IDFC context. Don't call from ISRs.
       
   362  */
       
   363 EXPORT_C __NAKED__ TInt NKern::PreemptionPoint()
       
   364 	{
       
   365 	asm("mov eax, ds:[%0]" : : "i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   366 	asm("shr eax, 24");
       
   367 	asm("mov ecx, [eax*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   368 #ifdef _DEBUG
       
   369 	asm("cmp dword ptr [ecx+%0], 0": : "i"_FOFF(TSubScheduler, iKernLockCount));
       
   370 	asm("jg _dbg1_pp");
       
   371 	asm("int 0xff");
       
   372 	asm("_dbg1_pp:");
       
   373 #endif
       
   374 	asm("cmp dword ptr [ecx+%0], 1": : "i"_FOFF(TSubScheduler, iKernLockCount));
       
   375 	asm("jnz still_locked_pp");
       
   376 //	asm("cmp dword ptr [ecx]TSubScheduler.iRescheduleNeededFlag, 0		VC6 ignores the "dword ptr"
       
   377 	asm("lea eax, [ecx+%0]": : "i"_FOFF(TSubScheduler, iRescheduleNeededFlag));
       
   378 	asm("cmp dword ptr [eax], 0");
       
   379 	asm("jnz do_resched");
       
   380 	asm("cli");
       
   381 	asm("lock xchg eax, [ecx+%0]": : "i"_FOFF(TSubScheduler, iReschedIPIs));
       
   382 	asm("test eax, eax");
       
   383 	asm("jz pp_no_resched_ipis");
       
   384 	asm("push eax");
       
   385 	asm("call %a0": :"i"(&send_resched_ipis));
       
   386 	asm("add esp, 4");
       
   387 	asm("pp_no_resched_ipis:");
       
   388 	asm("sti");
       
   389 
       
   390 	asm("still_locked_pp:");
       
   391 	asm("xor eax, eax");
       
   392 	asm("ret");
       
   393 
       
   394 	asm("do_resched:");
       
   395 	asm("call %a0" : : "i"(NKern_Unlock));
       
   396 	asm("call %a0" : : "i"(NKern_Lock));
       
   397 	asm("mov eax, 1");
       
   398 	asm("ret");
       
   399 	}
       
   400 
       
   401 
       
   402 /** Complete the saving of a thread's context
       
   403 
       
   404 This saves the FPU registers if necessary once we know that we are definitely
       
   405 switching threads.
       
   406 
       
   407 @internalComponent
       
   408 */
       
   409 __NAKED__ void NThread::CompleteContextSave()
       
   410 	{
       
   411 	THISCALL_PROLOG0()
       
   412 	asm("mov edx, [ecx+%0]": : "i"_FOFF(NThreadBase,iSavedSP));	// EDX points to saved state on thread stack
       
   413 	asm("test byte ptr [edx], 8");						// test thread's saved TS flag
       
   414 	asm("jnz no_fpu");									// if set, thread did not use FPU
       
   415 	asm("clts");
       
   416 	asm("fnsave [ecx+%0]": : "i"_FOFF(NThread, iCoprocessorState));	// else thread did use FPU - save its state
       
   417 	asm("or byte ptr [edx], 8");						// set TS flag so thread aborts next time it uses FPU
       
   418 	asm("fwait");
       
   419 
       
   420 	asm("no_fpu:");
       
   421 	THISCALL_EPILOG0()
       
   422 	}
       
   423 
       
   424 
       
   425 /** Check if the kernel is locked the specified number of times.
       
   426 
       
   427 	@param aCount	The number of times the kernel should be locked
       
   428 					If zero, tests if it is locked at all
       
   429 	@return TRUE if the tested condition is true.
       
   430 
       
   431 	@internalTechnology
       
   432 */
       
   433 EXPORT_C __NAKED__ TBool NKern::KernelLocked(TInt /*aCount*/)
       
   434 	{
       
   435 	asm("pushfd");
       
   436 	asm("cli");
       
   437 	asm("mov eax, ds:[%0]" : : "i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   438 	asm("shr eax, 24");
       
   439 	asm("mov eax, [eax*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   440 	asm("mov edx, [eax+%0]": : "i"_FOFF(TSubScheduler, iKernLockCount));
       
   441 	asm("popfd");
       
   442 	asm("cmp edx, 0");
       
   443 	asm("jz not_locked");
       
   444 	asm("mov eax, [esp+4]");
       
   445 	asm("cmp eax, 0");
       
   446 	asm("jz locked");
       
   447 	asm("cmp eax, edx");
       
   448 	asm("jnz not_locked");
       
   449 	asm("locked:");
       
   450 	asm("mov eax, 1");
       
   451 	asm("ret");
       
   452 	asm("not_locked:");
       
   453 	asm("xor eax, eax");
       
   454 	asm("ret");
       
   455 	}
       
   456 
       
   457 
       
   458 // Only call this if thread migration is disabled, i.e.
       
   459 // interrupts disabled, kernel locked or current thread in 'freeze cpu' mode
       
   460 extern "C" __NAKED__ TSubScheduler& SubScheduler()
       
   461 	{
       
   462 	asm("mov eax, ds:[%0]" : : "i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   463 	asm("shr eax, 24");
       
   464 	asm("mov eax, [eax*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   465 	asm("ret");
       
   466 	}
       
   467 
       
   468 /** Returns the NThread control block for the currently scheduled thread.
       
   469 
       
   470     Note that this is the calling thread if called from a thread context, or the
       
   471 	interrupted thread if called from an interrupt context.
       
   472 	
       
   473 	@return A pointer to the NThread for the currently scheduled thread.
       
   474 	
       
   475 	@pre Call in any context.
       
   476 */
       
   477 EXPORT_C __NAKED__ NThread* NKern::CurrentThread()
       
   478 	{
       
   479 	asm("pushfd");
       
   480 	asm("cli");		// stop thread migration between reading APIC ID and thread pointer
       
   481 	asm("mov eax, ds:[%0]" : : "i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   482 	asm("shr eax, 24");
       
   483 	asm("mov eax, [eax*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   484 	asm("cmp eax, 0");
       
   485 	asm("jz done");
       
   486 	asm("test al, 3");
       
   487 	asm("jnz bad_ct");
       
   488 	asm("mov eax, [eax+%0]": : "i"_FOFF(TSubScheduler, iCurrentThread));
       
   489 	asm("done:");
       
   490 	asm("popfd");
       
   491 	asm("ret");
       
   492 	asm("bad_ct:");
       
   493 	asm("popfd");
       
   494 	asm("xor eax, eax");
       
   495 	asm("ret");
       
   496 	}
       
   497 
       
   498 
       
   499 /** Returns the NThread control block for the currently scheduled thread.
       
   500 
       
   501     Note that this is the calling thread if called from a thread context, or the
       
   502 	interrupted thread if called from an interrupt context.
       
   503 	
       
   504 	@return A pointer to the NThread for the currently scheduled thread.
       
   505 	
       
   506 	@pre Call with migration disabled - i.e. from an ISR, IDFC, with interrupts
       
   507 			disabled or with preemption disabled.
       
   508 */
       
   509 extern "C" __NAKED__ NThread* NCurrentThreadL()
       
   510 	{
       
   511 	asm("mov eax, ds:[%0]" : : "i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   512 	asm("shr eax, 24");
       
   513 	asm("mov eax, [eax*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   514 	asm("mov eax, [eax+%0]": : "i"_FOFF(TSubScheduler, iCurrentThread));
       
   515 	asm("ret");
       
   516 	}
       
   517 
       
   518 
       
   519 /** Returns the CPU number of the calling CPU.
       
   520 
       
   521 	@return the CPU number of the calling CPU.
       
   522 	
       
   523 	@pre Call in any context.
       
   524 */
       
   525 EXPORT_C __NAKED__ TInt NKern::CurrentCpu()
       
   526 	{
       
   527 	asm("xor eax, eax");
       
   528 	asm("str ax");
       
   529 	asm("sub al, 0x28");
       
   530 	asm("shr al, 3");
       
   531 	asm("ret");
       
   532 	}
       
   533 
       
   534 
       
   535 /**	Return the current processor context type (thread, IDFC or interrupt)
       
   536 
       
   537 	@return	A value from NKern::TContext enumeration (but never EEscaped)
       
   538 	@pre	Any context
       
   539 
       
   540 	@see	NKern::TContext
       
   541  */
       
   542 EXPORT_C __NAKED__ TInt NKern::CurrentContext()
       
   543 	{
       
   544 	asm("pushfd");
       
   545 	asm("cli");		// stop thread migration between reading APIC ID and subscheduler stuff
       
   546 	asm("mov edx, ds:[%0]": :"i"(X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
       
   547 	asm("xor eax, eax");
       
   548 	asm("shr edx, 24");
       
   549 	asm("mov edx, [edx*4+%0]" : : "i"(&SubSchedulerLookupTable));
       
   550 	asm("cmp edx, eax");
       
   551 	asm("jz bad_cc");
       
   552 	asm("test dl, 3");
       
   553 	asm("jnz bad_cc");
       
   554 	asm("cmp eax, [edx+52+%0]": : "i"_FOFF(TSubScheduler,iExtras)); // i_IrqNestCount
       
   555 	asm("jle irq");
       
   556 	asm("cmp al, [edx+%0]": : "i"_FOFF(TSubScheduler, iInIDFC));
       
   557 	asm("jz thread");
       
   558 	asm("jmp idfc");
       
   559 
       
   560 	asm("bad_cc:");			// no subscheduler yet [initialising] - return EInterrupt
       
   561 	asm("irq:");			// return NKern::EInterrupt [=2]
       
   562 	asm("inc eax");
       
   563 	asm("idfc:");			// return NKern::EIDFC [=1]
       
   564 	asm("inc eax");
       
   565 	asm("thread:");			// return NKern::EThread [=0]
       
   566 	asm("popfd");
       
   567 	asm("ret");
       
   568 	}
       
   569 
       
   570 
       
   571 #ifdef __USE_LOGICAL_DEST_MODE__
       
   572 extern "C" __NAKED__ void __fastcall do_send_resched_ipis(TUint32)
       
   573 	{
       
   574 	asm("shl ecx, 24 ");		// CPUs mask into bits 24-31
       
   575 	asm("jz short sri0 ");		// no CPUs, so nothing to do
       
   576 	asm("pushfd ");
       
   577 	asm("cli ");
       
   578 	asm("mov ds:[%0], ecx" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRH));
       
   579 	asm("mov eax, %0" : : "i" (RESCHED_IPI_VECTOR | 0x4800));
       
   580 	asm("mov ds:[%0], eax" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRL));
       
   581 	asm("popfd ");
       
   582 	asm("sri0: ");
       
   583 	asm("ret ");
       
   584 	}
       
   585 #endif
       
   586 
       
   587 extern "C" __NAKED__ void __fastcall send_ipi(TUint32)
       
   588 	{
       
   589 	asm("pushfd ");
       
   590 	asm("cli ");
       
   591 	asm("mov ds:[%0], ecx" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRH));
       
   592 	asm("mov eax, %0" : : "i" (RESCHED_IPI_VECTOR | 0x4000));
       
   593 	asm("mov ds:[%0], eax" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRL));
       
   594 	asm("popfd ");
       
   595 	asm("ret ");
       
   596 	}
       
   597 
       
   598 // Send a reschedule IPI to the current processor
       
   599 // *** DON'T DO ANY TRACING OR INSTRUMENTATION ***
       
   600 extern "C" __NAKED__ void send_self_resched_ipi()
       
   601 	{
       
   602 	asm("pushfd ");
       
   603 	asm("cli ");
       
   604 	asm("xor ecx, ecx ");
       
   605 	asm("mov ds:[%0], ecx" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRH));
       
   606 	asm("mov eax, %0" : : "i" (RESCHED_IPI_VECTOR | 0x44000));	// destination shorthand = self
       
   607 	asm("mov ds:[%0], eax" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRL));
       
   608 	asm("popfd ");
       
   609 	asm("ret ");
       
   610 	}
       
   611 
       
   612 extern "C" __NAKED__ void send_irq_ipi(TSubScheduler*)
       
   613 	{
       
   614 	asm("mov ecx, [esp+4] ");
       
   615 	asm("pushfd ");
       
   616 	asm("mov edx, [ecx+%0]" : : "i" _FOFF(TSubScheduler, i_APICID));
       
   617 	asm("cli ");
       
   618 	asm("mov ds:[%0], edx" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRH));
       
   619 	asm("mov eax, %0" : : "i" (TRANSFERRED_IRQ_VECTOR | 0x4000));
       
   620 	asm("mov ds:[%0], eax" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ICRL));
       
   621 	asm("popfd ");
       
   622 	asm("ret ");
       
   623 	}
       
   624