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

Contents of /trunk/src/emul.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations)
Mon Oct 8 16:22:32 2007 UTC (12 years ago) by dpavlin
File MIME type: text/plain
File size: 24404 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1613 2007/06/15 20:11:26 debug Exp $
20070501	Continuing a little on m88k disassembly (control registers,
		more instructions).
		Adding a dummy mvme88k machine mode.
20070502	Re-adding MIPS load/store alignment exceptions.
20070503	Implementing more of the M88K disassembly code.
20070504	Adding disassembly of some more M88K load/store instructions.
		Implementing some relatively simple M88K instructions (br.n,
		xor[.u] imm, and[.u] imm).
20070505	Implementing M88K three-register and, or, xor, and jmp[.n],
		bsr[.n] including function call trace stuff.
		Applying a patch from Bruce M. Simpson which implements the
		SYSCON_BOARD_CPU_CLOCK_FREQ_ID object of the syscon call in
		the yamon PROM emulation.
20070506	Implementing M88K bb0[.n] and bb1[.n], and skeletons for
		ldcr and stcr (although no control regs are implemented yet).
20070509	Found and fixed the bug which caused Linux for QEMU_MIPS to
		stop working in 0.4.5.1: It was a faulty change to the MIPS
		'sc' and 'scd' instructions I made while going through gcc -W
		warnings on 20070428.
20070510	Updating the Linux/QEMU_MIPS section in guestoses.html to
		use mips-test-0.2.tar.gz instead of 0.1.
		A big thank you to Miod Vallat for sending me M88K manuals.
		Implementing more M88K instructions (addu, subu, div[u], mulu,
		ext[u], clr, set, cmp).
20070511	Fixing bugs in the M88K "and" and "and.u" instructions (found
		by comparing against the manual).
		Implementing more M88K instructions (mask[.u], mak, bcnd (auto-
		generated)) and some more control register details.
		Cleanup: Removing the experimental AVR emulation mode and
		corresponding devices; AVR emulation wasn't really meaningful.
		Implementing autogeneration of most M88K loads/stores. The
		rectangle drawing demo (with -O0) for M88K runs :-)
		Beginning on M88K exception handling.
		More M88K instructions: tb0, tb1, rte, sub, jsr[.n].
		Adding some skeleton MVME PROM ("BUG") emulation.
20070512	Fixing a bug in the M88K cmp instruction.
		Adding the M88K lda (scaled register) instruction.
		Fixing bugs in 64-bit (32-bit pairs) M88K loads/stores.
		Removing the unused tick_hz stuff from the machine struct.
		Implementing the M88K xmem instruction. OpenBSD/mvme88k gets
		far enough to display the Copyright banner :-)
		Implementing subu.co (guess), addu.co, addu.ci, ff0, and ff1.
		Adding a dev_mvme187, for MVME187-specific devices/registers.
		OpenBSD/mvme88k prints more boot messages. :)
20070515	Continuing on MVME187 emulation (adding more devices, beginning
		on the CMMUs, etc).
		Adding the M88K and.c, xor.c, and or.c instructions, and making
		sure that mul, div, etc cause exceptions if executed when SFD1
		is disabled.
20070517	Continuing on M88K and MVME187 emulation in general; moving
		the CMMU registers to the CPU struct, separating dev_pcc2 from
		dev_mvme187, and beginning on memory_m88k.c (BATC and PATC).
		Fixing a bug in 64-bit (32-bit pairs) M88K fast stores.
		Implementing the clock part of dev_mk48txx.
		Implementing the M88K fstcr and xcr instructions.
		Implementing m88k_cpu_tlbdump().
		Beginning on the implementation of a separate address space
		for M88K .usr loads/stores.
20070520	Removing the non-working (skeleton) Sandpoint, SonyNEWS, SHARK
		Dnard, and Zaurus machine modes.
		Experimenting with dyntrans to_be_translated read-ahead. It
		seems to give a very small performance increase for MIPS
		emulation, but a large performance degradation for SuperH. Hm.
20070522	Disabling correct SuperH ITLB emulation; it does not seem to be
		necessary in order to let SH4 guest OSes run, and it slows down
		userspace code.
		Implementing "samepage" branches for SuperH emulation, and some
		other minor speed hacks.
20070525	Continuing on M88K memory-related stuff: exceptions, memory
		transaction register contents, etc.
		Implementing the M88K subu.ci instruction.
		Removing the non-working (skeleton) Iyonix machine mode.
		OpenBSD/mvme88k reaches userland :-), starts executing
		/sbin/init's instructions, and issues a few syscalls, before
		crashing.
20070526	Fixing bugs in dev_mk48txx, so that OpenBSD/mvme88k detects
		the correct time-of-day.
		Implementing a generic IRQ controller for the test machines
		(dev_irqc), similar to a proposed patch from Petr Stepan.
		Experimenting some more with translation read-ahead.
		Adding an "expect" script for automated OpenBSD/landisk
		install regression/performance tests.
20070527	Adding a dummy mmEye (SH3) machine mode skeleton.
		FINALLY found the strange M88K bug I have been hunting: I had
		not emulated the SNIP value for exceptions occurring in
		branch delay slots correctly.
		Implementing correct exceptions for 64-bit M88K loads/stores.
		Address to symbol lookups are now disabled when M88K is
		running in usermode (because usermode addresses don't have
		anything to do with supervisor addresses).
20070531	Removing the mmEye machine mode skeleton.
20070604	Some minor code cleanup.
20070605	Moving src/useremul.c into a subdir (src/useremul/), and
		cleaning up some more legacy constructs.
		Adding -Wstrict-aliasing and -fstrict-aliasing detection to
		the configure script.
20070606	Adding a check for broken GCC on Solaris to the configure
		script. (GCC 3.4.3 on Solaris cannot handle static variables
		which are initialized to 0 or NULL. :-/)
		Removing the old (non-working) ARC emulation modes: NEC RD94,
		R94, R96, and R98, and the last traces of Olivetti M700 and
		Deskstation Tyne.
		Removing the non-working skeleton WDSC device (dev_wdsc).
20070607	Thinking about how to use the host's cc + ld at runtime to
		generate native code. (See experiments/native_cc_ld_test.i
		for an example.)
20070608	Adding a program counter sampling timer, which could be useful
		for native code generation experiments.
		The KN02_CSR_NRMMOD bit in the DECstation 5000/200 (KN02) CSR
		should always be set, to allow a 5000/200 PROM to boot.
20070609	Moving out breakpoint details from the machine struct into
		a helper struct, and removing the limit on max nr of
		breakpoints.
20070610	Moving out tick functions into a helper struct as well (which
		also gets rid of the max limit).
20070612	FINALLY figured out why Debian/DECstation stopped working when
		translation read-ahead was enabled: in src/memory_rw.c, the
		call to invalidate_code_translation was made also if the
		memory access was an instruction load (if the page was mapped
		as writable); it shouldn't be called in that case.
20070613	Implementing some more MIPS32/64 revision 2 instructions: di,
		ei, ext, dext, dextm, dextu, and ins.
20070614	Implementing an instruction combination for the NetBSD/arm
		idle loop (making the host not use any cpu if NetBSD/arm
		inside the emulator is not using any cpu).
		Increasing the nr of ARM VPH entries from 128 to 384.
20070615	Removing the ENABLE_arch stuff from the configure script, so
		that all included architectures are included in both release
		and development builds.
		Moving memory related helper functions from misc.c to memory.c.
		Adding preliminary instructions for netbooting NetBSD/pmppc to
		guestoses.html; it doesn't work yet, there are weird timeouts.
		Beginning a total rewrite of the userland emulation modes
		(removing all emulation modes, beginning from scratch with
		NetBSD/MIPS and FreeBSD/Alpha only).
20070616	After fixing a bug in the DEC21143 NIC (the TDSTAT_OWN bit was
		only cleared for the last segment when transmitting, not all
		segments), NetBSD/pmppc boots with root-on-nfs without the
		timeouts. Updating guestoses.html.
		Removing the skeleton PSP (Playstation Portable) mode.
		Moving X11-related stuff in the machine struct into a helper
		struct.
		Cleanup of out-of-memory checks, to use a new CHECK_ALLOCATION
		macro (which prints a meaningful error message).
		Adding a COMMENT to each machine and device (for automagic
		.index comment generation).
		Doing regression testing for the next release.

==============  RELEASE 0.4.6  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26