/[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 10 - (show annotations)
Mon Oct 8 16:18:27 2007 UTC (11 years, 11 months ago) by dpavlin
File MIME type: text/plain
File size: 37602 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.815 2005/06/27 23:04:35 debug Exp $
20050617	Experimenting some more with netbooting OpenBSD/sgi. Adding
		a hack which allows emulated ethernet networks to be
		distributed across multiple emulator processes.
20050618	Minor updates (documentation, dummy YAMON emulation, etc).
20050620	strcpy/strcat -> strlcpy/strlcat updates.
		Some more progress on evbmips (Malta).
20050621	Adding a section to doc/configfiles.html about ethernet
		emulation across multiple hosts.
		Beginning the work on the ARM translation engine (using the
		dynamic-but-not-binary translation method).
		Fixing a bintrans bug: 0x9fc00000 should always be treated as
		PROM area, just as 0xbfc00000 is.
		Minor progress on Malta emulation (the PCI-ISA bus).
20050622	NetBSD/evbmips can now be installed (using another emulated
		machine) and run (including userland and so on). :-)
		Spliting up the bintrans haddr_entry field into two (one for
		read, one for write). Probably not much of a speed increase,
		though.
		Updating some NetBSD 2.0 -> 2.0.2 in the documentation.
20050623	Minor updates (documentation, the TODO file, etc).
		gzipped kernels are now always automagically gunzipped when
		loaded.
20050624	Adding a dummy Playstation Portable (PSP) mode, just barely
		enough to run Hello World (in weird colors :-).
		Removing the -b command line option; old bintrans is enabled
		by default instead. It makes more sense.
		Trying to finally fix the non-working performance measurement
		thing (instr/second etc).
20050625	Continuing on the essential basics for ARM emulation. Two
		instructions seem to work, a branch and a simple "mov". (The
		mov arguments are not correct yet.) Performance is definitely
		reasonable.
		Various other minor updates.
		Adding the ARM "bl" instruction.
		Adding support for combining multiple ARM instructions into one
		function call. ("mov" + "mov" is the only one implemented so
		far, but it seems to work.)
		Cleaning up some IP32 interrupt things (crime/mace); disabling
		the PS/2 keyboard controller on IP32, so that NetBSD/sgimips
		boots into userland again.
20050626	Finally! NetBSD/sgimips netboots. Adding instructions to
		doc/guestoses.html on how to set up an nfs server etc.
		Various other minor fixes.
		Playstation Portable ".pbp" files can now be used directly.
		(The ELF part of the .pbp is extracted transparently.)
		Converting some sprintf -> snprintf.
		Adding some more instructions to the ARM disassembler.
20050627	More ARM updates. Adding some simple ldr(b), str(b),
		cmps, and conditional branch instructions, enough to run
		a simple Hello World program.
		All ARM instructions are now inlined/generated for all possible
		condition codes.
		Adding add and sub, and more load/store instructions.
		Removing dummy files: cpu_alpha.c, cpu_hppa.c, and cpu_sparc.c.
		Some minor documentation updates; preparing for a 0.3.4
		release. Updating some URLs.

==============  RELEASE 0.3.4  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26