/[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 38 - (hide annotations)
Mon Oct 8 16:21:53 2007 UTC (13 years ago) by dpavlin
File MIME type: text/plain
File size: 26841 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1515 2007/04/14 05:39:46 debug Exp $
20070324	Adding a "--debug" option to the configure script, to disable
		optimizations in unstable development builds.
		Moving out SCSI-specific stuff from diskimage.c into a new
		diskimage_scsicmd.c.
		Applying Hĺvard Eidnes' patch for SCSICDROM_READ_DISKINFO and
		SCSICDROM_READ_TRACKINFO. (Not really tested yet.)
		Implementing disk image "overlays" (to allow simple roll-back
		to previous disk state). Adding a 'V' disk flag for this, and
		updating the man page and misc.html.
20070325	Stability fix to cpu_dyntrans.c, when multiple physical pages
		share the same initial table entry. (The ppp == NULL check
		should be physpage_ofs == 0.) Bug found by analysing GXemul
		against a version patched for Godson.
		Fixing a second occurance of the same problem (also in
		cpu_dyntrans.c).
		Fixing a MAJOR physical page leak in cpu_dyntrans.c; pages
		weren't _added_ to the set of translated pages, they _replaced_
		all previous pages. It's amazing that this bug has been able
		to live for this long. (Triggered when emulating >128MB RAM.)
20070326	Removing the GDB debugging stub support; it was too hackish
		and ugly.
20070328	Moving around some native code generation skeleton code.
20070329	The -lm check in the configure script now also checks for sin()
		in addition to sqrt(). (Thanks to Nigel Horne for noticing that
		sqrt was not enough on Fedora Core 6.) (Not verified yet.)
20070330	Fixing an indexing bug in dev_sh4.c, found by using gcc version
		4.3.0 20070323.
20070331	Some more experimentation with native code generation.
20070404	Attempting to fix some more SH4 SCIF interrupt bugs; rewriting
		the SH interrupt assertion/deassertion code somewhat.
20070410	Splitting src/file.c into separate files in src/file/.
		Cleanup: Removing the dummy TS7200, Walnut, PB1000, and
		Meshcube emulation modes, and dev_epcom and dev_au1x00.
		Removing the experimental CHIP8/RCA180x code; it wasn't really
		working much lately, anyway. It was fun while it lasted.
		Also removing the experimental Transputer CPU support.
20070412	Moving the section about how the dynamic translation system
		works from intro.html to a separate translation.html file.
		Minor SH fixes; attempting to get OpenBSD/landisk to run
		without randomly bugging out, but no success yet.
20070413	SH SCI (serial bit interface) should now work together with a
		(new) RS5C313 clock device (for Landisk emulation).
20070414	Moving Redhat/MIPS down from supported to experimental, in
		guestoses.html.
		Preparing for a new release; doing some regression testing etc.

==============  RELEASE 0.4.5  ==============


1 dpavlin 2 /*
2 dpavlin 34 * Copyright (C) 2003-2007 Anders Gavare. All rights reserved.
3 dpavlin 2 *
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 38 * $Id: emul.c,v 1.282 2007/04/11 15:15:31 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 "cpu.h"
43     #include "emul.h"
44     #include "console.h"
45     #include "debugger.h"
46     #include "device.h"
47     #include "diskimage.h"
48 dpavlin 10 #include "exec_elf.h"
49 dpavlin 2 #include "machine.h"
50     #include "memory.h"
51     #include "mips_cpu_types.h"
52     #include "misc.h"
53     #include "net.h"
54 dpavlin 32 #include "settings.h"
55 dpavlin 2 #include "sgi_arcbios.h"
56 dpavlin 32 #include "timer.h"
57 dpavlin 2 #include "x11.h"
58    
59    
60     extern int extra_argc;
61     extern char **extra_argv;
62    
63     extern int verbose;
64     extern int quiet_mode;
65 dpavlin 26 extern int force_debugger_at_exit;
66     extern int single_step;
67     extern int old_show_trace_tree;
68     extern int old_instruction_trace;
69     extern int old_quiet_mode;
70     extern int quiet_mode;
71 dpavlin 2
72     extern struct emul *debugger_emul;
73     extern struct diskimage *diskimages[];
74    
75    
76 dpavlin 22 static void print_separator(void)
77     {
78     int i = 79;
79     while (i-- > 0)
80     debug("-");
81     debug("\n");
82     }
83    
84    
85 dpavlin 2 /*
86     * add_dump_points():
87     *
88     * Take the strings breakpoint_string[] and convert to addresses
89     * (and store them in breakpoint_addr[]).
90     *
91     * TODO: This function should be moved elsewhere.
92     */
93     static void add_dump_points(struct machine *m)
94     {
95     int i;
96     int string_flag;
97     uint64_t dp;
98    
99     for (i=0; i<m->n_breakpoints; i++) {
100     string_flag = 0;
101     dp = strtoull(m->breakpoint_string[i], NULL, 0);
102    
103     /*
104     * If conversion resulted in 0, then perhaps it is a
105     * symbol:
106     */
107     if (dp == 0) {
108     uint64_t addr;
109     int res = get_symbol_addr(&m->symbol_context,
110     m->breakpoint_string[i], &addr);
111 dpavlin 24 if (!res) {
112 dpavlin 2 fprintf(stderr,
113 dpavlin 24 "ERROR! Breakpoint '%s' could not be"
114 dpavlin 2 " parsed\n",
115     m->breakpoint_string[i]);
116 dpavlin 24 } else {
117 dpavlin 2 dp = addr;
118     string_flag = 1;
119     }
120     }
121    
122     /*
123     * TODO: It would be nice if things like symbolname+0x1234
124     * were automatically converted into the correct address.
125     */
126    
127 dpavlin 20 if (m->arch == ARCH_MIPS) {
128     if ((dp >> 32) == 0 && ((dp >> 31) & 1))
129     dp |= 0xffffffff00000000ULL;
130     }
131    
132 dpavlin 2 m->breakpoint_addr[i] = dp;
133    
134 dpavlin 20 debug("breakpoint %i: 0x%llx", i, (long long)dp);
135 dpavlin 2 if (string_flag)
136     debug(" (%s)", m->breakpoint_string[i]);
137     debug("\n");
138     }
139     }
140    
141    
142     /*
143     * fix_console():
144     */
145     static void fix_console(void)
146     {
147 dpavlin 32 console_deinit_main();
148 dpavlin 2 }
149    
150    
151     /*
152     * emul_new():
153     *
154     * Returns a reasonably initialized struct emul.
155     */
156 dpavlin 34 struct emul *emul_new(char *name, int id)
157 dpavlin 2 {
158     struct emul *e;
159     e = malloc(sizeof(struct emul));
160     if (e == NULL) {
161     fprintf(stderr, "out of memory in emul_new()\n");
162     exit(1);
163     }
164    
165     memset(e, 0, sizeof(struct emul));
166    
167 dpavlin 34 e->path = malloc(15);
168     if (e->path == NULL) {
169     fprintf(stderr, "out of memory\n");
170     exit(1);
171     }
172     snprintf(e->path, 15, "emul[%i]", id);
173    
174 dpavlin 32 e->settings = settings_new();
175    
176     settings_add(e->settings, "n_machines", 0,
177     SETTINGS_TYPE_INT, SETTINGS_FORMAT_DECIMAL,
178     (void *) &e->n_machines);
179    
180     /* TODO: More settings? */
181    
182 dpavlin 2 /* Sane default values: */
183     e->n_machines = 0;
184 dpavlin 10 e->next_serial_nr = 1;
185 dpavlin 2
186     if (name != NULL) {
187     e->name = strdup(name);
188     if (e->name == NULL) {
189     fprintf(stderr, "out of memory in emul_new()\n");
190     exit(1);
191     }
192 dpavlin 32
193     settings_add(e->settings, "name", 0,
194     SETTINGS_TYPE_STRING, SETTINGS_FORMAT_STRING,
195     (void *) &e->name);
196 dpavlin 2 }
197    
198     return e;
199     }
200    
201    
202     /*
203 dpavlin 32 * emul_destroy():
204     *
205     * Destroys a previously created emul object.
206     */
207     void emul_destroy(struct emul *emul)
208     {
209     int i;
210    
211     if (emul->name != NULL) {
212     settings_remove(emul->settings, "name");
213     free(emul->name);
214     }
215    
216     for (i=0; i<emul->n_machines; i++)
217     machine_destroy(emul->machines[i]);
218    
219     if (emul->machines != NULL)
220     free(emul->machines);
221    
222     /* Remove any remaining level-1 settings: */
223     settings_remove_all(emul->settings);
224     settings_destroy(emul->settings);
225    
226     free(emul);
227     }
228    
229    
230     /*
231 dpavlin 2 * emul_add_machine():
232     *
233     * Calls machine_new(), adds the new machine into the emul struct, and
234     * returns a pointer to the new machine.
235     *
236     * This function should be used instead of manually calling machine_new().
237     */
238     struct machine *emul_add_machine(struct emul *e, char *name)
239     {
240     struct machine *m;
241 dpavlin 32 char tmpstr[20];
242     int i;
243 dpavlin 2
244 dpavlin 34 m = machine_new(name, e, e->n_machines);
245 dpavlin 2 m->serial_nr = (e->next_serial_nr ++);
246    
247 dpavlin 32 i = e->n_machines;
248    
249 dpavlin 2 e->n_machines ++;
250     e->machines = realloc(e->machines,
251     sizeof(struct machine *) * e->n_machines);
252     if (e->machines == NULL) {
253     fprintf(stderr, "emul_add_machine(): out of memory\n");
254     exit(1);
255     }
256    
257 dpavlin 32 e->machines[i] = m;
258    
259     snprintf(tmpstr, sizeof(tmpstr), "machine[%i]", i);
260     settings_add(e->settings, tmpstr, 1, SETTINGS_TYPE_SUBSETTINGS, 0,
261     e->machines[i]->settings);
262    
263 dpavlin 2 return m;
264     }
265    
266    
267     /*
268     * add_arc_components():
269     *
270     * This function adds ARCBIOS memory descriptors for the loaded program,
271     * and ARCBIOS components for SCSI devices.
272     */
273     static void add_arc_components(struct machine *m)
274     {
275     struct cpu *cpu = m->cpus[m->bootstrap_cpu];
276     uint64_t start = cpu->pc & 0x1fffffff;
277     uint64_t len = 0xc00000 - start;
278     struct diskimage *d;
279     uint64_t scsicontroller, scsidevice, scsidisk;
280    
281     if ((cpu->pc >> 60) != 0xf) {
282     start = cpu->pc & 0xffffffffffULL;
283     len = 0xc00000 - start;
284     }
285    
286     len += 1048576 * m->memory_offset_in_mb;
287    
288 dpavlin 12 /*
289     * NOTE/TODO: magic 12MB end of load program area
290     *
291     * Hm. This breaks the old FreeBSD/MIPS snapshots...
292     */
293     #if 0
294 dpavlin 2 arcbios_add_memory_descriptor(cpu,
295     0x60000 + m->memory_offset_in_mb * 1048576,
296     start-0x60000 - m->memory_offset_in_mb * 1048576,
297     ARCBIOS_MEM_FreeMemory);
298 dpavlin 12 #endif
299 dpavlin 2 arcbios_add_memory_descriptor(cpu,
300     start, len, ARCBIOS_MEM_LoadedProgram);
301    
302 dpavlin 6 scsicontroller = arcbios_get_scsicontroller(m);
303 dpavlin 2 if (scsicontroller == 0)
304     return;
305    
306     /* TODO: The device 'name' should defined be somewhere else. */
307    
308     d = m->first_diskimage;
309     while (d != NULL) {
310     if (d->type == DISKIMAGE_SCSI) {
311     int a, b, flags = COMPONENT_FLAG_Input;
312     char component_string[100];
313     char *name = "DEC RZ58 (C) DEC2000";
314    
315     /* Read-write, or read-only? */
316     if (d->writable)
317     flags |= COMPONENT_FLAG_Output;
318     else
319     flags |= COMPONENT_FLAG_ReadOnly;
320    
321     a = COMPONENT_TYPE_DiskController;
322     b = COMPONENT_TYPE_DiskPeripheral;
323    
324     if (d->is_a_cdrom) {
325     flags |= COMPONENT_FLAG_Removable;
326     a = COMPONENT_TYPE_CDROMController;
327     b = COMPONENT_TYPE_FloppyDiskPeripheral;
328     name = "NEC CD-ROM CDR-210P 1.0 ";
329     }
330    
331     scsidevice = arcbios_addchild_manual(cpu,
332     COMPONENT_CLASS_ControllerClass,
333     a, flags, 1, 2, d->id, 0xffffffff,
334     name, scsicontroller, NULL, 0);
335    
336     scsidisk = arcbios_addchild_manual(cpu,
337     COMPONENT_CLASS_PeripheralClass,
338     b, flags, 1, 2, 0, 0xffffffff, NULL,
339     scsidevice, NULL, 0);
340    
341     /*
342     * Add device string to component address mappings:
343     * "scsi(0)disk(0)rdisk(0)partition(0)"
344     */
345    
346     if (d->is_a_cdrom) {
347     snprintf(component_string,
348     sizeof(component_string),
349     "scsi(0)cdrom(%i)", d->id);
350 dpavlin 6 arcbios_add_string_to_component(m,
351 dpavlin 2 component_string, scsidevice);
352    
353     snprintf(component_string,
354     sizeof(component_string),
355     "scsi(0)cdrom(%i)fdisk(0)", d->id);
356 dpavlin 6 arcbios_add_string_to_component(m,
357 dpavlin 2 component_string, scsidisk);
358     } else {
359     snprintf(component_string,
360     sizeof(component_string),
361     "scsi(0)disk(%i)", d->id);
362 dpavlin 6 arcbios_add_string_to_component(m,
363 dpavlin 2 component_string, scsidevice);
364    
365     snprintf(component_string,
366     sizeof(component_string),
367     "scsi(0)disk(%i)rdisk(0)", d->id);
368 dpavlin 6 arcbios_add_string_to_component(m,
369 dpavlin 2 component_string, scsidisk);
370     }
371     }
372    
373     d = d->next;
374     }
375     }
376    
377    
378     /*
379     * emul_machine_setup():
380     *
381     * o) Initialize the hardware (RAM, devices, CPUs, ...) which
382     * will be emulated in this machine.
383     *
384     * o) Load ROM code and/or other programs into emulated memory.
385     *
386     * o) Special hacks needed after programs have been loaded.
387     */
388     void emul_machine_setup(struct machine *m, int n_load, char **load_names,
389     int n_devices, char **device_names)
390     {
391     struct cpu *cpu;
392 dpavlin 22 int i, iadd = DEBUG_INDENTATION;
393 dpavlin 6 uint64_t memory_amount, entrypoint = 0, gp = 0, toc = 0;
394 dpavlin 2 int byte_order;
395    
396     debug("machine \"%s\":\n", m->name);
397     debug_indentation(iadd);
398    
399     /* For userland-only, this decides which ARCH/cpu_name to use: */
400     if (m->machine_type == MACHINE_USERLAND && m->userland_emul != NULL) {
401     useremul_name_to_useremul(NULL, m->userland_emul,
402     &m->arch, &m->machine_name, &m->cpu_name);
403     if (m->arch == ARCH_NOARCH) {
404     printf("Unsupported userland emulation mode.\n");
405     exit(1);
406     }
407     }
408    
409     if (m->machine_type == MACHINE_NONE) {
410     fatal("No machine type specified?\n");
411     exit(1);
412     }
413    
414     m->cpu_family = cpu_family_ptr_by_number(m->arch);
415    
416 dpavlin 12 if (m->arch == ARCH_ALPHA)
417     m->arch_pagesize = 8192;
418    
419 dpavlin 2 machine_memsize_fix(m);
420    
421     /*
422     * Create the system's memory:
423     *
424     * (Don't print the amount for userland-only emulation; the
425     * size doesn't matter.)
426     */
427     if (m->machine_type != MACHINE_USERLAND)
428     debug("memory: %i MB", m->physical_ram_in_mb);
429     memory_amount = (uint64_t)m->physical_ram_in_mb * 1048576;
430     if (m->memory_offset_in_mb > 0) {
431     /*
432     * A special hack is used for some SGI models,
433     * where memory is offset by 128MB to leave room for
434     * EISA space and other things.
435     */
436     debug(" (offset by %iMB)", m->memory_offset_in_mb);
437     memory_amount += 1048576 * m->memory_offset_in_mb;
438     }
439 dpavlin 12 m->memory = memory_new(memory_amount, m->arch);
440 dpavlin 2 if (m->machine_type != MACHINE_USERLAND)
441     debug("\n");
442    
443     /* Create CPUs: */
444     if (m->cpu_name == NULL)
445     machine_default_cputype(m);
446     if (m->ncpus == 0) {
447     /* TODO: This should be moved elsewhere... */
448     if (m->machine_type == MACHINE_BEBOX)
449     m->ncpus = 2;
450     else if (m->machine_type == MACHINE_ARC &&
451     m->machine_subtype == MACHINE_ARC_NEC_R96)
452     m->ncpus = 2;
453     else if (m->machine_type == MACHINE_ARC &&
454     m->machine_subtype == MACHINE_ARC_NEC_R98)
455     m->ncpus = 4;
456     else
457     m->ncpus = 1;
458     }
459     m->cpus = malloc(sizeof(struct cpu *) * m->ncpus);
460     if (m->cpus == NULL) {
461     fprintf(stderr, "out of memory\n");
462     exit(1);
463     }
464     memset(m->cpus, 0, sizeof(struct cpu *) * m->ncpus);
465    
466     debug("cpu0");
467     if (m->ncpus > 1)
468     debug(" .. cpu%i", m->ncpus - 1);
469     debug(": ");
470     for (i=0; i<m->ncpus; i++) {
471     m->cpus[i] = cpu_new(m->memory, m, i, m->cpu_name);
472 dpavlin 24 if (m->cpus[i] == NULL) {
473     fprintf(stderr, "Unable to create CPU object. "
474     "Aborting.");
475     exit(1);
476     }
477 dpavlin 2 }
478     debug("\n");
479    
480 dpavlin 10 #if 0
481     /* Special case: The Playstation Portable has an additional CPU: */
482     if (m->machine_type == MACHINE_PSP) {
483     debug("cpu%i: ", m->ncpus);
484     m->cpus[m->ncpus] = cpu_new(m->memory, m,
485     0 /* use 0 here to show info with debug() */,
486     "Allegrex" /* TODO */);
487     debug("\n");
488     m->ncpus ++;
489     }
490     #endif
491    
492 dpavlin 2 if (m->use_random_bootstrap_cpu)
493     m->bootstrap_cpu = random() % m->ncpus;
494     else
495     m->bootstrap_cpu = 0;
496    
497     cpu = m->cpus[m->bootstrap_cpu];
498    
499     /* Set cpu->useremul_syscall, and use userland_memory_rw: */
500     if (m->userland_emul != NULL) {
501     useremul_name_to_useremul(cpu,
502     m->userland_emul, NULL, NULL, NULL);
503 dpavlin 12
504     switch (m->arch) {
505     #ifdef ENABLE_ALPHA
506     case ARCH_ALPHA:
507     cpu->memory_rw = alpha_userland_memory_rw;
508     break;
509     #endif
510     default:cpu->memory_rw = userland_memory_rw;
511     }
512 dpavlin 2 }
513    
514     if (m->use_x11)
515     x11_init(m);
516    
517     /* Fill memory with random bytes: */
518     if (m->random_mem_contents) {
519     for (i=0; i<m->physical_ram_in_mb * 1048576; i+=256) {
520     unsigned char data[256];
521     unsigned int j;
522     for (j=0; j<sizeof(data); j++)
523     data[j] = random() & 255;
524 dpavlin 6 cpu->memory_rw(cpu, m->memory, i, data, sizeof(data),
525     MEM_WRITE, CACHE_NONE | NO_EXCEPTIONS | PHYSICAL);
526 dpavlin 2 }
527     }
528    
529     if (m->userland_emul != NULL) {
530     /*
531     * For userland-only emulation, no machine emulation
532     * is needed.
533     */
534     } else {
535     for (i=0; i<n_devices; i++)
536     device_add(m, device_names[i]);
537    
538     machine_setup(m);
539     }
540    
541     diskimage_dump_info(m);
542 dpavlin 22 console_debug_dump(m);
543 dpavlin 2
544     /* Load files (ROM code, boot code, ...) into memory: */
545     if (n_load == 0) {
546 dpavlin 6 if (m->first_diskimage != NULL) {
547     if (!load_bootblock(m, cpu, &n_load, &load_names)) {
548     fprintf(stderr, "\nNo executable files were"
549     " specified, and booting directly from disk"
550     " failed.\n");
551     exit(1);
552     }
553     } else {
554 dpavlin 2 fprintf(stderr, "No executable file(s) loaded, and "
555     "we are not booting directly from a disk image."
556     "\nAborting.\n");
557     exit(1);
558     }
559     }
560    
561     while (n_load > 0) {
562 dpavlin 6 FILE *tmp_f;
563     char *name_to_load = *load_names;
564     int remove_after_load = 0;
565    
566     /* Special hack for removing temporary files: */
567     if (name_to_load[0] == 8) {
568     name_to_load ++;
569     remove_after_load = 1;
570     }
571    
572     /*
573 dpavlin 10 * gzipped files are automagically gunzipped:
574     * NOTE/TODO: This isn't secure. system() is used.
575 dpavlin 6 */
576     tmp_f = fopen(name_to_load, "r");
577     if (tmp_f != NULL) {
578     unsigned char buf[2]; /* gzip header */
579     memset(buf, 0, sizeof(buf));
580     fread(buf, 1, sizeof(buf), tmp_f);
581     if (buf[0]==0x1f && buf[1]==0x8b) {
582 dpavlin 10 size_t zzlen = strlen(name_to_load)*2 + 100;
583     char *zz = malloc(zzlen);
584 dpavlin 6 debug("gunziping %s\n", name_to_load);
585 dpavlin 10 /*
586     * gzip header found. If this was a file
587     * extracted from, say, a CDROM image, then it
588     * already has a temporary name. Otherwise we
589     * have to gunzip into a temporary file.
590     */
591     if (remove_after_load) {
592     snprintf(zz, zzlen, "mv %s %s.gz",
593     name_to_load, name_to_load);
594     system(zz);
595     snprintf(zz, zzlen, "gunzip %s.gz",
596     name_to_load);
597     system(zz);
598     } else {
599     /* gunzip into new temp file: */
600     int tmpfile_handle;
601     char *new_temp_name =
602     strdup("/tmp/gxemul.XXXXXXXXXXXX");
603     tmpfile_handle = mkstemp(new_temp_name);
604     close(tmpfile_handle);
605     snprintf(zz, zzlen, "gunzip -c '%s' > "
606     "%s", name_to_load, new_temp_name);
607     system(zz);
608     name_to_load = new_temp_name;
609     remove_after_load = 1;
610     }
611 dpavlin 6 free(zz);
612     }
613     fclose(tmp_f);
614     }
615    
616 dpavlin 10 /*
617     * Ugly (but usable) hack for Playstation Portable: If the
618     * filename ends with ".pbp" and the file contains an ELF
619     * header, then extract the ELF file into a temporary file.
620     */
621     if (strlen(name_to_load) > 4 && strcasecmp(name_to_load +
622     strlen(name_to_load) - 4, ".pbp") == 0 &&
623     (tmp_f = fopen(name_to_load, "r")) != NULL) {
624     off_t filesize, j, found=0;
625     unsigned char *buf;
626     fseek(tmp_f, 0, SEEK_END);
627     filesize = ftello(tmp_f);
628     fseek(tmp_f, 0, SEEK_SET);
629     buf = malloc(filesize);
630     if (buf == NULL) {
631     fprintf(stderr, "out of memory while trying"
632     " to read %s\n", name_to_load);
633     exit(1);
634     }
635     fread(buf, 1, filesize, tmp_f);
636     fclose(tmp_f);
637     /* Search for the ELF header, from offset 1 (!): */
638     for (j=1; j<filesize - 4; j++)
639     if (memcmp(buf + j, ELFMAG, SELFMAG) == 0) {
640     found = j;
641     break;
642     }
643     if (found != 0) {
644     int tmpfile_handle;
645     char *new_temp_name =
646     strdup("/tmp/gxemul.XXXXXXXXXXXX");
647     debug("extracting ELF from %s (offset 0x%x)\n",
648     name_to_load, (int)found);
649     tmpfile_handle = mkstemp(new_temp_name);
650     write(tmpfile_handle, buf + found,
651     filesize - found);
652     close(tmpfile_handle);
653     name_to_load = new_temp_name;
654     remove_after_load = 1;
655     }
656     }
657    
658 dpavlin 2 byte_order = NO_BYTE_ORDER_OVERRIDE;
659    
660 dpavlin 6 /*
661     * Load the file: :-)
662     */
663     file_load(m, m->memory, name_to_load, &entrypoint,
664 dpavlin 2 m->arch, &gp, &byte_order, &toc);
665    
666 dpavlin 6 if (remove_after_load) {
667     debug("removing %s\n", name_to_load);
668     unlink(name_to_load);
669     }
670    
671 dpavlin 2 if (byte_order != NO_BYTE_ORDER_OVERRIDE)
672     cpu->byte_order = byte_order;
673    
674     cpu->pc = entrypoint;
675    
676     switch (m->arch) {
677 dpavlin 14
678     case ARCH_ALPHA:
679 dpavlin 18 /* For position-independent code: */
680 dpavlin 14 cpu->cd.alpha.r[ALPHA_T12] = cpu->pc;
681     break;
682    
683     case ARCH_ARM:
684 dpavlin 20 if (cpu->pc & 3) {
685     fatal("ARM: lowest bits of pc set: TODO\n");
686     exit(1);
687     }
688 dpavlin 14 cpu->pc &= 0xfffffffc;
689     break;
690    
691     case ARCH_AVR:
692     cpu->pc &= 0xfffff;
693     if (cpu->pc & 1) {
694     fatal("AVR: lowest bit of pc set: TODO\n");
695     exit(1);
696     }
697     break;
698    
699     case ARCH_M68K:
700     break;
701    
702 dpavlin 2 case ARCH_MIPS:
703 dpavlin 20 if ((cpu->pc >> 32) == 0 && (cpu->pc & 0x80000000ULL))
704 dpavlin 2 cpu->pc |= 0xffffffff00000000ULL;
705    
706     cpu->cd.mips.gpr[MIPS_GPR_GP] = gp;
707    
708     if ((cpu->cd.mips.gpr[MIPS_GPR_GP] >> 32) == 0 &&
709     (cpu->cd.mips.gpr[MIPS_GPR_GP] & 0x80000000ULL))
710     cpu->cd.mips.gpr[MIPS_GPR_GP] |=
711     0xffffffff00000000ULL;
712     break;
713 dpavlin 4
714 dpavlin 2 case ARCH_PPC:
715 dpavlin 6 /* See http://www.linuxbase.org/spec/ELF/ppc64/
716     spec/x458.html for more info. */
717 dpavlin 2 cpu->cd.ppc.gpr[2] = toc;
718 dpavlin 6 /* TODO */
719 dpavlin 14 if (cpu->cd.ppc.bits == 32)
720     cpu->pc &= 0xffffffffULL;
721 dpavlin 2 break;
722 dpavlin 4
723 dpavlin 14 case ARCH_SH:
724 dpavlin 30 if (cpu->cd.sh.cpu_type.bits == 32)
725 dpavlin 14 cpu->pc &= 0xffffffffULL;
726     cpu->pc &= ~1;
727 dpavlin 12 break;
728    
729 dpavlin 2 case ARCH_SPARC:
730     break;
731 dpavlin 4
732 dpavlin 2 default:
733     fatal("emul_machine_setup(): Internal error: "
734     "Unimplemented arch %i\n", m->arch);
735     exit(1);
736     }
737    
738     /*
739     * For userland emulation, the remaining items
740     * on the command line will be passed as parameters
741     * to the emulated program, and will not be treated
742     * as filenames to load into the emulator.
743     * The program's name will be in load_names[0], and the
744     * rest of the parameters in load_names[1] and up.
745     */
746     if (m->userland_emul != NULL)
747     break;
748    
749     n_load --;
750     load_names ++;
751     }
752    
753     if (m->byte_order_override != NO_BYTE_ORDER_OVERRIDE)
754     cpu->byte_order = m->byte_order_override;
755    
756     /* Same byte order and entrypoint for all CPUs: */
757     for (i=0; i<m->ncpus; i++)
758     if (i != m->bootstrap_cpu) {
759     m->cpus[i]->byte_order = cpu->byte_order;
760     m->cpus[i]->pc = cpu->pc;
761     }
762    
763     if (m->userland_emul != NULL)
764     useremul_setup(cpu, n_load, load_names);
765    
766     /* Startup the bootstrap CPU: */
767 dpavlin 30 cpu->running = 1;
768 dpavlin 2
769     /* ... or pause all CPUs, if start_paused is set: */
770     if (m->start_paused) {
771     for (i=0; i<m->ncpus; i++)
772     m->cpus[i]->running = 0;
773     }
774    
775     /* Add PC dump points: */
776     add_dump_points(m);
777    
778     /* TODO: This is MIPS-specific! */
779 dpavlin 22 if (m->machine_type == MACHINE_PMAX &&
780 dpavlin 2 cpu->cd.mips.cpu_type.mmu_model == MMU3K)
781     add_symbol_name(&m->symbol_context,
782 dpavlin 12 0x9fff0000, 0x10000, "r2k3k_cache", 0, 0);
783 dpavlin 2
784     symbol_recalc_sizes(&m->symbol_context);
785    
786     /* Special hack for ARC/SGI emulation: */
787     if ((m->machine_type == MACHINE_ARC ||
788     m->machine_type == MACHINE_SGI) && m->prom_emulation)
789     add_arc_components(m);
790    
791     debug("starting cpu%i at ", m->bootstrap_cpu);
792     switch (m->arch) {
793 dpavlin 14
794     case ARCH_ARM:
795     /* ARM cpus aren't 64-bit: */
796 dpavlin 38 debug("0x%08"PRIx32, (uint32_t) entrypoint);
797 dpavlin 14 break;
798    
799     case ARCH_AVR:
800     /* Atmel AVR uses a 16-bit or 22-bit program counter: */
801 dpavlin 38 debug("0x%04x", (int) entrypoint);
802 dpavlin 14 break;
803    
804 dpavlin 2 case ARCH_MIPS:
805 dpavlin 12 if (cpu->is_32bit) {
806 dpavlin 38 debug("0x%08"PRIx32, (uint32_t)
807     m->cpus[m->bootstrap_cpu]->pc);
808 dpavlin 2 if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
809 dpavlin 38 debug(" (gp=0x%08"PRIx32")", (uint32_t)
810     m->cpus[m->bootstrap_cpu]->cd.mips.gpr[
811 dpavlin 2 MIPS_GPR_GP]);
812     } else {
813 dpavlin 38 debug("0x%016"PRIx64, (uint64_t)
814     m->cpus[m->bootstrap_cpu]->pc);
815 dpavlin 2 if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
816 dpavlin 38 debug(" (gp=0x%016"PRIx64")", (uint64_t)
817 dpavlin 2 cpu->cd.mips.gpr[MIPS_GPR_GP]);
818     }
819     break;
820 dpavlin 14
821 dpavlin 2 case ARCH_PPC:
822     if (cpu->cd.ppc.bits == 32)
823 dpavlin 38 debug("0x%08"PRIx32, (uint32_t) entrypoint);
824 dpavlin 2 else
825 dpavlin 38 debug("0x%016"PRIx64, (uint64_t) entrypoint);
826 dpavlin 2 break;
827 dpavlin 14
828 dpavlin 2 default:
829 dpavlin 22 if (cpu->is_32bit)
830 dpavlin 38 debug("0x%08"PRIx32, (uint32_t) cpu->pc);
831 dpavlin 22 else
832 dpavlin 38 debug("0x%016"PRIx64, (uint64_t) cpu->pc);
833 dpavlin 2 }
834     debug("\n");
835    
836     debug_indentation(-iadd);
837     }
838    
839    
840     /*
841     * emul_dumpinfo():
842     *
843     * Dump info about all machines in an emul.
844     */
845     void emul_dumpinfo(struct emul *e)
846     {
847 dpavlin 22 int j, nm, iadd = DEBUG_INDENTATION;
848 dpavlin 2
849     if (e->net != NULL)
850     net_dumpinfo(e->net);
851    
852     nm = e->n_machines;
853     for (j=0; j<nm; j++) {
854     debug("machine %i: \"%s\"\n", j, e->machines[j]->name);
855     debug_indentation(iadd);
856     machine_dumpinfo(e->machines[j]);
857     debug_indentation(-iadd);
858     }
859     }
860    
861    
862     /*
863     * emul_simple_init():
864     *
865     * For a normal setup:
866     *
867     * o) Initialize a network.
868     * o) Initialize one machine.
869     *
870     * For a userland-only setup:
871     *
872     * o) Initialize a "pseudo"-machine.
873     */
874     void emul_simple_init(struct emul *emul)
875     {
876 dpavlin 22 int iadd = DEBUG_INDENTATION;
877 dpavlin 2 struct machine *m;
878    
879     if (emul->n_machines != 1) {
880     fprintf(stderr, "emul_simple_init(): n_machines != 1\n");
881     exit(1);
882     }
883    
884     m = emul->machines[0];
885    
886     if (m->userland_emul == NULL) {
887     debug("Simple setup...\n");
888     debug_indentation(iadd);
889    
890 dpavlin 10 /* Create a simple network: */
891 dpavlin 2 emul->net = net_init(emul, NET_INIT_FLAG_GATEWAY,
892 dpavlin 32 NET_DEFAULT_IPV4_MASK,
893     NET_DEFAULT_IPV4_LEN,
894     NULL, 0, 0, NULL);
895 dpavlin 2 } else {
896     /* Userland pseudo-machine: */
897     debug("Syscall emulation (userland-only) setup...\n");
898     debug_indentation(iadd);
899     }
900    
901     /* Create the machine: */
902     emul_machine_setup(m, extra_argc, extra_argv, 0, NULL);
903    
904     debug_indentation(-iadd);
905     }
906    
907    
908     /*
909     * emul_create_from_configfile():
910     *
911     * Create an emul struct by reading settings from a configuration file.
912     */
913 dpavlin 34 struct emul *emul_create_from_configfile(char *fname, int id)
914 dpavlin 2 {
915 dpavlin 22 int iadd = DEBUG_INDENTATION;
916 dpavlin 34 struct emul *e = emul_new(fname, id);
917 dpavlin 2
918     debug("Creating emulation from configfile \"%s\":\n", fname);
919     debug_indentation(iadd);
920    
921 dpavlin 24 emul_parse_config(e, fname);
922 dpavlin 2
923     debug_indentation(-iadd);
924     return e;
925     }
926    
927    
928     /*
929     * emul_run():
930     *
931     * o) Set up things needed before running emulations.
932     *
933     * o) Run emulations (one or more, in parallel).
934     *
935     * o) De-initialize things.
936     */
937     void emul_run(struct emul **emuls, int n_emuls)
938     {
939     struct emul *e;
940     int i = 0, j, go = 1, n, anything;
941    
942     if (n_emuls < 1) {
943     fprintf(stderr, "emul_run(): no thing to do\n");
944     return;
945     }
946    
947     atexit(fix_console);
948    
949     /* Initialize the interactive debugger: */
950     debugger_init(emuls, n_emuls);
951    
952 dpavlin 22 /* Run any additional debugger commands before starting: */
953     for (i=0; i<n_emuls; i++) {
954     struct emul *emul = emuls[i];
955     if (emul->n_debugger_cmds > 0) {
956     int j;
957     if (i == 0)
958     print_separator();
959     for (j = 0; j < emul->n_debugger_cmds; j ++) {
960     debug("> %s\n", emul->debugger_cmds[j]);
961     debugger_execute_cmd(emul->debugger_cmds[j],
962     strlen(emul->debugger_cmds[j]));
963     }
964     }
965     }
966    
967     print_separator();
968     debug("\n");
969    
970    
971 dpavlin 2 /*
972     * console_init_main() makes sure that the terminal is in a
973     * reasonable state.
974     *
975     * The SIGINT handler is for CTRL-C (enter the interactive debugger).
976     *
977     * The SIGCONT handler is invoked whenever the user presses CTRL-Z
978     * (or sends SIGSTOP) and then continues. It makes sure that the
979     * terminal is in an expected state.
980     */
981     console_init_main(emuls[0]); /* TODO: what is a good argument? */
982     signal(SIGINT, debugger_activate);
983     signal(SIGCONT, console_sigcont);
984    
985     /* Not in verbose mode? Then set quiet_mode. */
986     if (!verbose)
987     quiet_mode = 1;
988    
989     /* Initialize all CPUs in all machines in all emulations: */
990     for (i=0; i<n_emuls; i++) {
991     e = emuls[i];
992     if (e == NULL)
993     continue;
994     for (j=0; j<e->n_machines; j++)
995 dpavlin 12 cpu_run_init(e->machines[j]);
996 dpavlin 2 }
997    
998 dpavlin 12 /* TODO: Generalize: */
999     if (emuls[0]->machines[0]->show_trace_tree)
1000     cpu_functioncall_trace(emuls[0]->machines[0]->cpus[0],
1001     emuls[0]->machines[0]->cpus[0]->pc);
1002    
1003 dpavlin 32 /* Start emulated clocks: */
1004     timer_start();
1005    
1006 dpavlin 2 /*
1007     * MAIN LOOP:
1008     *
1009     * Run all emulations in parallel, running each machine in
1010     * each emulation.
1011     */
1012     while (go) {
1013     go = 0;
1014    
1015 dpavlin 26 /* Flush X11 and serial console output every now and then: */
1016 dpavlin 28 if (emuls[0]->machines[0]->ninstrs >
1017     emuls[0]->machines[0]->ninstrs_flush + (1<<19)) {
1018 dpavlin 26 x11_check_event(emuls, n_emuls);
1019     console_flush();
1020 dpavlin 28 emuls[0]->machines[0]->ninstrs_flush =
1021     emuls[0]->machines[0]->ninstrs;
1022 dpavlin 26 }
1023 dpavlin 2
1024 dpavlin 28 if (emuls[0]->machines[0]->ninstrs >
1025     emuls[0]->machines[0]->ninstrs_show + (1<<25)) {
1026     emuls[0]->machines[0]->ninstrs_since_gettimeofday +=
1027     (emuls[0]->machines[0]->ninstrs -
1028     emuls[0]->machines[0]->ninstrs_show);
1029 dpavlin 26 cpu_show_cycles(emuls[0]->machines[0], 0);
1030 dpavlin 28 emuls[0]->machines[0]->ninstrs_show =
1031     emuls[0]->machines[0]->ninstrs;
1032 dpavlin 26 }
1033 dpavlin 2
1034 dpavlin 26 if (single_step == ENTER_SINGLE_STEPPING) {
1035     /* TODO: Cleanup! */
1036     old_instruction_trace =
1037     emuls[0]->machines[0]->instruction_trace;
1038     old_quiet_mode = quiet_mode;
1039     old_show_trace_tree =
1040     emuls[0]->machines[0]->show_trace_tree;
1041     emuls[0]->machines[0]->instruction_trace = 1;
1042     emuls[0]->machines[0]->show_trace_tree = 1;
1043     quiet_mode = 0;
1044     single_step = SINGLE_STEPPING;
1045     }
1046 dpavlin 24
1047 dpavlin 26 if (single_step == SINGLE_STEPPING)
1048     debugger();
1049    
1050 dpavlin 32 for (i=0; i<n_emuls; i++) {
1051     e = emuls[i];
1052 dpavlin 26
1053 dpavlin 32 for (j=0; j<e->n_machines; j++) {
1054     anything = machine_run(e->machines[j]);
1055     if (anything)
1056     go = 1;
1057     }
1058 dpavlin 2 }
1059     }
1060    
1061 dpavlin 32 /* Stop any running timers: */
1062     timer_stop();
1063    
1064 dpavlin 2 /* Deinitialize all CPUs in all machines in all emulations: */
1065     for (i=0; i<n_emuls; i++) {
1066     e = emuls[i];
1067     if (e == NULL)
1068     continue;
1069     for (j=0; j<e->n_machines; j++)
1070 dpavlin 12 cpu_run_deinit(e->machines[j]);
1071 dpavlin 2 }
1072    
1073     /* force_debugger_at_exit flag set? Then enter the debugger: */
1074     if (force_debugger_at_exit) {
1075     quiet_mode = 0;
1076     debugger_reset();
1077     debugger();
1078     }
1079    
1080 dpavlin 32 /* Any machine using X11? Then wait before exiting: */
1081 dpavlin 2 n = 0;
1082     for (i=0; i<n_emuls; i++)
1083     for (j=0; j<emuls[i]->n_machines; j++)
1084     if (emuls[i]->machines[j]->use_x11)
1085     n++;
1086     if (n > 0) {
1087     printf("Press enter to quit.\n");
1088     while (!console_charavail(MAIN_CONSOLE)) {
1089     x11_check_event(emuls, n_emuls);
1090 dpavlin 32 usleep(10000);
1091 dpavlin 2 }
1092     console_readchar(MAIN_CONSOLE);
1093     }
1094    
1095 dpavlin 32 console_deinit_main();
1096 dpavlin 2 }
1097    

  ViewVC Help
Powered by ViewVC 1.1.26