// Copyright (c) 2007-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\kernel\x86\ckernel.cia
//
//
#include <x86_mem.h>
#ifdef __SMP__
#include <apic.h>
#endif
/********************************************
* Thread
********************************************/
const TLinAddr Kern_Exit_Addr = (TLinAddr)&Kern::Exit;
const TInt OFFSET_DThread_iType = _FOFF(DThread,iThreadType);
const TInt OFFSET_DThread_iFlags = _FOFF(DThread,iFlags);
const TInt OFFSET_DThread_iUserStackSize = _FOFF(DThread,iUserStackSize);
const TInt OFFSET_DThread_iNThread = _FOFF(DThread,iNThread);
const TInt OFFSET_DThread_iOwningProcess = _FOFF(DThread,iOwningProcess);
const TInt CONST_THREAD_RUNNING = DThread::EUserThreadRunning;
__NAKED__ void DThread::EpocThreadFunction(TAny*)
//
// Called when an EPOC thread starts running
//
{
#ifdef __SMP__
asm("mov ebx, ebp ");
asm("mov ebp, [esp+4] ");
asm("cli "); // so we don't migrate between reading APIC ID and thread pointer
asm("mov eax, ds:[%0]" : : "i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
asm("shr eax, 24 ");
asm("mov eax, [eax*4+%0]" : : "i" (&SubSchedulerLookupTable));
asm("mov edx, [eax+%0]" : : "i" _FOFF(TSubScheduler,iCurrentThread));
asm("sti ");
#else
asm("mov ebp, [esp+4] ");
asm("mov edx, [%a0]" : : "i" (&TheScheduler.iCurrentThread));
#endif
asm("sub edx, %0" : : "i" (OFFSET_DThread_iNThread));
asm("mov eax, %0" : : "i" (OFFSET_DThread_iType));
asm("lea eax, [edx+eax] ");
asm("cmp byte ptr [eax], 3 "); // EThreadUser
asm("je short epoc_user_thread ");
asm("mov eax, [ebp+%0]" : : "i" _FOFF(SThreadCreateInfo,iFunction));
asm("mov ecx, [ebp+%0]" : : "i" _FOFF(SThreadCreateInfo,iPtr));
#ifdef __SMP__
asm("mov esp, ebx ");
#else
asm("mov esp, ebp ");
asm("add esp, [ebp+%0]" : : "i" _FOFF(SThreadCreateInfo,iTotalSize));
#endif
asm("push ecx ");
asm("call eax ");
asm("push eax ");
asm("mov ecx, %0" : : "i" (Kern_Exit_Addr));
asm("call ecx ");
asm("epoc_user_thread: ");
asm("mov ecx, %0" : : "i" (CONST_THREAD_RUNNING));
asm("mov [edx+%0], ecx" : : "i" _FOFF(DThread,iUserThreadState));
asm("mov edi, [edx+%0]" : : "i" _FOFF(DThread,iUserStackRunAddress));
asm("mov ecx, %0" : : "i" (OFFSET_DThread_iUserStackSize));
asm("mov ecx, [edx+ecx] ");
asm("and cl, 0x0f0 ");
asm("shr ecx, 2 ");
asm("mov ax, %0" : : "i" (KRing3DS));
asm("mov gs, ax ");
asm("mov es, ax ");
asm("mov eax, 0x29292929 ");
asm("cld ");
asm("rep stosd "); // after this edi points to address after user stack
asm("mov esi, ebp ");
asm("add esi, [ebp+%0]" : : "i" _FOFF(SThreadCreateInfo,iTotalSize));
asm("copy_create_info: ");
asm("mov eax, [esi-4] ");
asm("sub esi, 4 ");
asm("mov gs:[edi-4], eax ");
asm("sub edi, 4 ");
asm("cmp esi, ebp ");
asm("ja copy_create_info ");
asm("mov esp, ebp ");
asm("add esp, [ebp+%0]" : : "i" _FOFF(SThreadCreateInfo,iTotalSize)); // restore supervisor stack balance
asm("mov ebx, %0" : : "i" (OFFSET_DThread_iFlags));
asm("xor eax, eax ");
asm("mov esi, %0" : : "i" (OFFSET_DThread_iOwningProcess));
asm("mov ebx, [edx+ebx] "); // ebx = thread flags
asm("mov ax, %0" : : "i" (KRing3DS));
asm("mov esi, [edx+esi] "); // esi -> owning process
asm("push eax "); // SS in user mode
asm("push edi "); // ESP in user mode
asm("mov eax, %0" : :"i" (0x41202)); // EAX = EFLAGS in user mode {AC=1, IOPL=1, IF=1, rest 0}
asm("push eax ");
asm("xor eax, eax ");
asm("mov edx, [esi+%0]" : : "i" _FOFF(DProcess,iReentryPoint));
asm("mov esi, [esi+%0]" : : "i" _FOFF(DProcess,iCodeSeg)); // esi->process code seg
asm("mov ax, %0" : : "i" (KRing3CS));
asm("push eax "); // CS in user mode
asm("and ebx, %0" : : "i" (KThreadFlagOriginal)); // test original thread flag
asm("setz bl "); // BL=0 if first thread in process, 1 otherwise
asm("jnz not_first ");
asm("mov edx, [esi+%0]" : : "i" _FOFF(DCodeSeg,iEntryPtVeneer)); // EIP in user mode = process entry point
asm("not_first: ");
asm("push edx "); // EIP in user mode = process entry point or reentry point
asm("movzx ebx, bl "); // EBX=0 if first thread in process, 1 otherwise
asm("mov ax, %0" : : "i" (KRing3DS));
asm("mov ds, ax ");
asm("mov es, ax ");
asm("iretd "); // jump to process entry point in user mode
// ebx=entry reason, esp->thread creation info
}
#ifdef __GCC32__
#define PUSH(x) asm("mov eax, [ecx+%0]" : : "i" _FOFF(TX86ExcInfo,x)); asm("sub edi, 4 "); asm("mov gs:[edi], eax ");
#else
#define PUSH(x) __asm mov eax, [ecx]TX86ExcInfo.x __asm sub edi, 4 __asm mov gs:[edi], eax;
#endif
__NAKED__ void __fastcall PushExcInfoOnUserStack(TX86ExcInfo*, TInt)
{
// Save registers on user stack (low to high addr)
// ExcType, iExcId, iExcErrorCode, iFaultAddress, iEax, iEcx, iEdx, iEbx, iEsp, iEbp, iEsi, iEdi,
// iSs, iDs, iEs, iFs, iGs, iEflags, iEip, iCs
asm("push edi ");
asm("mov edi, [ecx+%0]" : : "i" _FOFF(TX86ExcInfo,iEsp3));
PUSH(iCs);
PUSH(iEip);
PUSH(iEflags);
PUSH(iGs);
PUSH(iFs);
PUSH(iEs);
PUSH(iDs);
PUSH(iSs3);
PUSH(iEdi);
PUSH(iEsi);
PUSH(iEbp);
PUSH(iEsp3);
PUSH(iEbx);
PUSH(iEdx);
PUSH(iEcx);
PUSH(iEax);
PUSH(iFaultAddress);
PUSH(iExcErrorCode);
PUSH(iExcId);
asm("sub edi, 4 ");
asm("mov gs:[edi], edx ");
asm("mov [ecx+%0], edi" : : "i" _FOFF(TX86ExcInfo,iEsp3));
asm("pop edi ");
asm("ret ");
}
extern "C" {
__NAKED__ void TExcTrap_Trap()
{
// ecx = 'this' TExcTrap*
// returns eax = current thread
#ifdef __SMP__
asm("cli "); // so we don't migrate between reading APIC ID and thread pointer
asm("mov eax, ds:[%0]" : :"i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
asm("shr eax, 24 ");
asm("mov eax, [eax*4+%0]" : :"i" (&SubSchedulerLookupTable));
asm("mov eax, [eax+%0]" : :"i" _FOFF(TSubScheduler,iCurrentThread));
asm("sti ");
#else
asm("mov eax, [%a0]" : : "i" (&TheScheduler.iCurrentThread));
#endif
asm("mov edx, 0");
asm("lea edx, [edx+%0]": : "i"_FOFF(DThread,iNThread));
asm("sub eax, edx");
asm("mov [ecx+%0], eax": : "i"_FOFF(TExcTrap,iThread));
asm("mov [eax+%0], ecx": : "i"_FOFF(DThread,iExcTrap));
asm("mov [ecx+4], ebx");
asm("mov [ecx+8], ebp");
asm("mov [ecx+16], esi");
asm("mov [ecx+20], edi");
asm("mov [ecx+24], ds");
asm("mov [ecx+28], es");
asm("mov [ecx+32], fs");
asm("mov [ecx+36], gs");
asm("ret");
}
}
extern "C" void DefaultExcTrapHandler(TExcTrap* xt, DThread*, TAny*);
extern "C" void IpcExcHandler(TExcTrap* xt, DThread*, TAny*);
#ifdef _DEBUG
extern "C" void __FaultIpcClientNotNull();
#endif
__NAKED__ TBool TIpcExcTrap::IsTIpcExcTrap()
{
THISCALL_PROLOG0()
#ifdef __GCC32__
asm("mov eax, %0": : "i"(&IpcExcHandler));
#else
__asm lea eax, IpcExcHandler
#endif
asm("cmp eax, [ecx+%0]": : "i"_FOFF(TExcTrap,iHandler));
asm("mov eax, 0");
asm("sete al");
THISCALL_EPILOG0();
}
__NAKED__ TInt TIpcExcTrap::Trap(DThread*)
{
THISCALL_PROLOG1();
// ecx=this, [esp]=return address, [esp+4]=aThread
#ifdef __GCC32__
asm("mov eax, %0": : "i"(&IpcExcHandler));
#else
__asm lea eax, IpcExcHandler
#endif
asm("mov [ecx+%0], eax": : "i"_FOFF(TExcTrap,iHandler));
asm("mov eax, [esp]");
asm("mov [ecx+0], eax");
asm("mov eax, esp");
#ifndef __GCC32__ // MSVC __thiscall is callee-cleanup, whereas GCC isnt, and we
asm("add eax, 4"); // correct the saved ESP *before* the ret from TExcTrap::Exception
#endif
asm("mov [ecx+12], eax");
asm("call %a0": :"i"(&TExcTrap_Trap));
#ifdef _DEBUG
asm("mov edx, [eax+%0]": : "i"_FOFF(DThread,iIpcClient));
asm("cmp edx, 0");
asm("je ipcclientnull ");
asm("jmp %a0": :"i"(&__FaultIpcClientNotNull));
asm("ipcclientnull: ");
#endif
asm("mov edx, [esp+4]");
asm("mov [eax+%0], edx": : "i"_FOFF(DThread,iIpcClient));
asm("xor eax, eax");
THISCALL_EPILOG1();
}
EXPORT_C __NAKED__ TInt TExcTrap::Trap()
{
THISCALL_PROLOG0()
// ecx=this, [esp]=return address
#ifdef __GCC32__
asm("mov eax, %0" : : "i" (&DefaultExcTrapHandler));
asm("mov [ecx+%0], eax" : : "i" _FOFF(TExcTrap,iHandler));
#else
__asm lea eax, DefaultExcTrapHandler
__asm mov [ecx]this.iHandler, eax
#endif
asm("mov eax, [esp]"); // eax = retaddr
asm("mov [ecx+0], eax"); // iState[0] = retaddr
asm("mov eax, esp");
asm("mov [ecx+12], eax");
asm("call %a0": :"i"(&TExcTrap_Trap));
asm("xor eax, eax");
THISCALL_EPILOG0();
}
EXPORT_C __NAKED__ TInt TExcTrap::Trap(TExcTrapHandler /*aHandler*/)
{
THISCALL_PROLOG1()
// ecx=this, [esp]=return address, [esp+4]=aHandler
#ifdef __GCC32__
asm("mov eax, [esp+4] ");
asm("mov [ecx+%0], eax" : : "i" _FOFF(TExcTrap,iHandler));
#else
asm("mov eax, [esp+4] ");
__asm mov [ecx]this.iHandler, eax
#endif
asm("mov eax, [esp] ");
asm("mov [ecx+0], eax ");
asm("mov eax, esp ");
#ifndef __GCC32__ // MSVC __thiscall is callee-cleanup, whereas GCC isnt, and we
asm("add eax, 4"); // correct the saved ESP *before* the ret from TExcTrap::Exception
#endif
asm("mov [ecx+12], eax");
asm("call %a0": :"i"(&TExcTrap_Trap));
asm("xor eax, eax");
THISCALL_EPILOG1();
}
EXPORT_C __NAKED__ void TExcTrap::Exception(TInt /*aResult*/)
{
THISCALL_PROLOG1()
// On entry ecx=this, [esp]=return address (unused), [esp+4]=aResult
// Need ret 4 since saved esp leaves parameter to TExcTrap::Trap on the stack
#ifdef __GCC32__
asm("mov edx, [ecx+%0]" : : "i" _FOFF(TExcTrap,iThread));
#else
__asm mov edx, [ecx]this.iThread
#endif
asm("xor eax, eax ");
asm("mov [edx+%0], eax" : : "i" _FOFF(DThread,iExcTrap));
asm("mov [edx+%0], eax" : : "i" _FOFF(DThread,iPagingExcTrap));
asm("mov eax, [esp+4] ");
asm("mov edx, [ecx+0] ");
asm("mov ebx, [ecx+4] ");
asm("mov ebp, [ecx+8] ");
asm("mov esp, [ecx+12] ");
asm("mov esi, [ecx+16] ");
asm("mov edi, [ecx+20] ");
asm("mov ds, [ecx+24] ");
asm("mov es, [ecx+28] ");
asm("mov fs, [ecx+32] ");
asm("mov gs, [ecx+36] ");
asm("mov [esp],edx ");
asm("ret "); // behave as if returning from TExcTrap::Trap [with ESP already cleaned up for MSVC]
}
extern "C" {
__NAKED__ void TPagingExcTrap_Trap()
{
// ecx = 'this' TPagingExcTrap*
#ifdef __SMP__
asm("cli "); // so we don't migrate between reading APIC ID and thread pointer
asm("mov eax, ds:[%0]" : :"i" (X86_LOCAL_APIC_BASE + X86_LOCAL_APIC_OFFSET_ID));
asm("shr eax, 24 ");
asm("mov eax, [eax*4+%0]" : :"i" (&SubSchedulerLookupTable));
asm("mov eax, [eax+%0]" : :"i" _FOFF(TSubScheduler,iCurrentThread));
asm("sti ");
#else
asm("mov eax, [%a0]" : : "i" (&TheScheduler.iCurrentThread));
#endif
asm("mov edx, 0 ");
asm("lea edx, [edx+%0]" : : "i" _FOFF(DThread,iNThread));
asm("sub eax, edx ");
asm("mov [ecx+%0], eax" : : "i" _FOFF(TPagingExcTrap,iThread));
asm("mov [eax+%0], ecx" : : "i" _FOFF(DThread,iPagingExcTrap));
asm("mov [ecx+4], ebx ");
asm("mov [ecx+8], ebp ");
asm("mov [ecx+16], esi ");
asm("mov [ecx+20], edi ");
asm("mov [ecx+24], ds ");
asm("mov [ecx+28], es ");
asm("mov [ecx+32], fs ");
asm("mov [ecx+36], gs ");
asm("xor eax, eax ");
asm("ret ");
}
}
__NAKED__ TInt TPagingExcTrap::Trap()
{
THISCALL_PROLOG0();
// ecx=this, [esp]=return address
asm("mov eax, [esp] "); // eax = retaddr
asm("mov [ecx+0], eax "); // iState[0] = retaddr
asm("mov eax, esp ");
asm("mov [ecx+12], eax ");
asm("call %a0" : :"i" (&TPagingExcTrap_Trap));
THISCALL_EPILOG0();
}
__NAKED__ void TPagingExcTrap::Exception(TInt /*aResult*/)
{
THISCALL_PROLOG1();
// On entry ecx=this, [esp]=return address (unused), [esp+4]=aResult
// Need ret 4 since saved esp leaves parameter to TPagingExcTrap::Trap on the stack
#ifdef __GCC32__
asm("mov edx, [ecx+%0]" : : "i" _FOFF(TPagingExcTrap,iThread));
#else
__asm mov edx, [ecx]this.iThread
#endif
asm("xor eax, eax ");
asm("mov [edx+%0], eax" : : "i" _FOFF(DThread,iPagingExcTrap));
asm("mov eax, [esp+4] ");
asm("mov edx, [ecx+0] ");
asm("mov ebx, [ecx+4] ");
asm("mov ebp, [ecx+8] ");
asm("mov esp, [ecx+12] ");
asm("mov esi, [ecx+16] ");
asm("mov edi, [ecx+20] ");
asm("mov ds, [ecx+24] ");
asm("mov es, [ecx+28] ");
asm("mov fs, [ecx+32] ");
asm("mov gs, [ecx+36] ");
asm("mov [esp],edx ");
asm("ret "); // behave as if returning from TPagingExcTrap::Trap [with ESP already cleaned up for MSVC]
}