Important Changes between EKA1 and Kernel Architecture 2

This document is a summary of the important changes made between EKA2 and the Kernel Architecture 2.

Real time kernel

One of the main design goals for EKA2 is to provide real-time guarantees for a subset of kernel operations. Real time performance is one that guarantees a response time; it does not mean that the response time is necessarily fast, it means that the time taken is bounded, i.e. it is deterministic or predictable.

Very few changes to user side code are needed; the aim has been to maintain compatibility at the EUSER level as far as possible. Device drivers, however, will need some changes at the EUSER level, and more so on the kernel side. See the Device Driver Guide for details.

Multi-threaded kernel

In EKA2, the kernel is multi-threaded, i.e. any number of threads can now run on the kernel side. This means that a lower priority kernel thread can be preempted by a higher priority thread.

Each user thread has two stacks: a user stack as in EKA1, and an additional kernel stack allowing it to be preempted virtually anywhere while executing kernel-side code. This adds approximately 4k to each thread's memory requirements.

This change has the effect of simplifying device driver design. It also means that most kernel-side data structures need to be protected against concurrent accesses to prevent race conditions. Several synchronisation primitives are available to achieve this.

A deadlock prevention strategy must be followed by kernel-side code.

Memory model

The EKA2 core kernel is MMU-agnostic. All MMU code is isolated into a “memory model”, which is linked at build time against the kernel. EKA2 provides three different memory models:

  • Moving memory model: This is functionally equivalent to EKA1, and is intended for ARMv4 and ARMv5 CPUs. A single page directory is used and page directory entries are moved between the home and run sections at address space switch time.

  • Multiple memory model: This is a new model for CPUs based on ARMv6 architecture. There is one page directory per process

  • Single memory model: This model can be used for CPUs that have no MMU, or to simplify of porting the core kernel to a new MMU-aware CPU. In the latter case, the MMU is enabled but there is a single address space in the system. This allows incremental porting to a new CPU, by first porting the kernel and then the MMU.

The memory map (e.g. the base address of the ROM image) has changed. Therefore any code relying on the EKA1 memory map will need changing. The impact should be minimal, as code outside the kernel should not be making assumptions about memory layout.

Bootstrap

The C/C++ generic bootstrap used in EKA1 has been replaced with an assembler bootstrap, which is split into a generic layer usable on any ARM device and a device-specific layer.

Chunks per process

The number of chunks per process is limited to 16 in the moving memory model. This ensures that switching between address spaces operates in bounded time.

Releasing blocked threads

Threads that are blocked on semaphores and mutexes are now released in priority order, not in FIFO order.

Stricter checking on the use of invalid handles

Kernel is no longer dependent on EUSER.DLL

The kernel no longer has any dependency on EUSER.DLL. Only user side code can link against EUSER.DLL.

There are three main reasons for this:

  • It allows more robust on-target application debugging, as it is no longer possible to crash the kernel by putting an application breakpoint in shared code.

  • It reduces the size of the minimum system configuration.

  • It allows the kernel to be built using a different compiler to that used to build the user side.

This has important consequences for kernel side code. Any code that runs on the kernel side, including device drivers, cannot use classes, functionality, or behaviour that is implemented by EUSER.DLL. Although not a complete replacement for this lost functionality, the Nanokernel and the Symbian platform kernel do provide a set of utility functions such as memcpy(), memset(), and memmove().

Kernel-side code no longer leaves

Kernel-side code can no longer use the leave/cleanup stack mechanism. If appropriate, functions must now provide a return code, and callers need to check return values.

Note that this mechanism is still available on the user side.

No kernel-Side support for unicode

The EKA2 kernel only provides support for ASCII strings.

All kernel objects such as processes, threads, etc. have ASCII names. The kernel does use a few unicode strings but it treats them as opaque types. As kernel object names are limited to ASCII, user-side code must use only the ASCII subset of unicode when creating such objects.

Kernel-side code uses DBase instead of CBase

Classes that are instantiated on the kernel heap must now use DBase instead of CBase. CBase can only be used by user-side code.

DBase is defined in ...\e32\include\kernel\klib.h and exported to \epoc32\include\kernel\klib.h

Asynchronous deletion

The DBase class provides the behaviour that allows objects to be deleted asynchronously. This is particularly useful when memory deallocation needs to be done by time critical code.

Deletion is done by placing the object onto a queue and then triggering a DFC, which runs in the context of the supervisor thread.

Accessing user side memory directly not permitted

Kernel side code must not access user side memory directly. Use the functions specifically provided for the purpose:

kumemget(), kumemget32(), kumemput(), kumemput32(), kumemset(), umemget(), umemget32(), umemput(), umemput32(), umemset(), Kern::ThreadDesRead(), Kern::ThreadDesWrite(), Kern::ThreadRawRead(), Kern::ThreadRawWrite(), Kern::KUDesGet(), and Kern::KUDesPut()

See also accessing User Memory in Porting a device driver from EKA1 to EKA2

Kernel side code uses DObject instead of CObject

Kernel side objects that are reference counting, must now derive from DObject instead of CObject.

CObjectCon also has a kernel-side equivalent DObjectCon.

Device Driver Model

The device driver model has changed. See the Device Driver Guide for the detail.

Base porting

EKA1, allows only a single hardware interfacing architecture. The lowest layer of the kernel contains the ASSP-specific code and a separate variant DLL contains variant-specific code called by the kernel.

In contrast, the EKA2 core kernel, ekern.exe, is agnostic as regards the hardware interfacing architecture. It does not contain any peripheral-related code, and and only requires a variant DLL. The amount of functionality to be provided by this DLL is much smaller than in EKA1.

In general, EKA2 base ports use one of the following two architectures:

  • ASSP+Variant: where the Variant is split into an ASSP layer (a DLL) and a Variant layer (a DLL). The first DLL contains all the ASSP-specific / device-agnostic code, and the second DLL contains the device-specific code. This allows code sharing between different devices using the same ASSP/ASIC. The interface between the ASSP and variant DLLs is private.

  • Variant alone: Both the ASSP and the device specific code is included in a single Variant DLL. This saves the effort of partitioning the code if only one device is planned.

In addition, peripheral-related features that are part of the kernel in EKA1 (e.g. DMA framework) are now implemented as kernel extensions.

Emulator

A number of changes in the emulator architecture have a system-wide impact.

Process emulation

The emulator supports multiple Symbian platform processes. This removes the need for WINS-specific code in various places, notably in server start-up.

The emulator still runs as a single WIN32 process, so a Symbian platform process can still accidentally write into the address space of another Symbian platform process.

Thread scheduling

The emulator uses the same scheduling algorithm as Symbian platform running on hardware. This is in contrast with EKA1 that leaves Symbian platform thread scheduling to the Windows kernel leading to discrepancies in behaviour between the emulator and hardware.

Calling WIN32 APIs from Symbian platform code

Because the emulator now uses the same scheduling algorithm as native Symbian platform, the Symbian platform kernel may have to suspend a thread while it is executing some arbitrary code inside Windows. If the thread holds one or more (Windows) locks when it is suspended and another thread tries to acquire the same lock(s), a deadlock occurs. To prevent this, Symbian platform code that calls into the WIN32 API must use new Symbian platform APIs, Emulator::Lock()/Unlock() or Emulator::Escape()/Reenter(). The impact should be minimal because most of the code interacting with Windows is in the Base subsystem.

Kernel-side messages

Because the kernel is multi-threaded in EKA2, Kernel-side messages offer a means of communication between Symbian platform threads executing kernel-side code. Typically, they are used by device drivers to communicate between a client thread, usually a user thread, and a kernel thread running the actual device driver code.

Passing handles & data at process creation

In EKA2, handles and binary data (in the form of descriptors, or integer values) can be passed to a process at process creation time. Up to 16 separate pieces of information can be passed to a process on creation.

See Passing handles & data at process creation for more information.

Power management

The EKA1 power management framework has been replaced in EKA2 with a new framework that aims at fixing known issues with the EKA1 one. The main design goal for this new framework is increased flexibility, i.e.:

  • providing support for a wide variety of power management hardware

  • separating mechanism and policy to allow different power management strategies.

As an example of increased flexibility, the set of supported power states is extensible by licensees.

The changes mean that, on the user side, there is no binary or source compatibility. The impacted components are app-framework/UIKON and the GUI LAF (e.g. techview/toolkit).

On the kernel-side, there is no binary compatibility. Old-style power handlers can still be built but if one or more of them are active, the system cannot switch to standby mode.

In EKA2, the power model DLL no longer exists. EKA2 replaces the EKA1 user-side framework with a new one based on the concept of domain. A domain is a set of processes that share the same power management characteristics.

User-side changes

A new user-side domain server manages all domains in the system and centralises interactions with the kernel. The main requests supported by this server are:

  • Transition all processes in a given domain to a new power state.

  • Transition all domains to a new power state.

The set of all domains forms a tree. A request to transition a domain D to a new power state is cascaded down to its children first. D is then transitioned after all its children have themselves transitioned to the new state.

The domain server is policy-agnostic. It links against a customisable DLL that provides the policy part. This DLL notably specifies the structure of the domain tree. Usage of the domain framework is optional. Licensees are free to replace it with another user-side scheme or to implement all their power management policy kernel-side.

Kernel-side changes

The new kernel-side framework breaks down into the following items:

  • A power manager, embedded in the core kernel, which implements the power management executive calls and manages the other kernel-side entities

  • A set of power handlers implemented within device drivers. When a power transition is requested, the power manager dispatches it to every registered power handler.

  • A power controller implemented in a kernel extension. It manages the different sleep modes supported by the CPU.

The power handlers and power controller are device-specific. A specific implementation could specify a private interface between these components to extend the generic framework. For example power handlers could notify the power controller of state changes so the latter can select the most appropriate sleep mode.

Support for alternative heap allocators

User mode threads and processes can now instantiate their own concrete heap allocator classes for use as the default allocator rather than be required to use the Symbian platform supplied RHeap class.

This has been done by defining the new abstract class RAllocator. RHeap, now derives from this new class.

The new class allows threads and processes to replace the standard first-fit heap allocator class to improve allocation performance or solve memory fragmentation problems.

The change has been made to minimise binary compatibilty and source compatibility impact on existing software. The following standard allocation functions are completely compatible:

The following functions are compatible assuming that the thread has not replaced the heap allocator with a different object:

The following functions have been added to support software that wishes to replace the heap allocator:

RThread::GetRamSizes() is now deprecated – the heap size is always reported as zero, and the stack size should now be extracted using the new RThread::StackInfo() API.

Software that is dependent on the layout of the RHeap internal data or virtual function table may no longer work after this change. Such software is likely to be rare or limited to test code. There is no legitimate dependency on this information except where 3rd party software has derived a class from RHeap, but this is considered to be very unlikely for anything other than test purposes.

The default allocator object stored for each heap is no longer a RHeap, but a RAllocator. This means that it is possible that User::Heap() does not actually return a reference to a RHeap object, possibly resulting in broken software. However, as the default class used for allocation is still RHeap, then there is no additional impact unless a thread or process explicitly instantiates an alternative allocator class. In the latter case, we would expect that the code using the allocator to also be updated to use the alternative class.

The allocator is created by the owning thread when it first runs, which means that software that depends on the heap existing before the thread is resumed will no longer work. In particular calling RThread::Heap() before the thread runs and creates the heap will return NULL. This was a real pattern found in several places in Symbian platform code to work around the lack of processes in the EKA1 emulator, where a ‘command line’ is inserted into the created thread by allocating a cell on the new thread’s heap. This is not an issue for EKA2 as it supports processes in the emulator and such code should just be removed when adapting it for the EKA2 emulator.