In the chosen platform, Linux on an ix86, the task will be quite straightforward. In order to have the capability of even touching the hardware without Linux knowing, we need to use loadable modules. Hence, Adeos is a loadable module that is dynamically loaded and that will take control of the hardware. Taking control of the hardware on an unmodified Linux kernel on the ix86 may seem impossible since the kernel was compiled with ix86 instructions such as cli/sti which are the ``disable interrupt''/ ``enable interrupt'' respectively on the ix86. Hence, even if we were to modify the interrupt descriptor table register using the ``lidt'' instruction, Linux would still be able to stop Adeos from operating in the intended fashion.
To bypass this shortcoming we use one of the ix86's own capabilities against the OS. Remember that the ix86 posses 4 privilege levels, 0 being the highest and 3 being the lowest. Linux normally operates at PL 0 with its applications running at PL 3. With these privilege levels come different capabilities. Most importantly, software running at PL 0, often called ring-zero, will be able to do what it may wish to do with the hardware. On the other end, software running at ring 3 is not permitted to do certain operations that may modify the overall system's behavior and hinder the OS's job of protecting the different processes running on the system. The privileges provided to a level or another may be controlled using the I/O privilege level (IOPL) mechanisms provided on the ix86.
An example of interest, Plex86 runs the ``virtualized 1'' OS at ring 3, as explained in . In the same document, K. Lawton speculates that it may be possible to virtualize an OS at ring 1 instead of ring 3. This would enable for more native execution of the different page protection mechanisms used on the ix86, hence accelerating system virtualization. In our case, virtualization is of no interest since we trust the OSes we are running on top of Adeos.
The solution therefore is to push Linux out of ring-zero and into ring-one. By doing so, all privileged instructions such as cli and sti aren't permitted and will generated a fault by the processor. These instructions will then be caught by a Linux handling domain (LHD), that precedes the Linux domain (LD) in the interrupt pipeline and that will convert these instructions into a stall/unstall LD pipeline stage. Using this technique, Linux is still in control of all the page management and Adeos doesn't necessarily need to take it away unless we plan to run another general-purpose OS side-by-side with Linux. This will be covered in section 5.
The downside to this technique is that other important instructions that we would like Linux to proceed with will be caught. The following instructions will not be permitted to Linux once it is placed in ring-one (as in the ix86 manuals provided by intel):
Most of these instructions do not execute very often. The critical ones are in/out/ins/outs as these may be heavily used by device drivers. In order not to generate faults each time these instructions are called, the task state segment bitmaps may be altered in order to permit ring-one IO. That done, device drivers will be able to operate as designed without Adeos seeing any traps for the in/out/ins/outs instructions.
Still, there remains a substantial amount of other instructions that still need to be taken care of. There are two ways to take care of those: emulation and single-stepping. With emulation, the contents of the memory and registers are modified to let Linux believe that he actually executed the instruction. Single-stepping involves setting the single-stepping bit for Linux and lowering the IOPL to 1. Hence, when Linux goes to run, he gets to run the instruction but is cutoff right afterwards and the LHD resets the IOPL back to 0 and removes the single-stepping bit. With this, Linux can continue operating at will.
There are cases that plex86 takes care of which we don't need to take care of or that we omit voluntarily to treat because we know Linux's behavior. Self-modifying code is one of these cases. Here, we assume that Linux will not attempt to modify its own binary code. This is a safe assumption given the current kernel components.
There's the case where we want additional loaded modules to have access to Adeos and we may even want some modules loaded by a ring-one Linux to become ring-zero elements. This may be provided for using a special software interrupt which traps into Adeos and which Adeos may use to modify the mappings of these modules to become part of ring-zero. This is possible since Linux's module mappings are accessible in kernel space.
This whole scheme means that Adeos itself needs to be running at ring-zero. This is achievable by adding extra entries in the GDT which can be used by Adeos and all other modules who want to become ring-zero. The inverse of this is that removing Linux from ring-zero is done by modifying the GDT entries that pertain to its code and data to have descriptor privilege levels of 1 instead of 0. Hence, the GDT register isn't modified, only the content of the global descriptor table pointed to by the GDT register is modified. The IDT register, on the other hand, is completely overwritten by Adeos to point to its own tables. Prior to overwriting it, though, Adeos keeps a pointer to Linux's initial table in order to invoke the correct entry when Linux's turn comes while going through the interrupt pipeline.
These modifications are then propagated to the rest of the elements recognized by the hardware in order to reflect the change of privilege level. This may involve changing the task-state segments maintained by Linux for every task it runs. As Linux may create new tasks while Adeos is active, he may create TSSes that are not expected by Adeos. This may be detected because of traps caused by the unexpected behavior and then fixed by the LHD.
The rest of the implementation is not hardware specific and may be implemented as described in section 3.
``Bootstrapping'' Adeos on Linux/ix86 would involve loading the Adeos module first. This module's init_module() would not do much except return a value to inform the caller that loading succeeded. The reason Adeos can't go any further is that the Linux domain handler isn't loaded yet. It is this module's loading that will generate the chain-reaction that will result in Linux loosing control of the hardware. Once loaded, the LHD will initialize itself and call on Adeos to complete hardware hijacking.
Unloading Adeos involves unloading all modules that use it and returning Linux to ring-zero. Unloading ring-zero modules may be done using another special software interrupt recognized by Adeos. Thereafter the Adeos kernel module may be unloaded. Notice that a similar discussion may have been done with FreeBSD instead of Linux on the same hardware platform with the same end result.