Save access to i/o ports

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
FlashBurn

Save access to i/o ports

Post by FlashBurn »

How let you are your drivers access the i/o ports? Could every driver access every i/o port?

First I thought to use the io-map of the tss, but this would take to much of mem, because every driver needs one. Maybe a linked list would be ok. I also thought of a 4KiB page where are the ports that the driver can access. This 2 methods are not that fast if I want to test if a driver could access the port it wants to.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Save access to i/o ports

Post by Brendan »

Hi,
FlashBurn wrote: How let you are your drivers access the i/o ports? Could every driver access every i/o port?
I have full IO port protection..

My drivers run at CPL=3. The kernel has a 256 Kb table (4 bytes per IO port) which contains the thread ID of each IO port owner. When a device driver tries to access an IO port it causes a general protection fault, and the GPF handler emulates the IO port instruction after checking if the thread has access.

The GPF part isn't too fast, but it's better than using kernel API functions. 256 Kb sounds like a lot of memory, but it's only actually allocated when it's used (most of the IO ports aren't used). It's also much less memory than the IO permission bitmap in TSS's if you want to run lots of threads.

Using a single table makes it much easier/faster to allocate and de-allocate IO ports too, for e.g. "cmp dword[IO_port_table+edx*4], 0" is much faster than checking every thread to see if the IO port is already being used.

Some of my code that runs at CPL=0 also uses IO ports. In this case there will be no GPF and the IO ports are marked as "permanently used by the kernel". These IO ports include the ISA DMA controller, RTC/CMOS, PIT, PIC chips, PCI configuration, plug & play configuration and the PS/2 controller.


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.
User avatar
gaf
Member
Member
Posts: 349
Joined: Thu Oct 21, 2004 11:00 pm
Location: Munich, Germany

Re:Save access to i/o ports

Post by gaf »

Hello,
there are four different ways to protect the I/O ports:

* IOPL-bit in the EFLAGS (hardware)
Specifies the lowest (numerically highest) ring from which I/O instructions like IN, OUT, INS, OUTS, CLI, STI can be executed. If you were to run your drivers on ring1 and change the IOPL-bit accordingly, they could all access the whole port address-space.

* I/O-Bitmap in the TSS (hardware)
This bitmap allows you to grant access to single I/O ports or port ranges. Each bit represents a port, so that the maximum size is 2^16/8 bytes => 8KB. Remember that a) only driver tasks need one and b) this is the *maximum* size.

* Software I
As described by Brendan: The I/O instruction triggers and exception, so that the kernel can decide whether to grant access or not.

* Software II
Simply offer an system-call for I/O access to the drivers. The kernel can then check if the app has access-rights and (if so) execute the I/O instrucion.

Intel reference manual:
http://my.tele2.ee/mtx/i386/chp08-03.htm#08-03

regards,
gaf
FlashBurn

Re:Save access to i/o ports

Post by FlashBurn »

@gaf

I know how to protect the i/o ports, but I need a way of doing it fast and w/o much memory overhead.

@brendan
This would be also a good way, but then you have to scan through the table when a thread or task is killed. Also I think that a task-based i/o port protection is enough! I think that the task should also have to do some work for itsself ;)
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:Save access to i/o ports

Post by Brendan »

Hi,
FlashBurn wrote:I know how to protect the i/o ports, but I need a way of doing it fast and w/o much memory overhead.
"Fast" comes in 3 flavours - fast access to IO ports for device drivers, fast allocation/deallocation of IO ports and making the OS as a whole run fast by reducing overheads elsewhere. There's also differences in how much protection you want to provide. Using the methods listed by Gaf:


* IOPL-bit in the EFLAGS (hardware)

This method uses no CPU time and doesn't use any memory either. The compromise is that you have to give device drivers access to all IO ports, and either run drivers at CPL = 0 (IOPL = 0), use three different privelege levels (IOPL = 1 or 2) or just give all software access to all IO ports (IOPL = 3). The side effect here is that it also controls CLI and STI, which may or may not be desirable.


* I/O-Bitmap in the TSS (hardware)

This method doesn't use any CPU time when drivers access IO ports. If you're using software task switching then you need to adjust the TSS during each task switch, which adds a little overhead to every task switch. If you're using hardware task switching then IMHO you shouldn't be too concerned with speed anyway. With the IO permission bitmaps alone the kernel's code to allocate an IO port would need to scan the IO permission bitmap for every task to find out if an IO port is in use or not. This can be made faster by maintaining a global bitmap that determines if each IO port is allocated or free (it'd take an extra 8 Kb). Either way this method does provide per port protection.


* Software I - GPF emulation

This is the same as Software II (see the notes there) apart from using the general protection handler instead of system calls. The speed of the GPF handler is several cycles slower than a software interrupt (call gates, syscall, sysenter are usually even better). The part I like is that device drivers can use the IO instructions without caring if they are emulated or not, which also means that the kernel can disable all IO port protection by setting IOPL to 3 (for people who don't care and just want speed, or perhaps after everything has run without problems for several hours).


* Software II - System calls

This method (and Sofware I) uses the most CPU time when an IO port is accessed but offers the best IO port protection possible (if you want it) - per bit protection. One example would be the keyboard controller chip, where you want the keyboard driver to have access to everything except for the bits that control the A20 gate and system reset.

The easiest way to implement this is with a table, one entry for each IO port. The size of the entry depends on how many threads your OS supports (or processes depending on what owns the IO port), but for alignment purposes it'd be either 128 Kb or 256 Kb (for 16 bit or 32 bit entries). The memory actually used by this table will be more like 32 Kb or 64 Kb if unused pages aren't mapped.

FlashBurn wrote:@brendan
This would be also a good way, but then you have to scan through the table when a thread or task is killed. Also I think that a task-based i/o port protection is enough! I think that the task should also have to do some work for itsself ;)
The OS can keep track of the number of IO ports each thread owns and expect the threads to free any IO ports itself before terminating so that the IO owner table doesn't normally need to be checked at all. If the driver is killed without notice (ie. crashed) the kernel needs to check through the IO owner table, but can stop looking once the right number of IO ports have been found (it wouldn't need to scan the entire table). Hopefully device drivers won't crash often :-)


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.
FlashBurn

Re:Save access to i/o ports

Post by FlashBurn »

I think that I will take the i/o-bitmap. This would mean a 8KiB global table and 8 KiB for every driver! I think I can handle this memory overhead!

But also thanks for your replies!
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Save access to i/o ports

Post by Pype.Clicker »

afaik, you don't have to have the *whole* 8KB io bitmap for every TSS. The size of the actual bitmap is stored in the TSS and only ports you need to enable require a bit in the map (ports with bits out of the map are implicitly disabled, afaik.

still, you may prefer to keep a *single* almost-empty map and perform map deconstruction/reconstruction on task switches (e.g. check the outgoing task's io permission list and clear corresponding bits, then checking the incoming task's list and setting the corresponding bits. It will probably consume less memory though i dunno how "faster" it will be...
FlashBurn

Re:Save access to i/o ports

Post by FlashBurn »

Yeah, I know that I needn?t to have the whole bitmap, but I will then use 4KiB or 8 KiB for the bitmap, because this would easy somehtings.

Only drivers will get this bitmap and I think 4/8KiB per driver is a memory overhead I can have in the kernel! I would use one TSS and a special place where the io-bitmap would be mapped for every driver task!
Post Reply