mips64emul documentation: Experimenting with mips64emul |
The emulator can be run in a mode where it doesn't emulate a real machine. In this "bare" machine mode, only a few simple devices are emulated, such as putchar device, and a simple framebuffer (for outputing pixels in a simple manner).
/* Hello world for mips64emul */ /* Note: The cast to a signed int causes the address to be sign-extended correctly to 0xffffffffb00000xx when compiled in 64-bit mode */ #define PUTCHAR_ADDRESS ((signed int)0xb0000000) #define HALT_ADDRESS ((signed int)0xb0000010) void printchar(char ch) { *((volatile unsigned char *) PUTCHAR_ADDRESS) = ch; } void halt(void) { *((volatile unsigned char *) HALT_ADDRESS) = 0; } void printstr(char *s) { while (*s) printchar(*s++); } void f(void) { printstr("Hello world\n"); halt(); } |
I recommend that you build a GCC cross compiler for the mips64-unknown-elf target, and install it. Other compilers could work too, but GCC is good because of its portability. Then try to compile the hello world program:
$ mips64-unknown-elf-gcc -O2 hello_mips.c -mips4 -mabi=64 -c $ mips64-unknown-elf-ld -Ttext 0xa800000000030000 -e f hello_mips.o -o hello_mips --oformat=elf64-bigmips $ file hello_mips hello_mips: ELF 64-bit MSB mips-4 executable, MIPS R3000_BE, version 1 (SYSV), statically linked, not stripped $ ./mips64emul -q -E testmips hello_mips Hello world $ mips64-unknown-elf-gcc -O2 hello_mips.c -c $ mips64-unknown-elf-ld -Ttext 0x80030000 -e f hello_mips.o -o hello_mips $ file hello_mips hello_mips: ELF 32-bit MSB mips-3 executable, MIPS R3000_BE, version 1 (SYSV), statically linked, not stripped $ ./mips64emul -q -E testmips hello_mips Hello world
As you can see above, a GCC configured for mips64-unknown-elf can produce both 64-bit and 32-bit binaries. If you don't want to run the entire Hello World program, but want to single-step through the execution to learn more about how MIPS programs run, then add -V to the command line:
$ ./mips64emul -V -E testmips hello_mips .. mips64emul> r cpu0: pc = a800000000030078cpu0: hi = 0000000000000000 lo = 0000000000000000 cpu0: zr = 0000000000000000 at = 0000000000000000 cpu0: v0 = 0000000000000000 v1 = 0000000000000000 .. cpu0: gp = a8000000000780c0 sp = ffffffffa0007f00 cpu0: fp = 0000000000000000 ra = 0000000000000000 mips64emul> s 15 <f> a800000000030078: 67bdfff0 daddiu sp,sp,-16 a80000000003007c: 3c04a800 lui a0,0xa800 a800000000030080: 3c010003 lui at,0x3 a800000000030084: 64840000 daddiu a0,a0,0 a800000000030088: 642100b8 daddiu at,at,184 a80000000003008c: 0004203c dsll32 a0,a0,0 a800000000030090: 0081202d daddu a0,a0,at a800000000030094: ffbf0000 sd ra,0(sp) [0xffffffffa0007ef0, data=0x0000000000000000] a800000000030098: 0c00c00a jal 0xa800000000030028 <printstr> a80000000003009c: 00000000 (d) nop <printstr("Hello world\n",0,0,0,..)> <printstr> a800000000030028: 67bdfff0 daddiu sp,sp,-16 a80000000003002c: ffb00000 sd s0,0(sp) [0xffffffffa0007ee0, data=0x0000000000000000] a800000000030030: ffbf0008 sd ra,8(sp) [0xffffffffa0007ee8, data=0xa8000000000300a0] a800000000030034: 90820000 lbu v0,0(a0) [0xa8000000000300b8 = $LC0, data=0x48] a800000000030038: 00021600 sll v0,v0,24 mips64emul> print v0 v0 = 0x0000000048000000 mips64emul>
The syntax of the single-step debugger shouldn't be too hard to grasp. Type 's' to single-step one instruction. Just pressing enter after that will repeat the 's' command. Type 'quit' to quit.
Hopefully this is enough to get you inspired. :-)
$ ppc-unknown-elf-gcc -O2 hello_ppc.c -c $ ppc-unknown-elf-ld -e f hello_ppc.o -o hello_ppc $ file hello_ppc hello_ppc: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), statically linked, not stripped $ ./mips64emul -q -E testppc hello_ppc Hello world
[ 2005-02-18: I haven't yet been able to build a GCC for ppc64 (only the binutils toolchain), because the gcc sources seem to include Linux header files that aren't present on my FreeBSD system. 32-bit PPC works ok, though. ]
cons:
This is a simple console device, for writing characters to the controlling terminal. Source code: devices/dev_cons.c
|
|
|||||||||||||||||||||||
mp:
This device controls the behaviour of CPUs in an emulated multi-processor system. Source code: devices/dev_mp.c
|
|
|||||||||||||||||||||||
fb:
A simple linear framebuffer, for graphics output. 640 x 480 pixels, 3 bytes per pixel (red, green, blue, 8 bits each). Source code: devices/dev_fb.c
|
|
While these devices may resemble real-world hardware, they are intentionally made simpler to use. (An exception is the framebuffer; some machines actually have simple linear framebuffers like this.)
If the physical address is 0x10000000, then for MIPS that means that it can be accessed at virtual address 0xffffffffb0000000. (Actually it can be accessed at 0xffffffff90000000 too, but devices should usually be accessed in a non-cached manner.)
(When using the bare PPC test machine, "testppc", the addresses are 0x10000000, 0x11000000 etc., so no need to add any virtual displacement.)
The mp device is agnostic when it comes to word-length. For example, when reading offset 0x0000 of the mp device, you may use any kind of read (an 8-bit read will work just as well as a 64-bit read, although the value will be truncated to 8 bits in the first case).
The cons device should be accessed using 8-bit reads and writes. Doing a getchar() (ie reading from offset 0x0000) returns 0x00 if no character was available.
On MIPS, the cons device is hardwired to interrupt 2 (the lowest hardware interrupt). Whenever a character is available, the interrupt is asserted. When there are no more available characters, the interrupt is deasserted. (Remember that the interrupt has to be enabled in the status register of the system coprocessor.)
Operating system kernels and other test programs can be downloaded from various places. Here are links to some of the kernels that I usually experiment with.
NOTE: This is not a list of kernels that work in the emulator. It is a list of kernels that I experiment with.
For more information about which of these that actually work, read the section in the Introduction chapter that lists guest operating systems. If a system is not listed there, it probably doesn't work in mips64emul.
ar x kernel-image-2.4.26-r3k-kn02_2.4.26-0.040505.1_mipsel.deb data.tar.gz tar xfzv data.tar.gz ./boot/vmlinux-2.4.26-r3k-kn02 mv boot/vmlinux-* .; rmdir boot
The following work even less than the ones listed above:
The following don't work at all, actually, because the PPC and SPARC modes are just skeletons so far. (To enable PPC and/or SPARC support, add --enable-ppc and --enable-sparc to the configure command line, respectively.)