/[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 12 - (show annotations)
Mon Oct 8 16:18:38 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 37513 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.905 2005/08/16 09:16:24 debug Exp $
20050628	Continuing the work on the ARM translation engine. end_of_page
		works. Experimenting with load/store translation caches
		(virtual -> physical -> host).
20050629	More ARM stuff (memory access translation cache, mostly). This
		might break a lot of stuff elsewhere, probably some MIPS-
		related translation things.
20050630	Many load/stores are now automatically generated and included
		into cpu_arm_instr.c; 1024 functions in total (!).
		Fixes based on feedback from Alec Voropay: only print 8 hex
		digits instead of 16 in some cases when emulating 32-bit
		machines; similar 8 vs 16 digit fix for breakpoint addresses;
		4Kc has 16 TLB entries, not 48; the MIPS config select1
		register is now printed with "reg ,0".
		Also changing many other occurances of 16 vs 8 digit output.
		Adding cache associativity fields to mips_cpu_types.h; updating
		some other cache fields; making the output of
		mips_cpu_dumpinfo() look nicer.
		Generalizing the bintrans stuff for device accesses to also
		work with the new translation system. (This might also break
		some MIPS things.)
		Adding multi-load/store instructions to the ARM disassembler
		and the translator, and some optimizations of various kinds.
20050701	Adding a simple dev_disk (it can read/write sectors from
		disk images).
20050712	Adding dev_ether (a simple ethernet send/receive device).
		Debugger command "ninstrs" for toggling show_nr_of_instructions
		during runtime.
		Removing the framebuffer logo.
20050713	Continuing on dev_ether.
		Adding a dummy cpu_alpha (again).
20050714	More work on cpu_alpha.
20050715	More work on cpu_alpha. Many instructions work, enough to run
		a simple framebuffer fill test (similar to the ARM test).
20050716	More Alpha stuff.
20050717	Minor updates (Alpha stuff).
20050718	Minor updates (Alpha stuff).
20050719	Generalizing some Alpha instructions.
20050720	More Alpha-related updates.
20050721	Continuing on cpu_alpha. Importing rpb.h from NetBSD/alpha.
20050722	Alpha-related updates: userland stuff (Hello World using
		write() compiled statically for FreeBSD/Alpha runs fine), and
		more instructions are now implemented.
20050723	Fixing ldq_u and stq_u.
		Adding more instructions (conditional moves, masks, extracts,
		shifts).
20050724	More FreeBSD/Alpha userland stuff, and adding some more
		instructions (inserts).
20050725	Continuing on the Alpha stuff. (Adding dummy ldt/stt.)
		Adding a -A command line option to turn off alignment checks
		in some cases (for translated code).
		Trying to remove the old bintrans code which updated the pc
		and nr_of_executed_instructions for every instruction.
20050726	Making another attempt att removing the pc/nr of instructions
		code. This time it worked, huge performance increase for
		artificial test code, but performance loss for real-world
		code :-( so I'm scrapping that code for now.
		Tiny performance increase on Alpha (by using ret instead of
		jmp, to play nice with the Alpha's branch prediction) for the
		old MIPS bintrans backend.
20050727	Various minor fixes and cleanups.
20050728	Switching from a 2-level virtual to host/physical translation
		system for ARM emulation, to a 1-level translation.
		Trying to switch from 2-level to 1-level for the MIPS bintrans
		system as well (Alpha only, so far), but there is at least one
		problem: caches and/or how they work with device mappings.
20050730	Doing the 2-level to 1-level conversion for the i386 backend.
		The cache/device bug is still there for R2K/3K :(
		Various other minor updates (Malta etc).
		The mc146818 clock now updates the UIP bit in a way which works
		better with Linux for at least sgimips and Malta emulation.
		Beginning the work on refactoring the dyntrans system.
20050731	Continuing the dyntrans refactoring.
		Fixing a small but serious host alignment bug in memory_rw.
		Adding support for big-endian load/stores to the i386 bintrans
		backend.
		Another minor i386 bintrans backend update: stores from the
		zero register are now one (or two) loads shorter.
		The slt and sltu instructions were incorrectly implemented for
		the i386 backend; only using them for 32-bit mode for now.
20050801	Continuing the dyntrans refactoring.
		Cleanup of the ns16550 serial controller (removing unnecessary
		code).
		Bugfix (memory corruption bug) in dev_gt, and a patch/hack from
		Alec Voropay for Linux/Malta.
20050802	More cleanup/refactoring of the dyntrans subsystem: adding
		phys_page pointers to the lookup tables, for quick jumps
		between translated pages.
		Better fix for the ns16550 device (but still no real FIFO
		functionality).
		Converting cpu_ppc to the new dyntrans system. This means that
		I will have to start from scratch with implementing each
		instruction, and figure out how to implement dual 64/32-bit
		modes etc.
		Removing the URISC CPU family, because it was useless.
20050803	When selecting a machine type, the main type can now be omitted
		if the subtype name is unique. (I.e. -E can be omitted.)
		Fixing a dyntrans/device update bug. (Writes to offset 0 of
		a device could sometimes go unnoticed.)
		Adding an experimental "instruction combination" hack for
		ARM for memset-like byte fill loops.
20050804	Minor progress on cpu_alpha and related things.
		Finally fixing the MIPS dmult/dmultu bugs.
		Fixing some minor TODOs.
20050805	Generalizing the 8259 PIC. It now also works with Cobalt
		and evbmips emulation, in addition to the x86 hack.
		Finally converting the ns16550 device to use devinit.
		Continuing the work on the dyntrans system. Thinking about
		how to add breakpoints.
20050806	More dyntrans updates. Breakpoints seem to work now.
20050807	Minor updates: cpu_alpha and related things; removing
		dev_malta (as it isn't used any more).
		Dyntrans: working on general "show trace tree" support.
		The trace tree stuff now works with both the old MIPS code and
		with newer dyntrans modes. :)
		Continuing on Alpha-related stuff (trying to get *BSD to boot
		a bit further, adding more instructions, etc).
20050808	Adding a dummy IA64 cpu family, and continuing the refactoring
		of the dyntrans system.
		Removing the regression test stuff, because it was more or
		less useless.
		Adding loadlinked/storeconditional type instructions to the
		Alpha emulation. (Needed for Linux/alpha. Not very well tested
		yet.)
20050809	The function call trace tree now prints a per-function nr of
		arguments. (Semi-meaningless, since that data isn't read yet
		from the ELFs; some hardcoded symbols such as memcpy() and
		strlen() work fine, though.)
		More dyntrans refactoring; taking out more of the things that
		are common to all cpu families.
20050810	Working on adding support for "dual mode" for PPC dyntrans
		(i.e. both 64-bit and 32-bit modes).
		(Re)adding some simple PPC instructions.
20050811	Adding a dummy M68K cpu family. The dyntrans system isn't ready
		for variable-length ISAs yet, so it's completely bogus so far.
		Re-adding more PPC instructions.
		Adding a hack to src/file.c which allows OpenBSD/mac68k a.out
		kernels to be loaded.
		Beginning to add PPC loads/stores. So far they only work in
		32-bit mode.
20050812	The configure file option "add_remote" now accepts symbolic
		host names, in addition to numeric IPv4 addresses.
		Re-adding more PPC instructions.
20050814	Continuing to port back more PPC instructions.
		Found and fixed the cache/device write-update bug for 32-bit
		MIPS bintrans. :-)
		Triggered a really weird and annoying bug in Compaq's C
		compiler; ccc sometimes outputs code which loads from an
		address _before_ checking whether the pointer was NULL or not.
		(I'm not sure how to handle this problem.)
20050815	Removing all of the old x86 instruction execution code; adding
		a new (dummy) dyntrans module for x86.
		Taking the first steps to extend the dyntrans system to support
		variable-length instructions.
		Slowly preparing for the next release.
20050816	Adding a dummy SPARC cpu module.
		Minor updates (documentation etc) for the release.

==============  RELEASE 0.3.5  ==============


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.225 2005/08/14 19:35:54 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 /*
713 * NOTE/TODO: magic 12MB end of load program area
714 *
715 * Hm. This breaks the old FreeBSD/MIPS snapshots...
716 */
717 #if 0
718 arcbios_add_memory_descriptor(cpu,
719 0x60000 + m->memory_offset_in_mb * 1048576,
720 start-0x60000 - m->memory_offset_in_mb * 1048576,
721 ARCBIOS_MEM_FreeMemory);
722 #endif
723 arcbios_add_memory_descriptor(cpu,
724 start, len, ARCBIOS_MEM_LoadedProgram);
725
726 scsicontroller = arcbios_get_scsicontroller(m);
727 if (scsicontroller == 0)
728 return;
729
730 /* TODO: The device 'name' should defined be somewhere else. */
731
732 d = m->first_diskimage;
733 while (d != NULL) {
734 if (d->type == DISKIMAGE_SCSI) {
735 int a, b, flags = COMPONENT_FLAG_Input;
736 char component_string[100];
737 char *name = "DEC RZ58 (C) DEC2000";
738
739 /* Read-write, or read-only? */
740 if (d->writable)
741 flags |= COMPONENT_FLAG_Output;
742 else
743 flags |= COMPONENT_FLAG_ReadOnly;
744
745 a = COMPONENT_TYPE_DiskController;
746 b = COMPONENT_TYPE_DiskPeripheral;
747
748 if (d->is_a_cdrom) {
749 flags |= COMPONENT_FLAG_Removable;
750 a = COMPONENT_TYPE_CDROMController;
751 b = COMPONENT_TYPE_FloppyDiskPeripheral;
752 name = "NEC CD-ROM CDR-210P 1.0 ";
753 }
754
755 scsidevice = arcbios_addchild_manual(cpu,
756 COMPONENT_CLASS_ControllerClass,
757 a, flags, 1, 2, d->id, 0xffffffff,
758 name, scsicontroller, NULL, 0);
759
760 scsidisk = arcbios_addchild_manual(cpu,
761 COMPONENT_CLASS_PeripheralClass,
762 b, flags, 1, 2, 0, 0xffffffff, NULL,
763 scsidevice, NULL, 0);
764
765 /*
766 * Add device string to component address mappings:
767 * "scsi(0)disk(0)rdisk(0)partition(0)"
768 */
769
770 if (d->is_a_cdrom) {
771 snprintf(component_string,
772 sizeof(component_string),
773 "scsi(0)cdrom(%i)", d->id);
774 arcbios_add_string_to_component(m,
775 component_string, scsidevice);
776
777 snprintf(component_string,
778 sizeof(component_string),
779 "scsi(0)cdrom(%i)fdisk(0)", d->id);
780 arcbios_add_string_to_component(m,
781 component_string, scsidisk);
782 } else {
783 snprintf(component_string,
784 sizeof(component_string),
785 "scsi(0)disk(%i)", d->id);
786 arcbios_add_string_to_component(m,
787 component_string, scsidevice);
788
789 snprintf(component_string,
790 sizeof(component_string),
791 "scsi(0)disk(%i)rdisk(0)", d->id);
792 arcbios_add_string_to_component(m,
793 component_string, scsidisk);
794 }
795 }
796
797 d = d->next;
798 }
799 }
800
801
802 /*
803 * emul_machine_setup():
804 *
805 * o) Initialize the hardware (RAM, devices, CPUs, ...) which
806 * will be emulated in this machine.
807 *
808 * o) Load ROM code and/or other programs into emulated memory.
809 *
810 * o) Special hacks needed after programs have been loaded.
811 */
812 void emul_machine_setup(struct machine *m, int n_load, char **load_names,
813 int n_devices, char **device_names)
814 {
815 struct emul *emul;
816 struct cpu *cpu;
817 int i, iadd=4;
818 uint64_t memory_amount, entrypoint = 0, gp = 0, toc = 0;
819 int byte_order;
820
821 emul = m->emul;
822
823 debug("machine \"%s\":\n", m->name);
824 debug_indentation(iadd);
825
826 /* For userland-only, this decides which ARCH/cpu_name to use: */
827 if (m->machine_type == MACHINE_USERLAND && m->userland_emul != NULL) {
828 useremul_name_to_useremul(NULL, m->userland_emul,
829 &m->arch, &m->machine_name, &m->cpu_name);
830 if (m->arch == ARCH_NOARCH) {
831 printf("Unsupported userland emulation mode.\n");
832 exit(1);
833 }
834 }
835
836 if (m->machine_type == MACHINE_NONE) {
837 fatal("No machine type specified?\n");
838 exit(1);
839 }
840
841 m->cpu_family = cpu_family_ptr_by_number(m->arch);
842
843 if (m->arch == ARCH_ALPHA)
844 m->arch_pagesize = 8192;
845
846 if (m->arch != ARCH_MIPS)
847 m->bintrans_enable = 0;
848
849 machine_memsize_fix(m);
850
851 /*
852 * Create the system's memory:
853 *
854 * (Don't print the amount for userland-only emulation; the
855 * size doesn't matter.)
856 */
857 if (m->machine_type != MACHINE_USERLAND)
858 debug("memory: %i MB", m->physical_ram_in_mb);
859 memory_amount = (uint64_t)m->physical_ram_in_mb * 1048576;
860 if (m->memory_offset_in_mb > 0) {
861 /*
862 * A special hack is used for some SGI models,
863 * where memory is offset by 128MB to leave room for
864 * EISA space and other things.
865 */
866 debug(" (offset by %iMB)", m->memory_offset_in_mb);
867 memory_amount += 1048576 * m->memory_offset_in_mb;
868 }
869 m->memory = memory_new(memory_amount, m->arch);
870 if (m->machine_type != MACHINE_USERLAND)
871 debug("\n");
872
873 /* Create CPUs: */
874 if (m->cpu_name == NULL)
875 machine_default_cputype(m);
876 if (m->ncpus == 0) {
877 /* TODO: This should be moved elsewhere... */
878 if (m->machine_type == MACHINE_BEBOX)
879 m->ncpus = 2;
880 else if (m->machine_type == MACHINE_ARC &&
881 m->machine_subtype == MACHINE_ARC_NEC_R96)
882 m->ncpus = 2;
883 else if (m->machine_type == MACHINE_ARC &&
884 m->machine_subtype == MACHINE_ARC_NEC_R98)
885 m->ncpus = 4;
886 else
887 m->ncpus = 1;
888 }
889 m->cpus = malloc(sizeof(struct cpu *) * m->ncpus);
890 if (m->cpus == NULL) {
891 fprintf(stderr, "out of memory\n");
892 exit(1);
893 }
894 memset(m->cpus, 0, sizeof(struct cpu *) * m->ncpus);
895
896 /* Initialize dynamic binary translation, if available: */
897 if (m->bintrans_enable)
898 bintrans_init(m, m->memory);
899
900 debug("cpu0");
901 if (m->ncpus > 1)
902 debug(" .. cpu%i", m->ncpus - 1);
903 debug(": ");
904 for (i=0; i<m->ncpus; i++) {
905 m->cpus[i] = cpu_new(m->memory, m, i, m->cpu_name);
906 if (m->bintrans_enable)
907 bintrans_init_cpu(m->cpus[i]);
908 }
909 debug("\n");
910
911 #if 0
912 /* Special case: The Playstation Portable has an additional CPU: */
913 if (m->machine_type == MACHINE_PSP) {
914 debug("cpu%i: ", m->ncpus);
915 m->cpus[m->ncpus] = cpu_new(m->memory, m,
916 0 /* use 0 here to show info with debug() */,
917 "Allegrex" /* TODO */);
918 if (m->bintrans_enable)
919 bintrans_init_cpu(m->cpus[m->ncpus]);
920 debug("\n");
921 m->ncpus ++;
922 }
923 #endif
924
925 if (m->use_random_bootstrap_cpu)
926 m->bootstrap_cpu = random() % m->ncpus;
927 else
928 m->bootstrap_cpu = 0;
929
930 cpu = m->cpus[m->bootstrap_cpu];
931
932 /* Set cpu->useremul_syscall, and use userland_memory_rw: */
933 if (m->userland_emul != NULL) {
934 useremul_name_to_useremul(cpu,
935 m->userland_emul, NULL, NULL, NULL);
936
937 switch (m->arch) {
938 #ifdef ENABLE_ALPHA
939 case ARCH_ALPHA:
940 cpu->memory_rw = alpha_userland_memory_rw;
941 break;
942 #endif
943 default:cpu->memory_rw = userland_memory_rw;
944 }
945 }
946
947 if (m->use_x11)
948 x11_init(m);
949
950 /* Fill memory with random bytes: */
951 if (m->random_mem_contents) {
952 for (i=0; i<m->physical_ram_in_mb * 1048576; i+=256) {
953 unsigned char data[256];
954 unsigned int j;
955 for (j=0; j<sizeof(data); j++)
956 data[j] = random() & 255;
957 cpu->memory_rw(cpu, m->memory, i, data, sizeof(data),
958 MEM_WRITE, CACHE_NONE | NO_EXCEPTIONS | PHYSICAL);
959 }
960 }
961
962 if (m->userland_emul != NULL) {
963 /*
964 * For userland-only emulation, no machine emulation
965 * is needed.
966 */
967 } else {
968 for (i=0; i<n_devices; i++)
969 device_add(m, device_names[i]);
970
971 machine_setup(m);
972 }
973
974 diskimage_dump_info(m);
975
976 /* Load files (ROM code, boot code, ...) into memory: */
977 if (n_load == 0) {
978 if (m->first_diskimage != NULL) {
979 if (!load_bootblock(m, cpu, &n_load, &load_names)) {
980 fprintf(stderr, "\nNo executable files were"
981 " specified, and booting directly from disk"
982 " failed.\n");
983 exit(1);
984 }
985 } else {
986 fprintf(stderr, "No executable file(s) loaded, and "
987 "we are not booting directly from a disk image."
988 "\nAborting.\n");
989 exit(1);
990 }
991 }
992
993 while (n_load > 0) {
994 FILE *tmp_f;
995 char *name_to_load = *load_names;
996 int remove_after_load = 0;
997
998 /* Special hack for removing temporary files: */
999 if (name_to_load[0] == 8) {
1000 name_to_load ++;
1001 remove_after_load = 1;
1002 }
1003
1004 /*
1005 * gzipped files are automagically gunzipped:
1006 * NOTE/TODO: This isn't secure. system() is used.
1007 */
1008 tmp_f = fopen(name_to_load, "r");
1009 if (tmp_f != NULL) {
1010 unsigned char buf[2]; /* gzip header */
1011 memset(buf, 0, sizeof(buf));
1012 fread(buf, 1, sizeof(buf), tmp_f);
1013 if (buf[0]==0x1f && buf[1]==0x8b) {
1014 size_t zzlen = strlen(name_to_load)*2 + 100;
1015 char *zz = malloc(zzlen);
1016 debug("gunziping %s\n", name_to_load);
1017 /*
1018 * gzip header found. If this was a file
1019 * extracted from, say, a CDROM image, then it
1020 * already has a temporary name. Otherwise we
1021 * have to gunzip into a temporary file.
1022 */
1023 if (remove_after_load) {
1024 snprintf(zz, zzlen, "mv %s %s.gz",
1025 name_to_load, name_to_load);
1026 system(zz);
1027 snprintf(zz, zzlen, "gunzip %s.gz",
1028 name_to_load);
1029 system(zz);
1030 } else {
1031 /* gunzip into new temp file: */
1032 int tmpfile_handle;
1033 char *new_temp_name =
1034 strdup("/tmp/gxemul.XXXXXXXXXXXX");
1035 tmpfile_handle = mkstemp(new_temp_name);
1036 close(tmpfile_handle);
1037 snprintf(zz, zzlen, "gunzip -c '%s' > "
1038 "%s", name_to_load, new_temp_name);
1039 system(zz);
1040 name_to_load = new_temp_name;
1041 remove_after_load = 1;
1042 }
1043 free(zz);
1044 }
1045 fclose(tmp_f);
1046 }
1047
1048 /*
1049 * Ugly (but usable) hack for Playstation Portable: If the
1050 * filename ends with ".pbp" and the file contains an ELF
1051 * header, then extract the ELF file into a temporary file.
1052 */
1053 if (strlen(name_to_load) > 4 && strcasecmp(name_to_load +
1054 strlen(name_to_load) - 4, ".pbp") == 0 &&
1055 (tmp_f = fopen(name_to_load, "r")) != NULL) {
1056 off_t filesize, j, found=0;
1057 unsigned char *buf;
1058 fseek(tmp_f, 0, SEEK_END);
1059 filesize = ftello(tmp_f);
1060 fseek(tmp_f, 0, SEEK_SET);
1061 buf = malloc(filesize);
1062 if (buf == NULL) {
1063 fprintf(stderr, "out of memory while trying"
1064 " to read %s\n", name_to_load);
1065 exit(1);
1066 }
1067 fread(buf, 1, filesize, tmp_f);
1068 fclose(tmp_f);
1069 /* Search for the ELF header, from offset 1 (!): */
1070 for (j=1; j<filesize - 4; j++)
1071 if (memcmp(buf + j, ELFMAG, SELFMAG) == 0) {
1072 found = j;
1073 break;
1074 }
1075 if (found != 0) {
1076 int tmpfile_handle;
1077 char *new_temp_name =
1078 strdup("/tmp/gxemul.XXXXXXXXXXXX");
1079 debug("extracting ELF from %s (offset 0x%x)\n",
1080 name_to_load, (int)found);
1081 tmpfile_handle = mkstemp(new_temp_name);
1082 write(tmpfile_handle, buf + found,
1083 filesize - found);
1084 close(tmpfile_handle);
1085 name_to_load = new_temp_name;
1086 remove_after_load = 1;
1087 }
1088 }
1089
1090 /* Special things required _before_ loading the file: */
1091 switch (m->arch) {
1092 case ARCH_X86:
1093 /*
1094 * X86 machines normally don't need to load any files,
1095 * they can boot from disk directly. Therefore, an x86
1096 * machine usually boots up in 16-bit real mode. When
1097 * loading a 32-bit (or even 64-bit) ELF, that's not
1098 * very nice, hence this special case.
1099 */
1100 pc_bios_simple_pmode_setup(cpu);
1101 break;
1102 }
1103
1104 byte_order = NO_BYTE_ORDER_OVERRIDE;
1105
1106 /*
1107 * Load the file: :-)
1108 */
1109 file_load(m, m->memory, name_to_load, &entrypoint,
1110 m->arch, &gp, &byte_order, &toc);
1111
1112 if (remove_after_load) {
1113 debug("removing %s\n", name_to_load);
1114 unlink(name_to_load);
1115 }
1116
1117 if (byte_order != NO_BYTE_ORDER_OVERRIDE)
1118 cpu->byte_order = byte_order;
1119
1120 cpu->pc = entrypoint;
1121
1122 switch (m->arch) {
1123 case ARCH_MIPS:
1124 if ((cpu->pc >> 32) == 0
1125 && (cpu->pc & 0x80000000ULL))
1126 cpu->pc |= 0xffffffff00000000ULL;
1127
1128 cpu->cd.mips.gpr[MIPS_GPR_GP] = gp;
1129
1130 if ((cpu->cd.mips.gpr[MIPS_GPR_GP] >> 32) == 0 &&
1131 (cpu->cd.mips.gpr[MIPS_GPR_GP] & 0x80000000ULL))
1132 cpu->cd.mips.gpr[MIPS_GPR_GP] |=
1133 0xffffffff00000000ULL;
1134 break;
1135
1136 case ARCH_PPC:
1137 /* See http://www.linuxbase.org/spec/ELF/ppc64/
1138 spec/x458.html for more info. */
1139 cpu->cd.ppc.gpr[2] = toc;
1140 /* TODO */
1141 break;
1142
1143 case ARCH_ALPHA:
1144 /* For position-independant code: */
1145 cpu->cd.alpha.r[ALPHA_T12] = cpu->pc;
1146 break;
1147
1148 case ARCH_SPARC:
1149 break;
1150
1151 case ARCH_IA64:
1152 break;
1153
1154 case ARCH_M68K:
1155 break;
1156
1157 case ARCH_ARM:
1158 cpu->pc &= 0xfffffffc;
1159 cpu->cd.arm.r[ARM_PC] = cpu->pc;
1160 break;
1161
1162 case ARCH_X86:
1163 /*
1164 * NOTE: The toc field is used to indicate an ELF32
1165 * or ELF64 load.
1166 */
1167 switch (toc) {
1168 case 0: /* 16-bit? TODO */
1169 cpu->pc &= 0xffffffffULL;
1170 break;
1171 case 1: /* 32-bit. */
1172 cpu->pc &= 0xffffffffULL;
1173 break;
1174 case 2: /* 64-bit: TODO */
1175 fatal("64-bit x86 load. TODO\n");
1176 exit(1);
1177 }
1178 break;
1179
1180 default:
1181 fatal("emul_machine_setup(): Internal error: "
1182 "Unimplemented arch %i\n", m->arch);
1183 exit(1);
1184 }
1185
1186 /*
1187 * For userland emulation, the remaining items
1188 * on the command line will be passed as parameters
1189 * to the emulated program, and will not be treated
1190 * as filenames to load into the emulator.
1191 * The program's name will be in load_names[0], and the
1192 * rest of the parameters in load_names[1] and up.
1193 */
1194 if (m->userland_emul != NULL)
1195 break;
1196
1197 n_load --;
1198 load_names ++;
1199 }
1200
1201 if (m->byte_order_override != NO_BYTE_ORDER_OVERRIDE)
1202 cpu->byte_order = m->byte_order_override;
1203
1204 /* Same byte order and entrypoint for all CPUs: */
1205 for (i=0; i<m->ncpus; i++)
1206 if (i != m->bootstrap_cpu) {
1207 m->cpus[i]->byte_order = cpu->byte_order;
1208 m->cpus[i]->pc = cpu->pc;
1209 }
1210
1211 if (m->userland_emul != NULL)
1212 useremul_setup(cpu, n_load, load_names);
1213
1214 /* Startup the bootstrap CPU: */
1215 cpu->bootstrap_cpu_flag = 1;
1216 cpu->running = 1;
1217
1218 /* ... or pause all CPUs, if start_paused is set: */
1219 if (m->start_paused) {
1220 for (i=0; i<m->ncpus; i++)
1221 m->cpus[i]->running = 0;
1222 }
1223
1224 /* Add PC dump points: */
1225 add_dump_points(m);
1226
1227 /* TODO: This is MIPS-specific! */
1228 if (m->machine_type == MACHINE_DEC &&
1229 cpu->cd.mips.cpu_type.mmu_model == MMU3K)
1230 add_symbol_name(&m->symbol_context,
1231 0x9fff0000, 0x10000, "r2k3k_cache", 0, 0);
1232
1233 symbol_recalc_sizes(&m->symbol_context);
1234
1235 if (m->max_random_cycles_per_chunk > 0)
1236 debug("using random cycle chunks (1 to %i cycles)\n",
1237 m->max_random_cycles_per_chunk);
1238
1239 /* Special hack for ARC/SGI emulation: */
1240 if ((m->machine_type == MACHINE_ARC ||
1241 m->machine_type == MACHINE_SGI) && m->prom_emulation)
1242 add_arc_components(m);
1243
1244 debug("starting cpu%i at ", m->bootstrap_cpu);
1245 switch (m->arch) {
1246 case ARCH_MIPS:
1247 if (cpu->is_32bit) {
1248 debug("0x%08x", (int)m->cpus[
1249 m->bootstrap_cpu]->pc);
1250 if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
1251 debug(" (gp=0x%08x)", (int)m->cpus[
1252 m->bootstrap_cpu]->cd.mips.gpr[
1253 MIPS_GPR_GP]);
1254 } else {
1255 debug("0x%016llx", (long long)m->cpus[
1256 m->bootstrap_cpu]->pc);
1257 if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
1258 debug(" (gp=0x%016llx)", (long long)
1259 cpu->cd.mips.gpr[MIPS_GPR_GP]);
1260 }
1261 break;
1262 case ARCH_PPC:
1263 if (cpu->cd.ppc.bits == 32)
1264 debug("0x%08x", (int)entrypoint);
1265 else
1266 debug("0x%016llx", (long long)entrypoint);
1267 break;
1268 case ARCH_ARM:
1269 /* ARM cpus aren't 64-bit: */
1270 debug("0x%08x", (int)entrypoint);
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->machines[j]);
1445 }
1446
1447 /* TODO: Generalize: */
1448 if (emuls[0]->machines[0]->show_trace_tree)
1449 cpu_functioncall_trace(emuls[0]->machines[0]->cpus[0],
1450 emuls[0]->machines[0]->cpus[0]->pc);
1451
1452 /*
1453 * MAIN LOOP:
1454 *
1455 * Run all emulations in parallel, running each machine in
1456 * each emulation.
1457 */
1458 while (go) {
1459 go = 0;
1460
1461 x11_check_event(emuls, n_emuls);
1462
1463 for (i=0; i<n_emuls; i++) {
1464 e = emuls[i];
1465 if (e == NULL)
1466 continue;
1467
1468 for (j=0; j<e->n_machines; j++) {
1469 /* TODO: cpu_run() is a strange name, since
1470 there can be multiple cpus in a machine */
1471 anything = cpu_run(e, e->machines[j]);
1472 if (anything)
1473 go = 1;
1474 }
1475 }
1476 }
1477
1478 /* Deinitialize all CPUs in all machines in all emulations: */
1479 for (i=0; i<n_emuls; i++) {
1480 e = emuls[i];
1481 if (e == NULL)
1482 continue;
1483 for (j=0; j<e->n_machines; j++)
1484 cpu_run_deinit(e->machines[j]);
1485 }
1486
1487 /* force_debugger_at_exit flag set? Then enter the debugger: */
1488 if (force_debugger_at_exit) {
1489 quiet_mode = 0;
1490 debugger_reset();
1491 debugger();
1492 }
1493
1494 /* Any machine using X11? Then we should wait before exiting: */
1495 n = 0;
1496 for (i=0; i<n_emuls; i++)
1497 for (j=0; j<emuls[i]->n_machines; j++)
1498 if (emuls[i]->machines[j]->use_x11)
1499 n++;
1500 if (n > 0) {
1501 printf("Press enter to quit.\n");
1502 while (!console_charavail(MAIN_CONSOLE)) {
1503 x11_check_event(emuls, n_emuls);
1504 usleep(1);
1505 }
1506 console_readchar(MAIN_CONSOLE);
1507 }
1508
1509 console_deinit();
1510 }
1511

  ViewVC Help
Powered by ViewVC 1.1.26