/[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 36 - (hide annotations)
Mon Oct 8 16:21:34 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 26990 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1497 2007/03/18 03:41:36 debug Exp $
20070224	Minor update to the initialization of the ns16550 in
		machine_walnut.c, to allow that machine type to boot with the
		new interrupt system (although it is still a dummy machine).
		Adding a wdc at 0x14000000 to machine_landisk.c, and fixing
		the SCIF serial interrupts of the SH4 cpu enough to get
		NetBSD/landisk booting from a disk image :-)  Adding a
		preliminary install instruction skeleton to guestoses.html.
20070306	Adding SH-IPL+G PROM emulation, and also passing the "end"
		symbol in r5 on bootup, for Landisk emulation. This is enough
		to get OpenBSD/landisk to install :)  Adding a preliminary
		install instruction skeleton to the documentation. SuperH
		emulation is still shaky, though :-/
20070307	Fixed a strangeness in memory_sh.c (read/write was never
		returned for any page). (Unknown whether this fixes any actual
		problems, though.)
20070308	dev_ram.c fix: invalidate code translations on writes to
		RAM, emulated as separate devices. Linux/dreamcast gets
		further in the boot process than before, but still bugs out
		in userland.
		Fixing bugs in the "stc.l gbr,@-rN" and "ldc.l @rN+,gbr" SuperH 
		instructions (they should NOT check the MD bit), allowing the
		Linux/dreamcast Live CD to reach userland correctly :-)
20070310	Changing the cpu name "Alpha" in src/useremul.c to "21364" to
		unbreak userland syscall emulation of FreeBSD/Alpha binaries.
20070314	Applying a patch from Michael Yaroslavtsev which fixes the
		previous Linux lib64 patch to the configure script.
20070315	Adding a (dummy) sun4v machine type, and SPARC T1 cpu type.
20070316	Creating a new directory, src/disk, and moving diskimage.c
		to it. Separating out bootblock loading stuff from emul.c into
		new files in src/disk.
		Adding some more SPARC registers.
20070318	Preparing/testing for a minirelease, 0.4.4.1.

==============  RELEASE 0.4.4.1  ==============


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 36 * $Id: emul.c,v 1.279 2007/03/16 14:45:30 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 dpavlin 32 case ARCH_RCA180X:
700     cpu->pc &= 0xffff;
701     break;
702    
703 dpavlin 14 case ARCH_M68K:
704     break;
705    
706 dpavlin 2 case ARCH_MIPS:
707 dpavlin 20 if ((cpu->pc >> 32) == 0 && (cpu->pc & 0x80000000ULL))
708 dpavlin 2 cpu->pc |= 0xffffffff00000000ULL;
709    
710     cpu->cd.mips.gpr[MIPS_GPR_GP] = gp;
711    
712     if ((cpu->cd.mips.gpr[MIPS_GPR_GP] >> 32) == 0 &&
713     (cpu->cd.mips.gpr[MIPS_GPR_GP] & 0x80000000ULL))
714     cpu->cd.mips.gpr[MIPS_GPR_GP] |=
715     0xffffffff00000000ULL;
716     break;
717 dpavlin 4
718 dpavlin 2 case ARCH_PPC:
719 dpavlin 6 /* See http://www.linuxbase.org/spec/ELF/ppc64/
720     spec/x458.html for more info. */
721 dpavlin 2 cpu->cd.ppc.gpr[2] = toc;
722 dpavlin 6 /* TODO */
723 dpavlin 14 if (cpu->cd.ppc.bits == 32)
724     cpu->pc &= 0xffffffffULL;
725 dpavlin 2 break;
726 dpavlin 4
727 dpavlin 14 case ARCH_SH:
728 dpavlin 30 if (cpu->cd.sh.cpu_type.bits == 32)
729 dpavlin 14 cpu->pc &= 0xffffffffULL;
730     cpu->pc &= ~1;
731 dpavlin 12 break;
732    
733 dpavlin 2 case ARCH_SPARC:
734     break;
735 dpavlin 4
736 dpavlin 28 case ARCH_TRANSPUTER:
737     cpu->pc &= 0xffffffffULL;
738     break;
739    
740 dpavlin 2 default:
741     fatal("emul_machine_setup(): Internal error: "
742     "Unimplemented arch %i\n", m->arch);
743     exit(1);
744     }
745    
746     /*
747     * For userland emulation, the remaining items
748     * on the command line will be passed as parameters
749     * to the emulated program, and will not be treated
750     * as filenames to load into the emulator.
751     * The program's name will be in load_names[0], and the
752     * rest of the parameters in load_names[1] and up.
753     */
754     if (m->userland_emul != NULL)
755     break;
756    
757     n_load --;
758     load_names ++;
759     }
760    
761     if (m->byte_order_override != NO_BYTE_ORDER_OVERRIDE)
762     cpu->byte_order = m->byte_order_override;
763    
764     /* Same byte order and entrypoint for all CPUs: */
765     for (i=0; i<m->ncpus; i++)
766     if (i != m->bootstrap_cpu) {
767     m->cpus[i]->byte_order = cpu->byte_order;
768     m->cpus[i]->pc = cpu->pc;
769     }
770    
771     if (m->userland_emul != NULL)
772     useremul_setup(cpu, n_load, load_names);
773    
774     /* Startup the bootstrap CPU: */
775 dpavlin 30 cpu->running = 1;
776 dpavlin 2
777     /* ... or pause all CPUs, if start_paused is set: */
778     if (m->start_paused) {
779     for (i=0; i<m->ncpus; i++)
780     m->cpus[i]->running = 0;
781     }
782    
783     /* Add PC dump points: */
784     add_dump_points(m);
785    
786     /* TODO: This is MIPS-specific! */
787 dpavlin 22 if (m->machine_type == MACHINE_PMAX &&
788 dpavlin 2 cpu->cd.mips.cpu_type.mmu_model == MMU3K)
789     add_symbol_name(&m->symbol_context,
790 dpavlin 12 0x9fff0000, 0x10000, "r2k3k_cache", 0, 0);
791 dpavlin 2
792     symbol_recalc_sizes(&m->symbol_context);
793    
794     /* Special hack for ARC/SGI emulation: */
795     if ((m->machine_type == MACHINE_ARC ||
796     m->machine_type == MACHINE_SGI) && m->prom_emulation)
797     add_arc_components(m);
798    
799     debug("starting cpu%i at ", m->bootstrap_cpu);
800     switch (m->arch) {
801 dpavlin 14
802     case ARCH_ARM:
803     /* ARM cpus aren't 64-bit: */
804     debug("0x%08x", (int)entrypoint);
805     break;
806    
807     case ARCH_AVR:
808     /* Atmel AVR uses a 16-bit or 22-bit program counter: */
809     debug("0x%04x", (int)entrypoint);
810     break;
811    
812 dpavlin 2 case ARCH_MIPS:
813 dpavlin 12 if (cpu->is_32bit) {
814 dpavlin 2 debug("0x%08x", (int)m->cpus[
815     m->bootstrap_cpu]->pc);
816     if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
817     debug(" (gp=0x%08x)", (int)m->cpus[
818     m->bootstrap_cpu]->cd.mips.gpr[
819     MIPS_GPR_GP]);
820     } else {
821     debug("0x%016llx", (long long)m->cpus[
822     m->bootstrap_cpu]->pc);
823     if (cpu->cd.mips.gpr[MIPS_GPR_GP] != 0)
824     debug(" (gp=0x%016llx)", (long long)
825     cpu->cd.mips.gpr[MIPS_GPR_GP]);
826     }
827     break;
828 dpavlin 14
829 dpavlin 2 case ARCH_PPC:
830     if (cpu->cd.ppc.bits == 32)
831     debug("0x%08x", (int)entrypoint);
832     else
833     debug("0x%016llx", (long long)entrypoint);
834     break;
835 dpavlin 14
836 dpavlin 2 default:
837 dpavlin 22 if (cpu->is_32bit)
838     debug("0x%08x", (int)cpu->pc);
839     else
840     debug("0x%016llx", (long long)cpu->pc);
841 dpavlin 2 }
842     debug("\n");
843    
844     debug_indentation(-iadd);
845     }
846    
847    
848     /*
849     * emul_dumpinfo():
850     *
851     * Dump info about all machines in an emul.
852     */
853     void emul_dumpinfo(struct emul *e)
854     {
855 dpavlin 22 int j, nm, iadd = DEBUG_INDENTATION;
856 dpavlin 2
857     if (e->net != NULL)
858     net_dumpinfo(e->net);
859    
860     nm = e->n_machines;
861     for (j=0; j<nm; j++) {
862     debug("machine %i: \"%s\"\n", j, e->machines[j]->name);
863     debug_indentation(iadd);
864     machine_dumpinfo(e->machines[j]);
865     debug_indentation(-iadd);
866     }
867     }
868    
869    
870     /*
871     * emul_simple_init():
872     *
873     * For a normal setup:
874     *
875     * o) Initialize a network.
876     * o) Initialize one machine.
877     *
878     * For a userland-only setup:
879     *
880     * o) Initialize a "pseudo"-machine.
881     */
882     void emul_simple_init(struct emul *emul)
883     {
884 dpavlin 22 int iadd = DEBUG_INDENTATION;
885 dpavlin 2 struct machine *m;
886    
887     if (emul->n_machines != 1) {
888     fprintf(stderr, "emul_simple_init(): n_machines != 1\n");
889     exit(1);
890     }
891    
892     m = emul->machines[0];
893    
894     if (m->userland_emul == NULL) {
895     debug("Simple setup...\n");
896     debug_indentation(iadd);
897    
898 dpavlin 10 /* Create a simple network: */
899 dpavlin 2 emul->net = net_init(emul, NET_INIT_FLAG_GATEWAY,
900 dpavlin 32 NET_DEFAULT_IPV4_MASK,
901     NET_DEFAULT_IPV4_LEN,
902     NULL, 0, 0, NULL);
903 dpavlin 2 } else {
904     /* Userland pseudo-machine: */
905     debug("Syscall emulation (userland-only) setup...\n");
906     debug_indentation(iadd);
907     }
908    
909     /* Create the machine: */
910     emul_machine_setup(m, extra_argc, extra_argv, 0, NULL);
911    
912     debug_indentation(-iadd);
913     }
914    
915    
916     /*
917     * emul_create_from_configfile():
918     *
919     * Create an emul struct by reading settings from a configuration file.
920     */
921 dpavlin 34 struct emul *emul_create_from_configfile(char *fname, int id)
922 dpavlin 2 {
923 dpavlin 22 int iadd = DEBUG_INDENTATION;
924 dpavlin 34 struct emul *e = emul_new(fname, id);
925 dpavlin 2
926     debug("Creating emulation from configfile \"%s\":\n", fname);
927     debug_indentation(iadd);
928    
929 dpavlin 24 emul_parse_config(e, fname);
930 dpavlin 2
931     debug_indentation(-iadd);
932     return e;
933     }
934    
935    
936     /*
937     * emul_run():
938     *
939     * o) Set up things needed before running emulations.
940     *
941     * o) Run emulations (one or more, in parallel).
942     *
943     * o) De-initialize things.
944     */
945     void emul_run(struct emul **emuls, int n_emuls)
946     {
947     struct emul *e;
948     int i = 0, j, go = 1, n, anything;
949    
950     if (n_emuls < 1) {
951     fprintf(stderr, "emul_run(): no thing to do\n");
952     return;
953     }
954    
955     atexit(fix_console);
956    
957     /* Initialize the interactive debugger: */
958     debugger_init(emuls, n_emuls);
959    
960 dpavlin 22 /* Run any additional debugger commands before starting: */
961     for (i=0; i<n_emuls; i++) {
962     struct emul *emul = emuls[i];
963     if (emul->n_debugger_cmds > 0) {
964     int j;
965     if (i == 0)
966     print_separator();
967     for (j = 0; j < emul->n_debugger_cmds; j ++) {
968     debug("> %s\n", emul->debugger_cmds[j]);
969     debugger_execute_cmd(emul->debugger_cmds[j],
970     strlen(emul->debugger_cmds[j]));
971     }
972     }
973     }
974    
975     print_separator();
976     debug("\n");
977    
978    
979 dpavlin 2 /*
980     * console_init_main() makes sure that the terminal is in a
981     * reasonable state.
982     *
983     * The SIGINT handler is for CTRL-C (enter the interactive debugger).
984     *
985     * The SIGCONT handler is invoked whenever the user presses CTRL-Z
986     * (or sends SIGSTOP) and then continues. It makes sure that the
987     * terminal is in an expected state.
988     */
989     console_init_main(emuls[0]); /* TODO: what is a good argument? */
990     signal(SIGINT, debugger_activate);
991     signal(SIGCONT, console_sigcont);
992    
993     /* Not in verbose mode? Then set quiet_mode. */
994     if (!verbose)
995     quiet_mode = 1;
996    
997     /* Initialize all CPUs in all machines in all emulations: */
998     for (i=0; i<n_emuls; i++) {
999     e = emuls[i];
1000     if (e == NULL)
1001     continue;
1002     for (j=0; j<e->n_machines; j++)
1003 dpavlin 12 cpu_run_init(e->machines[j]);
1004 dpavlin 2 }
1005    
1006 dpavlin 12 /* TODO: Generalize: */
1007     if (emuls[0]->machines[0]->show_trace_tree)
1008     cpu_functioncall_trace(emuls[0]->machines[0]->cpus[0],
1009     emuls[0]->machines[0]->cpus[0]->pc);
1010    
1011 dpavlin 32 /* Start emulated clocks: */
1012     timer_start();
1013    
1014 dpavlin 2 /*
1015     * MAIN LOOP:
1016     *
1017     * Run all emulations in parallel, running each machine in
1018     * each emulation.
1019     */
1020     while (go) {
1021     go = 0;
1022    
1023 dpavlin 26 /* Flush X11 and serial console output every now and then: */
1024 dpavlin 28 if (emuls[0]->machines[0]->ninstrs >
1025     emuls[0]->machines[0]->ninstrs_flush + (1<<19)) {
1026 dpavlin 26 x11_check_event(emuls, n_emuls);
1027     console_flush();
1028 dpavlin 28 emuls[0]->machines[0]->ninstrs_flush =
1029     emuls[0]->machines[0]->ninstrs;
1030 dpavlin 26 }
1031 dpavlin 2
1032 dpavlin 28 if (emuls[0]->machines[0]->ninstrs >
1033     emuls[0]->machines[0]->ninstrs_show + (1<<25)) {
1034     emuls[0]->machines[0]->ninstrs_since_gettimeofday +=
1035     (emuls[0]->machines[0]->ninstrs -
1036     emuls[0]->machines[0]->ninstrs_show);
1037 dpavlin 26 cpu_show_cycles(emuls[0]->machines[0], 0);
1038 dpavlin 28 emuls[0]->machines[0]->ninstrs_show =
1039     emuls[0]->machines[0]->ninstrs;
1040 dpavlin 26 }
1041 dpavlin 2
1042 dpavlin 26 if (single_step == ENTER_SINGLE_STEPPING) {
1043     /* TODO: Cleanup! */
1044     old_instruction_trace =
1045     emuls[0]->machines[0]->instruction_trace;
1046     old_quiet_mode = quiet_mode;
1047     old_show_trace_tree =
1048     emuls[0]->machines[0]->show_trace_tree;
1049     emuls[0]->machines[0]->instruction_trace = 1;
1050     emuls[0]->machines[0]->show_trace_tree = 1;
1051     quiet_mode = 0;
1052     single_step = SINGLE_STEPPING;
1053     }
1054 dpavlin 24
1055 dpavlin 26 if (single_step == SINGLE_STEPPING)
1056     debugger();
1057    
1058 dpavlin 32 for (i=0; i<n_emuls; i++) {
1059     e = emuls[i];
1060 dpavlin 26
1061 dpavlin 32 for (j=0; j<e->n_machines; j++) {
1062     if (e->machines[j]->gdb.port > 0)
1063     debugger_gdb_check_incoming(
1064     e->machines[j]);
1065 dpavlin 26
1066 dpavlin 32 anything = machine_run(e->machines[j]);
1067     if (anything)
1068     go = 1;
1069     }
1070 dpavlin 2 }
1071     }
1072    
1073 dpavlin 32 /* Stop any running timers: */
1074     timer_stop();
1075    
1076 dpavlin 2 /* Deinitialize all CPUs in all machines in all emulations: */
1077     for (i=0; i<n_emuls; i++) {
1078     e = emuls[i];
1079     if (e == NULL)
1080     continue;
1081     for (j=0; j<e->n_machines; j++)
1082 dpavlin 12 cpu_run_deinit(e->machines[j]);
1083 dpavlin 2 }
1084    
1085     /* force_debugger_at_exit flag set? Then enter the debugger: */
1086     if (force_debugger_at_exit) {
1087     quiet_mode = 0;
1088     debugger_reset();
1089     debugger();
1090     }
1091    
1092 dpavlin 32 /* Any machine using X11? Then wait before exiting: */
1093 dpavlin 2 n = 0;
1094     for (i=0; i<n_emuls; i++)
1095     for (j=0; j<emuls[i]->n_machines; j++)
1096     if (emuls[i]->machines[j]->use_x11)
1097     n++;
1098     if (n > 0) {
1099     printf("Press enter to quit.\n");
1100     while (!console_charavail(MAIN_CONSOLE)) {
1101     x11_check_event(emuls, n_emuls);
1102 dpavlin 32 usleep(10000);
1103 dpavlin 2 }
1104     console_readchar(MAIN_CONSOLE);
1105     }
1106    
1107 dpavlin 32 console_deinit_main();
1108 dpavlin 2 }
1109    

  ViewVC Help
Powered by ViewVC 1.1.26