Hi Guys and Girls,
I've been killing brain cells trying to come up with a way to interface device drivers with my kernel.
Certain questions have been bugging me, can't come up with solutions I'm happy with. I'd like your comments please.
Here is my design so far,(still a work in progress)
I treat drivers the same a processes, they are created in the same way.
The service header structure containes the following fields
- name of the driver
- array of functions the driver provides
- interrupt number it want to use
The driver has to initialize this structure before it registers itself with the kernel.
If a process is a driver, it has to call the RegisterDeviceService(service header) function during its initialization. This function adds a new entry in the device driver table for the device. It also returns a handle for the driver.
[Problem 1]
I need to come up with a way for device drivers to process the device interrupt they want to service and for them to have access to the data a device generates when an interrupt fires.
Allowing them to run at CPL 0 is not Secure.
Also device drivers might need to write their own Interrupt Service Routines.
How can I allow device drivers to use their own interrupt routines without creating a security problem?
Processes that needs a service i.e. read key from keyboard has to use findService(service name,Family class) to find the service they want. In my example they will have to use findService("Keyboard",INPUT);
This returns a handle to be used for subsequent calls.
[problem 2]
I'm sure we will have name clashes.
I was thinking of using FIXED names, CONSTANTS like DEVICE\KEYBOARD,DEVICE\MOUSE. Is there a better way to do this?
Then the final function is requestService(service handle,function,data buffer). Service handle is provided by the call above, the function is a service the driver provides eg read key and data buffer is where the driver will put the data.
This what I have so far, will add to it as I clear up these problems.
Any suggests, pointers or references to reading materials will be highly appreciated. Thanks
Device Driver Interface design
Re:Device Driver Interface design
I don't know what architecture you are using, but on Intel x86 architectures, it should be possible to use a Priviledge Level 1,2 or 3 code segment in the interrupt handler (place the appropriate segment selector in the required interrupt descriptor).
In terms of naming, you shouldn't use names like DEVICE\MOUSE because it is perfectly possible to have 2 mice. A possible solution to this is the Unix-y number postfix, e.g. DEVICE\MOUSE0.
Personally, I will be using a system along the lines of "Device\<Bus Type>\<Bus Location>", e.g. "Device\PCI\1:0:0" or "Device\ISA\PS2" with dynamically-created symlinks (or similar) for "Device\Mouse0" etc. I know this system is not perfect, but it is currently in the design stage.
HTH,
Fraser
P.S, on PCI systems, "Device\ISA" will itself be a symlink for "Device\PCI\x.x.x", where "x.x.x" is the address of the ISA bridge.
In terms of naming, you shouldn't use names like DEVICE\MOUSE because it is perfectly possible to have 2 mice. A possible solution to this is the Unix-y number postfix, e.g. DEVICE\MOUSE0.
Personally, I will be using a system along the lines of "Device\<Bus Type>\<Bus Location>", e.g. "Device\PCI\1:0:0" or "Device\ISA\PS2" with dynamically-created symlinks (or similar) for "Device\Mouse0" etc. I know this system is not perfect, but it is currently in the design stage.
HTH,
Fraser
P.S, on PCI systems, "Device\ISA" will itself be a symlink for "Device\PCI\x.x.x", where "x.x.x" is the address of the ISA bridge.
Re:Device Driver Interface design
What if a driver has to share an interrupt with another one?Code Slasher wrote: The service header structure containes the following fields
- name of the driver
- array of functions the driver provides
- interrupt number it want to use
Do device drivers really need to register their own interrupt service routines? You could also handle all IRQ's at kernel level and then dispatch these interrupts to user level. But as soon as you let a driver's service routine run with interrupts disabled (and on ISR entry, AFAIK, interrupts are disabled), you create a security hole. I suspect that most microkernels do it this way.Code Slasher wrote: Also device drivers might need to write their own Interrupt Service Routines.
How can I allow device drivers to use their own interrupt routines without creating a security problem?
cheers Joe
Re:Device Driver Interface design
@ Fraser Gordon:
I want to make it easy for programs to access a device without having to know if its PCI or ISA.
I might provide a means to say e.g. active keyboard or device/keyboard/1 or something like that. I'll give it more thought
@ JoeKayzA
I'll chain multiple drivers that share an interrupt. So when the interrupt fires, all the driver on that interrupt number get notified.
I was hoping (and am still hoping) that the drivers could do this themselves as the driver writer will be more familiar with the device.
Thought: Should device drivers state which ports they need to access (if any) so I can restrict them to those ports only? what about memory mapped devices e.g. PCI/AGP?
thanks guys, please keep making comments and suggestions
I want to make it easy for programs to access a device without having to know if its PCI or ISA.
I might provide a means to say e.g. active keyboard or device/keyboard/1 or something like that. I'll give it more thought
@ JoeKayzA
I'll chain multiple drivers that share an interrupt. So when the interrupt fires, all the driver on that interrupt number get notified.
I am handling interrupts in the kernel. Doing it this way mean I have to write interrupt handlers for all possible devices i.e. get the data and status information from the device and then pass it on to the drivers.You could also handle all IRQ's at kernel level and then dispatch these interrupts to user level
I was hoping (and am still hoping) that the drivers could do this themselves as the driver writer will be more familiar with the device.
Thought: Should device drivers state which ports they need to access (if any) so I can restrict them to those ports only? what about memory mapped devices e.g. PCI/AGP?
thanks guys, please keep making comments and suggestions
Re:Device Driver Interface design
The IRQ itself doesn't include any information but is only meant to tell the driver that the device has finished its task and needs further instructions. This means that you only have to write a generic interrupt handler that sends messages to all drivers holding that IRQ line. The drivers can then get the data from the device using normal I/O and the kernel releases the IRQ line once the driver has returned.Code Slasher wrote:I am handling interrupts in the kernel. Doing it this way mean I have to write interrupt handlers for all possible devices i.e. get the data and status information from the device and then pass it on to the drivers.
Drivers should be able to access all I/O ports and memory-mapped ranges of their device directly without having to call the kernel. The x86 architecture allows you to accomplish this using the I/O bitmap in the TSS for ports and paging for memory. For the beginning it's a good idea to use a script that defines which drivers need which resources, future releases of your kernel might additionally use plug&play for automatic detection.Code Slasher wrote:Should device drivers state which ports they need to access (if any) so I can restrict them to those ports only? what about memory mapped devices e.g. PCI/AGP?
regards,
gaf
Re:Device Driver Interface design
Hi,
The idea would be to have the OS (e.g. a device manager) scan the buses and determine which devices are present and what resources they use before the device driver is started.
This way the OS knows which device drivers are needed and would tell the device driver which IO ports, IRQ, memory areas, etc it must use (and not the other way around, where the device driver tells the OS). This also makes it impossible for a device driver to ask for something it shouldn't have access to.
Cheers,
Brendan
Possibly, but IMHO definately not!Code Slasher wrote:Thought: Should device drivers state which ports they need to access (if any) so I can restrict them to those ports only? what about memory mapped devices e.g. PCI/AGP?
The idea would be to have the OS (e.g. a device manager) scan the buses and determine which devices are present and what resources they use before the device driver is started.
This way the OS knows which device drivers are needed and would tell the device driver which IO ports, IRQ, memory areas, etc it must use (and not the other way around, where the device driver tells the OS). This also makes it impossible for a device driver to ask for something it shouldn't have access to.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re:Device Driver Interface design
Probably you'll need several different views on the devices.Code Slasher wrote: @ Fraser Gordon:
I want to make it easy for programs to access a device without having to know if its PCI or ISA.
I might provide a means to say e.g. active keyboard or device/keyboard/1 or something like that. I'll give it more thought
Grouped by category, or ordered by connections, etc.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Device Driver Interface design
that's indeed possible, but beware:Fraser Gordon wrote: I don't know what architecture you are using, but on Intel x86 architectures, it should be possible to use a Priviledge Level 1,2 or 3 code segment in the interrupt handler (place the appropriate segment selector in the required interrupt descriptor).
This means that if you want your driver to safely handle an IRQ at CPL 1, you need to make sure any code at CPL0 cannot be interrupted ...The privilege-level protection for exception- and interrupt-handler procedures is similar to that used for ordinary procedure calls when called through a call gate (see Section 4.8.4., Accessing a Code Segment Through a Call Gate ). The processor does not permit transfer of execution to an exception- or interrupt-handler procedure in a less privileged code segment (numerically greater privilege level) than the CPL.
-- intel pentium IV manual, section 5-16
I'd ask myself "how are those name going to be used". If -for instance- your driver is loaded from a file, then the pathname is supposed to be a unique name for the driver itself. On the other side, programs that access the driver (e.g. filesystems, GUI managers, etc) will rather use a symbolic name than a unique name.In terms of naming, you shouldn't use names like DEVICE\MOUSE because it is perfectly possible to have 2 mice. A possible solution to this is the Unix-y number postfix, e.g. DEVICE\MOUSE0.
E.g. if there are 4 mice connected to your machine, the GUI will simply ask for "device/mouse/*" to a device manager which will return "device/mouse/0, device/mouse/1 ..." hopefully along with real-world identification for selection purpose ("Microsoft Intellimouse", "Logitech Chordless Wheelmouse", ...) and a flat number that the application can later use for communication purposes.
I'm not at all convinced by the "device/pci/bus0/device3" approach: that's only useful for the device manager, not for the user (who prefers to see "AC97 audio") nor for the other software designers (who prefers "device/media/audio0")
nowadays, most hardware can allows PnP probing and setup of hardware resource, so you're better to have the device manager reporting to the OS what needs to be allocated -- not the driver itself.Should device drivers state which ports they need to access (if any) so I can restrict them to those ports only? what about memory mapped devices e.g. PCI/AGP?
For legacy ISA devices, you might design a "fake" bus manager that says "oh, i detected device/input/keyboard/0 and it reports using ports 0x60-0x64". The same device manager could be asked to check "can soundblaster16.drv be running with iobase=one of(0x200..0x280 step 0x20) and irq=one of (2 3 5 7 10)
The device manager would then check what's still free (e.g. don't we have a PnP device using 0x200..0x21f already ? and what about IRQ5) and will then instanciate driver for the different combos and see what instance succesfully initialize the device (e.g. startdriver(irq=5, iobase=220) || startdriver(irq=7, iobase=220) || startdriver(irq=5, iobase=240) || startdriver(irq=7, iobase=240).
Re:Device Driver Interface design
I obviously need to explain my plan a little more thoroughly
The idea is that the "Device\<Bus Type>\*" names are used as the internal device representation (similar to WinXP's scheme where the device tree is visible to the programmer, but not the user). These are grouped by bus to allow for easier bookeeping of, e.g power management (where the OS needs to know which devices are connected to what). These names are only useful to device driver programmers and don't actually allow IO to or from the device. Reading from them gets information like PCI assigned IO ports and interrupt numbers.
The view for the programmer is made easier - the devices are grouped by function rather than location, for example "Device\Disk Drives\IDE\0\0\Partition 0". These devices are provided my the device drivers themselves and not by the kernel resource manager. These names function like the standard UNIX "/dev/hda1, etc" files.
For terminal-level programs, access to input devices not connected to the current terminal is not required (e.g. Keyboard 0 is connected to Terminal 0 and Keyboard 1 is connected to terminal 1. Terminal 0 will only want input from Keyboard 0). These programs can access their devices through "Device\Terminal\Current\", giving "Device\Terminal\Current\Keyboard". These heirarchies are to be formed by the terminal driver.
Although this system is complex and involves a lot of indirection (what can't be solved by it, anyway!), it simplifies the APIs. This way, only device drivers need to know where a device is connected and programs interacting with the terminal can access keyboard input easily, without even caring which keyboard it is coming from (or if it even is a physical keyboard). More than one of each device type will be supported by the terminal (though not necessarily the application). For this, a reference to "Terminal\Current\Keyboard" will mean the default keyboard and "Terminal\Current\Keyboard\x" will refer to a specific keyboard.
I hope this makes my system seem a little more sensible. Feel free to post any suggestions/queries/cutting put-downs! One last thing, the namespaces for the internal device tree will be changed to something along the lines of "\Device\Bus\<Bus Name>\<Location>" to avoid conflicts (and to comply with the functional groupings).
Fraser
The idea is that the "Device\<Bus Type>\*" names are used as the internal device representation (similar to WinXP's scheme where the device tree is visible to the programmer, but not the user). These are grouped by bus to allow for easier bookeeping of, e.g power management (where the OS needs to know which devices are connected to what). These names are only useful to device driver programmers and don't actually allow IO to or from the device. Reading from them gets information like PCI assigned IO ports and interrupt numbers.
The view for the programmer is made easier - the devices are grouped by function rather than location, for example "Device\Disk Drives\IDE\0\0\Partition 0". These devices are provided my the device drivers themselves and not by the kernel resource manager. These names function like the standard UNIX "/dev/hda1, etc" files.
For terminal-level programs, access to input devices not connected to the current terminal is not required (e.g. Keyboard 0 is connected to Terminal 0 and Keyboard 1 is connected to terminal 1. Terminal 0 will only want input from Keyboard 0). These programs can access their devices through "Device\Terminal\Current\", giving "Device\Terminal\Current\Keyboard". These heirarchies are to be formed by the terminal driver.
Although this system is complex and involves a lot of indirection (what can't be solved by it, anyway!), it simplifies the APIs. This way, only device drivers need to know where a device is connected and programs interacting with the terminal can access keyboard input easily, without even caring which keyboard it is coming from (or if it even is a physical keyboard). More than one of each device type will be supported by the terminal (though not necessarily the application). For this, a reference to "Terminal\Current\Keyboard" will mean the default keyboard and "Terminal\Current\Keyboard\x" will refer to a specific keyboard.
I hope this makes my system seem a little more sensible. Feel free to post any suggestions/queries/cutting put-downs! One last thing, the namespaces for the internal device tree will be changed to something along the lines of "\Device\Bus\<Bus Name>\<Location>" to avoid conflicts (and to comply with the functional groupings).
Fraser