/[gxemul]/trunk/src/emul.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/emul.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6 - (show annotations)
Mon Oct 8 16:18:11 2007 UTC (13 years ago) by dpavlin
File MIME type: text/plain
File size: 35130 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.772 2005/06/04 12:02:16 debug Exp $
20050428	Disabling the "-fmove-all-movables" option in the configure
		script, because it causes the compile to fail on OpenBSD/sgi.
20050502	Minor updates.
20050503	Removing the WRT54G mode (it was bogus anyway), and adding a
		comment about Windows NT for MIPS in doc/experiments.html.
		Minor updates to the x86 instruction decoding.
20050504	Adding some more x86 instructions.
		Adding support for reading files from ISO9660 CDROMs (including
		gzipped files). It's an ugly hack, but it seems to work.
		Various other minor updates (dev_vga.c, pc_bios.c etc).
20050505	Some more x86-related updates.
		Beginning (what I hope will be) a major code cleanup phase.
		"bootris" (an x86 bootsector) runs :-)
20050506	Adding some more x86 instructions.
20050507	tmpnam => mkstemp.
		Working on a hack to allow VGA charcells to be shown even when
		not running with X11.
		Adding more x86 instructions.
20050508	x86 32-bit SIB addressing fix, and more instructions.
20050509	Adding more x86 instructions.
20050510	Minor documentation updates, and other updates (x86 stuff etc.)
20050511	More x86-related updates.
20050513	Various updates, mostly x86-related. (Trying to fix flag 
		calculation, factoring out the ugly shift/rotate code, and
		some other things.)
20050514	Adding support for loading some old i386 a.out executables.
		Finally beginning the cleanup of machine/PROM/bios dependant
		info.
		Some minor documentation updates.
		Trying to clean up ARCBIOS stuff a little.
20050515	Trying to make it possible to actually use more than one disk
		type per machine (floppy, ide, scsi).
		Trying to clean up the kbd vs PROM console stuff. (For PC and
		ARC emulation modes, mostly.)
		Beginning to add an 8259 interrupt controller, and connecting
		it to the x86 emulation.
20050516	The first x86 interrupts seem to work (keyboard stuff).
		Adding a 8253/8254 programmable interval timer skeleton.
		FreeDOS now reaches a command prompt and can be interacted
		with.
20050517	After some bugfixes, MS-DOS also (sometimes) reaches a
		command prompt now.
		Trying to fix the pckbc to work with MS-DOS' keyb.com, but no
		success yet.
20050518	Adding a simple 32-bit x86 MMU skeleton.
20050519	Some more work on the x86 stuff. (Beginning the work on paging,
		and various other fixes).
20050520	More updates. Working on dev_vga (4-bit graphics modes), adding
		40 columns support to the PC bios emulation.
		Trying to add support for resizing windows when switching
		between graphics modes.
20050521	Many more x86-related updates.
20050522	Correcting the initial stack pointer's sign-extension for
		ARCBIOS emulation (thanks to Alec Voropay for noticing the
		error).
		Continuing on the cleanup (ARCBIOS etc).
		dev_vga updates.
20050523	More x86 updates: trying to add some support for protected mode
		interrupts (via gate descriptors) and many other fixes.
		More ARCBIOS cleanup.
		Adding a device flag which indicates that reads cause no
		side-effects. (Useful for the "dump" command in the debugger,
		and other things.)
		Adding support for directly starting up x86 ELFs, skipping the
		bootloader stage. (Most ELFs, however, are not suitable for
		this.)
20050524	Adding simple 32-bit x86 TSS task switching, but no privilege
		level support yet.
		More work on dev_vga. A small "Copper bars" demo works. :-)
		Adding support for Trap Flag (single-step exceptions), at least
		in real mode, and various other x86-related fixes.
20050525	Adding a new disk image prefix (gH;S;) which can be used to
		override the default nr of heads and sectors per track.
20050527	Various bug fixes, more work on the x86 mode (stack change on
		interrupts between different priv.levels), and some minor
		documentation updates.
20050528	Various fixes (x86 stuff).
20050529	More x86 fixes. An OpenBSD/i386 bootfloppy reaches userland
		and can be interacted with (although there are problems with
		key repetition). NetBSD/i386 triggers a serious CISC-related
		problem: instruction fetches across page boundaries, where
		the later part isn't actually part of the instruction.
20050530	Various minor updates. (Documentation updates, etc.)
20050531	Adding some experimental code (experiments/new_test_*) which
		could be useful for dynamic (but not binary) translation in
		the future.
20050602	Adding a dummy ARM skeleton.
		Fixing the pckbc key repetition problem (by adding release
		scancodes for all keypresses).
20050603	Minor updates for the next release.
20050604	Release testing. Minor updates.

==============  RELEASE 0.3.3  ==============

20050604	There'll probably be a 0.3.3.1 release soon, with some very
		very tiny updates.


1 /*
2 * Copyright (C) 2003-2005 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: emul.c,v 1.203 2005/06/03 07:39:27 debug Exp $
29 *
30 * Emulation startup and misc. routines.
31 */
32
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <limits.h>
37 #include <stdarg.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "arcbios.h"
42 #include "bintrans.h"
43 #include "cpu.h"
44 #include "emul.h"
45 #include "console.h"
46 #include "debugger.h"
47 #include "device.h"
48 #include "diskimage.h"
49 #include "machine.h"
50 #include "memory.h"
51 #include "mips_cpu_types.h"
52 #include "misc.h"
53 #include "net.h"
54 #include "sgi_arcbios.h"
55 #include "x11.h"
56
57
58 extern int force_debugger_at_exit;
59
60 extern int extra_argc;
61 extern char **extra_argv;
62
63 extern int verbose;
64 extern int quiet_mode;
65
66 extern struct emul *debugger_emul;
67 extern struct diskimage *diskimages[];
68
69 static char *diskimage_types[] = DISKIMAGE_TYPES;
70
71
72 /*
73 * add_dump_points():
74 *
75 * Take the strings breakpoint_string[] and convert to addresses
76 * (and store them in breakpoint_addr[]).
77 *
78 * TODO: This function should be moved elsewhere.
79 */
80 static void add_dump_points(struct machine *m)
81 {
82 int i;
83 int string_flag;
84 uint64_t dp;
85
86 for (i=0; i<m->n_breakpoints; i++) {
87 string_flag = 0;
88 dp = strtoull(m->breakpoint_string[i], NULL, 0);
89
90 /*
91 * If conversion resulted in 0, then perhaps it is a
92 * symbol:
93 */
94 if (dp == 0) {
95 uint64_t addr;
96 int res = get_symbol_addr(&m->symbol_context,
97 m->breakpoint_string[i], &addr);
98 if (!res)
99 fprintf(stderr,
100 "WARNING! Breakpoint '%s' could not be"
101 " parsed\n",
102 m->breakpoint_string[i]);
103 else {
104 dp = addr;
105 string_flag = 1;
106 }
107 }
108
109 /*
110 * TODO: It would be nice if things like symbolname+0x1234
111 * were automatically converted into the correct address.
112 */
113
114 if ((dp >> 32) == 0 && ((dp >> 31) & 1))
115 dp |= 0xffffffff00000000ULL;
116 m->breakpoint_addr[i] = dp;
117
118 debug("breakpoint %i: 0x%016llx", i, (long long)dp);
119 if (string_flag)
120 debug(" (%s)", m->breakpoint_string[i]);
121 debug("\n");
122 }
123 }
124
125
126 /*
127 * fix_console():
128 */
129 static void fix_console(void)
130 {
131 console_deinit();
132 }
133
134
135 /*
136 * iso_load_bootblock():
137 *
138 * Try to load a kernel from an ISO 9660 disk image. iso_type is 1 for
139 * "CD001" (standard), 2 for "CDW01" (ECMA), and 3 for "CDROM" (Sierra).
140 *
141 * TODO: This function uses too many magic offsets and so on; it should be
142 * cleaned up some day.
143 *
144 * Returns 1 on success, 0 on failure.
145 */
146 static int iso_load_bootblock(struct machine *m, struct cpu *cpu,
147 int disk_id, int disk_type, int iso_type, unsigned char *buf,
148 int *n_loadp, char ***load_namesp)
149 {
150 char str[35];
151 int filenr, i, ofs, dirlen, res = 0, res2, iadd = 4;
152 int found_dir;
153 uint64_t dirofs;
154 uint64_t fileofs, filelen;
155 unsigned char *dirbuf = NULL, *dp;
156 unsigned char *match_entry = NULL;
157 char *p, *filename_orig;
158 char *filename = strdup(cpu->machine->boot_kernel_filename);
159 unsigned char *filebuf = NULL;
160 char *tmpfilename = NULL;
161 char **new_array;
162 int tmpfile_handle;
163
164 if (filename == NULL) {
165 fatal("out of memory\n");
166 exit(1);
167 }
168 filename_orig = filename;
169
170 debug("ISO9660 boot:\n");
171 debug_indentation(iadd);
172
173 /* Volume ID: */
174 ofs = iso_type == 3? 48 : 40;
175 memcpy(str, buf + ofs, sizeof(str));
176 str[32] = '\0'; i = 31;
177 while (i >= 0 && str[i]==' ')
178 str[i--] = '\0';
179 if (str[0])
180 debug("\"%s\"", str);
181 else {
182 /* System ID: */
183 ofs = iso_type == 3? 16 : 8;
184 memcpy(str, buf + ofs, sizeof(str));
185 str[32] = '\0'; i = 31;
186 while (i >= 0 && str[i]==' ')
187 str[i--] = '\0';
188 if (str[0])
189 debug("\"%s\"", str);
190 else
191 debug("(no ID)");
192 }
193
194 debug(":%s\n", filename);
195
196
197 /*
198 * Traverse the directory structure to find the kernel.
199 */
200
201 dirlen = buf[0x84] + 256*buf[0x85] + 65536*buf[0x86];
202 if (dirlen != buf[0x8b] + 256*buf[0x8a] + 65536*buf[0x89])
203 fatal("WARNING: Root directory length mismatch?\n");
204
205 dirofs = (int64_t)(buf[0x8c] + (buf[0x8d] << 8) + (buf[0x8e] << 16) +
206 (buf[0x8f] << 24)) * 2048;
207
208 /* debug("root = %i bytes at 0x%llx\n", dirlen, (long long)dirofs); */
209
210 dirbuf = malloc(dirlen);
211 if (dirbuf == NULL) {
212 fatal("out of memory in iso_load_bootblock()\n");
213 exit(1);
214 }
215
216 res2 = diskimage_access(m, disk_id, disk_type, 0, dirofs, dirbuf,
217 dirlen);
218 if (!res2) {
219 fatal("Couldn't read the disk image. Aborting.\n");
220 goto ret;
221 }
222
223 found_dir = 1; /* Assume root dir */
224 dp = dirbuf; filenr = 1;
225 p = NULL;
226 while (dp < dirbuf + dirlen) {
227 int i, nlen = dp[0];
228 int x = dp[2] + (dp[3] << 8) + (dp[4] << 16) + (dp[5] << 24);
229 int y = dp[6] + (dp[7] << 8);
230 char direntry[65];
231
232 dp += 8;
233
234 /*
235 * As long as there is an \ or / in the filename, then we
236 * have not yet found the directory.
237 */
238 p = strchr(filename, '/');
239 if (p == NULL)
240 p = strchr(filename, '\\');
241
242 /* debug("%i%s: %i, %i, \"", filenr, filenr == found_dir?
243 " [CURRENT]" : "", x, y); */
244 for (i=0; i<nlen && i<sizeof(direntry)-1; i++)
245 if (dp[i]) {
246 direntry[i] = dp[i];
247 /* debug("%c", dp[i]); */
248 } else
249 break;
250 /* debug("\"\n"); */
251 direntry[i] = '\0';
252
253 /* A directory name match? */
254 if (p != NULL && strncasecmp(filename, direntry, nlen) == 0
255 && nlen == (size_t)p - (size_t)filename && found_dir == y) {
256 found_dir = filenr;
257 filename = p+1;
258 dirofs = 2048 * (int64_t)x;
259 }
260
261 dp += nlen;
262
263 /* 16-bit aligned lenght: */
264 if (nlen & 1)
265 dp ++;
266
267 filenr ++;
268 }
269
270 p = strchr(filename, '/');
271 if (p == NULL)
272 p = strchr(filename, '\\');
273
274 if (p != NULL) {
275 char *blah = filename_orig;
276
277 fatal("could not find '%s' in /", filename);
278
279 /* Print the first part of the filename: */
280 while (blah != filename)
281 fatal("%c", *blah++);
282
283 fatal("\n");
284 goto ret;
285 }
286
287 /* debug("dirofs = 0x%llx\n", (long long)dirofs); */
288
289 /* Free the old dirbuf, and allocate a new one: */
290 free(dirbuf);
291 dirbuf = malloc(512);
292 if (dirbuf == NULL) {
293 fatal("out of memory in iso_load_bootblock()\n");
294 exit(1);
295 }
296
297 for (;;) {
298 int len, i;
299
300 /* Too close to another sector? Then realign. */
301 if ((dirofs & 2047) + 70 > 2047) {
302 dirofs = (dirofs | 2047) + 1;
303 /* debug("realign dirofs = 0x%llx\n", dirofs); */
304 }
305
306 res2 = diskimage_access(m, disk_id, disk_type, 0, dirofs,
307 dirbuf, 256);
308 if (!res2) {
309 fatal("Couldn't read the disk image. Aborting.\n");
310 goto ret;
311 }
312
313 dp = dirbuf;
314 len = dp[0];
315 if (len < 2)
316 break;
317
318 /*
319 * TODO: Actually parse the directory entry!
320 *
321 * Haha, this must be rewritten.
322 */
323 for (i=32; i<len; i++) {
324 if (i < len - strlen(filename))
325 if (strncasecmp(filename, (char *)dp + i,
326 strlen(filename)) == 0) {
327 /* The filename was found somewhere
328 in the directory entry. */
329 if (match_entry != NULL) {
330 fatal("TODO: I'm too lazy to"
331 " implement a correct "
332 "directory parser right "
333 "now... (BUG)\n");
334 exit(1);
335 }
336 match_entry = malloc(512);
337 if (match_entry == NULL) {
338 fatal("out of memory\n");
339 exit(1);
340 }
341 memcpy(match_entry, dp, 512);
342 break;
343 }
344 }
345
346 dirofs += len;
347 }
348
349 if (match_entry == NULL) {
350 char *blah = filename_orig;
351
352 fatal("could not find '%s' in /", filename);
353
354 /* Print the first part of the filename: */
355 while (blah != filename)
356 fatal("%c", *blah++);
357
358 fatal("\n");
359 goto ret;
360 }
361
362 fileofs = match_entry[2] + (match_entry[3] << 8) +
363 (match_entry[4] << 16) + (match_entry[5] << 24);
364 filelen = match_entry[10] + (match_entry[11] << 8) +
365 (match_entry[12] << 16) + (match_entry[13] << 24);
366 fileofs *= 2048;
367
368 /* debug("filelen=%llx fileofs=%llx\n", (long long)filelen,
369 (long long)fileofs); */
370
371 filebuf = malloc(filelen);
372 if (filebuf == NULL) {
373 fatal("could not allocate %lli bytes to read the file"
374 " from the disk image!\n", (long long)filelen);
375 goto ret;
376 }
377
378 tmpfilename = strdup("/tmp/gxemul.XXXXXXXXXXXX");
379
380 debug("extracting %lli bytes into %s\n",
381 (long long)filelen, tmpfilename);
382
383 res2 = diskimage_access(m, disk_id, disk_type, 0, fileofs, filebuf,
384 filelen);
385 if (!res2) {
386 fatal("could not read the file from the disk image!\n");
387 goto ret;
388 }
389
390 tmpfile_handle = mkstemp(tmpfilename);
391 if (tmpfile_handle < 0) {
392 fatal("could not create %s\n", tmpfilename);
393 exit(1);
394 }
395 write(tmpfile_handle, filebuf, filelen);
396 close(tmpfile_handle);
397
398 /* Add the temporary filename to the load_namesp array: */
399 (*n_loadp)++;
400 new_array = malloc(sizeof(char *) * (*n_loadp));
401 if (new_array == NULL) {
402 fatal("out of memory\n");
403 exit(1);
404 }
405 memcpy(new_array, *load_namesp, sizeof(char *) * (*n_loadp));
406 *load_namesp = new_array;
407
408 /* This adds a Backspace char in front of the filename; this
409 is a special hack which causes the file to be removed once
410 it has been loaded. */
411 tmpfilename = realloc(tmpfilename, strlen(tmpfilename) + 2);
412 memmove(tmpfilename + 1, tmpfilename, strlen(tmpfilename) + 1);
413 tmpfilename[0] = 8;
414
415 (*load_namesp)[*n_loadp - 1] = tmpfilename;
416
417 res = 1;
418
419 ret:
420 if (dirbuf != NULL)
421 free(dirbuf);
422
423 if (filebuf != NULL)
424 free(filebuf);
425
426 if (match_entry != NULL)
427 free(match_entry);
428
429 free(filename_orig);
430
431 debug_indentation(-iadd);
432 return res;
433 }
434
435
436 /*
437 * load_bootblock():
438 *
439 * For some emulation modes, it is possible to boot from a harddisk image by
440 * loading a bootblock from a specific disk offset into memory, and executing
441 * that, instead of requiring a separate kernel file. It is then up to the
442 * bootblock to load a kernel.
443 *
444 * Returns 1 on success, 0 on failure.
445 */
446 static int load_bootblock(struct machine *m, struct cpu *cpu,
447 int *n_loadp, char ***load_namesp)
448 {
449 int boot_disk_id, boot_disk_type = 0, n_blocks, res, readofs,
450 iso_type, retval = 0;
451 unsigned char minibuf[0x20];
452 unsigned char *bootblock_buf;
453 uint64_t bootblock_offset;
454 uint64_t bootblock_loadaddr, bootblock_pc;
455
456 boot_disk_id = diskimage_bootdev(m, &boot_disk_type);
457 if (boot_disk_id < 0)
458 return 0;
459
460 switch (m->machine_type) {
461 case MACHINE_DEC:
462 /*
463 * The first few bytes of a disk contains information about
464 * where the bootblock(s) are located. (These are all 32-bit
465 * little-endian words.)
466 *
467 * Offset 0x10 = load address
468 * 0x14 = initial PC value
469 * 0x18 = nr of 512-byte blocks to read
470 * 0x1c = offset on disk to where the bootblocks
471 * are (in 512-byte units)
472 * 0x20 = nr of blocks to read...
473 * 0x24 = offset...
474 *
475 * nr of blocks to read and offset are repeated until nr of
476 * blocks to read is zero.
477 */
478 res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0,
479 minibuf, sizeof(minibuf));
480
481 bootblock_loadaddr = minibuf[0x10] + (minibuf[0x11] << 8)
482 + (minibuf[0x12] << 16) + (minibuf[0x13] << 24);
483
484 /* Convert loadaddr to uncached: */
485 if ((bootblock_loadaddr & 0xf0000000ULL) != 0x80000000 &&
486 (bootblock_loadaddr & 0xf0000000ULL) != 0xa0000000)
487 fatal("\nWARNING! Weird load address 0x%08x.\n\n",
488 (int)bootblock_loadaddr);
489 bootblock_loadaddr &= 0x0fffffffULL;
490 bootblock_loadaddr |= 0xffffffffa0000000ULL;
491
492 bootblock_pc = minibuf[0x14] + (minibuf[0x15] << 8)
493 + (minibuf[0x16] << 16) + (minibuf[0x17] << 24);
494
495 bootblock_pc &= 0x0fffffffULL;
496 bootblock_pc |= 0xffffffffa0000000ULL;
497 cpu->pc = bootblock_pc;
498
499 debug("DEC boot: loadaddr=0x%08x, pc=0x%08x",
500 (int)bootblock_loadaddr, (int)bootblock_pc);
501
502 readofs = 0x18;
503
504 for (;;) {
505 res = diskimage_access(m, boot_disk_id, boot_disk_type,
506 0, readofs, minibuf, sizeof(minibuf));
507 if (!res) {
508 fatal("Couldn't read the disk image. "
509 "Aborting.\n");
510 return 0;
511 }
512
513 n_blocks = minibuf[0] + (minibuf[1] << 8)
514 + (minibuf[2] << 16) + (minibuf[3] << 24);
515
516 bootblock_offset = (minibuf[4] + (minibuf[5] << 8)
517 + (minibuf[6] << 16) + (minibuf[7] << 24)) * 512;
518
519 if (n_blocks < 1)
520 break;
521
522 debug(readofs == 0x18? ": %i" : " + %i", n_blocks);
523
524 if (n_blocks * 512 > 65536)
525 fatal("\nWARNING! Unusually large bootblock "
526 "(%i bytes)\n\n", n_blocks * 512);
527
528 bootblock_buf = malloc(n_blocks * 512);
529 if (bootblock_buf == NULL) {
530 fprintf(stderr, "out of memory in "
531 "load_bootblock()\n");
532 exit(1);
533 }
534
535 res = diskimage_access(m, boot_disk_id, boot_disk_type,
536 0, bootblock_offset, bootblock_buf, n_blocks * 512);
537 if (!res) {
538 fatal("WARNING: could not load bootblocks from"
539 " disk offset 0x%llx\n",
540 (long long)bootblock_offset);
541 }
542
543 store_buf(cpu, bootblock_loadaddr,
544 (char *)bootblock_buf, n_blocks * 512);
545
546 bootblock_loadaddr += 512*n_blocks;
547 free(bootblock_buf);
548 readofs += 8;
549 }
550
551 debug(readofs == 0x18? ": no blocks?\n" : " blocks\n");
552 return 1;
553
554 case MACHINE_X86:
555 /* TODO: "El Torito" etc? */
556 if (diskimage_is_a_cdrom(cpu->machine, boot_disk_id,
557 boot_disk_type))
558 break;
559
560 bootblock_buf = malloc(512);
561 if (bootblock_buf == NULL) {
562 fprintf(stderr, "Out of memory.\n");
563 exit(1);
564 }
565
566 debug("loading PC bootsector from %s id %i\n",
567 diskimage_types[boot_disk_type], boot_disk_id);
568
569 res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0,
570 bootblock_buf, 512);
571 if (!res) {
572 fatal("Couldn't read the disk image. Aborting.\n");
573 return 0;
574 }
575
576 if (bootblock_buf[510] != 0x55 || bootblock_buf[511] != 0xaa)
577 debug("WARNING! The 0x55,0xAA marker is missing! "
578 "Booting anyway.\n");
579 store_buf(cpu, 0x7c00, (char *)bootblock_buf, 512);
580 free(bootblock_buf);
581
582 return 1;
583 }
584
585
586 /*
587 * Try reading a kernel manually from the disk. The code here
588 * does not rely on machine-dependant boot blocks etc.
589 */
590 /* ISO9660: (0x800 bytes at 0x8000) */
591 bootblock_buf = malloc(0x800);
592 if (bootblock_buf == NULL) {
593 fprintf(stderr, "Out of memory.\n");
594 exit(1);
595 }
596
597 res = diskimage_access(m, boot_disk_id, boot_disk_type,
598 0, 0x8000, bootblock_buf, 0x800);
599 if (!res) {
600 fatal("Couldn't read the disk image. Aborting.\n");
601 return 0;
602 }
603
604 iso_type = 0;
605 if (strncmp((char *)bootblock_buf+1, "CD001", 5) == 0)
606 iso_type = 1;
607 if (strncmp((char *)bootblock_buf+1, "CDW01", 5) == 0)
608 iso_type = 2;
609 if (strncmp((char *)bootblock_buf+1, "CDROM", 5) == 0)
610 iso_type = 3;
611
612 if (iso_type != 0) {
613 /* We can't load a kernel if the name
614 isn't specified. */
615 if (cpu->machine->boot_kernel_filename == NULL ||
616 cpu->machine->boot_kernel_filename[0] == '\0')
617 fatal("\nISO9660 filesystem, but no kernel "
618 "specified? (Use the -j option.)\n");
619 else
620 retval = iso_load_bootblock(m, cpu, boot_disk_id,
621 boot_disk_type, iso_type, bootblock_buf,
622 n_loadp, load_namesp);
623 }
624
625 free(bootblock_buf);
626 return retval;
627 }
628
629
630 /*
631 * emul_new():
632 *
633 * Returns a reasonably initialized struct emul.
634 */
635 struct emul *emul_new(char *name)
636 {
637 struct emul *e;
638 e = malloc(sizeof(struct emul));
639 if (e == NULL) {
640 fprintf(stderr, "out of memory in emul_new()\n");
641 exit(1);
642 }
643
644 memset(e, 0, sizeof(struct emul));
645
646 /* Sane default values: */
647 e->n_machines = 0;
648
649 if (name != NULL) {
650 e->name = strdup(name);
651 if (e->name == NULL) {
652 fprintf(stderr, "out of memory in emul_new()\n");
653 exit(1);
654 }
655 }
656
657 return e;
658 }
659
660
661 /*
662 * emul_add_machine():
663 *
664 * Calls machine_new(), adds the new machine into the emul struct, and
665 * returns a pointer to the new machine.
666 *
667 * This function should be used instead of manually calling machine_new().
668 */
669 struct machine *emul_add_machine(struct emul *e, char *name)
670 {
671 struct machine *m;
672
673 m = machine_new(name, e);
674 m->serial_nr = (e->next_serial_nr ++);
675
676 e->n_machines ++;
677 e->machines = realloc(e->machines,
678 sizeof(struct machine *) * e->n_machines);
679 if (e->machines == NULL) {
680 fprintf(stderr, "emul_add_machine(): out of memory\n");
681 exit(1);
682 }
683
684 e->machines[e->n_machines - 1] = m;
685 return m;
686 }
687
688
689 /*
690 * add_arc_components():
691 *
692 * This function adds ARCBIOS memory descriptors for the loaded program,
693 * and ARCBIOS components for SCSI devices.
694 */
695 static void add_arc_components(struct machine *m)
696 {
697 struct cpu *cpu = m->cpus[m->bootstrap_cpu];
698 uint64_t start = cpu->pc & 0x1fffffff;
699 uint64_t len = 0xc00000 - start;
700 struct diskimage *d;
701 uint64_t scsicontroller, scsidevice, scsidisk;
702
703 if ((cpu->pc >> 60) != 0xf) {
704 start = cpu->pc & 0xffffffffffULL;
705 len = 0xc00000 - start;
706 }
707
708 len += 1048576 * m->memory_offset_in_mb;
709
710 /* NOTE/TODO: magic 12MB end of load program area */
711 arcbios_add_memory_descriptor(cpu,
712 0x60000 + m->memory_offset_in_mb * 1048576,
713 start-0x60000 - m->memory_offset_in_mb * 1048576,
714 ARCBIOS_MEM_FreeMemory);
715 arcbios_add_memory_descriptor(cpu,
716 start, len, ARCBIOS_MEM_LoadedProgram);
717
718 scsicontroller = arcbios_get_scsicontroller(m);
719 if (scsicontroller == 0)
720 return;
721
722 /* TODO: The device 'name' should defined be somewhere else. */
723
724 d = m->first_diskimage;
725 while (d != NULL) {
726 if (d->type == DISKIMAGE_SCSI) {
727 int a, b, flags = COMPONENT_FLAG_Input;
728 char component_string[100];
729 char *name = "DEC RZ58 (C) DEC2000";
730
731 /* Read-write, or read-only? */
732 if (d->writable)
733 flags |= COMPONENT_FLAG_Output;
734 else
735 flags |= COMPONENT_FLAG_ReadOnly;
736
737 a = COMPONENT_TYPE_DiskController;
738 b = COMPONENT_TYPE_DiskPeripheral;
739
740 if (d->is_a_cdrom) {
741 flags |= COMPONENT_FLAG_Removable;
742 a = COMPONENT_TYPE_CDROMController;
743 b = COMPONENT_TYPE_FloppyDiskPeripheral;
744 name = "NEC CD-ROM CDR-210P 1.0 ";
745 }
746
747 scsidevice = arcbios_addchild_manual(cpu,
748 COMPONENT_CLASS_ControllerClass,
749 a, flags, 1, 2, d->id, 0xffffffff,
750 name, scsicontroller, NULL, 0);
751
752 scsidisk = arcbios_addchild_manual(cpu,
753 COMPONENT_CLASS_PeripheralClass,
754 b, flags, 1, 2, 0, 0xffffffff, NULL,
755 scsidevice, NULL, 0);
756
757 /*
758 * Add device string to component address mappings:
759 * "scsi(0)disk(0)rdisk(0)partition(0)"
760 */
761
762 if (d->is_a_cdrom) {
763 snprintf(component_string,
764 sizeof(component_string),
765 "scsi(0)cdrom(%i)", d->id);
766 arcbios_add_string_to_component(m,
767 component_string, scsidevice);
768
769 snprintf(component_string,
770 sizeof(component_string),
771 "scsi(0)cdrom(%i)fdisk(0)", d->id);
772 arcbios_add_string_to_component(m,
773 component_string, scsidisk);
774 } else {
775 snprintf(component_string,
776 sizeof(component_string),
777 "scsi(0)disk(%i)", d->id);
778 arcbios_add_string_to_component(m,
779 component_string, scsidevice);
780
781 snprintf(component_string,
782 sizeof(component_string),
783 "scsi(0)disk(%i)rdisk(0)", d->id);
784 arcbios_add_string_to_component(m,
785 component_string, scsidisk);
786 }
787 }
788
789 d = d->next;
790 }
791 }
792
793
794 /*
795 * emul_machine_setup():
796 *
797 * o) Initialize the hardware (RAM, devices, CPUs, ...) which
798 * will be emulated in this machine.
799 *
800 * o) Load ROM code and/or other programs into emulated memory.
801 *
802 * o) Special hacks needed after programs have been loaded.
803 */
804 void emul_machine_setup(struct machine *m, int n_load, char **load_names,
805 int n_devices, char **device_names)
806 {
807 struct emul *emul;
808 struct cpu *cpu;
809 int i, iadd=4;
810 uint64_t memory_amount, entrypoint = 0, gp = 0, toc = 0;
811 int byte_order;
812
813 emul = m->emul;
814
815 debug("machine \"%s\":\n", m->name);
816 debug_indentation(iadd);
817
818 /* For userland-only, this decides which ARCH/cpu_name to use: */
819 if (m->machine_type == MACHINE_USERLAND && m->userland_emul != NULL) {
820 useremul_name_to_useremul(NULL, m->userland_emul,
821 &m->arch, &m->machine_name, &m->cpu_name);
822 if (m->arch == ARCH_NOARCH) {
823 printf("Unsupported userland emulation mode.\n");
824 exit(1);
825 }
826 }
827
828 if (m->machine_type == MACHINE_NONE) {
829 fatal("No machine type specified?\n");
830 exit(1);
831 }
832
833 m->cpu_family = cpu_family_ptr_by_number(m->arch);
834
835 machine_memsize_fix(m);
836
837 /*
838 * Create the system's memory:
839 *
840 * (Don't print the amount for userland-only emulation; the
841 * size doesn't matter.)
842 */
843 if (m->machine_type != MACHINE_USERLAND)
844 debug("memory: %i MB", m->physical_ram_in_mb);
845 memory_amount = (uint64_t)m->physical_ram_in_mb * 1048576;
846 if (m->memory_offset_in_mb > 0) {
847 /*
848 * A special hack is used for some SGI models,
849 * where memory is offset by 128MB to leave room for
850 * EISA space and other things.
851 */
852 debug(" (offset by %iMB)", m->memory_offset_in_mb);
853 memory_amount += 1048576 * m->memory_offset_in_mb;
854 }
855 m->memory = memory_new(memory_amount);
856 if (m->machine_type != MACHINE_USERLAND)
857 debug("\n");
858
859 /* Create CPUs: */
860 if (m->cpu_name == NULL)
861 machine_default_cputype(m);
862 if (m->ncpus == 0) {
863 /* TODO: This should be moved elsewhere... */
864 if (m->machine_type == MACHINE_BEBOX)
865 m->ncpus = 2;
866 else if (m->machine_type == MACHINE_ARC &&
867 m->machine_subtype == MACHINE_ARC_NEC_R96)
868 m->ncpus = 2;
869 else if (m->machine_type == MACHINE_ARC &&
870 m->machine_subtype == MACHINE_ARC_NEC_R98)
871 m->ncpus = 4;
872 else
873 m->ncpus = 1;
874 }
875 m->cpus = malloc(sizeof(struct cpu *) * m->ncpus);
876 if (m->cpus == NULL) {
877 fprintf(stderr, "out of memory\n");
878 exit(1);
879 }
880 memset(m->cpus, 0, sizeof(struct cpu *) * m->ncpus);
881
882 /* Initialize dynamic binary translation, if available: */
883 if (m->bintrans_enable)
884 bintrans_init(m, m->memory);
885
886 debug("cpu0");
887 if (m->ncpus > 1)
888 debug(" .. cpu%i", m->ncpus - 1);
889 debug(": ");
890 for (i=0; i<m->ncpus; i++) {
891 m->cpus[i] = cpu_new(m->memory, m, i, m->cpu_name);
892 if (m->bintrans_enable)
893 bintrans_init_cpu(m->cpus[i]);
894 }
895 debug("\n");
896
897 if (m->use_random_bootstrap_cpu)
898 m->bootstrap_cpu = random() % m->ncpus;
899 else
900 m->bootstrap_cpu = 0;
901
902 cpu = m->cpus[m->bootstrap_cpu];
903
904 /* Set cpu->useremul_syscall, and use userland_memory_rw: */
905 if (m->userland_emul != NULL) {
906 useremul_name_to_useremul(cpu,
907 m->userland_emul, NULL, NULL, NULL);
908 cpu->memory_rw = userland_memory_rw;
909 }
910
911 if (m->use_x11)
912 x11_init(m);
913
914 /* Fill memory with random bytes: */
915 if (m->random_mem_contents) {
916 for (i=0; i<m->physical_ram_in_mb * 1048576; i+=256) {
917 unsigned char data[256];
918 unsigned int j;
919 for (j=0; j<sizeof(data); j++)
920 data[j] = random() & 255;
921 cpu->memory_rw(cpu, m->memory, i, data, sizeof(data),
922 MEM_WRITE, CACHE_NONE | NO_EXCEPTIONS | PHYSICAL);
923 }
924 }
925
926 if (m->userland_emul != NULL) {
927 /*
928 * For userland-only emulation, no machine emulation
929 * is needed.
930 */
931 } else {
932 for (i=0; i<n_devices; i++)
933 device_add(m, device_names[i]);
934
935 machine_setup(m);
936 }
937
938 diskimage_dump_info(m);
939
940 /* Load files (ROM code, boot code, ...) into memory: */
941 if (n_load == 0) {
942 if (m->first_diskimage != NULL) {
943 if (!load_bootblock(m, cpu, &n_load, &load_names)) {
944 fprintf(stderr, "\nNo executable files were"
945 " specified, and booting directly from disk"
946 " failed.\n");
947 exit(1);
948 }
949 } else {
950 fprintf(stderr, "No executable file(s) loaded, and "
951 "we are not booting directly from a disk image."
952 "\nAborting.\n");
953 exit(1);
954 }
955 }
956
957 while (n_load > 0) {
958 FILE *tmp_f;
959 char *name_to_load = *load_names;
960 int remove_after_load = 0;
961
962 /* Special hack for removing temporary files: */
963 if (name_to_load[0] == 8) {
964 name_to_load ++;
965 remove_after_load = 1;
966 }
967
968 /*
969 * Another special hack for temporary files; running gunzip
970 * on them, if they have a gzip header. TODO: Change this
971 * into some kind of generic support for gzipped files!
972 */
973 tmp_f = fopen(name_to_load, "r");
974 if (tmp_f != NULL) {
975 unsigned char buf[2]; /* gzip header */
976 memset(buf, 0, sizeof(buf));
977 fread(buf, 1, sizeof(buf), tmp_f);
978 if (buf[0]==0x1f && buf[1]==0x8b) {
979 char *zz = malloc(strlen(name_to_load)*2 + 100);
980 debug("gunziping %s\n", name_to_load);
981 sprintf(zz, "mv %s %s.gz", name_to_load,
982 name_to_load);
983 system(zz);
984 sprintf(zz, "gunzip %s.gz", name_to_load);
985 system(zz);
986 free(zz);
987 }
988 fclose(tmp_f);
989 }
990
991 /* Special things required _before_ loading the file: */
992 switch (m->arch) {
993 case ARCH_X86:
994 /*
995 * X86 machines normally don't need to load any files,
996 * they can boot from disk directly. Therefore, an x86
997 * machine usually boots up in 16-bit real mode. When
998 * loading a 32-bit (or even 64-bit) ELF, that's not
999 * very nice, hence this special case.
1000 */
1001 pc_bios_simple_pmode_setup(cpu);
1002 break;
1003 }
1004
1005 byte_order = NO_BYTE_ORDER_OVERRIDE;
1006
1007 /*
1008 * Load the file: :-)
1009 */
1010 file_load(m, m->memory, name_to_load, &entrypoint,
1011 m->arch, &gp, &byte_order, &toc);
1012
1013 if (remove_after_load) {
1014 debug("removing %s\n", name_to_load);
1015 unlink(name_to_load);
1016 }
1017
1018 if (byte_order != NO_BYTE_ORDER_OVERRIDE)
1019 cpu->byte_order = byte_order;
1020
1021 cpu->pc = entrypoint;
1022
1023 switch (m->arch) {
1024 case ARCH_MIPS:
1025 if ((cpu->pc >> 32) == 0
1026 && (cpu->pc & 0x80000000ULL))
1027 cpu->pc |= 0xffffffff00000000ULL;
1028
1029 cpu->cd.mips.gpr[MIPS_GPR_GP] = gp;
1030
1031 if ((cpu->cd.mips.gpr[MIPS_GPR_GP] >> 32) == 0 &&
1032 (cpu->cd.mips.gpr[MIPS_GPR_GP] & 0x80000000ULL))
1033 cpu->cd.mips.gpr[MIPS_GPR_GP] |=
1034 0xffffffff00000000ULL;
1035 break;
1036
1037 case ARCH_PPC:
1038 /* See http://www.linuxbase.org/spec/ELF/ppc64/
1039 spec/x458.html for more info. */
1040 cpu->cd.ppc.gpr[2] = toc;
1041 /* TODO */
1042 break;
1043
1044 case ARCH_ALPHA:
1045 case ARCH_HPPA:
1046 case ARCH_SPARC:
1047 case ARCH_URISC:
1048 break;
1049
1050 case ARCH_ARM:
1051 cpu->pc &= 0xffffffff;
1052 break;
1053
1054 case ARCH_X86:
1055 /*
1056 * NOTE: The toc field is used to indicate an ELF32
1057 * or ELF64 load.
1058 */
1059 switch (toc) {
1060 case 0: /* 16-bit? TODO */
1061 cpu->pc &= 0xffffffffULL;
1062 break;
1063 case 1: /* 32-bit. */
1064 cpu->pc &= 0xffffffffULL;
1065 break;
1066 case 2: /* 64-bit: TODO */
1067 fatal("64-bit x86 load. TODO\n");
1068 exit(1);
1069 }
1070 break;
1071
1072 default:
1073 fatal("emul_machine_setup(): Internal error: "
1074 "Unimplemented arch %i\n", m->arch);
1075 exit(1);
1076 }
1077
1078 /*
1079 * For userland emulation, the remaining items
1080 * on the command line will be passed as parameters
1081 * to the emulated program, and will not be treated
1082 * as filenames to load into the emulator.
1083 * The program's name will be in load_names[0], and the
1084 * rest of the parameters in load_names[1] and up.
1085 */
1086 if (m->userland_emul != NULL)
1087 break;
1088
1089 n_load --;
1090 load_names ++;
1091 }
1092
1093 if (m->byte_order_override != NO_BYTE_ORDER_OVERRIDE)
1094 cpu->byte_order = m->byte_order_override;
1095
1096 /* Same byte order and entrypoint for all CPUs: */
1097 for (i=0; i<m->ncpus; i++)
1098 if (i != m->bootstrap_cpu) {
1099 m->cpus[i]->byte_order = cpu->byte_order;
1100 m->cpus[i]->pc = cpu->pc;
1101 }
1102
1103 if (m->userland_emul != NULL)
1104 useremul_setup(cpu, n_load, load_names);
1105
1106 /* Startup the bootstrap CPU: */
1107 cpu->bootstrap_cpu_flag = 1;
1108 cpu->running = 1;
1109
1110 /* ... or pause all CPUs, if start_paused is set: */
1111 if (m->start_paused) {
1112 for (i=0; i<m->ncpus; i++)
1113 m->cpus[i]->running = 0;
1114 }
1115
1116 /* Add PC dump points: */
1117 add_dump_points(m);
1118
1119 /* TODO: This is MIPS-specific! */
1120 if (m->machine_type == MACHINE_DEC &&
1121 cpu->cd.mips.cpu_type.mmu_model == MMU3K)
1122 add_symbol_name(&m->symbol_context,
1123 0x9fff0000, 0x10000, "r2k3k_cache", 0);
1124
1125 symbol_recalc_sizes(&m->symbol_context);
1126
1127 if (m->max_random_cycles_per_chunk > 0)
1128 debug("using random cycle chunks (1 to %i cycles)\n",
1129 m->max_random_cycles_per_chunk);
1130
1131 /* Special hack for ARC/SGI emulation: */
1132 if ((m->machine_type == MACHINE_ARC ||
1133 m->machine_type == MACHINE_SGI) && m->prom_emulation)
1134 add_arc_components(m);
1135
1136 debug("starting cpu%i at ", m->bootstrap_cpu);
1137 switch (m->arch) {
1138 case ARCH_MIPS:
1139 if (cpu->cd.mips.cpu_type.isa_level < 3 ||
1140 cpu->cd.mips.cpu_type.isa_level == 32) {
1141 debug("0x%08x", (int)m->cpus[
1142 m->bootstrap_cpu]->pc);
1143 if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
1144 debug(" (gp=0x%08x)", (int)m->cpus[
1145 m->bootstrap_cpu]->cd.mips.gpr[
1146 MIPS_GPR_GP]);
1147 } else {
1148 debug("0x%016llx", (long long)m->cpus[
1149 m->bootstrap_cpu]->pc);
1150 if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
1151 debug(" (gp=0x%016llx)", (long long)
1152 cpu->cd.mips.gpr[MIPS_GPR_GP]);
1153 }
1154 break;
1155 case ARCH_PPC:
1156 if (cpu->cd.ppc.bits == 32)
1157 debug("0x%08x", (int)entrypoint);
1158 else
1159 debug("0x%016llx", (long long)entrypoint);
1160 break;
1161 case ARCH_ARM:
1162 /* ARM cpus aren't 64-bit: */
1163 debug("0x%08x", (int)entrypoint);
1164 break;
1165 case ARCH_URISC:
1166 {
1167 char tmps[100];
1168 unsigned char buf[sizeof(uint64_t)];
1169
1170 cpu->memory_rw(cpu, m->memory, 0, buf, sizeof(buf),
1171 MEM_READ, CACHE_NONE | NO_EXCEPTIONS);
1172
1173 entrypoint = 0;
1174 for (i=0; i<cpu->cd.urisc.wordlen/8; i++) {
1175 entrypoint <<= 8;
1176 if (cpu->byte_order == EMUL_BIG_ENDIAN)
1177 entrypoint += buf[i];
1178 else
1179 entrypoint += buf[cpu->
1180 cd.urisc.wordlen/8 - 1 - i];
1181 }
1182
1183 sprintf(tmps, "0x%%0%illx", cpu->cd.urisc.wordlen / 4);
1184 debug(tmps, (long long)entrypoint);
1185 cpu->pc = entrypoint;
1186 }
1187 break;
1188 case ARCH_X86:
1189 debug("0x%04x:0x%llx", cpu->cd.x86.s[X86_S_CS],
1190 (long long)cpu->pc);
1191 break;
1192 default:
1193 debug("0x%016llx", (long long)cpu->pc);
1194 }
1195 debug("\n");
1196
1197 debug_indentation(-iadd);
1198 }
1199
1200
1201 /*
1202 * emul_dumpinfo():
1203 *
1204 * Dump info about all machines in an emul.
1205 */
1206 void emul_dumpinfo(struct emul *e)
1207 {
1208 int j, nm, iadd = 4;
1209
1210 if (e->net != NULL)
1211 net_dumpinfo(e->net);
1212
1213 nm = e->n_machines;
1214 for (j=0; j<nm; j++) {
1215 debug("machine %i: \"%s\"\n", j, e->machines[j]->name);
1216 debug_indentation(iadd);
1217 machine_dumpinfo(e->machines[j]);
1218 debug_indentation(-iadd);
1219 }
1220 }
1221
1222
1223 /*
1224 * emul_simple_init():
1225 *
1226 * For a normal setup:
1227 *
1228 * o) Initialize a network.
1229 * o) Initialize one machine.
1230 *
1231 * For a userland-only setup:
1232 *
1233 * o) Initialize a "pseudo"-machine.
1234 */
1235 void emul_simple_init(struct emul *emul)
1236 {
1237 int iadd=4;
1238 struct machine *m;
1239
1240 if (emul->n_machines != 1) {
1241 fprintf(stderr, "emul_simple_init(): n_machines != 1\n");
1242 exit(1);
1243 }
1244
1245 m = emul->machines[0];
1246
1247 if (m->userland_emul == NULL) {
1248 debug("Simple setup...\n");
1249 debug_indentation(iadd);
1250
1251 /* Create a network: */
1252 emul->net = net_init(emul, NET_INIT_FLAG_GATEWAY,
1253 "10.0.0.0", 8);
1254 } else {
1255 /* Userland pseudo-machine: */
1256 debug("Syscall emulation (userland-only) setup...\n");
1257 debug_indentation(iadd);
1258 }
1259
1260 /* Create the machine: */
1261 emul_machine_setup(m, extra_argc, extra_argv, 0, NULL);
1262
1263 debug_indentation(-iadd);
1264 }
1265
1266
1267 /*
1268 * emul_create_from_configfile():
1269 *
1270 * Create an emul struct by reading settings from a configuration file.
1271 */
1272 struct emul *emul_create_from_configfile(char *fname)
1273 {
1274 int iadd = 4;
1275 struct emul *e = emul_new(fname);
1276 FILE *f;
1277 char buf[128];
1278 size_t len;
1279
1280 debug("Creating emulation from configfile \"%s\":\n", fname);
1281 debug_indentation(iadd);
1282
1283 f = fopen(fname, "r");
1284 if (f == NULL) {
1285 perror(fname);
1286 exit(1);
1287 }
1288
1289 /* Read header: (must be !!gxemul) */
1290 len = fread(buf, 1, 8, f);
1291 if (len != 8 || strncmp(buf, "!!gxemul", 8) != 0) {
1292 fprintf(stderr, "%s: must start with '!!gxemul'\n", fname);
1293 exit(1);
1294 }
1295
1296 /* Restart from beginning: */
1297 rewind(f);
1298
1299 emul_parse_config(e, f);
1300
1301 fclose(f);
1302 debug_indentation(-iadd);
1303 return e;
1304 }
1305
1306
1307 /*
1308 * emul_run():
1309 *
1310 * o) Set up things needed before running emulations.
1311 *
1312 * o) Run emulations (one or more, in parallel).
1313 *
1314 * o) De-initialize things.
1315 */
1316 void emul_run(struct emul **emuls, int n_emuls)
1317 {
1318 struct emul *e;
1319 int i = 0, j, go = 1, n, anything;
1320
1321 if (n_emuls < 1) {
1322 fprintf(stderr, "emul_run(): no thing to do\n");
1323 return;
1324 }
1325
1326 atexit(fix_console);
1327
1328 i = 79;
1329 while (i-- > 0)
1330 debug("-");
1331 debug("\n\n");
1332
1333 /* Initialize the interactive debugger: */
1334 debugger_init(emuls, n_emuls);
1335
1336 /*
1337 * console_init_main() makes sure that the terminal is in a
1338 * reasonable state.
1339 *
1340 * The SIGINT handler is for CTRL-C (enter the interactive debugger).
1341 *
1342 * The SIGCONT handler is invoked whenever the user presses CTRL-Z
1343 * (or sends SIGSTOP) and then continues. It makes sure that the
1344 * terminal is in an expected state.
1345 */
1346 console_init_main(emuls[0]); /* TODO: what is a good argument? */
1347 signal(SIGINT, debugger_activate);
1348 signal(SIGCONT, console_sigcont);
1349
1350 /* Not in verbose mode? Then set quiet_mode. */
1351 if (!verbose)
1352 quiet_mode = 1;
1353
1354 /* Initialize all CPUs in all machines in all emulations: */
1355 for (i=0; i<n_emuls; i++) {
1356 e = emuls[i];
1357 if (e == NULL)
1358 continue;
1359 for (j=0; j<e->n_machines; j++)
1360 cpu_run_init(e, e->machines[j]);
1361 }
1362
1363 /*
1364 * MAIN LOOP:
1365 *
1366 * Run all emulations in parallel, running each machine in
1367 * each emulation.
1368 */
1369 while (go) {
1370 go = 0;
1371
1372 x11_check_event(emuls, n_emuls);
1373
1374 for (i=0; i<n_emuls; i++) {
1375 e = emuls[i];
1376 if (e == NULL)
1377 continue;
1378
1379 for (j=0; j<e->n_machines; j++) {
1380 /* TODO: cpu_run() is a strange name, since
1381 there can be multiple cpus in a machine */
1382 anything = cpu_run(e, e->machines[j]);
1383 if (anything)
1384 go = 1;
1385 }
1386 }
1387 }
1388
1389 /* Deinitialize all CPUs in all machines in all emulations: */
1390 for (i=0; i<n_emuls; i++) {
1391 e = emuls[i];
1392 if (e == NULL)
1393 continue;
1394 for (j=0; j<e->n_machines; j++)
1395 cpu_run_deinit(e, e->machines[j]);
1396 }
1397
1398 /* force_debugger_at_exit flag set? Then enter the debugger: */
1399 if (force_debugger_at_exit) {
1400 quiet_mode = 0;
1401 debugger_reset();
1402 debugger();
1403 }
1404
1405 /* Any machine using X11? Then we should wait before exiting: */
1406 n = 0;
1407 for (i=0; i<n_emuls; i++)
1408 for (j=0; j<emuls[i]->n_machines; j++)
1409 if (emuls[i]->machines[j]->use_x11)
1410 n++;
1411 if (n > 0) {
1412 printf("Press enter to quit.\n");
1413 while (!console_charavail(MAIN_CONSOLE)) {
1414 x11_check_event(emuls, n_emuls);
1415 usleep(1);
1416 }
1417 console_readchar(MAIN_CONSOLE);
1418 }
1419
1420 console_deinit();
1421 }
1422

  ViewVC Help
Powered by ViewVC 1.1.26