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