| | 1 | = Call Gates = |
|---|
| | 2 | 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. |
|---|
| | 3 | |
|---|
| | 4 | 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. |
|---|
| | 5 | |
|---|
| | 6 | 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. |
|---|
| | 7 | |
|---|
| | 8 | 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. |
|---|
| | 9 | |