-
Notifications
You must be signed in to change notification settings - Fork 1
QEMU
QEMU is a system emulator. QEMU allows a full system, a guest image written for a CPU architecture with all its peripherals, to be emulated on a different CPU architecture. One of its popular uses is as Android Emulator for Android SDK. DECAF modifies QEMU code to include its functionalities.
QEMU is a dynamic translator; it translates the guest code step by step at the run time. Figure 1 shows the general structure of QEMU. Simplifying the process, the following steps will be taken:
- QEMU reads a block of the guest code and then it translates the code to an Intermediate Representation (IR) called Tiny Code Generator (TCG). The translated code is put in a code cache
- QEMU reads the translated code and generates an executable binary code on the host. Note that TCG is not executable but this generated code is based on the host architecture. We further explain this in the binary code generation section.
- QEMU executes the guest code up to the end of the block and then transfers the control back to the emulation manager
- The emulation manager checks to see if the next block is previously translated. It does so by checking the Address Lookup Table (ALT) that its entries show the translated address of the branch targets i.e. the jump address for the next block. If ALT does not have an entry for the new block, it again goes to (1). Otherwise, it jumps to the translated block in the code cache. We further explain this in Block chaining and patching.
Figure 1. QEMU execution structure
The above description is a simplification of the real emulation process. There are several important notes. First, QEMU must give an illusion that the guest has access to the hardware it requires. This means that QEMU must provide a software way of answering interrupts. Further, the above execution structure should answer interrupts in a timely manner. Second, jumping back and forth to the emulation manager needs some extra works i.e. execution Prologue and Epilogue.
The translation is the task of preparing an intermediate code that can be compiled into different platforms. The intermediates language is called tcg. tcg instructions in nature are C macros. Figure 2 shows an example of the block translation to the intermediate language. Note that tcg contains only the opcodes for an instruction. While translating (or disassembling), we need to keep the arguments (or variables) in a buffer and load them when generating the tcg to the binary code.
Figure 2. Intermediate translation of a block of guest code
Qemu translates one block at a time. After executing one block, we have to jump to the next block. Qemu chains a block to its following after it translates the following black. Henceforth, after all the code is covered (seen and translated), the blocks are chained together and then further translation is not required; after one block is executed to the end, it jumps to the next chained block.
Qemu emulates the hardware for its guest. Qemu does the memory mapped IO and responds the guests requests without actually sending the interrupts directly to the hardware. Instead, Qemu modifies the interrupt table so that the requests are transferred to the codes under the control of the Qemu. These codes use the main memory and respond to the requests in a satisfactory manner for the guest platform.
Most of the functionalities in modern computer systems such as paging, I/O and low level security are provided through interrupts and exceptions. Before explaining the technical details specific to QEMU, let’s review how a computer system handles I/O requests. When an application requests input from a user, in a high level language, a program calls a library function that handles the low level details. Underneath, the library function makes a system call. Finally, the system call issues an interrupt request for keyboard input. In order to handle the interrupt request, in this case keyboard read, CPU needs to load and execute an piece of code. Operating systems provide this piece of code through Interrupt Descriptor Table. Interrupt Descriptor Table entries are pointers for interrupt handlers of an interrupt code. For instance, the interrupt handler for interrupt 3 is known by looking at the 3rd element of the interrupt descriptor table. As another example, let’s see how a computer handles a page fault. Page fault is a hardware exception that happens when a virtual address does not resolve to a valid physical address. It is a hardware exception because CPU (which is the hardware) is aware of such scenario and whenever it happens, CPU asks operating system how to handle it. Once again, operating system returns the exception handler for this fault through the interrupt descriptor table.
Modifying Interrupt descriptor table (IDT) entries will change the functionality of the system in a regard. For the sake of security and integrity of the system, changing IDT requires access to privileged instructions LIDT. In the code translation phase, QEMU can instrument this instruction and hence implement its own hardware emulation through manipulating IDT.
See the Qemu execution and data flow in this Wiki for further details about Qemu Internals.