1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
|
<html>
<head><title>mips64emul Technical Documentation</title>
</head>
<body bgcolor="#ffffff" text="#000000" link="#4040f0" vlink="#404040" alink="#ff0000">
<p>
<table width=100%>
<tr><td width=100% bgcolor=#808070><font color=#ffffe0 size=6>
<b>mips64emul</b></font></td></tr>
</table>
<p>
<!-- The first 10 lines are cut away by the homepage updating script. -->
<!--
$Id: technical.html,v 1.37 2005/01/10 23:21:31 debug Exp $
Copyright (C) 2004-2005 Anders Gavare. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-->
<ul>
<li><h3><a href="index.html">User Documentation</a></h3>
<li><h3>Technical Documentation</h3>
</ul>
<hr>
<p><br>
<h2>mips64emul Technical Documentation</h2>
<p>
This page describes some of the internals of mips64emul. For more general
documentation, please read the <a href="index.html">User
Documentation</a>.
<p><br>
<h3>Contents:</h3>
<p>
<ul>
<li><a href="#overview">Overview</a>
<li><a href="#speed">Speed</a>
<li><a href="#net">Networking</a>
<li><a href="#devices">Emulation of hardware devices</a>
<li><a href="#regtest">Regression tests</a>
</ul>
<p><br>
<a name="overview"></a>
<h3>Overview</h3>
In simple terms, mips64emul is just a simple fetch-and-execute
loop; an instruction is fetched from memory, and executed.
<p>
In reality, a lot of things need to be handled. Before each instruction is
executed, the emulator checks to see if any interrupts are asserted which
are not masked away. If so, then an INT exception is generated. Exceptions
cause the program counter to be set to a specific value, and some of the
system coprocessor's registers to be set to values signifying what kind of
exception it was (an interrupt exception in this case).
<p>
Reading instructions from memory is done through a TLB, a translation
lookaside buffer. The TLB on MIPS is software controlled, which means that
the program running inside the emulator (for example an operating system
kernel) has to take care of manually updating the TLB. Some memory
addresses are translated into physical addresses directly, some are
translated into valid physical addresses via the TLB, and some memory
references are not valid. Invalid memory references cause exceptions.
<p>
After an instruction has been read from memory, the emulator checks which
opcode it contains and executes the instruction. Executing an instruction
usually involves reading some register and writing some register, or perhaps a
load from memory (or a store to memory). The program counter is increased
for every instruction.
<p>
Some memory references point to physical addresses which are not in the
normal RAM address space. They may point to hardware devices. If that is
the case, then loads and stores are converted into calls to a device
access function. The device access function is then responsible for
handling these reads and writes. For example, a graphical framebuffer
device may put a pixel on the screen when a value is written to it, or a
serial controller device may output a character to stdout when written to.
<p><br>
<a name="speed"></a>
<h3>Speed</h3>
There are two modes in which the emulator can run, <b>a</b>) a straight forward
loop which fetches one instruction from emulated RAM and executes it
(described in the previous section), and <b>b</b>)
using dynamic binary translation.
<p>
Mode <b>a</b> is very slow. On a 2.8 GHz Intel Xeon host the resulting
emulated machine is rougly equal to a 7 MHz R3000 (or a 3.5 MHz R4000).
The actual performance varies a lot, maybe between 5 and 10 million
instructions per second, depending on workload.
<p>
Mode <b>b</b> ("bintrans") is still to be considered experimental, but
gives higher performance than mode <b>a</b>. It translates MIPS machine
code into machine code that can be executed on the host machine
on-the-fly. The translation itself obviously takes some time, but this is
usually made up for by the fact that the translated code chunks are
executed multiple times.
To run the emulator with binary translation enabled, just add <b>-b</b>
to the command line.
<p>
Only small pieces of MIPS machine code are translated, usually the size of
a function, or less. There is no "intermediate representation" code, so
all translations are done directly from MIPS to host machine code.
<p>
The default bintrans cache size is 16 MB, but you can change this by adding
-DBINTRANS_SIZE_IN_MB=<i>xx</i> to your CFLAGS environment variable before
running the configure script.
<p>
By default, an emulated OS running under DECstation emulation which listens to
interrupts from the mc146818 clock will get interrupts that are close to the
host's clock. That is, if the emulated OS says it wants 100 interrupts per
second, it will get approximately 100 interrupts per real second.
<p>
There is however a -I option, which sets the number of emulated cycles per
seconds to a fixed value. Let's say you wish to make the emulated OS think it
is running on a 40 MHz DECstation, and not a 7 MHz one, then you can add
-I 40000000 to the command line. This will not make the emulation faster, of
course. It might even make it seem slower; for example, if NetBSD/pmax waits
2 seconds for SCSI devices to settle during bootup, those 2 seconds will take
2*40000000 cycles (which will take more time than 2*7000000).
<p>
The -I option is also necessary if you want to run deterministic experiments,
if a mc146818 device is present.
<p>
Some emulators make claims such as "x times slowdown," but in the case of
mips64emul, the host is often not a MIPS-based machine, and hence comparing
one MIPS instruction to a host instruction doesn't work. Performance depends on
a lot of factors, including (but not limited to) host architecture, host speed,
which compiler and compiler flags were used to build mips64emul, what the
workload is, and so on. For example, if an emulated operating system tries
to read a block from disk, from its point of view the read was instantaneous
(no waiting). So 1 MIPS in an emulated OS might have taken more than one
million instructions on a real machine. Because of this, imho it is best
to measure performance as the actual (real-world) time it takes to perform
a task with the emulator.
<p><br>
<a name="net"></a>
<h3>Networking</h3>
Running an entire operating system under emulation is very interesting in
itself, but for several reasons, running a modern OS without access to
TCP/IP networking is a bit akward. Hence, I feel the need to implement TCP/IP
(networking) support in the emulator.
<p>
As far as I have understood it, there seems to be two different ways to go:
<ol>
<li>Forward ethernet packets from the emulated ethernet controller to
the host machine's ethernet controller, and capture incoming
packets on the host's controller, giving them back to the
emulated OS. Characteristics are:
<ul>
<li>Requires <i>direct</i> access to the host's NIC, which
means on most platforms that the emulator cannot be
run as a normal user!
<li>Reduced portability, as not every host operating system
uses the same programming interface for dealing with
hardware ethernet controllers directly.
<li>When run on a switched network, it might be problematic to
connect from the emulated OS to the OS running on the
host, as packets sent out on the host's NIC are not
received by itself. (?)
</ul>
<p>
or
<p>
<li>Whenever the emulated ethernet controller wishes to send a packet,
the emulator looks at the packet and creates a response. Packets
that can have an immediate response never go outside the emulator,
other packet types have to be converted into suitable other
connection types (UDP, TCP, etc). Characteristics:
<ul>
<li>Each packet type sent out on the emulated NIC must be handled.
This means that I have to do a lot of coding.
(I like this, because it gives me an opportunity to
learn about networking protocols.)
<li>By not relying on access to the host's NIC directly,
portability is maintained. (It would be sad if the networking
portion of a portable emulator isn't as portable as the
rest of the emulator.)
<li>The emulator can be run as a normal user process, does
not require root privilegies.
<li>Connecting from the emulated OS to the host's OS should
not be problematic.
<li>The emulated OS will experience the network just as a single
machine behind a NAT gateway/firewall would. The emulated
OS is thus automatically protected from the outside world.
</ul>
</ol>
Other emulators that I have heard of seem to use the first one, if they
support networking.
<p>
Since I have choosen the second kind of implementation, I have to write
support explicitly for any kind of network protocol that should be
supported. As of 2004-07-09, the following has been implemented and seems
to work under at least NetBSD/pmax and OpenBSD/pmax under DECstation -D2
emulation:
<ul>
<li>ARP requests sent out from the emulated NIC are interpreted,
and converted to ARP responses. (This is used by the emulated OS
to find out the MAC address of the gateway.)
<li>ICMP echo requests (that is the kind of packet produced by the
<b>ping</b> program) are interpreted and converted to ICMP echo
replies, <i>regardless of the IP address</i>. This means that
running ping from within the emulated OS will <i>always</i>
receive a response. The ping packets never leave the emulated
environment.
<li>UDP packets are interpreted and passed along to the outside world.
If the emulator receives an UDP packet from the outside world, it
is converted into an UDP packet for the emulated OS. (This is not
implemented very well yet, but seems to be enough for nameserver
lookups, tftp file transfers, and NFS mounts using UDP.)
<li>TCP packets are interpreted one at a time, similar to how UDP
packets are handled (but more state is kept for each connection).
<font color="#ff0000">NOTE: Much of the TCP handling code is very
ugly and hardcoded.</font>
<li>RARP is not implemented yet. (I haven't needed it so far.)
</ul>
The gateway machine, which is the only "other" machine that the emulated
OS sees on its emulated network, works as a NAT-style firewall/gateway. It
has a fixed IPv4 address of 10.0.0.254. An OS running in the emulator
can thus have any 10.x.x.x address; a typical choice would be 10.0.0.1.
<p>
Inside emulated NetBSD or OpenBSD, running the following commands should
configure the emulated NIC:
<pre>
# <b>ifconfig le0 10.0.0.1</b>
# <b>route add default 10.0.0.254</b>
add net default: gateway 10.0.0.254
</pre>
If you want nameserver lookups to work, you need a valid /etc/resolv.conf
as well:
<pre>
# <b>echo nameserver 129.16.1.3 > /etc/resolv.conf</b>
</pre>
(But replace 129.16.1.3 with the actual real-world IP address of your
nearest nameserver.)
<p>
Now, host lookups should work:
<pre>
# <b>host -a www.netbsd.org</b>
Trying null domain
rcode = 0 (Success), ancount=2
The following answer is not authoritative:
The following answer is not verified as authentic by the server:
www.netbsd.org 86400 IN AAAA 2001:4f8:4:7:290:27ff:feab:19a7
www.netbsd.org 86400 IN A 204.152.184.116
For authoritative answers, see:
netbsd.org 83627 IN NS uucp-gw-2.pa.dec.com
netbsd.org 83627 IN NS ns.netbsd.org
netbsd.org 83627 IN NS adns1.berkeley.edu
netbsd.org 83627 IN NS adns2.berkeley.edu
netbsd.org 83627 IN NS uucp-gw-1.pa.dec.com
Additional information:
ns.netbsd.org 83627 IN A 204.152.184.164
uucp-gw-1.pa.dec.com 172799 IN A 204.123.2.18
uucp-gw-2.pa.dec.com 172799 IN A 204.123.2.19
</pre>
To transfer files via UDP, you can use the tftp program.
<pre>
# <b>tftp 12.34.56.78</b>
tftp> <b>get filename</b>
Received XXXXXX bytes in X.X seconds
tftp> <b>quit</b>
#
</pre>
or, to do it non-interactively (with ugly output):
<pre>
# <b>echo get filename | tftp 12.34.56.78</b>
tftp> Received XXXXXX bytes in X.X seconds
tftp> #
</pre>
This, of course, requires that you have put the file <i>filename</i> in
the root directory of the tftp server (12.34.56.78).
<p>
It is also possible to run NFS via UDP. This is very useful if you want to
share entire directory trees between the emulated environment and another
machine. These instruction will work for FreeBSD, if you are running
something else, use your imagination to modify them:
<ul>
<li>On the server, add a line to your /etc/exports file, exporting
the files you wish to use in the emulator:<pre>
<b>/tftpboot -mapall=nobody -ro 123.11.22.33</b>
</pre>
where 123.11.22.33 is the IP address of the machine running the
emulator process, as seen from the outside world.
<p>
<li>Then start up the programs needed to serve NFS via UDP. Note the
-n argument to mountd. This is needed to tell mountd to accept
connections from unprivileged ports (because the emulator does
not need to run as root).<pre>
# <b>portmap</b>
# <b>nfsd -u</b> <--- u for UDP
# <b>mountd -n</b>
</pre>
<li>In the guest OS in the emulator, once you have ethernet and IPv4
configured so that you can use UDP, mounting the filesystem
should now be possible: (this example is for NetBSD/pmax
or OpenBSD/pmax)<pre>
# <b>mount -o ro,-r=1024,-w=1024,-U,-3 my.server.com:/tftpboot /mnt</b>
or
# <b>mount my.server.com:/tftpboot /mnt</b>
</pre>
If you don't supply the read and write sizes, there is a risk
that the default values are too large. The emulator currently
does not handle fragmentation/defragmentation of <i>outgoing</i>
packets, so going above the ethernet frame size (1518) is a very
bad idea. Incoming packets (reading from nfs) should work, though,
for example during an NFS install.
</ul>
The example above uses read-only mounts. That is enough for things like
letting NetBSD/pmax or OpenBSD/pmax install via NFS, without the need for
a CDROM ISO image. You can use a read-write mount if you wish to share
files in both directions, but then you should be aware of the
fragmentation issue mentioned above.
<p>
TCP is implemented to some extent, but should not be considered to be
stable yet. It is enough to let NetBSD/pmax and OpenBSD/pmax install via
ftp, though.
<p><br>
<a name="devices"></a>
<h3>Emulation of hardware devices</h3>
Each file in the device/ directory is responsible for one hardware
device. These are used from src/machine.c, when initializing which hardware a
particular machine model will be using.
(I'll be using the name 'foo' as the name of the device in all these
examples. This is pseudo code, it might need some modification to
actually compile and run.)
<p>
Each device should have the following:
<p>
<ul>
<li>An init function in dev_foo.c. It would typically look
something like this:
<pre>
void dev_foo_init(struct cpu *cpu, struct memory *mem,
uint64_t baseaddr, int irq_nr)
{
struct foo_data *d;
d = malloc(sizeof(struct foo_data));
if (d == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
memset(d, 0, sizeof(struct foo_data));
d->irq_nr = irq_nr;
memory_device_register(mem, "foo", baseaddr,
DEV_FOO_LENGTH, dev_foo_access, d);
/* This should only be here if the device
has a tick function: */
cpu_add_tickfunction(cpu, dev_foo_tick, d,
FOO_TICKSHIFT);
}
</pre><br>
<li>The init function, the access function (described further down)
and DEV_FOO_LENGTH should be defined in include/devices.h.
<p>
<li>At the top of dev_foo.c, the foo_data struct should be defined.
<pre>
struct foo_data {
int irq_nr;
/* ... */
}
</pre><br>
<li>If foo has a tick function (that is, something that needs to be
run at regular intervals) then FOO_TICKSHIFT and a tick function
need to be defined as well:
<pre>
#define FOO_TICKSHIFT 10
void dev_foo_tick(struct cpu *cpu, void *extra)
{
struct foo_data *d = (struct foo_data *) extra;
if (.....)
cpu_interrupt(cpu, d->irq_nr);
else
cpu_interrupt_ack(cpu, d->irq_nr);
}
</pre><br>
<li>And last but not least, the device should have an access function.
The access function is called whenever there is a load or store
to an address which is in the device' memory mapped region.
<pre>
int dev_foo_access(struct cpu *cpu, struct memory *mem,
uint64_t relative_addr, unsigned char *data, size_t len,
int writeflag, void *extra)
{
struct foo_data *d = extra;
uint64_t idata = 0, odata = 0;
idata = memory_readmax64(cpu, data, len);
switch (relative_addr) {
/* .... */
}
if (writeflag == MEM_READ)
memory_writemax64(cpu, data, len, odata);
/* Perhaps interrupts need to be asserted or
deasserted: */
dev_foo_tick(cpu, extra);
/* Return successfully. */
return 1;
}
</pre><br>
</ul>
<p>
The return value of the access function has until 20040702 been a
true/false value; 1 for success, or 0 for device access failure. A device
access failure will be seen as a MIPS DBE exception from the CPU.
<p>
Right now I'm converting the devices to support arbitrary memory latency
values. The return value is now the number of cycles that the read or
write access took. A value of 1 means one cycle, a value of 10 means 10
cycles. Negative values are used for device access failures, and the
absolute value of the value is then the number of cycles; a value of -5
means that the access failed, and took 5 cycles.
<p>
To be compatible with pre-20040702 devices, a return value of 0 is treated
by the caller (in src/memory.c) as a value of -1.
<p><br>
<a name="regtest"></a>
<h3>Regression tests</h3>
In order to make sure that the emulator actually works like it is supposed
to, it must be tested. For this purpose, there is a simple regression
testing framework in the <b>tests/</b> directory.
<p>
<i>NOTE: The regression testing framework is basically just a skeleton so
far.
Regression tests are very good to have. However, the fact that complete
operating systems can run in the emulator indicate that the emulation is
probably not too incorrect. This makes it less of a priority to write
regression tests.</i>
<p>
To run all the regression tests, type <b>make regtest</b>. Each assembly
language file matching the pattern <b>test_*.S</b> will be compiled and
linked into a 64-bit MIPS ELF (using a gcc cross compiler), and run in the
emulator. If everything goes well, you should see something like this:
<pre>
$ make regtest
cd tests; make run_tests; cd ..
gcc33 -Wall -fomit-frame-pointer -fmove-all-movables -fpeephole -O2
-mcpu=ev5 -I/usr/X11R6/include -lm -L/usr/X11R6/lib -lX11 do_tests.c
-o do_tests
do_tests.c: In function `main':
do_tests.c:173: warning: unused variable `s'
/var/tmp//ccFOupvD.o: In function `do_tests':
/var/tmp//ccFOupvD.o(.text+0x3a8): warning: tmpnam() possibly used
unsafely; consider using mkstemp()
mips64-unknown-elf-gcc -g -O3 -fno-builtin -fschedule-insns -mips64
-mabi=64 test_common.c -c -o test_common.o
./do_tests "mips64-unknown-elf-gcc -g -O3 -fno-builtin -fschedule-insns
-mips64 -mabi=64" "mips64-unknown-elf-as -mabi=64 -mips64"
"mips64-unknown-elf-ld -Ttext 0xa800000000030000 -e main
--oformat=elf64-bigmips" "../mips64emul"
Starting tests:
test_addu.S
test_daddu.S
test_dummy.S
test_clo_clz.S
test_dclo_dclz.S
Done. (5 tests done)
PASS: 5
FAIL: 0
All tests OK
</pre>
<p>
Each test writes output to stdout, and there is a <b>test_*.good</b> for
each <b>.S</b> file which contains the wanted output. If the actual output
matches the <b>.good</b> file, then the test passes, otherwise it fails.
<p>
Read <b>tests/README</b> for more information.
</body>
</html>
|