/[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 14 - (hide annotations)
Mon Oct 8 16:18:51 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 40506 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.982 2005/10/07 22:45:32 debug Exp $
20050816	Some success in decoding the way the SGI O2 PROM draws graphics
		during bootup; lines/rectangles and bitmaps work, enough to
		show the bootlogo etc. :-)
		Adding more PPC instructions, and (dummy) BAT registers.
20050817	Updating the pckbc to support scancode type 3 keyboards
		(required in order to interact with the SGI O2 PROM).
		Adding more PPC instructions.
20050818	Adding more ARM instructions; general register forms.
		Importing armreg.h from NetBSD (ARM cpu ids). Adding a (dummy)
		CATS machine mode (using SA110 as the default CPU).
		Continuing on general dyntrans related stuff.
20050819	Register forms for ARM load/stores. Gaah! The Compaq C Compiler
		bug is triggered for ARM loads as well, not just PPC :-(
		Adding full support for ARM PC-relative load/stores, and load/
		stores where the PC register is the destination register.
		Adding support for ARM a.out binaries.
20050820	Continuing to add more ARM instructions, and correcting some
		bugs. Continuing on CATS emulation.
		More work on the PPC stuff.
20050821	Minor PPC and ARM updates. Adding more machine types.
20050822	All ARM "data processing instructions" are now generated
		automatically.
20050824	Beginning the work on the ARM system control coprocessor.
		Adding support for ARM halfword load/stores, and signed loads.
20050825	Fixing an important bug related to the ARM condition codes.
		OpenBSD/zaurus and NetBSD/netwinder now print some boot
		messages. :)
		Adding a dummy SH (Hitachi SuperH) cpu family.
		Beginning to add some ARM virtual address translation.
		MIPS bugfixes: unaligned PC now cause an ADEL exception (at
		least for non-bintrans execution), and ADEL/ADES (not
		TLBL/TLBS) are used if userland tries to access kernel space.
		(Thanks to Joshua Wise for making me aware of these bugs.)
20050827	More work on the ARM emulation, and various other updates.
20050828	More ARM updates.
		Finally taking the time to work on translation invalidation
		(i.e. invalidating translated code mappings when memory is
		written to). Hopefully this doesn't break anything.
20050829	Moving CPU related files from src/ to a new subdir, src/cpus/.
		Moving PROM emulation stuff from src/ to src/promemul/.
		Better debug instruction trace for ARM loads and stores.
20050830	Various ARM updates (correcting CMP flag calculation, etc).
20050831	PPC instruction updates. (Flag fixes, etc.)
20050901	Various minor PPC and ARM instruction emulation updates.
		Minor OpenFirmware emulation updates.
20050903	Adding support for adding arbitrary ARM coprocessors (with
		the i80321 I/O coprocessor as a first test).
		Various other ARM and PPC updates.
20050904	Adding some SHcompact disassembly routines.
20050907	(Re)adding a dummy HPPA CPU module, and a dummy i960 module.
20050908	Began hacking on some Apple Partition Table support.
20050909	Adding support for loading Mach-O (Darwin PPC) binaries.
20050910	Fixing an ARM bug (Carry flag was incorrectly updated for some
		data processing instructions); OpenBSD/cats and NetBSD/
		netwinder get quite a bit further now.
		Applying a patch to dev_wdc, and a one-liner to dev_pcic, to
		make them work better when emulating new versions of OpenBSD.
		(Thanks to Alexander Yurchenko for the patches.)
		Also doing some other minor updates to dev_wdc. (Some cleanup,
		and finally converting to devinit, etc.)
20050912	IRIX doesn't have u_int64_t by default (noticed by Andreas
		<avr@gnulinux.nl>); configure updated to reflect this.
		Working on ARM register bank switching, CPSR vs SPSR issues,
		and beginning the work on interrupt/exception support.
20050913	Various minor ARM updates (speeding up load/store multiple,
		and fixing a ROR bug in R(); NetBSD/cats now boots as far as
		OpenBSD/cats).
20050917	Adding a dummy Atmel AVR (8-bit) cpu family skeleton.
20050918	Various minor updates.
20050919	Symbols are now loaded from Mach-O executables.
		Continuing the work on adding ARM exception support.
20050920	More work on ARM stuff: OpenBSD/cats and NetBSD/cats reach
		userland! :-)
20050921	Some more progress on ARM interrupt specifics.
20050923	Fixing linesize for VR4121 (patch by Yurchenko). Also fixing
		linesizes/cachesizes for some other VR4xxx.
		Adding a dummy Acer Labs M1543 PCI-ISA bridge (for CATS) and a
		dummy Symphony Labs 83C553 bridge (for Netwinder), usable by 
		dev_footbridge.
20050924	Some PPC progress.
20050925	More PPC progress.
20050926	PPC progress (fixing some bugs etc); Darwin's kernel gets
		slightly further than before.
20050928	Various updates: footbridge/ISA/pciide stuff, and finally
		fixing the VGA text scroll-by-changing-the-base-offset bug.
20050930	Adding a dummy S3 ViRGE pci card for CATS emulation, which
		both NetBSD and OpenBSD detects as VGA.
		Continuing on Footbridge (timers, ISA interrupt stuff).
20051001	Continuing... there are still bugs, probably interrupt-
		related.
20051002	More work on the Footbridge (interrupt stuff).
20051003	Various minor updates. (Trying to find the bug(s).)
20051004	Continuing on the ARM stuff.
20051005	More ARM-related fixes.
20051007	FINALLY! Found and fixed 2 ARM bugs: 1 memory related, and the
		other was because of an error in the ARM manual (load multiple
		with the S-bit set should _NOT_ load usermode registers, as the
		manual says, but it should load saved registers, which may or
		may not happen to be usermode registers).
		NetBSD/cats and OpenBSD/cats seem to install fine now :-)
		except for a minor bug at the end of the OpenBSD/cats install.
		Updating the documentation, preparing for the next release.
20051008	Continuing with release testing and cleanup.

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 14 * $Id: emul.c,v 1.234 2005/09/17 21:55:19 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 14 * apple_load_bootblock():
439     *
440     * Try to load a kernel from a disk image with an Apple Partition Table.
441     *
442     * TODO: This function uses too many magic offsets and so on; it should be
443     * cleaned up some day. See http://www.awprofessional.com/articles/
444     * article.asp?p=376123&seqNum=3&rl=1 for some info on the Apple
445     * partition format.
446     *
447     * Returns 1 on success, 0 on failure.
448     */
449     static int apple_load_bootblock(struct machine *m, struct cpu *cpu,
450     int disk_id, int disk_type, int *n_loadp, char ***load_namesp)
451     {
452     unsigned char buf[0x8000];
453     int res, partnr, n_partitions = 0, n_hfs_partitions = 0;
454     uint64_t hfs_start, hfs_length;
455    
456     res = diskimage_access(m, disk_id, disk_type, 0, 0x0, buf, sizeof(buf));
457     if (!res) {
458     fatal("apple_load_bootblock: couldn't read the disk "
459     "image. Aborting.\n");
460     return 0;
461     }
462    
463     partnr = 0;
464     do {
465     int start, length;
466     int ofs = 0x200 * (partnr + 1);
467     if (partnr == 0)
468     n_partitions = buf[ofs + 7];
469     start = (buf[ofs + 8] << 24) + (buf[ofs + 9] << 16) +
470     (buf[ofs + 10] << 8) + buf[ofs + 11];
471     length = (buf[ofs + 12] << 24) + (buf[ofs + 13] << 16) +
472     (buf[ofs + 14] << 8) + buf[ofs + 15];
473    
474     debug("partition %i: '%s', type '%s', start %i, length %i\n",
475     partnr, buf + ofs + 0x10, buf + ofs + 0x30,
476     start, length);
477    
478     if (strcmp((char *)buf + ofs + 0x30, "Apple_HFS") == 0) {
479     n_hfs_partitions ++;
480     hfs_start = 512 * start;
481     hfs_length = 512 * length;
482     }
483    
484     /* Any more partitions? */
485     partnr ++;
486     } while (partnr < n_partitions);
487    
488     if (n_hfs_partitions == 0) {
489     fatal("Error: No HFS partition found! TODO\n");
490     return 0;
491     }
492     if (n_hfs_partitions >= 2) {
493     fatal("Error: Too many HFS partitions found! TODO\n");
494     return 0;
495     }
496    
497     return 0;
498     }
499    
500    
501     /*
502 dpavlin 2 * load_bootblock():
503     *
504     * For some emulation modes, it is possible to boot from a harddisk image by
505     * loading a bootblock from a specific disk offset into memory, and executing
506     * that, instead of requiring a separate kernel file. It is then up to the
507     * bootblock to load a kernel.
508 dpavlin 6 *
509     * Returns 1 on success, 0 on failure.
510 dpavlin 2 */
511 dpavlin 6 static int load_bootblock(struct machine *m, struct cpu *cpu,
512     int *n_loadp, char ***load_namesp)
513 dpavlin 2 {
514 dpavlin 6 int boot_disk_id, boot_disk_type = 0, n_blocks, res, readofs,
515     iso_type, retval = 0;
516 dpavlin 2 unsigned char minibuf[0x20];
517     unsigned char *bootblock_buf;
518     uint64_t bootblock_offset;
519     uint64_t bootblock_loadaddr, bootblock_pc;
520    
521 dpavlin 6 boot_disk_id = diskimage_bootdev(m, &boot_disk_type);
522 dpavlin 2 if (boot_disk_id < 0)
523 dpavlin 6 return 0;
524 dpavlin 2
525     switch (m->machine_type) {
526     case MACHINE_DEC:
527     /*
528     * The first few bytes of a disk contains information about
529     * where the bootblock(s) are located. (These are all 32-bit
530     * little-endian words.)
531     *
532     * Offset 0x10 = load address
533     * 0x14 = initial PC value
534     * 0x18 = nr of 512-byte blocks to read
535     * 0x1c = offset on disk to where the bootblocks
536     * are (in 512-byte units)
537     * 0x20 = nr of blocks to read...
538     * 0x24 = offset...
539     *
540     * nr of blocks to read and offset are repeated until nr of
541     * blocks to read is zero.
542     */
543 dpavlin 6 res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0,
544 dpavlin 2 minibuf, sizeof(minibuf));
545    
546     bootblock_loadaddr = minibuf[0x10] + (minibuf[0x11] << 8)
547     + (minibuf[0x12] << 16) + (minibuf[0x13] << 24);
548    
549     /* Convert loadaddr to uncached: */
550     if ((bootblock_loadaddr & 0xf0000000ULL) != 0x80000000 &&
551     (bootblock_loadaddr & 0xf0000000ULL) != 0xa0000000)
552     fatal("\nWARNING! Weird load address 0x%08x.\n\n",
553     (int)bootblock_loadaddr);
554     bootblock_loadaddr &= 0x0fffffffULL;
555     bootblock_loadaddr |= 0xffffffffa0000000ULL;
556    
557     bootblock_pc = minibuf[0x14] + (minibuf[0x15] << 8)
558     + (minibuf[0x16] << 16) + (minibuf[0x17] << 24);
559    
560     bootblock_pc &= 0x0fffffffULL;
561     bootblock_pc |= 0xffffffffa0000000ULL;
562     cpu->pc = bootblock_pc;
563    
564     debug("DEC boot: loadaddr=0x%08x, pc=0x%08x",
565     (int)bootblock_loadaddr, (int)bootblock_pc);
566    
567     readofs = 0x18;
568    
569     for (;;) {
570 dpavlin 6 res = diskimage_access(m, boot_disk_id, boot_disk_type,
571     0, readofs, minibuf, sizeof(minibuf));
572 dpavlin 2 if (!res) {
573 dpavlin 6 fatal("Couldn't read the disk image. "
574     "Aborting.\n");
575     return 0;
576 dpavlin 2 }
577    
578     n_blocks = minibuf[0] + (minibuf[1] << 8)
579     + (minibuf[2] << 16) + (minibuf[3] << 24);
580    
581     bootblock_offset = (minibuf[4] + (minibuf[5] << 8)
582     + (minibuf[6] << 16) + (minibuf[7] << 24)) * 512;
583    
584     if (n_blocks < 1)
585     break;
586    
587     debug(readofs == 0x18? ": %i" : " + %i", n_blocks);
588    
589     if (n_blocks * 512 > 65536)
590     fatal("\nWARNING! Unusually large bootblock "
591     "(%i bytes)\n\n", n_blocks * 512);
592    
593     bootblock_buf = malloc(n_blocks * 512);
594     if (bootblock_buf == NULL) {
595     fprintf(stderr, "out of memory in "
596     "load_bootblock()\n");
597     exit(1);
598     }
599    
600 dpavlin 6 res = diskimage_access(m, boot_disk_id, boot_disk_type,
601     0, bootblock_offset, bootblock_buf, n_blocks * 512);
602 dpavlin 2 if (!res) {
603     fatal("WARNING: could not load bootblocks from"
604     " disk offset 0x%llx\n",
605     (long long)bootblock_offset);
606     }
607    
608     store_buf(cpu, bootblock_loadaddr,
609     (char *)bootblock_buf, n_blocks * 512);
610    
611     bootblock_loadaddr += 512*n_blocks;
612     free(bootblock_buf);
613     readofs += 8;
614     }
615    
616     debug(readofs == 0x18? ": no blocks?\n" : " blocks\n");
617 dpavlin 6 return 1;
618 dpavlin 4
619     case MACHINE_X86:
620 dpavlin 6 /* TODO: "El Torito" etc? */
621     if (diskimage_is_a_cdrom(cpu->machine, boot_disk_id,
622     boot_disk_type))
623     break;
624 dpavlin 4
625     bootblock_buf = malloc(512);
626     if (bootblock_buf == NULL) {
627     fprintf(stderr, "Out of memory.\n");
628     exit(1);
629     }
630    
631 dpavlin 6 debug("loading PC bootsector from %s id %i\n",
632     diskimage_types[boot_disk_type], boot_disk_id);
633    
634     res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0,
635 dpavlin 4 bootblock_buf, 512);
636     if (!res) {
637 dpavlin 6 fatal("Couldn't read the disk image. Aborting.\n");
638     return 0;
639 dpavlin 4 }
640    
641     if (bootblock_buf[510] != 0x55 || bootblock_buf[511] != 0xaa)
642     debug("WARNING! The 0x55,0xAA marker is missing! "
643     "Booting anyway.\n");
644     store_buf(cpu, 0x7c00, (char *)bootblock_buf, 512);
645     free(bootblock_buf);
646    
647 dpavlin 6 return 1;
648     }
649    
650    
651     /*
652     * Try reading a kernel manually from the disk. The code here
653     * does not rely on machine-dependant boot blocks etc.
654     */
655     /* ISO9660: (0x800 bytes at 0x8000) */
656     bootblock_buf = malloc(0x800);
657     if (bootblock_buf == NULL) {
658     fprintf(stderr, "Out of memory.\n");
659 dpavlin 2 exit(1);
660     }
661 dpavlin 6
662     res = diskimage_access(m, boot_disk_id, boot_disk_type,
663     0, 0x8000, bootblock_buf, 0x800);
664     if (!res) {
665     fatal("Couldn't read the disk image. Aborting.\n");
666     return 0;
667     }
668    
669     iso_type = 0;
670     if (strncmp((char *)bootblock_buf+1, "CD001", 5) == 0)
671     iso_type = 1;
672     if (strncmp((char *)bootblock_buf+1, "CDW01", 5) == 0)
673     iso_type = 2;
674     if (strncmp((char *)bootblock_buf+1, "CDROM", 5) == 0)
675     iso_type = 3;
676    
677     if (iso_type != 0) {
678     /* We can't load a kernel if the name
679     isn't specified. */
680     if (cpu->machine->boot_kernel_filename == NULL ||
681     cpu->machine->boot_kernel_filename[0] == '\0')
682     fatal("\nISO9660 filesystem, but no kernel "
683     "specified? (Use the -j option.)\n");
684     else
685     retval = iso_load_bootblock(m, cpu, boot_disk_id,
686     boot_disk_type, iso_type, bootblock_buf,
687     n_loadp, load_namesp);
688     }
689    
690 dpavlin 14 if (retval != 0)
691     goto ret_ok;
692    
693     /* Apple parition table: */
694     res = diskimage_access(m, boot_disk_id, boot_disk_type,
695     0, 0x0, bootblock_buf, 0x800);
696     if (!res) {
697     fatal("Couldn't read the disk image. Aborting.\n");
698     return 0;
699     }
700     if (bootblock_buf[0x000] == 'E' && bootblock_buf[0x001] == 'R' &&
701     bootblock_buf[0x200] == 'P' && bootblock_buf[0x201] == 'M') {
702     /* We can't load a kernel if the name
703     isn't specified. */
704     if (cpu->machine->boot_kernel_filename == NULL ||
705     cpu->machine->boot_kernel_filename[0] == '\0')
706     fatal("\nApple partition table, but no kernel "
707     "specified? (Use the -j option.)\n");
708     else
709     retval = apple_load_bootblock(m, cpu, boot_disk_id,
710     boot_disk_type, n_loadp, load_namesp);
711     }
712    
713     ret_ok:
714 dpavlin 6 free(bootblock_buf);
715     return retval;
716 dpavlin 2 }
717    
718    
719     /*
720     * emul_new():
721     *
722     * Returns a reasonably initialized struct emul.
723     */
724     struct emul *emul_new(char *name)
725     {
726     struct emul *e;
727     e = malloc(sizeof(struct emul));
728     if (e == NULL) {
729     fprintf(stderr, "out of memory in emul_new()\n");
730     exit(1);
731     }
732    
733     memset(e, 0, sizeof(struct emul));
734    
735     /* Sane default values: */
736     e->n_machines = 0;
737 dpavlin 10 e->next_serial_nr = 1;
738 dpavlin 2
739     if (name != NULL) {
740     e->name = strdup(name);
741     if (e->name == NULL) {
742     fprintf(stderr, "out of memory in emul_new()\n");
743     exit(1);
744     }
745     }
746    
747     return e;
748     }
749    
750    
751     /*
752     * emul_add_machine():
753     *
754     * Calls machine_new(), adds the new machine into the emul struct, and
755     * returns a pointer to the new machine.
756     *
757     * This function should be used instead of manually calling machine_new().
758     */
759     struct machine *emul_add_machine(struct emul *e, char *name)
760     {
761     struct machine *m;
762    
763     m = machine_new(name, e);
764     m->serial_nr = (e->next_serial_nr ++);
765    
766     e->n_machines ++;
767     e->machines = realloc(e->machines,
768     sizeof(struct machine *) * e->n_machines);
769     if (e->machines == NULL) {
770     fprintf(stderr, "emul_add_machine(): out of memory\n");
771     exit(1);
772     }
773    
774     e->machines[e->n_machines - 1] = m;
775     return m;
776     }
777    
778    
779     /*
780     * add_arc_components():
781     *
782     * This function adds ARCBIOS memory descriptors for the loaded program,
783     * and ARCBIOS components for SCSI devices.
784     */
785     static void add_arc_components(struct machine *m)
786     {
787     struct cpu *cpu = m->cpus[m->bootstrap_cpu];
788     uint64_t start = cpu->pc & 0x1fffffff;
789     uint64_t len = 0xc00000 - start;
790     struct diskimage *d;
791     uint64_t scsicontroller, scsidevice, scsidisk;
792    
793     if ((cpu->pc >> 60) != 0xf) {
794     start = cpu->pc & 0xffffffffffULL;
795     len = 0xc00000 - start;
796     }
797    
798     len += 1048576 * m->memory_offset_in_mb;
799    
800 dpavlin 12 /*
801     * NOTE/TODO: magic 12MB end of load program area
802     *
803     * Hm. This breaks the old FreeBSD/MIPS snapshots...
804     */
805     #if 0
806 dpavlin 2 arcbios_add_memory_descriptor(cpu,
807     0x60000 + m->memory_offset_in_mb * 1048576,
808     start-0x60000 - m->memory_offset_in_mb * 1048576,
809     ARCBIOS_MEM_FreeMemory);
810 dpavlin 12 #endif
811 dpavlin 2 arcbios_add_memory_descriptor(cpu,
812     start, len, ARCBIOS_MEM_LoadedProgram);
813    
814 dpavlin 6 scsicontroller = arcbios_get_scsicontroller(m);
815 dpavlin 2 if (scsicontroller == 0)
816     return;
817    
818     /* TODO: The device 'name' should defined be somewhere else. */
819    
820     d = m->first_diskimage;
821     while (d != NULL) {
822     if (d->type == DISKIMAGE_SCSI) {
823     int a, b, flags = COMPONENT_FLAG_Input;
824     char component_string[100];
825     char *name = "DEC RZ58 (C) DEC2000";
826    
827     /* Read-write, or read-only? */
828     if (d->writable)
829     flags |= COMPONENT_FLAG_Output;
830     else
831     flags |= COMPONENT_FLAG_ReadOnly;
832    
833     a = COMPONENT_TYPE_DiskController;
834     b = COMPONENT_TYPE_DiskPeripheral;
835    
836     if (d->is_a_cdrom) {
837     flags |= COMPONENT_FLAG_Removable;
838     a = COMPONENT_TYPE_CDROMController;
839     b = COMPONENT_TYPE_FloppyDiskPeripheral;
840     name = "NEC CD-ROM CDR-210P 1.0 ";
841     }
842    
843     scsidevice = arcbios_addchild_manual(cpu,
844     COMPONENT_CLASS_ControllerClass,
845     a, flags, 1, 2, d->id, 0xffffffff,
846     name, scsicontroller, NULL, 0);
847    
848     scsidisk = arcbios_addchild_manual(cpu,
849     COMPONENT_CLASS_PeripheralClass,
850     b, flags, 1, 2, 0, 0xffffffff, NULL,
851     scsidevice, NULL, 0);
852    
853     /*
854     * Add device string to component address mappings:
855     * "scsi(0)disk(0)rdisk(0)partition(0)"
856     */
857    
858     if (d->is_a_cdrom) {
859     snprintf(component_string,
860     sizeof(component_string),
861     "scsi(0)cdrom(%i)", d->id);
862 dpavlin 6 arcbios_add_string_to_component(m,
863 dpavlin 2 component_string, scsidevice);
864    
865     snprintf(component_string,
866     sizeof(component_string),
867     "scsi(0)cdrom(%i)fdisk(0)", d->id);
868 dpavlin 6 arcbios_add_string_to_component(m,
869 dpavlin 2 component_string, scsidisk);
870     } else {
871     snprintf(component_string,
872     sizeof(component_string),
873     "scsi(0)disk(%i)", d->id);
874 dpavlin 6 arcbios_add_string_to_component(m,
875 dpavlin 2 component_string, scsidevice);
876    
877     snprintf(component_string,
878     sizeof(component_string),
879     "scsi(0)disk(%i)rdisk(0)", d->id);
880 dpavlin 6 arcbios_add_string_to_component(m,
881 dpavlin 2 component_string, scsidisk);
882     }
883     }
884    
885     d = d->next;
886     }
887     }
888    
889    
890     /*
891     * emul_machine_setup():
892     *
893     * o) Initialize the hardware (RAM, devices, CPUs, ...) which
894     * will be emulated in this machine.
895     *
896     * o) Load ROM code and/or other programs into emulated memory.
897     *
898     * o) Special hacks needed after programs have been loaded.
899     */
900     void emul_machine_setup(struct machine *m, int n_load, char **load_names,
901     int n_devices, char **device_names)
902     {
903     struct emul *emul;
904     struct cpu *cpu;
905     int i, iadd=4;
906 dpavlin 6 uint64_t memory_amount, entrypoint = 0, gp = 0, toc = 0;
907 dpavlin 2 int byte_order;
908    
909     emul = m->emul;
910    
911     debug("machine \"%s\":\n", m->name);
912     debug_indentation(iadd);
913    
914     /* For userland-only, this decides which ARCH/cpu_name to use: */
915     if (m->machine_type == MACHINE_USERLAND && m->userland_emul != NULL) {
916     useremul_name_to_useremul(NULL, m->userland_emul,
917     &m->arch, &m->machine_name, &m->cpu_name);
918     if (m->arch == ARCH_NOARCH) {
919     printf("Unsupported userland emulation mode.\n");
920     exit(1);
921     }
922     }
923    
924     if (m->machine_type == MACHINE_NONE) {
925     fatal("No machine type specified?\n");
926     exit(1);
927     }
928    
929     m->cpu_family = cpu_family_ptr_by_number(m->arch);
930    
931 dpavlin 12 if (m->arch == ARCH_ALPHA)
932     m->arch_pagesize = 8192;
933    
934 dpavlin 10 if (m->arch != ARCH_MIPS)
935     m->bintrans_enable = 0;
936    
937 dpavlin 2 machine_memsize_fix(m);
938    
939     /*
940     * Create the system's memory:
941     *
942     * (Don't print the amount for userland-only emulation; the
943     * size doesn't matter.)
944     */
945     if (m->machine_type != MACHINE_USERLAND)
946     debug("memory: %i MB", m->physical_ram_in_mb);
947     memory_amount = (uint64_t)m->physical_ram_in_mb * 1048576;
948     if (m->memory_offset_in_mb > 0) {
949     /*
950     * A special hack is used for some SGI models,
951     * where memory is offset by 128MB to leave room for
952     * EISA space and other things.
953     */
954     debug(" (offset by %iMB)", m->memory_offset_in_mb);
955     memory_amount += 1048576 * m->memory_offset_in_mb;
956     }
957 dpavlin 12 m->memory = memory_new(memory_amount, m->arch);
958 dpavlin 2 if (m->machine_type != MACHINE_USERLAND)
959     debug("\n");
960    
961     /* Create CPUs: */
962     if (m->cpu_name == NULL)
963     machine_default_cputype(m);
964     if (m->ncpus == 0) {
965     /* TODO: This should be moved elsewhere... */
966     if (m->machine_type == MACHINE_BEBOX)
967     m->ncpus = 2;
968     else if (m->machine_type == MACHINE_ARC &&
969     m->machine_subtype == MACHINE_ARC_NEC_R96)
970     m->ncpus = 2;
971     else if (m->machine_type == MACHINE_ARC &&
972     m->machine_subtype == MACHINE_ARC_NEC_R98)
973     m->ncpus = 4;
974     else
975     m->ncpus = 1;
976     }
977     m->cpus = malloc(sizeof(struct cpu *) * m->ncpus);
978     if (m->cpus == NULL) {
979     fprintf(stderr, "out of memory\n");
980     exit(1);
981     }
982     memset(m->cpus, 0, sizeof(struct cpu *) * m->ncpus);
983    
984     /* Initialize dynamic binary translation, if available: */
985     if (m->bintrans_enable)
986     bintrans_init(m, m->memory);
987    
988     debug("cpu0");
989     if (m->ncpus > 1)
990     debug(" .. cpu%i", m->ncpus - 1);
991     debug(": ");
992     for (i=0; i<m->ncpus; i++) {
993     m->cpus[i] = cpu_new(m->memory, m, i, m->cpu_name);
994     if (m->bintrans_enable)
995     bintrans_init_cpu(m->cpus[i]);
996     }
997     debug("\n");
998    
999 dpavlin 10 #if 0
1000     /* Special case: The Playstation Portable has an additional CPU: */
1001     if (m->machine_type == MACHINE_PSP) {
1002     debug("cpu%i: ", m->ncpus);
1003     m->cpus[m->ncpus] = cpu_new(m->memory, m,
1004     0 /* use 0 here to show info with debug() */,
1005     "Allegrex" /* TODO */);
1006     if (m->bintrans_enable)
1007     bintrans_init_cpu(m->cpus[m->ncpus]);
1008     debug("\n");
1009     m->ncpus ++;
1010     }
1011     #endif
1012    
1013 dpavlin 2 if (m->use_random_bootstrap_cpu)
1014     m->bootstrap_cpu = random() % m->ncpus;
1015     else
1016     m->bootstrap_cpu = 0;
1017    
1018     cpu = m->cpus[m->bootstrap_cpu];
1019    
1020     /* Set cpu->useremul_syscall, and use userland_memory_rw: */
1021     if (m->userland_emul != NULL) {
1022     useremul_name_to_useremul(cpu,
1023     m->userland_emul, NULL, NULL, NULL);
1024 dpavlin 12
1025     switch (m->arch) {
1026     #ifdef ENABLE_ALPHA
1027     case ARCH_ALPHA:
1028     cpu->memory_rw = alpha_userland_memory_rw;
1029     break;
1030     #endif
1031     default:cpu->memory_rw = userland_memory_rw;
1032     }
1033 dpavlin 2 }
1034    
1035     if (m->use_x11)
1036     x11_init(m);
1037    
1038     /* Fill memory with random bytes: */
1039     if (m->random_mem_contents) {
1040     for (i=0; i<m->physical_ram_in_mb * 1048576; i+=256) {
1041     unsigned char data[256];
1042     unsigned int j;
1043     for (j=0; j<sizeof(data); j++)
1044     data[j] = random() & 255;
1045 dpavlin 6 cpu->memory_rw(cpu, m->memory, i, data, sizeof(data),
1046     MEM_WRITE, CACHE_NONE | NO_EXCEPTIONS | PHYSICAL);
1047 dpavlin 2 }
1048     }
1049    
1050     if (m->userland_emul != NULL) {
1051     /*
1052     * For userland-only emulation, no machine emulation
1053     * is needed.
1054     */
1055     } else {
1056     for (i=0; i<n_devices; i++)
1057     device_add(m, device_names[i]);
1058    
1059     machine_setup(m);
1060     }
1061    
1062     diskimage_dump_info(m);
1063    
1064     /* Load files (ROM code, boot code, ...) into memory: */
1065     if (n_load == 0) {
1066 dpavlin 6 if (m->first_diskimage != NULL) {
1067     if (!load_bootblock(m, cpu, &n_load, &load_names)) {
1068     fprintf(stderr, "\nNo executable files were"
1069     " specified, and booting directly from disk"
1070     " failed.\n");
1071     exit(1);
1072     }
1073     } else {
1074 dpavlin 2 fprintf(stderr, "No executable file(s) loaded, and "
1075     "we are not booting directly from a disk image."
1076     "\nAborting.\n");
1077     exit(1);
1078     }
1079     }
1080    
1081     while (n_load > 0) {
1082 dpavlin 6 FILE *tmp_f;
1083     char *name_to_load = *load_names;
1084     int remove_after_load = 0;
1085    
1086     /* Special hack for removing temporary files: */
1087     if (name_to_load[0] == 8) {
1088     name_to_load ++;
1089     remove_after_load = 1;
1090     }
1091    
1092     /*
1093 dpavlin 10 * gzipped files are automagically gunzipped:
1094     * NOTE/TODO: This isn't secure. system() is used.
1095 dpavlin 6 */
1096     tmp_f = fopen(name_to_load, "r");
1097     if (tmp_f != NULL) {
1098     unsigned char buf[2]; /* gzip header */
1099     memset(buf, 0, sizeof(buf));
1100     fread(buf, 1, sizeof(buf), tmp_f);
1101     if (buf[0]==0x1f && buf[1]==0x8b) {
1102 dpavlin 10 size_t zzlen = strlen(name_to_load)*2 + 100;
1103     char *zz = malloc(zzlen);
1104 dpavlin 6 debug("gunziping %s\n", name_to_load);
1105 dpavlin 10 /*
1106     * gzip header found. If this was a file
1107     * extracted from, say, a CDROM image, then it
1108     * already has a temporary name. Otherwise we
1109     * have to gunzip into a temporary file.
1110     */
1111     if (remove_after_load) {
1112     snprintf(zz, zzlen, "mv %s %s.gz",
1113     name_to_load, name_to_load);
1114     system(zz);
1115     snprintf(zz, zzlen, "gunzip %s.gz",
1116     name_to_load);
1117     system(zz);
1118     } else {
1119     /* gunzip into new temp file: */
1120     int tmpfile_handle;
1121     char *new_temp_name =
1122     strdup("/tmp/gxemul.XXXXXXXXXXXX");
1123     tmpfile_handle = mkstemp(new_temp_name);
1124     close(tmpfile_handle);
1125     snprintf(zz, zzlen, "gunzip -c '%s' > "
1126     "%s", name_to_load, new_temp_name);
1127     system(zz);
1128     name_to_load = new_temp_name;
1129     remove_after_load = 1;
1130     }
1131 dpavlin 6 free(zz);
1132     }
1133     fclose(tmp_f);
1134     }
1135    
1136 dpavlin 10 /*
1137     * Ugly (but usable) hack for Playstation Portable: If the
1138     * filename ends with ".pbp" and the file contains an ELF
1139     * header, then extract the ELF file into a temporary file.
1140     */
1141     if (strlen(name_to_load) > 4 && strcasecmp(name_to_load +
1142     strlen(name_to_load) - 4, ".pbp") == 0 &&
1143     (tmp_f = fopen(name_to_load, "r")) != NULL) {
1144     off_t filesize, j, found=0;
1145     unsigned char *buf;
1146     fseek(tmp_f, 0, SEEK_END);
1147     filesize = ftello(tmp_f);
1148     fseek(tmp_f, 0, SEEK_SET);
1149     buf = malloc(filesize);
1150     if (buf == NULL) {
1151     fprintf(stderr, "out of memory while trying"
1152     " to read %s\n", name_to_load);
1153     exit(1);
1154     }
1155     fread(buf, 1, filesize, tmp_f);
1156     fclose(tmp_f);
1157     /* Search for the ELF header, from offset 1 (!): */
1158     for (j=1; j<filesize - 4; j++)
1159     if (memcmp(buf + j, ELFMAG, SELFMAG) == 0) {
1160     found = j;
1161     break;
1162     }
1163     if (found != 0) {
1164     int tmpfile_handle;
1165     char *new_temp_name =
1166     strdup("/tmp/gxemul.XXXXXXXXXXXX");
1167     debug("extracting ELF from %s (offset 0x%x)\n",
1168     name_to_load, (int)found);
1169     tmpfile_handle = mkstemp(new_temp_name);
1170     write(tmpfile_handle, buf + found,
1171     filesize - found);
1172     close(tmpfile_handle);
1173     name_to_load = new_temp_name;
1174     remove_after_load = 1;
1175     }
1176     }
1177    
1178 dpavlin 6 /* Special things required _before_ loading the file: */
1179     switch (m->arch) {
1180     case ARCH_X86:
1181     /*
1182     * X86 machines normally don't need to load any files,
1183     * they can boot from disk directly. Therefore, an x86
1184     * machine usually boots up in 16-bit real mode. When
1185     * loading a 32-bit (or even 64-bit) ELF, that's not
1186     * very nice, hence this special case.
1187     */
1188     pc_bios_simple_pmode_setup(cpu);
1189     break;
1190     }
1191    
1192 dpavlin 2 byte_order = NO_BYTE_ORDER_OVERRIDE;
1193    
1194 dpavlin 6 /*
1195     * Load the file: :-)
1196     */
1197     file_load(m, m->memory, name_to_load, &entrypoint,
1198 dpavlin 2 m->arch, &gp, &byte_order, &toc);
1199    
1200 dpavlin 6 if (remove_after_load) {
1201     debug("removing %s\n", name_to_load);
1202     unlink(name_to_load);
1203     }
1204    
1205 dpavlin 2 if (byte_order != NO_BYTE_ORDER_OVERRIDE)
1206     cpu->byte_order = byte_order;
1207    
1208     cpu->pc = entrypoint;
1209    
1210     switch (m->arch) {
1211 dpavlin 14
1212     case ARCH_ALPHA:
1213     /* For position-independant code: */
1214     cpu->cd.alpha.r[ALPHA_T12] = cpu->pc;
1215     break;
1216    
1217     case ARCH_ARM:
1218     cpu->pc &= 0xfffffffc;
1219     cpu->cd.arm.r[ARM_PC] = cpu->pc;
1220     break;
1221    
1222     case ARCH_AVR:
1223     cpu->pc &= 0xfffff;
1224     if (cpu->pc & 1) {
1225     fatal("AVR: lowest bit of pc set: TODO\n");
1226     exit(1);
1227     }
1228     break;
1229    
1230     case ARCH_HPPA:
1231     break;
1232    
1233     case ARCH_I960:
1234     break;
1235    
1236     case ARCH_IA64:
1237     break;
1238    
1239     case ARCH_M68K:
1240     break;
1241    
1242 dpavlin 2 case ARCH_MIPS:
1243     if ((cpu->pc >> 32) == 0
1244     && (cpu->pc & 0x80000000ULL))
1245     cpu->pc |= 0xffffffff00000000ULL;
1246    
1247     cpu->cd.mips.gpr[MIPS_GPR_GP] = gp;
1248    
1249     if ((cpu->cd.mips.gpr[MIPS_GPR_GP] >> 32) == 0 &&
1250     (cpu->cd.mips.gpr[MIPS_GPR_GP] & 0x80000000ULL))
1251     cpu->cd.mips.gpr[MIPS_GPR_GP] |=
1252     0xffffffff00000000ULL;
1253     break;
1254 dpavlin 4
1255 dpavlin 2 case ARCH_PPC:
1256 dpavlin 6 /* See http://www.linuxbase.org/spec/ELF/ppc64/
1257     spec/x458.html for more info. */
1258 dpavlin 2 cpu->cd.ppc.gpr[2] = toc;
1259 dpavlin 6 /* TODO */
1260 dpavlin 14 if (cpu->cd.ppc.bits == 32)
1261     cpu->pc &= 0xffffffffULL;
1262 dpavlin 2 break;
1263 dpavlin 4
1264 dpavlin 14 case ARCH_SH:
1265     if (cpu->cd.sh.bits == 32)
1266     cpu->pc &= 0xffffffffULL;
1267     cpu->pc &= ~1;
1268 dpavlin 12 break;
1269    
1270 dpavlin 2 case ARCH_SPARC:
1271     break;
1272 dpavlin 4
1273     case ARCH_X86:
1274     /*
1275 dpavlin 6 * NOTE: The toc field is used to indicate an ELF32
1276     * or ELF64 load.
1277 dpavlin 4 */
1278 dpavlin 6 switch (toc) {
1279     case 0: /* 16-bit? TODO */
1280 dpavlin 4 cpu->pc &= 0xffffffffULL;
1281 dpavlin 6 break;
1282     case 1: /* 32-bit. */
1283     cpu->pc &= 0xffffffffULL;
1284     break;
1285     case 2: /* 64-bit: TODO */
1286     fatal("64-bit x86 load. TODO\n");
1287     exit(1);
1288     }
1289 dpavlin 2 break;
1290 dpavlin 4
1291 dpavlin 2 default:
1292     fatal("emul_machine_setup(): Internal error: "
1293     "Unimplemented arch %i\n", m->arch);
1294     exit(1);
1295     }
1296    
1297     /*
1298     * For userland emulation, the remaining items
1299     * on the command line will be passed as parameters
1300     * to the emulated program, and will not be treated
1301     * as filenames to load into the emulator.
1302     * The program's name will be in load_names[0], and the
1303     * rest of the parameters in load_names[1] and up.
1304     */
1305     if (m->userland_emul != NULL)
1306     break;
1307    
1308     n_load --;
1309     load_names ++;
1310     }
1311    
1312     if (m->byte_order_override != NO_BYTE_ORDER_OVERRIDE)
1313     cpu->byte_order = m->byte_order_override;
1314    
1315     /* Same byte order and entrypoint for all CPUs: */
1316     for (i=0; i<m->ncpus; i++)
1317     if (i != m->bootstrap_cpu) {
1318     m->cpus[i]->byte_order = cpu->byte_order;
1319     m->cpus[i]->pc = cpu->pc;
1320     }
1321    
1322     if (m->userland_emul != NULL)
1323     useremul_setup(cpu, n_load, load_names);
1324    
1325     /* Startup the bootstrap CPU: */
1326     cpu->bootstrap_cpu_flag = 1;
1327     cpu->running = 1;
1328    
1329     /* ... or pause all CPUs, if start_paused is set: */
1330     if (m->start_paused) {
1331     for (i=0; i<m->ncpus; i++)
1332     m->cpus[i]->running = 0;
1333     }
1334    
1335     /* Add PC dump points: */
1336     add_dump_points(m);
1337    
1338     /* TODO: This is MIPS-specific! */
1339     if (m->machine_type == MACHINE_DEC &&
1340     cpu->cd.mips.cpu_type.mmu_model == MMU3K)
1341     add_symbol_name(&m->symbol_context,
1342 dpavlin 12 0x9fff0000, 0x10000, "r2k3k_cache", 0, 0);
1343 dpavlin 2
1344     symbol_recalc_sizes(&m->symbol_context);
1345    
1346     if (m->max_random_cycles_per_chunk > 0)
1347     debug("using random cycle chunks (1 to %i cycles)\n",
1348     m->max_random_cycles_per_chunk);
1349    
1350     /* Special hack for ARC/SGI emulation: */
1351     if ((m->machine_type == MACHINE_ARC ||
1352     m->machine_type == MACHINE_SGI) && m->prom_emulation)
1353     add_arc_components(m);
1354    
1355     debug("starting cpu%i at ", m->bootstrap_cpu);
1356     switch (m->arch) {
1357 dpavlin 14
1358     case ARCH_ARM:
1359     /* ARM cpus aren't 64-bit: */
1360     debug("0x%08x", (int)entrypoint);
1361     break;
1362    
1363     case ARCH_AVR:
1364     /* Atmel AVR uses a 16-bit or 22-bit program counter: */
1365     debug("0x%04x", (int)entrypoint);
1366     break;
1367    
1368 dpavlin 2 case ARCH_MIPS:
1369 dpavlin 12 if (cpu->is_32bit) {
1370 dpavlin 2 debug("0x%08x", (int)m->cpus[
1371     m->bootstrap_cpu]->pc);
1372     if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
1373     debug(" (gp=0x%08x)", (int)m->cpus[
1374     m->bootstrap_cpu]->cd.mips.gpr[
1375     MIPS_GPR_GP]);
1376     } else {
1377     debug("0x%016llx", (long long)m->cpus[
1378     m->bootstrap_cpu]->pc);
1379     if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
1380     debug(" (gp=0x%016llx)", (long long)
1381     cpu->cd.mips.gpr[MIPS_GPR_GP]);
1382     }
1383     break;
1384 dpavlin 14
1385 dpavlin 2 case ARCH_PPC:
1386     if (cpu->cd.ppc.bits == 32)
1387     debug("0x%08x", (int)entrypoint);
1388     else
1389     debug("0x%016llx", (long long)entrypoint);
1390     break;
1391 dpavlin 14
1392 dpavlin 4 case ARCH_X86:
1393 dpavlin 6 debug("0x%04x:0x%llx", cpu->cd.x86.s[X86_S_CS],
1394     (long long)cpu->pc);
1395 dpavlin 4 break;
1396 dpavlin 14
1397 dpavlin 2 default:
1398 dpavlin 4 debug("0x%016llx", (long long)cpu->pc);
1399 dpavlin 2 }
1400     debug("\n");
1401    
1402     debug_indentation(-iadd);
1403     }
1404    
1405    
1406     /*
1407     * emul_dumpinfo():
1408     *
1409     * Dump info about all machines in an emul.
1410     */
1411     void emul_dumpinfo(struct emul *e)
1412     {
1413     int j, nm, iadd = 4;
1414    
1415     if (e->net != NULL)
1416     net_dumpinfo(e->net);
1417    
1418     nm = e->n_machines;
1419     for (j=0; j<nm; j++) {
1420     debug("machine %i: \"%s\"\n", j, e->machines[j]->name);
1421     debug_indentation(iadd);
1422     machine_dumpinfo(e->machines[j]);
1423     debug_indentation(-iadd);
1424     }
1425     }
1426    
1427    
1428     /*
1429     * emul_simple_init():
1430     *
1431     * For a normal setup:
1432     *
1433     * o) Initialize a network.
1434     * o) Initialize one machine.
1435     *
1436     * For a userland-only setup:
1437     *
1438     * o) Initialize a "pseudo"-machine.
1439     */
1440     void emul_simple_init(struct emul *emul)
1441     {
1442     int iadd=4;
1443     struct machine *m;
1444    
1445     if (emul->n_machines != 1) {
1446     fprintf(stderr, "emul_simple_init(): n_machines != 1\n");
1447     exit(1);
1448     }
1449    
1450     m = emul->machines[0];
1451    
1452     if (m->userland_emul == NULL) {
1453     debug("Simple setup...\n");
1454     debug_indentation(iadd);
1455    
1456 dpavlin 10 /* Create a simple network: */
1457 dpavlin 2 emul->net = net_init(emul, NET_INIT_FLAG_GATEWAY,
1458 dpavlin 10 "10.0.0.0", 8, NULL, 0, 0);
1459 dpavlin 2 } else {
1460     /* Userland pseudo-machine: */
1461     debug("Syscall emulation (userland-only) setup...\n");
1462     debug_indentation(iadd);
1463     }
1464    
1465     /* Create the machine: */
1466     emul_machine_setup(m, extra_argc, extra_argv, 0, NULL);
1467    
1468     debug_indentation(-iadd);
1469     }
1470    
1471    
1472     /*
1473     * emul_create_from_configfile():
1474     *
1475     * Create an emul struct by reading settings from a configuration file.
1476     */
1477     struct emul *emul_create_from_configfile(char *fname)
1478     {
1479     int iadd = 4;
1480     struct emul *e = emul_new(fname);
1481     FILE *f;
1482     char buf[128];
1483     size_t len;
1484    
1485     debug("Creating emulation from configfile \"%s\":\n", fname);
1486     debug_indentation(iadd);
1487    
1488     f = fopen(fname, "r");
1489     if (f == NULL) {
1490     perror(fname);
1491     exit(1);
1492     }
1493    
1494     /* Read header: (must be !!gxemul) */
1495     len = fread(buf, 1, 8, f);
1496     if (len != 8 || strncmp(buf, "!!gxemul", 8) != 0) {
1497     fprintf(stderr, "%s: must start with '!!gxemul'\n", fname);
1498     exit(1);
1499     }
1500    
1501     /* Restart from beginning: */
1502     rewind(f);
1503    
1504     emul_parse_config(e, f);
1505    
1506     fclose(f);
1507     debug_indentation(-iadd);
1508     return e;
1509     }
1510    
1511    
1512     /*
1513     * emul_run():
1514     *
1515     * o) Set up things needed before running emulations.
1516     *
1517     * o) Run emulations (one or more, in parallel).
1518     *
1519     * o) De-initialize things.
1520     */
1521     void emul_run(struct emul **emuls, int n_emuls)
1522     {
1523     struct emul *e;
1524     int i = 0, j, go = 1, n, anything;
1525    
1526     if (n_emuls < 1) {
1527     fprintf(stderr, "emul_run(): no thing to do\n");
1528     return;
1529     }
1530    
1531     atexit(fix_console);
1532    
1533     i = 79;
1534     while (i-- > 0)
1535     debug("-");
1536     debug("\n\n");
1537    
1538     /* Initialize the interactive debugger: */
1539     debugger_init(emuls, n_emuls);
1540    
1541     /*
1542     * console_init_main() makes sure that the terminal is in a
1543     * reasonable state.
1544     *
1545     * The SIGINT handler is for CTRL-C (enter the interactive debugger).
1546     *
1547     * The SIGCONT handler is invoked whenever the user presses CTRL-Z
1548     * (or sends SIGSTOP) and then continues. It makes sure that the
1549     * terminal is in an expected state.
1550     */
1551     console_init_main(emuls[0]); /* TODO: what is a good argument? */
1552     signal(SIGINT, debugger_activate);
1553     signal(SIGCONT, console_sigcont);
1554    
1555     /* Not in verbose mode? Then set quiet_mode. */
1556     if (!verbose)
1557     quiet_mode = 1;
1558    
1559     /* Initialize all CPUs in all machines in all emulations: */
1560     for (i=0; i<n_emuls; i++) {
1561     e = emuls[i];
1562     if (e == NULL)
1563     continue;
1564     for (j=0; j<e->n_machines; j++)
1565 dpavlin 12 cpu_run_init(e->machines[j]);
1566 dpavlin 2 }
1567    
1568 dpavlin 12 /* TODO: Generalize: */
1569     if (emuls[0]->machines[0]->show_trace_tree)
1570     cpu_functioncall_trace(emuls[0]->machines[0]->cpus[0],
1571     emuls[0]->machines[0]->cpus[0]->pc);
1572    
1573 dpavlin 2 /*
1574     * MAIN LOOP:
1575     *
1576     * Run all emulations in parallel, running each machine in
1577     * each emulation.
1578     */
1579     while (go) {
1580     go = 0;
1581    
1582     x11_check_event(emuls, n_emuls);
1583    
1584     for (i=0; i<n_emuls; i++) {
1585     e = emuls[i];
1586     if (e == NULL)
1587     continue;
1588    
1589     for (j=0; j<e->n_machines; j++) {
1590     /* TODO: cpu_run() is a strange name, since
1591     there can be multiple cpus in a machine */
1592     anything = cpu_run(e, e->machines[j]);
1593     if (anything)
1594     go = 1;
1595     }
1596     }
1597     }
1598    
1599     /* Deinitialize all CPUs in all machines in all emulations: */
1600     for (i=0; i<n_emuls; i++) {
1601     e = emuls[i];
1602     if (e == NULL)
1603     continue;
1604     for (j=0; j<e->n_machines; j++)
1605 dpavlin 12 cpu_run_deinit(e->machines[j]);
1606 dpavlin 2 }
1607    
1608     /* force_debugger_at_exit flag set? Then enter the debugger: */
1609     if (force_debugger_at_exit) {
1610     quiet_mode = 0;
1611     debugger_reset();
1612     debugger();
1613     }
1614    
1615     /* Any machine using X11? Then we should wait before exiting: */
1616     n = 0;
1617     for (i=0; i<n_emuls; i++)
1618     for (j=0; j<emuls[i]->n_machines; j++)
1619     if (emuls[i]->machines[j]->use_x11)
1620     n++;
1621     if (n > 0) {
1622     printf("Press enter to quit.\n");
1623     while (!console_charavail(MAIN_CONSOLE)) {
1624     x11_check_event(emuls, n_emuls);
1625     usleep(1);
1626     }
1627     console_readchar(MAIN_CONSOLE);
1628     }
1629    
1630     console_deinit();
1631     }
1632    

  ViewVC Help
Powered by ViewVC 1.1.26