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.