Symbian OS Exec Calls | NewLC
Symbian OS Exec Calls
Tutorial posted February 27th, 2006 by girishshetty in
Tips
Introduction
In any typical OS, OS provides “system call” using which user-mode code (user applications) can get services done by kernel. In other words, kernel provides services to user application using a mechanism called system calls. System call makes use of software exception or interrupt (SWI) as a gateway to enter into kernel code. This SWI will also do other operations like CPU mode switching.
As a consequence of a user application making a system call, control will get into kernel code with different privileged CPU mode. It may lock the kernel depending on the severity of the system call. And will start executing kernel code at well-defined entry point for that SWI.
Symbian Exec Calls
In case of Symbian OS, system calls are called as executive calls or exec calls. Symbian supports two types of executive calls: Slow and Fast Exec calls. Nanokernel dispatcher (__ArmVectorSwi) checks a bit (bit 23) of ARM opcode to decide the type of exec call.
Before explaining difference between these two, let me introduce two different locks used by kernel scheduler (defined in TScheduler).
System Lock: It's a nanokernel fast mutex (NFastMutex iLock) used by kernel for protecting address space changes.
Kernel Lock: It's an integer counter (TInt iKernCSLocked) which will be >= 0. This is also known as preemption lock. IDFcs and the reschedule may run immediately following an interrupt only if iKernCSLocked is 0. Else they must be deferred until the count becomes zero.
For every function that issues a exec call, there will be corresponding handler in Exec class, which will be part of User library and handler in ExecHandler class, which will be part of kernel code, called by the nanokernel dispatcher.
User Library code for Exec Call
There is one file “\base\e32\kernel\execs.txt” (this is part of source code, not header. So, it is not exported to \epoc32\include) using which source code for user-side executive calls will be generated. This file is used to generate exec related enumerators, user side and kernel side exec handler codes in below path:
\epoc32\include\exec_enum.h
\epoc32\include\exec_user.h
\epoc32\include\kernel\exec_kernel.h
There will be two different tables with different structure, one for slow exec table and one for fast exec table. We can see set of fast and slow exec calls supported by Symbian defined in the above-mentioned txt file.
Slow and Fast Exec Table
There are two tables as I have mentioned above, for handling and referring slow and fast exec calls. The nano-kernel thread maintains these two tables. They are defined like this is in base\e32\include\kernel\kern_priv.h
GLREF_D const TUint32 EpocFastExecTable[];
GLREF_D const TUint32 EpocSlowExecTable[];
Call to RThread::Create will end up in calling DThread::DoCreate() via exec calls. This function will fill all the information required for the thread like user stack, supervisor stack, priority, time-slice, thread entry function, and also fast and slow exec table in SNThreadCreateInfo structure and calls NKern::ThreadCreate. This function will call NThread::Create. Pointer to Slow and Fast Exec table is maintained by each nano-kernel thread (NThreadBase), which gets this information during the creation of the thread from SNThreadCreateInfo.
Fast Exec Call
When kernel is executing fast exec calls, interrupts will be disabled and kernel will be locked. Because of this, fast exec calls must be very short. Fast exec calls can pass only one 32-bit parameter and may also return 32-bit value. Some of the user-side calls, which will raise fast exec calls, are:
User::SwitchAllocator()
User::SetActiveScheduler()
User::WaitForAnyRequest()
RomRootDirectoryAddress()
The definition of Fast Exec table is as below:
struct SFastExecTable
{
TInt iFastExecCount; // includes implicit function#0
TLinAddr iFunction[1]; // first entry is for call number 1
};
Slow Exec Call
When kernel is executing slow exec calls, it can be preempted either by some other thread or by interrupt. That is, slow exec calls run with interrupts enabled and the kernel unlocked. The kernel dispatcher will perform certain operations before calling actual handler for this slow exec call: like checking some bits in attribute word of the slow exec table and performing some operations like deciding the actual exec call, acquiring the system lock, calling preprocessing handler.
The definition of Fast Exec table is as below:
struct SSlowExecEntry
{
TUint32 iFlags; // information about call
TLinAddr iFunction; // address of function to be called
};
struct SSlowExecTable
{
TInt iSlowExecCount;
TLinAddr iInvalidExecHandler; // used if call number invalid
TLinAddr iPreprocessHandler; // used for handle lookups
SSlowExecEntry iEntries[1]; // first entry is for call number 0
};
Slow execs can have up to 4 direct 32-bit arguments and can return one 32-bit value. Apart from this, slow exec calls can also copy as many as 8 additional 32-bit values from user space to the current thread's supervisor stack. In this case, one of the 4 direct arguments has to point to the additional arguments. So, we can pass 11 arguments in case of slow exec calls.
Flow of Exec Call
I will explain the flow of an exec call by taking one SLOW EXEC call. Programmer will use below statement for setting a new exception handler for the current thread:
TExceptionHandler exceptionHandler = MyExceptionHandler;
User::SetExceptionHandler(exceptionHandler);
Implementation of User::SetExceptionHandler is as below:
TInt User::SetExceptionHandler(TExceptionHandler aHandler,TUint32 aMask)
{
return(Exec::SetExceptionHandler(KCurrentThreadHandle, aHandler, aMask));
}
KHandleNoClose is a flag used by the kernel to mark a handle as not being closable. KCurrentThreadHandle is a flag used by the Kernel to indicate the current thread.
const TInt KHandleNoClose = 0x00008000;
const TInt KCurrentThreadHandle = 0xffff0001KHandleNoClose;
In \base\e32\kernel\execs.txt there is one entry for SetExceptionHandler exec call as below:
slow {
name = SetExceptionHandler
return = TInt
arg2 = TExceptionHandler
arg3 = TUint32
handle = thread
}
All fields in the above structure defined in the text file are self-explanatory. Description of name is as below:
Field Description For SetExceptionHandler
Name Specifies generic name for the executive call SetExceptionHandler
Enumerator EExec
User function Exec::
Kernel function ExecHandler::
arg2 and arg3 specify the type of argument 2 and 3.
handle specify that the first argument is a handle which should be translated into a pointer to a DObject-derived class on the way in to the executive call.
return set the return type. If this keyword is not present, defaults to void.
Using the above definition, below function definition will be generated in \epoc32\include\exec_user.h.
__EXECDECL__ TInt Exec::SetExceptionHandler(TInt, TExceptionHandler, TUint32 )
{
SLOW_EXEC3 (EExecSetExceptionHandler);
}
Executive call MACRO is defined in e32\include\u32exec.h as below:
#elif defined(__CPU_ARM)
// Executive call macros for ARM
#define EXECUTIVE_FAST 0x00800000
#define EXECUTIVE_SLOW 0x00000000
#define __DISPATCH(n) \
asm("mov ip, lr "); \
asm("swi %a0" : : "i" (n));
#define FAST_EXEC0(n) __DISPATCH((n)EXECUTIVE_FAST)
#define FAST_EXEC1(n) __DISPATCH((n)EXECUTIVE_FAST)
#define SLOW_EXEC0(n) __DISPATCH((n)EXECUTIVE_SLOW)
#define SLOW_EXEC1(n) __DISPATCH((n)EXECUTIVE_SLOW)
#define SLOW_EXEC2(n) __DISPATCH((n)EXECUTIVE_SLOW)
#define SLOW_EXEC3(n) __DISPATCH((n)EXECUTIVE_SLOW)
#define SLOW_EXEC4(n) __DISPATCH((n)EXECUTIVE_SLOW)
So, Exec::SetExceptionHandler will make one SWI call as below:
SWI EExecSetExceptionHandler
After this, Nanokernel dispatcher (__ArmVectorSwi) will handle the exec call. I will explain about SWI later.
__ArmVectorSwi is defined in \base\e32\nkern\arm\vectors.cia, which is fully ARM assembly code. It makes use of fast and slow executable tables of the current thread. The main operations done by the dispatcher is as below:
Check bit 23 of the ARM opcode. If it is set, then it is a fast exec call.
The dispatcher will switch interrupts off.
Call the Kernel function for this exec call using the fast exec table and the exec number.
If bit 23 is not set, then it is slow exec call.
Now dispatcher checks the bit 31 of attribute word of the slow exec table. If this bit is set, then it will lock the system by using system lock.
Dispatcher also checks other bit in the attribute word to check whether it should call SOS preprocessing hander, PreprocessHandler, which is part of SSlowExecTable (iPreprocessHandler).
Before calling this PreprocessHandler, dispatcher should claim system lock
Release the system lock depending on whether the bits in the attribute word are set or not.
Call the Kernel function for this exec call using slow exec table and the exec number.
Typically PreprocessHandler is used for:
Accessing the arguments passed to the executive function and also handle look up.
Accessing the attribute flags of the executive call.
In case of User::SetExceptionHandler, it will call below function:
TInt ExecHandler::SetExceptionHandler(DThread* aThread, TExceptionHandler aHandler, TUint32 aMask)
{
//Some code, like checking whether its the current thread
aThread->iExceptionHandler = aHandler;
aThread->iExceptionMask = aMask;
}
Above code is very straightforward: it sets the thread's exception handler and exception mask with the one given by the user.
For Emulator, Exec call Dispatcher is defined by Dispatch() function which is defined in \base\e32\nkern\win32\vectors.cpp file. This does not have any assembly code and we can understand it very easily and can get the clear picture of the difference in handling fast and slow Exec calls.
ARM SWI Instruction
SWI is an ARM instruction used for accessing OS routines or a device specific routine. When an SWI instruction is issued, it will perform below operations:
Change the CPU mode to supervisor mode
Set the PC to execute the next instruction at address &08.
ARM has 16 general-purpose resisters R0-R15. R13 and R14 are banked across all modes except system mode (Symbian OS does not make use of this mode), as system mode is the only privileged mode that can't be entered by exception. So all other modes (User, Supervisor, Undefined, Interrupt, Fast Interrupt) has it's own R13 and R14. These registers hold the stack pointer and the return address from function call.
When the processor switches the mode to supervisor mode, it will switch registers R13 and R14 with R13_svc and R14_svc. While entering supervisor mode, R14_svc holds the return address (address after the SWI instruction). Now instruction at address &08 will be executed which will jump to another address, which is the address of real SWI handler.
Till this point none of the parameter (like SWI number) is sent to SWI handler nor extracted by the SWI handler. Now the SWI handler does below operations:
Store R0-R12 in stack.
As R14 holds the return address, subtract 4 from that to get address SWI instruction.
Use the SWI instruction for getting SWI number by clearing bits 31-24 (which represents Opcode SWI).
Symbian OS uses bit 23 for finding type of SWI (FAST or SLOW EXEC)
Now handle the SWI using the SWI number by getting address of the routine/handler to be executed using FAST or SLOW EXEC table. Handler may make use of R13, which holds the stack address for getting all other parameters it requires.
Restore R0-R12 registers
Jump back to the return address stored in R14
Reference
Symbian OS Internals By Jane Sales
VLSI ARM Instruction Set.
Labels: Symbian OS Exec Calls

0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home