/[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 34 - (show annotations)
Mon Oct 8 16:21:17 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 43320 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1480 2007/02/19 01:34:42 debug Exp $
20061029	Changing usleep(1) calls in the debugger to usleep(10000)
20061107	Adding a new disk image option (-d o...) which sets the ISO9660
		filesystem base offset; also making some other hacks to allow
		NetBSD/dreamcast and homebrew demos/games to boot directly
		from a filesystem image.
		Moving Dreamcast-specific stuff in the documentation to its
		own page (dreamcast.html).
		Adding a border to the Dreamcast PVR framebuffer.
20061108	Adding a -T command line option (again?), for halting the
		emulator on unimplemented memory accesses.
20061109	Continuing on various SH4 and Dreamcast related things.
		The emulator should now halt on more unimplemented device
		accesses, instead of just printing a warning, forcing me to
		actually implement missing stuff :)
20061111	Continuing on SH4 and Dreamcast stuff.
		Adding a bogus Landisk (SH4) machine mode.
20061112	Implementing some parts of the Dreamcast GDROM device. With
		some ugly hacks, NetBSD can (barely) mount an ISO image.
20061113	NetBSD/dreamcast now starts booting from the Live CD image,
		but crashes randomly quite early on in the boot process.
20061122	Beginning on a skeleton interrupt.h and interrupt.c for the
		new interrupt subsystem.
20061124	Continuing on the new interrupt system; taking the first steps
		to attempt to connect CPUs (SuperH and MIPS) and devices
		(dev_cons and SH4 timer interrupts) to it. Many things will
		probably break from now on.
20061125	Converting dev_ns16550, dev_8253 to the new interrupt system.
		Attempting to begin to convert the ISA bus.
20061130	Incorporating a patch from Brian Foley for the configure
		script, which checks for X11 libs in /usr/X11R6/lib64 (which
		is used on some Linux systems).
20061227	Adding a note in the man page about booting from Dreamcast
		CDROM images (i.e. that no external kernel is needed).
20061229	Continuing on the interrupt system rewrite: beginning to
		convert more devices, adding abort() calls for legacy interrupt
		system calls so that everything now _has_ to be rewritten!
		Almost all machine modes are now completely broken.
20061230	More progress on removing old interrupt code, mostly related
		to the ISA bus + devices, the LCA bus (on AlphaBook1), and
		the Footbridge bus (for CATS). And some minor PCI stuff.
		Connecting the ARM cpu to the new interrupt system.
		The CATS, NetWinder, and QEMU_MIPS machine modes now work with
		the new interrupt system :)
20061231	Connecting PowerPC CPUs to the new interrupt system.
		Making PReP machines (IBM 6050) work again.
		Beginning to convert the GT PCI controller (for e.g. Malta
		and Cobalt emulation). Some things work, but not everything.
		Updating Copyright notices for 2007.
20070101	Converting dev_kn02 from legacy style to devinit; the 3max
		machine mode now works with the new interrupt system :-]
20070105	Beginning to convert the SGI O2 machine to the new interrupt
		system; finally converting O2 (IP32) devices to devinit, etc.
20070106	Continuing on the interrupt system redesign/rewrite; KN01
		(PMAX), KN230, and Dreamcast ASIC interrupts should work again,
		moving out stuff from machine.h and devices.h into the
		corresponding devices, beginning the rewrite of i80321
		interrupts, etc.
20070107	Beginning on the rewrite of Eagle interrupt stuff (PReP, etc).
20070117	Beginning the rewrite of Algor (V3) interrupts (finally
		changing dev_v3 into devinit style).
20070118	Removing the "bus" registry concept from machine.h, because
		it was practically meaningless.
		Continuing on the rewrite of Algor V3 ISA interrupts.
20070121	More work on Algor interrupts; they are now working again,
		well enough to run NetBSD/algor. :-)
20070122	Converting VR41xx (HPCmips) interrupts. NetBSD/hpcmips
		can be installed using the new interrupt system :-)
20070123	Making the testmips mode work with the new interrupt system.
20070127	Beginning to convert DEC5800 devices to devinit, and to the
		new interrupt system.
		Converting Playstation 2 devices to devinit, and converting
		the interrupt system. Also fixing a severe bug: the interrupt
		mask register on Playstation 2 is bitwise _toggled_ on writes.
20070128	Removing the dummy NetGear machine mode and the 8250 device
		(which was only used by the NetGear machine).
		Beginning to convert the MacPPC GC (Grand Central) interrupt
		controller to the new interrupt system.
		Converting Jazz interrupts (PICA61 etc.) to the new interrupt
		system. NetBSD/arc can be installed again :-)
		Fixing the JAZZ timer (hardcoding it at 100 Hz, works with
		NetBSD and it is better than a completely dummy timer as it
		was before).
		Converting dev_mp to the new interrupt system, although I
		haven't had time to actually test it yet.
		Completely removing src/machines/interrupts.c, cpu_interrupt
		and cpu_interrupt_ack in src/cpu.c, and
		src/include/machine_interrupts.h! Adding fatal error messages
		+ abort() in the few places that are left to fix.
		Converting dev_z8530 to the new interrupt system.
		FINALLY removing the md_int struct completely from the
		machine struct.
		SH4 fixes (adding a PADDR invalidation in the ITLB replacement
		code in memory_sh.c); the NetBSD/dreamcast LiveCD now runs
		all the way to the login prompt, and can be interacted with :-)
		Converting the CPC700 controller (PCI and interrupt controller
		for PM/PPC) to the new interrupt system.
20070129	Fixing MACE ISA interrupts (SGI IP32 emulation). Both NetBSD/
		sgimips' and OpenBSD/sgi's ramdisk kernels can now be
		interacted with again.
20070130	Moving out the MIPS multi_lw and _sw instruction combinations
		so that they are auto-generated at compile time instead.
20070131	Adding detection of amd64/x86_64 hosts in the configure script,
		for doing initial experiments (again :-) with native code
		generation.
		Adding a -k command line option to set the size of the dyntrans
		cache, and a -B command line option to disable native code
		generation, even if GXemul was compiled with support for
		native code generation for the specific host CPU architecture.
20070201	Experimenting with a skeleton for native code generation.
		Changing the default behaviour, so that native code generation
		is now disabled by default, and has to be enabled by using
		-b on the command line.
20070202	Continuing the native code generation experiments.
		Making PCI interrupts work for Footbridge again.
20070203	More native code generation experiments.
		Removing most of the native code generation experimental code,
		it does not make sense to include any quick hacks like this.
		Minor cleanup/removal of some more legacy MIPS interrupt code.
20070204	Making i80321 interrupts work again (for NetBSD/evbarm etc.),
		and fixing the timer at 100 Hz.
20070206	Experimenting with removing the wdc interrupt slowness hack.
20070207	Lowering the number of dyntrans TLB entries for MIPS from
		192 to 128, resulting in a minor speed improvement.
		Minor optimization to the code invalidation routine in
		cpu_dyntrans.c.
20070208	Increasing (experimentally) the nr of dyntrans instructions per
		loop from 60 to 120.
20070210	Commenting out (experimentally) the dyntrans_device_danger
		detection in memory_rw.c.
		Changing the testmips and baremips machines to use a revision 2
		MIPS64 CPU by default, instead of revision 1.
		Removing the dummy i960, IA64, x86, AVR32, and HP PA-RISC
		files, the PC bios emulation, and the Olivetti M700 (ARC) and
		db64360 emulation modes.
20070211	Adding an "mp" demo to the demos directory, which tests the
		SMP functionality of the testmips machine.
		Fixing PReP interrupts some more. NetBSD/prep now boots again.
20070216	Adding a "nop workaround" for booting Mach/PMAX to the
		documentation; thanks to Artur Bujdoso for the values.
		Converting more of the MacPPC interrupt stuff to the new
		system.
		Beginning to convert BeBox interrupts to the new system.
		PPC603e should NOT have the PPC_NO_DEC flag! Removing it.
		Correcting BeBox clock speed (it was set to 100 in the NetBSD
		bootinfo block, but should be 33000000/4), allowing NetBSD
		to start without using the (incorrect) PPC_NO_DEC hack.
20070217	Implementing (slow) AltiVec vector loads and stores, allowing
		NetBSD/macppc to finally boot using the GENERIC kernel :-)
		Updating the documentation with install instructions for
		NetBSD/macppc.
20070218-19	Regression testing for the release.

==============  RELEASE 0.4.4  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26