Call Gates

There are many kernel functions that drivers may call, and these need to work in rings 1/2 just like they do in ring 0. Since the drivers can no longer call these functions directly (since they are in the ring 0 portion of the address space), a wrapper must exist for calling these. The solution is to use x86 call gates. A call gate is a protected mechanism for calling higher privilege level code. It will also copy parameters from the driver's stack to the kernel stack as needed.

In the Ring Cycle, the kenrel sets up several call gates in the GDT for drivers to use. In order to save space, each call gate copies a specified number of parameters and then uses a jump table to call the appropriate C function. Therefore there is 1 call gate for all the functions that take 1 parameter, 1 call gate for those that take 3 parameters, etc. The call gates' descriptors have their DPL set to 2 so user programs cannot call these services.

Since drivers need not be rewritten, how do they call these services? The DAPI takes care of this. The DAPI (Driver API) located in include/asm/dapi.h consists of many pre-processor macros that implement functions such as misc_register, request_irq, etc. These macros expand to inline assembly code that pushes the parameters on the stack, and then execute a far (or long) call (lcall in gas syntax). The kernel call gate handling code will execute a long return (lret) insturction at the end to pop the parameters of BOTH stacks and return control to the driver thread.

Some kernel data that needs to be accessible to drivers (like the jiffies) are also implemented as calls to the kernel to get these values. This does introduce a performance hit since a privilege level switch must happen for each kernel call the drivers make, but based on early benchmarks this seems to be a relatively small hit.