Page 1 of 1

driver interface design

Posted: Sat Mar 03, 2012 1:47 pm
by bluemoon
I'm currently redesigning & rewriting major parts of my kernel.

So how do you define the communication interface between kernel & driver?


This is my current design:
1. kernel call driver directly (ie C call) by calling driver's exported symbols.

Code: Select all

// kernel code
func = (int(*)(void)) ELF32_sym ( &elf, "driver_init" );
rez = func();
2. driver also call kernel's function directly (C call). the driver is linked as .so and with undefined symbols, the kernel patch it when loading the driver.

Code: Select all

// driver code
void* kmalloc(size_t s); // define prototype only, the body is in the kernel.bin
…
rez = kmalloc(16); // call it directly

I also considered alternatives, like provide another interrupt entry but I can't think of an elegant way to tell if the caller is a driver or an user-apps, checking the return address is ugly.

Re: driver interface design

Posted: Sat Mar 03, 2012 2:36 pm
by Solar
Well, there's UDI, or CDI, which I would give a look if you're redesigning your driver interface anyway. 8)

Re: driver interface design

Posted: Sat Mar 03, 2012 2:45 pm
by Jezze
In my case the kernel parses the driver's symbol table and for each undefined reference it places the corresponding kernel function address there. This makes communication driver -> kernel work so the driver can now call all kernel function it wants.

When it comes to the opposite direction kernel -> driver it is a bit more trickier. The kernel knows what drivers have been loaded but it doesn't know, for some supported feature, exactly what driver is suppose to handle what. Let's take logging for instance. Perhaps you have two different drivers that both can handle system logging. Which one should you use? For all those instances I've had to implement a table with callbacks where the driver can place a callback in like in this case table[LOG] = driver->log;

So basically there is no direct kernel -> driver communication in the sense that the kernel knows what driver is doing what. It only uses the callback table.

This is what I did. I don't know if there is any other way really.

Re: driver interface design

Posted: Sat Mar 03, 2012 3:50 pm
by Kevin
bluemoon wrote:This is my current design:
1. kernel call driver directly (ie C call) by calling driver's exported symbols.
2. driver also call kernel's function directly (C call). the driver is linked as .so and with undefined symbols, the kernel patch it when loading the driver.

I also considered alternatives, like provide another interrupt entry but I can't think of an elegant way to tell if the caller is a driver or an user-apps, checking the return address is ugly
Let me rephrase your question: Should I write a microkernel or a monolithic one? There are good reasons for either option and we can't make that decision for your (well, we could, but after all it's your kernel).

One counter question, though: Why does it matter if some task is a "driver" or a "user-app"? Isn't the only possible difference about privileges?

Re: driver interface design

Posted: Sat Mar 03, 2012 3:57 pm
by bluemoon
Jezze :

An alternative would be passing an API entry point to driver instead of dynamic resolve kernel's symbols.
something like this:

Code: Select all

struct DRIVER {
  int type;
  int id;
  int (api)(int cmd, ...);
  ...
};
As for kernel->driver, I agree for specific purpose there should be some kind of function registry, and the driver simply set hook/chain/extend in the function table - except I call that plugin module, not a driver.

For my drivers, I have type and id per each driver, and a driver registry that you can do driver = driver_find(kTypeFS,'FAT ');
And I don't have two drivers that do the same thing.

One counter question, though: Why does it matter if some task is a "driver" or a "user-app"? Isn't the only possible difference about privileges?
I don't want my kernel functions to be callable from user apps. If I open up an INT interface that would require permission checks in the interrupt handler, and check every time upon a call - unless there is some more elagent way to do it?

Re: driver interface design

Posted: Sat Mar 03, 2012 4:32 pm
by Kevin
If you're doing it right, almost all syscalls will need quite some checks. Things like "does this pointer really point to valid user memory" or "may this process open the file it's trying to access". Having another syscall that requires a check "may this process get access to an I/O port" doesn't introduce anything fundamentally new.

Re: driver interface design

Posted: Sat Mar 03, 2012 5:02 pm
by bluemoon
I see your point, thank you!

Re: driver interface design

Posted: Sat Mar 03, 2012 5:30 pm
by Jezze
Yeah it is a really good point. If the syscall interface checks for just about everything that can go wrong the rest of the kernel does not need to be bothered checking for buffer overruns, array boundries, invalid ranges et al because they just can't happen.

Re: driver interface design

Posted: Sat Mar 03, 2012 8:13 pm
by Gigasoft
bluemoon wrote:I don't want my kernel functions to be callable from user apps. If I open up an INT interface that would require permission checks in the interrupt handler, and check every time upon a call - unless there is some more elagent way to do it?
Yes there is. Set the DPL field to 0.

Windows 95 used an interesting technique, where a driver would call the kernel (and other drivers) using an INT instruction followed by a function code. This would be replaced with an indirect call so that further invocations would not have the overhead of executing the interrupt handler.

As for me, the way I have it planned is that calls will be fixed up at loading using a relocation table. However, most of the intermodule communication will take place through COM-like interfaces.