Hi,
I revised my last text about virtual devices a bit. Here the
new version.
jens
-------------
Devices
-------
What is a device from an os point of view?
* I/O-Port(s) in I/O address space; in/out instructions can be caught easily
* Memory mapped range; accesses can be caught easily
* (Hardware-)Interrupt(s); we set our own IDT
* DMA; 8237A-DMA-chip easy to implement in software, problem is access by
busmastering (PCI-) devices
* Communication protocol; as much protocols as devices, ugly to handle
because of the huge number of devices
* Inner state; as much as devices...
There is a large diversity of devices. Some are similar, other not.
Some handle little amounts of data, other huge.
But all are constructed to be driven by one and only one device driver.
When there are more than one operating systems are running simultaneously,
there are two possible ways for using devices: scheduled shared or exclusive
for one.
If a operating system used as guest gets access to a device, the device may
be a virtualized real device or an emulated pseudo device.
When creating an environment for a guest os, the developer has to keep in
mind all these facts and has to select the best ways to drive the devices.
But how to find the "best way"?
Let's assume the following model:
App | App
---------
OS
---------
Driver
---------
Device
The device is given. The device driver may be part of an os, but there is no
hard need for this. One may also argue that this model
App | App
---------
Driver
---------
OS
---------
Device
is the better, because they have micro kernels in mind. But let's take the
first one, because the os isn't only the kernel and consists of more parts.
The device driver talks with the device and exports some abstactions used
by some other parts of the os.
We know that the devices are constucted to be driven by one device
driver simultaneously. Let's assume that each running os tryes to drive the
device. Collisions will happen, sooner or later - presumably sooner.
So we need mechanisms to drive one device by more than one driver.
Next were four possible architectures shown.
1.
App | App | App | App
----------------------
OS1 | OS2
----------------------
Driver1 | Driver2
----------------------
Device1 | Device2
Devices were divided into partitions. Each device is driven by only one
driver/os. It's relatively simple, and we need a mechanism to drive a device
by guest os. (specially DMA and interrupts)
But the penalty is that the device can't be used by more than one os.
* Ports: Divided into partitions
* Memory mapped range: Assigned to devices as usual
* (Hardware-)Interrupts: Divided into partitions; caution with shared interrupts
* DMA: No special DMA problem: see problems above
* Communication Protocol: Direct by driver
* Inner State: doesn't matter
-> may work
2.
App | App | App | App
----------------------
OS1 | OS2
----------------------
Driver1 | Driver2
----------------------
Device
One device is (would be) driven by two drivers directly. For this, the drivers
have to communicate with each other to work right. No driver does this now,
all drivers (os'es) had to be changed. Very bad. Or hardware must be
changed, but that's not (yet??? ;) in our power...
* Ports: used directly by the two drivers
* Memory mapped range: used directly by the two drivers
* (Hardware-)Interrupts: depends on protocol and inner state
* DMA: used directly by the two drivers
* Communication Protocol: Direct by driver
* Inner State: will be inconsistent
-> doesn't work without general changes in drivers
3.
App | App | App | App
----------------------
OS1 | OS2
----------------------
Driver1 | Driver2
----------------------
MUX
----------------------
Device
As above, but a multiplexer (MUX) coordinates the communication with the device.
For this, the MUX must know the devices and protocols the drivers talk to the
devices. Both drivers must not talk to devices directly, but to MUX - voluntary
or not. In most cases not. Drivers have to be modified to talk to MUX or they
must run in user mode so that accesses to devices can be caught. In case of
guest no problem, it's planed so in our architecture - but in case of host?
The MUX has to be implemented in monitor and can be conceived as a state machine
representing the states of the device. We suppose, there are ``safe states''.
In a safe state every driver can assume, it has the device for itself and alone.
A safe state for a modem would be the unused modem, after a user hang up, or
for a hard disk after a block transfer is finnished. New commands from a second
driver don't destroy the state, the first driver relyes on. Using the safe states,
the model is similar to (1.)
* Ports: multiplexed (virtualized) by MUX
* Memory mapped range: multiplexed (virtualized) by MUX
* (Hardware-)Interrupts: multiplexed (virtualized) by MUX
* DMA: multiplexed (virtualized) by MUX
* Communication Protocol: multiplexed (virtualized) by MUX
* Inner State: multiplexed (virtualized) by MUX
-> may work with a special VMM architecture
4.
App | App | App | App
-----------------------
| OS2
------------
OS1 | Driver2
------------
| Translate
-----------------------
Driver1
-----------------------
Device
Here the one os runs as usual. For the second (third, fourth...) os some kind
of translater must be placed between the two drivers to translate between the
bottom side of Driver2 (its device side interface) and the upper side of
Driver1 (its os side interface). The translater may be an emulator for a
not real built in device - like to find in VMware.
* Ports: Used by Driver1 only
* Memory mapped range: Used by Driver1 only
* (Hardware-)Interrupts: Used by Driver1 only
* DMA: Used by Driver1 only
* Communication Protocol: Used by Driver1 only
* Inner State: Used by Driver1 only
-> may work, if ``Translate'' can provide virtual devices for Driver2 and
translates or emulates all correctly
5.
App | App | App | App
----------------------
OS1 | OS2
----------------------
Driver
----------------------
Device
The two os'es use the same driver. Probably the simplest way, but doesn't work
because each os has it's own driver model. Also it may be difficult to make a
driver to be seen in all contexts, so a pseudo driver may translate between
the os2-driver-interface and the os1-driver-interface. Then it looks like that:
App | App | App | App
--------------------------
OS1 | OS2
--------------------------
| Pseudo driver
--------------------------
Driver
--------------------------
Device
* Ports: Used by Driver1 only
* Memory mapped range: Used by Driver1 only
* (Hardware-)Interrupts: Used by Driver1 only
* DMA: Used by Driver1 only
* Communication Protocol: Used by Driver1 only
* Inner State: Used by Driver1 only
(note: Driver1 == Driver, not Pseudo driver)
-> may work
And that's like (4.). We can emulate easy and well understood devices
For our purposes most promising seems to be (4) or (5). (3) is better for an
architecture
without host, where only guests run on top of a monitor
Examples
* to be done for:
* PIC
* Console, VGA, Graphic adapter
* Tastatur
* Floppy
* Hard disk