/[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

Annotation of /trunk/src/emul.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (hide 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 dpavlin 2 /*
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 dpavlin 12 * $Id: emul.c,v 1.225 2005/08/14 19:35:54 debug Exp $
29 dpavlin 2 *
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 dpavlin 10 #include "exec_elf.h"
50 dpavlin 2 #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 dpavlin 6 static char *diskimage_types[] = DISKIMAGE_TYPES;
71 dpavlin 2
72 dpavlin 6
73 dpavlin 2 /*
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 dpavlin 6 * 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 dpavlin 2 * 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 dpavlin 6 *
445     * Returns 1 on success, 0 on failure.
446 dpavlin 2 */
447 dpavlin 6 static int load_bootblock(struct machine *m, struct cpu *cpu,
448     int *n_loadp, char ***load_namesp)
449 dpavlin 2 {
450 dpavlin 6 int boot_disk_id, boot_disk_type = 0, n_blocks, res, readofs,
451     iso_type, retval = 0;
452 dpavlin 2 unsigned char minibuf[0x20];
453     unsigned char *bootblock_buf;
454     uint64_t bootblock_offset;
455     uint64_t bootblock_loadaddr, bootblock_pc;
456    
457 dpavlin 6 boot_disk_id = diskimage_bootdev(m, &boot_disk_type);
458 dpavlin 2 if (boot_disk_id < 0)
459 dpavlin 6 return 0;
460 dpavlin 2
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 dpavlin 6 res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0,
480 dpavlin 2 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 dpavlin 6 res = diskimage_access(m, boot_disk_id, boot_disk_type,
507     0, readofs, minibuf, sizeof(minibuf));
508 dpavlin 2 if (!res) {
509 dpavlin 6 fatal("Couldn't read the disk image. "
510     "Aborting.\n");
511     return 0;
512 dpavlin 2 }
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 dpavlin 6 res = diskimage_access(m, boot_disk_id, boot_disk_type,
537     0, bootblock_offset, bootblock_buf, n_blocks * 512);
538 dpavlin 2 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 dpavlin 6 return 1;
554 dpavlin 4
555     case MACHINE_X86:
556 dpavlin 6 /* TODO: "El Torito" etc? */
557     if (diskimage_is_a_cdrom(cpu->machine, boot_disk_id,
558     boot_disk_type))
559     break;
560 dpavlin 4
561     bootblock_buf = malloc(512);
562     if (bootblock_buf == NULL) {
563     fprintf(stderr, "Out of memory.\n");
564     exit(1);
565     }
566    
567 dpavlin 6 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 dpavlin 4 bootblock_buf, 512);
572     if (!res) {
573 dpavlin 6 fatal("Couldn't read the disk image. Aborting.\n");
574     return 0;
575 dpavlin 4 }
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 dpavlin 6 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 dpavlin 2 exit(1);
596     }
597 dpavlin 6
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 dpavlin 2 }
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 dpavlin 10 e->next_serial_nr = 1;
650 dpavlin 2
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 dpavlin 12 /*
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 dpavlin 2 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 dpavlin 12 #endif
723 dpavlin 2 arcbios_add_memory_descriptor(cpu,
724     start, len, ARCBIOS_MEM_LoadedProgram);
725    
726 dpavlin 6 scsicontroller = arcbios_get_scsicontroller(m);
727 dpavlin 2 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 dpavlin 6 arcbios_add_string_to_component(m,
775 dpavlin 2 component_string, scsidevice);
776    
777     snprintf(component_string,
778     sizeof(component_string),
779     "scsi(0)cdrom(%i)fdisk(0)", d->id);
780 dpavlin 6 arcbios_add_string_to_component(m,
781 dpavlin 2 component_string, scsidisk);
782     } else {
783     snprintf(component_string,
784     sizeof(component_string),
785     "scsi(0)disk(%i)", d->id);
786 dpavlin 6 arcbios_add_string_to_component(m,
787 dpavlin 2 component_string, scsidevice);
788    
789     snprintf(component_string,
790     sizeof(component_string),
791     "scsi(0)disk(%i)rdisk(0)", d->id);
792 dpavlin 6 arcbios_add_string_to_component(m,
793 dpavlin 2 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 dpavlin 6 uint64_t memory_amount, entrypoint = 0, gp = 0, toc = 0;
819 dpavlin 2 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 dpavlin 12 if (m->arch == ARCH_ALPHA)
844     m->arch_pagesize = 8192;
845    
846 dpavlin 10 if (m->arch != ARCH_MIPS)
847     m->bintrans_enable = 0;
848    
849 dpavlin 2 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 dpavlin 12 m->memory = memory_new(memory_amount, m->arch);
870 dpavlin 2 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 dpavlin 10 #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 dpavlin 2 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 dpavlin 12
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 dpavlin 2 }
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 dpavlin 6 cpu->memory_rw(cpu, m->memory, i, data, sizeof(data),
958     MEM_WRITE, CACHE_NONE | NO_EXCEPTIONS | PHYSICAL);
959 dpavlin 2 }
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 dpavlin 6 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 dpavlin 2 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 dpavlin 6 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 dpavlin 10 * gzipped files are automagically gunzipped:
1006     * NOTE/TODO: This isn't secure. system() is used.
1007 dpavlin 6 */
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 dpavlin 10 size_t zzlen = strlen(name_to_load)*2 + 100;
1015     char *zz = malloc(zzlen);
1016 dpavlin 6 debug("gunziping %s\n", name_to_load);
1017 dpavlin 10 /*
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 dpavlin 6 free(zz);
1044     }
1045     fclose(tmp_f);
1046     }
1047    
1048 dpavlin 10 /*
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 dpavlin 6 /* 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 dpavlin 2 byte_order = NO_BYTE_ORDER_OVERRIDE;
1105    
1106 dpavlin 6 /*
1107     * Load the file: :-)
1108     */
1109     file_load(m, m->memory, name_to_load, &entrypoint,
1110 dpavlin 2 m->arch, &gp, &byte_order, &toc);
1111    
1112 dpavlin 6 if (remove_after_load) {
1113     debug("removing %s\n", name_to_load);
1114     unlink(name_to_load);
1115     }
1116    
1117 dpavlin 2 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 dpavlin 4
1136 dpavlin 2 case ARCH_PPC:
1137 dpavlin 6 /* See http://www.linuxbase.org/spec/ELF/ppc64/
1138     spec/x458.html for more info. */
1139 dpavlin 2 cpu->cd.ppc.gpr[2] = toc;
1140 dpavlin 6 /* TODO */
1141 dpavlin 2 break;
1142 dpavlin 4
1143     case ARCH_ALPHA:
1144 dpavlin 12 /* For position-independant code: */
1145     cpu->cd.alpha.r[ALPHA_T12] = cpu->pc;
1146     break;
1147    
1148 dpavlin 2 case ARCH_SPARC:
1149     break;
1150 dpavlin 4
1151 dpavlin 12 case ARCH_IA64:
1152     break;
1153    
1154     case ARCH_M68K:
1155     break;
1156    
1157 dpavlin 6 case ARCH_ARM:
1158 dpavlin 10 cpu->pc &= 0xfffffffc;
1159     cpu->cd.arm.r[ARM_PC] = cpu->pc;
1160 dpavlin 6 break;
1161    
1162 dpavlin 4 case ARCH_X86:
1163     /*
1164 dpavlin 6 * NOTE: The toc field is used to indicate an ELF32
1165     * or ELF64 load.
1166 dpavlin 4 */
1167 dpavlin 6 switch (toc) {
1168     case 0: /* 16-bit? TODO */
1169 dpavlin 4 cpu->pc &= 0xffffffffULL;
1170 dpavlin 6 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 dpavlin 2 break;
1179 dpavlin 4
1180 dpavlin 2 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 dpavlin 12 0x9fff0000, 0x10000, "r2k3k_cache", 0, 0);
1232 dpavlin 2
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 dpavlin 12 if (cpu->is_32bit) {
1248 dpavlin 2 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 dpavlin 6 case ARCH_ARM:
1269     /* ARM cpus aren't 64-bit: */
1270     debug("0x%08x", (int)entrypoint);
1271     break;
1272 dpavlin 4 case ARCH_X86:
1273 dpavlin 6 debug("0x%04x:0x%llx", cpu->cd.x86.s[X86_S_CS],
1274     (long long)cpu->pc);
1275 dpavlin 4 break;
1276 dpavlin 2 default:
1277 dpavlin 4 debug("0x%016llx", (long long)cpu->pc);
1278 dpavlin 2 }
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 dpavlin 10 /* Create a simple network: */
1336 dpavlin 2 emul->net = net_init(emul, NET_INIT_FLAG_GATEWAY,
1337 dpavlin 10 "10.0.0.0", 8, NULL, 0, 0);
1338 dpavlin 2 } 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 dpavlin 12 cpu_run_init(e->machines[j]);
1445 dpavlin 2 }
1446    
1447 dpavlin 12 /* 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 dpavlin 2 /*
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 dpavlin 12 cpu_run_deinit(e->machines[j]);
1485 dpavlin 2 }
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