MP3 – Adding New Instructions and CPU registers to QEMU: Due April 5th Before Class

 

Announcement

 

 

Overview

 

In this MP, you will add new instructions and CPU registers to a virtual machine monitor. You will extend the IA-32 (x86) instruction set in three ways:

 

1. Extend the CPUID instruction so that it identifies your VMM as CS 423 MP3 compliant.
2. Add a new instruction with opcode 0xF1 - make this a clone of the instruction "PUSH DS"
3. Add a new instruction with opcode 0xD6 - swap the contents of EAX with those of a new register you will add to the x86 ISA

 

To keep your edit-compile-debug cycle down, we will be using the QEMU binary rewriting VMM in user mode on Linux.

 

As in MP1, we suggest to work on a dedicated machine prepared by TSG: csil-vmserv1.cs.uiuc.edu. Also please try to avoid running it in your csil home directory. The home directory is mounted from a department server using NFS and heavy applications such as QEMU can crash the server. You can always connect to csil-vmserv1 through csil machines, though.

 

Although you can also develop on other machines (e.g., your own machines), assignments will be graded on csil-vmserv1.

 

NOTE: You do not need to use your VM from MP1.

 

 

Compiling QEMU

 

The following instructions assume you are using a CSIL machine. Assignments will be graded on CSIL.

1. In your local directory, copy and unpack the QEMU source code from csil-vmserv1:/home/class/sp10/cs423/mp3. We'll be using version 0.11.0 for this MP.

 

cp /home/class/sp10/cs423/mp3/qemu-0.11.0.tar.gz ./

tar xzf qemu-0.11.0.tar.gz

 

2. Build QEMU. From the qemu-0.11.0 directory:
(2a) Run the configuration script:

 

./configure --disable-kqemu --disable-sdl --audio-drv-list='' --audio-card-list='' --disable-vnc-tls --target-list=i386-linux-user --prefix=.

 

(2b) Run "make"

 

make

 

3. Run QEMU's user space emulator.

 

cd i386-linux-user

echo 'int main() { printf("hello qemu\n"); }' > hello.c

cc -m32 hello.c -o hello

./qemu-i386 hello

 

 

Useful references

 

You may find it useful to refer to the following information for this MP:


Intel(R) Architecture Software Developer's Manual, Volume 2: Instruction Set Reference Manual - the IA-32 instruction set. In addition to the manual pages for individual instructions, Appendix A contains 1-byte and 2-byte opcode maps you can use to decode what instructions a sequence of bytes contains. The 1-byte opcode map on pages A-6 and A-7 in particular may be useful.
3DNow! virtualization patch for QEMU  - an example of how to add instructions to QEMU. Look through this patch to figure out what parts of the QEMU source code you may need to change to solve this MP.

 

 

Problem 1: Customizing a CPUID bit

 

The CPUID instruction on the IA-32 and IA-64 architectures allows software to query the CPU for information about its capabilities. For example, CPUID can be used to determine the processor manufacturer, model, whether MMX, SSE, and other extensions are supported, and so on. Much of the information in /proc/cpuinfo on Linux is available via the CPUID instruction.

Your first programming task in this MP is to extend QEMU's emulation of the CPUID instruction to include a bit which indicates that a program is running on a VMM modified for CS 423 MP3. This should be indicated by setting bit 10 of EDX to 1 in the result of CPUID function 1. You don't need to edit the build system and create a special target; simply modify the behavior of the i386 target. Here's a test program:

 

 

 // test-cpuid.c

 // compile with: cc -m32 test-cpuid.c -o test-cpuid

 // run with: ./qemu-0.11.0/i386-linux-user/qemu-i386 ./test-cpuid

 #include <stdio.h>

 

 int main(int argc, char **argv)

 {

        unsigned int eax, ebx, ecx, edx;

        printf("[%s]\n", argv[0]);

        __asm__ __volatile__ ("movl $1, %%eax\n\t"

                              "cpuid"

                              : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx));

        if (edx & (1 << 10)) {

                printf("cpuid: cs423mp3 bit set\n");

        } else {

                printf("cpuid: cs423mp3 bit not set\n");

        }

        return 0;

 }

 

Please include comments describing the purpose of all of your changes to the qemu source code. Imagine you were creating an example to teach future CS 423 students how to extend qemu.

 

 

Problem 2: Cloning the PUSH DS instruction

 

The IA-32 ISA includes a notion of segments that qualify the memory address space in which data is stored. But you don't need to worry too much about what segments are for this MP; all you need to know is that there is a special 32-bit register called DS (the data segment selector) which can be accessed via the PUSH, POP and MOV instructions. For example, the following AT&T-style IA-32 assembly code would copy the value of DS into the ECX 32-bit general-purpose register, via the stack:

 

 pushl %ds

 popl %ecx

 

The instruction "pushl %ds" assembles to the single-byte opcode 0x1E.

 

Your goal in this part of the MP is to modify QEMU so that the opcode 0xF1 will have the same behavior as "pushl %ds". Note that opcode 0xF1 is normally used for the In-Circuit Emulator (ICE) breakpoint instruction (ICEBP). Since this instruction is typically used by hardware debuggers, not user-mode programs, we have chosen to repurpose it for this MP. You will need to disable QEMU's default behavior for the 0xF1 opcode as part of your extension. The following code snippet, when run under your modified QEMU, should have the same effects as the previous code snippet.

 

 .byte 0xf1

 popl %ecx

 

You can use the following C code to check your extension:

 

 // test-push-ds.c

 // compile with: cc -m32 test-push-ds.c -o test-push-ds

 // run with: ./qemu-0.11.0/i386-linux-user/qemu-i386 ./test-push-ds

 #include <stdio.h>

 

 int main(int argc, char **argv)

 {

         unsigned int ecx, edx;

         printf("[%s]\n", argv[0]);

         __asm__ __volatile__ ("pushl %%ds\n\t"

                               "popl %%ecx\n\t"

                               ".byte 0xf1\n\t"

                               "popl %%edx"

                               : "=c" (ecx), "=d" (edx));

         printf("expected vs. actual\n");

         printf("%08x     %08x\n", ecx, edx);

         return 0;

 }

 

Please include comments describing the purpose of all of your changes to the qemu source code. Imagine you were creating an example to teach future CS 423 students how to extend qemu.

 

 

Problem 3: Creating a new register

 

The IA-32 instruction set contains 8 32-bit general-purpose registers: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI. Not all registers are entirely equivalent; for example, ESP is used as the stack pointer for PUSH and POP instructions; EAX is used as the accumulator for some instructions.

 

Your goal in this part of the MP is to modify QEMU in two ways:
1. Create an additional 32-bit register (EEX) in the CPU state. Although the actual CPU does not contain this register, your modified QEMU will simulate a machine which has this new register.
2. Create a new instruction, with opcode 0xD6, which swaps the contents of your new register and EAX.

 

For the purposes of this MP, you do not need to extend the PUSHA instruction, the TSS, or any other CPU features to support your new register. While 0xD6 is generally defined as a "bad" or "invalid" instruction, QEMU must nonetheless have some default behavior defined for this instruction. As with opcode 0xF1, you will need to disable the default behavior of opcode 0xD6 as part of your extension.

 

Here's a test program which attempts to use the new register:

 

 // test-swap-eax-newreg.c

 // compile with: cc -m32 test-swap-eax-newreg.c -o test-swap-eax-newreg

 // run with: ./qemu-0.11.0/i386-linux-user/qemu-i386 ./test-swap-eax-newreg

 #include <stdio.h>

 

 int main(int argc, char **argv)

 {

        unsigned int eax, ecx, edx;

        printf("[%s]\n", argv[0]);

        __asm__ __volatile__ ("movl $0xc50423, %%eax\n\t"

                              ".byte 0xd6\n\t"

                              "movl %%eax, %%edx\n\t"

                              "movl $0x31337, %%eax\n\t"

                              ".byte 0xd6\n\t"

                              "movl %%eax, %%ecx\n\t"

                              "xorl %%eax, %%eax\n\t"

                              ".byte 0xd6\n\t"

                              : "=a" (eax), "=c" (ecx), "=d" (edx));

        printf("expected vs. actual\n");

        printf("00c50423     %08x\n", 0xc50423);

        printf("anything     %08x\n", edx);

        printf("00031337     %08x\n", 0x31337);

        printf("00c50423     %08x\n", ecx);

        printf("00000000     %08x\n", 0);

        printf("00031337     %08x\n", eax);

        return 0;

 }

 

Please include comments describing the purpose of all of your changes to the qemu source code. Imagine you were creating an example to teach future CS 423 students how to extend qemu.

 

 

Submitting your work

 

1. Make sure you've included comments describing the purpose of all of your changes to the qemu source code. Imagine you were creating an example to teach future CS 423 students how to extend qemu. Also, please ensure that your assignment works properly on csil-vmserv1 for grading purposes.

 

2. Create two directory structures: one containing the unmodified qemu source code and one containing your modified source code. Then use the diff command to extract your changes. The example below assumes that the source code that you have modified resides in the qemu-0.11.0 directory and that the original tarball of unmodified qemu source code still exists in this same top-level directory as well:

 

 mv qemu-0.11.0 qemu-0.11.1

 tar xzf qemu-0.11.0.tar.gz

 diff -uNr -x '*.out' -x '*.mak' -x 'config*.h' -x '*.texi' -x 'Makefile' -x '*.html' -x 'dflag' -x '*.1' -x '*.8' qemu-0.11.0 qemu-0.11.1 > cs423-mp3-<groupid>.diff

 

3. Check that the diff file includes only your changes!

 

4. Send an email with your cs423-mp3-<groupid>.diff and README files to the TA. The subject line should include "CS423 SP10 MP3".

 

5. You can resubmit your work anytime before the deadline.

 

 

Please be sure that your patch file compiles properly!

 

Place your patch file and the distributed qemu tar file into an empty directory and then use the following commands to verify:

 

tar xzf qemu-0.11.0.tar.gz

patch -d ./qemu-0.11.0 -p1 < cs423-mp3-<groupid>.diff

cd qemu-0.11.0

./configure --disable-kqemu --disable-sdl --audio-drv-list='' --audio-card-list='' --disable-vnc-tls --target-list=i386-linux-user --prefix=.

make

 

Your program should compile without error. If it doesn't, please verify your patch file and resubmit.