/[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 20 - (hide annotations)
Mon Oct 8 16:19:23 2007 UTC (13 years ago) by dpavlin
File MIME type: text/plain
File size: 40733 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1055 2005/11/25 22:48:36 debug Exp $
20051031	Adding disassembly support for more ARM instructions (clz,
		smul* etc), and adding a hack to support "new tiny" pages
		for StrongARM.
20051101	Minor documentation updates (NetBSD 2.0.2 -> 2.1, and OpenBSD
		3.7 -> 3.8, and lots of testing).
		Changing from 1-sector PIO mode 0 transfers to 128-sector PIO
		mode 3 (in dev_wdc).
		Various minor ARM dyntrans updates (pc-relative loads from
		within the same page as the instruction are now treated as
		constant "mov").
20051102	Re-enabling instruction combinations (they were accidentally
		disabled).
		Dyntrans TLB entries are now overwritten using a round-robin
		scheme instead of randomly. This increases performance.
		Fixing a typo in file.c (thanks to Chuan-Hua Chang for
		noticing it).
		Experimenting with adding ATAPI support to dev_wdc (to make
		emulated *BSD detect cdroms as cdroms, not harddisks).
20051104	Various minor updates.
20051105	Continuing on the ATAPI emulation. Seems to work well enough
		for a NetBSD/cats installation, but not OpenBSD/cats.
		Various other updates.
20051106	Modifying the -Y command line option to allow scaleup with
		certain graphic controllers (only dev_vga so far), not just
		scaledown.
		Some minor dyntrans cleanups.
20051107	Beginning a cleanup up the PCI subsystem (removing the
		read_register hack, etc).
20051108	Continuing the cleanup; splitting up some pci devices into a
		normal autodev device and some separate pci glue code.
20051109	Continuing on the PCI bus stuff; all old pci_*.c have been
		incorporated into normal devices and/or rewritten as glue code
		only, adding a dummy Intel 82371AB PIIX4 for Malta (not really
		tested yet).
		Minor pckbc fix so that Linux doesn't complain.
		Working on the DEC 21143 NIC (ethernet mac rom stuff mostly).
		Various other minor fixes.
20051110	Some more ARM dyntrans fine-tuning (e.g. some instruction
		combinations (cmps followed by conditional branch within the
		same page) and special cases for DPIs with regform when the
		shifter isn't used).
20051111	ARM dyntrans updates: O(n)->O(1) for just-mark-as-non-
		writable in the generic pc_to_pointers function, and some other
		minor hacks.
		Merging Cobalt and evbmips (Malta) ISA interrupt handling,
		and some minor fixes to allow Linux to accept harddisk irqs.
20051112	Minor device updates (pckbc, dec21143, lpt, ...), most
		importantly fixing the ALI M1543/M5229 so that harddisk irqs
		work with Linux/CATS.
20051113	Some more generalizations of the PCI subsystem.
		Finally took the time to add a hack for SCSI CDROM TOCs; this
		enables OpenBSD to use partition 'a' (as needed by the OpenBSD
		installer), and Windows NT's installer to get a bit further.
		Also fixing dev_wdc to allow Linux to detect ATAPI CDROMs.
		Continuing on the DEC 21143.
20051114	Minor ARM dyntrans tweaks; ARM cmps+branch optimization when
		comparing with 0, and generalizing the xchg instr. comb.
		Adding disassembly of ARM mrrc/mcrr and q{,d}{add,sub}.
20051115	Continuing on various PPC things (BATs, other address trans-
		lation things, various loads/stores, BeBox emulation, etc.).
		Beginning to work on PPC interrupt/exception support.
20051116	Factoring out some code which initializes legacy ISA devices
		from those machines that use them (bus_isa).
		Continuing on PPC interrupt/exception support.
20051117	Minor Malta fixes: RTC year offset = 80, disabling a speed hack
		which caused NetBSD to detect a too fast cpu, and adding a new
		hack to make Linux detect a faster cpu.
		Continuing on the Artesyn PM/PPC emulation mode.
		Adding an Algor emulation skeleton (P4032 and P5064);
		implementing some of the basics.
		Continuing on PPC emulation in general; usage of unimplemented
		SPRs is now easier to track, continuing on memory/exception
		related issues, etc.
20051118	More work on PPC emulation (tgpr0..3, exception handling,
		memory stuff, syscalls, etc.).
20051119	Changing the ARM dyntrans code to mostly use cpu->pc, and not
		necessarily use arm reg 15. Seems to work.
		Various PPC updates; continuing on the PReP emulation mode.
20051120	Adding a workaround/hack to dev_mc146818 to allow NetBSD/prep
		to detect the clock.
20051121	More cleanup of the PCI bus (memory and I/O bases, etc).
		Continuing on various PPC things (decrementer and timebase,
		WDCs on obio (on PReP) use irq 13, not 14/15).
20051122	Continuing on the CPC700 controller (interrupts etc) for PMPPC,
		and on PPC stuff in general.
		Finally! After some bug fixes to the virtual to physical addr
		translation, NetBSD/{prep,pmppc} 2.1 reach userland and are
		stable enough to be interacted with.
		More PCI updates; reverse-endian device access for PowerPC etc.
20051123	Generalizing the IEEE floating point subsystem (moving it out
		from src/cpus/cpu_mips_coproc.c into a new src/float_emul.c).
		Input via slave xterms was sometimes not really working; fixing
		this for ns16550, and a warning message is now displayed if
		multiple non-xterm consoles are active.
		Adding some PPC floating point support, etc.
		Various interrupt related updates (dev_wdc, _ns16550, _8259,
		and the isa32 common code in machine.c).
		NetBSD/prep can now be installed! :-) (Well, with some manual
		commands necessary before running sysinst.) Updating the
		documentation and various other things to reflect this.
20051124	Various minor documentation updates.
		Continuing the work on the DEC 21143 NIC.
20051125	LOTS of work on the 21143. Both OpenBSD and NetBSD work fine
		with it now, except that OpenBSD sometimes gives a time-out
		warning.
		Minor documentation updates.

==============  RELEASE 0.3.7  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26