/[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 32 - (show annotations)
Mon Oct 8 16:20:58 2007 UTC (16 years, 5 months ago) by dpavlin
File MIME type: text/plain
File size: 43364 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1421 2006/11/06 05:32:37 debug Exp $
20060816	Adding a framework for emulated/virtual timers (src/timer.c),
		using only setitimer().
		Rewriting the mc146818 to use the new timer framework.
20060817	Adding a call to gettimeofday() every now and then (once every
		second, at the moment) to resynch the timer if it drifts.
		Beginning to convert the ISA timer interrupt mechanism (8253
		and 8259) to use the new timer framework.
		Removing the -I command line option.
20060819	Adding the -I command line option again, with new semantics.
		Working on Footbridge timer interrupts; NetBSD/NetWinder and
		NetBSD/CATS now run at correct speed, but unfortunately with
		HUGE delays during bootup.
20060821	Some minor m68k updates. Adding the first instruction: nop. :)
		Minor Alpha emulation updates.
20060822	Adding a FreeBSD development specific YAMON environment
		variable ("khz") (as suggested by Bruce M. Simpson).
		Moving YAMON environment variable initialization from
		machine_evbmips.c into promemul/yamon.c, and adding some more
		variables.
		Continuing on the LCA PCI bus controller (for Alpha machines).
20060823	Continuing on the timer stuff: experimenting with MIPS count/
		compare interrupts connected to the timer framework.
20060825	Adding bogus SCSI commands 0x51 (SCSICDROM_READ_DISCINFO) and
		0x52 (SCSICDROM_READ_TRACKINFO) to the SCSI emulation layer,
		to allow NetBSD/pmax 4.0_BETA to be installed from CDROM.
		Minor updates to the LCA PCI controller.
20060827	Implementing a CHIP8 cpu mode, and a corresponding CHIP8
		machine, for fun. Disassembly support for all instructions,
		and most of the common instructions have been implemented: mvi,
		mov_imm, add_imm, jmp, rand, cls, sprite, skeq_imm, jsr,
		skne_imm, bcd, rts, ldr, str, mov, or, and, xor, add, sub,
		font, ssound, sdelay, gdelay, bogus skup/skpr, skeq, skne.
20060828	Beginning to convert the CHIP8 cpu in the CHIP8 machine to a
		(more correct) RCA 180x cpu. (Disassembly for all 1802
		instructions has been implemented, but no execution yet, and
		no 1805 extended instructions.)
20060829	Minor Alpha emulation updates.
20060830	Beginning to experiment a little with PCI IDE for SGI O2.
		Fixing the cursor key mappings for MobilePro 770 emulation.
		Fixing the LK201 warning caused by recent NetBSD/pmax.
		The MIPS R41xx standby, suspend, and hibernate instructions now
		behave like the RM52xx/MIPS32/MIPS64 wait instruction.
		Fixing dev_wdc so it calculates correct (64-bit) offsets before
		giving them to diskimage_access().
20060831	Continuing on Alpha emulation (OSF1 PALcode).
20060901	Minor Alpha updates; beginning on virtual memory pagetables.
		Removed the limit for max nr of devices (in preparation for
		allowing devices' base addresses to be changed during runtime).
		Adding a hack for MIPS [d]mfc0 select 0 (except the count
		register), so that the coproc register is simply copied.
		The MIPS suspend instruction now exits the emulator, instead
		of being treated as a wait instruction (this causes NetBSD/
		hpcmips to get correct 'halt' behavior).
		The VR41xx RTC now returns correct time.
		Connecting the VR41xx timer to the timer framework (fixed at
		128 Hz, for now).
		Continuing on SPARC emulation, adding more instructions:
		restore, ba_xcc, ble. The rectangle drawing demo works :)
		Removing the last traces of the old ENABLE_CACHE_EMULATION
		MIPS stuff (not usable with dyntrans anyway).
20060902	Splitting up src/net.c into several smaller files in its own
		subdirectory (src/net/).
20060903	Cleanup of the files in src/net/, to make them less ugly.
20060904	Continuing on the 'settings' subsystem.
		Minor progress on the SPARC emulation mode.
20060905	Cleanup of various things, and connecting the settings
		infrastructure to various subsystems (emul, machine, cpu, etc).
		Changing the lk201 mouse update routine to not rely on any
		emulated hardware framebuffer cursor coordinates, but instead
		always do (semi-usable) relative movements.
20060906	Continuing on the lk201 mouse stuff. Mouse behaviour with
		multiple framebuffers (which was working in Ultrix) is now
		semi-broken (but it still works, in a way).
		Moving the documentation about networking into its own file
		(networking.html), and refreshing it a bit. Adding an example
		of how to use ethernet frame direct-access (udp_snoop).
20060907	Continuing on the settings infrastructure.
20060908	Minor updates to SH emulation: for 32-bit emulation: delay
		slots and the 'jsr @Rn' instruction. I'm putting 64-bit SH5 on
		ice, for now.
20060909-10	Implementing some more 32-bit SH instructions. Removing the
		64-bit mode completely. Enough has now been implemented to run
		the rectangle drawing demo. :-)
20060912	Adding more SH instructions.
20060916	Continuing on SH emulation (some more instructions: div0u,
		div1, rotcl/rotcr, more mov instructions, dt, braf, sets, sett,
		tst_imm, dmuls.l, subc, ldc_rm_vbr, movt, clrt, clrs, clrmac).
		Continuing on the settings subsystem (beginning on reading/
		writing settings, removing bugs, and connecting more cpus to
		the framework).
20060919	More work on SH emulation; adding an ldc banked instruction,
		and attaching a 640x480 framebuffer to the Dreamcast machine
		mode (NetBSD/dreamcast prints the NetBSD copyright banner :-),
		and then panics).
20060920	Continuing on the settings subsystem.
20060921	Fixing the Footbridge timer stuff so that NetBSD/cats and
		NetBSD/netwinder boot up without the delays.
20060922	Temporarily hardcoding MIPS timer interrupt to 100 Hz. With
		'wait' support disabled, NetBSD/malta and Linux/malta run at
		correct speed.
20060923	Connecting dev_gt to the timer framework, so that NetBSD/cobalt
		runs at correct speed.
		Moving SH4-specific memory mapped registers into its own
		device (dev_sh4.c).
		Running with -N now prints "idling" instead of bogus nr of
		instrs/second (which isn't valid anyway) while idling.
20060924	Algor emulation should now run at correct speed.
		Adding disassembly support for some MIPS64 revision 2
		instructions: ext, dext, dextm, dextu.
20060926	The timer framework now works also when the MIPS wait
		instruction is used.
20060928	Re-implementing checks for coprocessor availability for MIPS
		cop0 instructions. (Thanks to Carl van Schaik for noticing the
		lack of cop0 availability checks.)
20060929	Implementing an instruction combination hack which treats
		NetBSD/pmax' idle loop as a wait-like instruction.
20060930	The ENTRYHI_R_MASK was missing in (at least) memory_mips_v2p.c,
		causing TLB lookups to sometimes succeed when they should have
		failed. (A big thank you to Juli Mallett for noticing the
		problem.)
		Adding disassembly support for more MIPS64 revision 2 opcodes
		(seb, seh, wsbh, jalr.hb, jr.hb, synci, ins, dins, dinsu,
		dinsm, dsbh, dshd, ror, dror, rorv, drorv, dror32). Also
		implementing seb, seh, dsbh, dshd, and wsbh.
		Implementing an instruction combination hack for Linux/pmax'
		idle loop, similar to the NetBSD/pmax case.
20061001	Changing the NetBSD/sgimips install instructions to extract
		files from an iso image, instead of downloading them via ftp.
20061002	More-than-31-bit userland addresses in memory_mips_v2p.c were
		not actually working; applying a fix from Carl van Schaik to
		enable them to work + making some other updates (adding kuseg
		support).
		Fixing hpcmips (vr41xx) timer initialization.
		Experimenting with O(n)->O(1) reduction in the MIPS TLB lookup
		loop. Seems to work both for R3000 and non-R3000.
20061003	Continuing a little on SH emulation (adding more control
		registers; mini-cleanup of memory_sh.c).
20061004	Beginning on a dev_rtc, a clock/timer device for the test
		machines; also adding a demo, and some documentation.
		Fixing a bug in SH "mov.w @(disp,pc),Rn" (the result wasn't
		sign-extended), and adding the addc and ldtlb instructions.
20061005	Contining on SH emulation: virtual to physical address
		translation, and a skeleton exception mechanism.
20061006	Adding more SH instructions (various loads and stores, rte,
		negc, muls.w, various privileged register-move instructions).
20061007	More SH instructions: various move instructions, trapa, div0s,
		float, fdiv, ftrc.
		Continuing on dev_rtc; removing the rtc demo.
20061008	Adding a dummy Dreamcast PROM module. (Homebrew Dreamcast
		programs using KOS libs need this.)
		Adding more SH instructions: "stc vbr,rn", rotl, rotr, fsca,
		fmul, fadd, various floating-point moves, etc. A 256-byte
		demo for Dreamcast runs :-)
20061012	Adding the SH "lds Rm,pr" and bsr instructions.
20061013	More SH instructions: "sts fpscr,rn", tas.b, and some more
		floating point instructions, cmp/str, and more moves.
		Adding a dummy dev_pvr (Dreamcast graphics controller).
20061014	Generalizing the expression evaluator (used in the built-in
		debugger) to support parentheses and +-*/%^&|.
20061015	Removing the experimental tlb index hint code in
		mips_memory_v2p.c, since it didn't really have any effect.
20061017	Minor SH updates; adding the "sts pr,Rn", fcmp/gt, fneg,
		frchg, and some other instructions. Fixing missing sign-
		extension in an 8-bit load instruction.
20061019	Adding a simple dev_dreamcast_rtc.
		Implementing memory-mapped access to the SH ITLB/UTLB arrays.
20061021	Continuing on various SH and Dreamcast things: sh4 timers,
		debug messages for dev_pvr, fixing some virtual address
		translation bugs, adding the bsrf instruction.
		The NetBSD/dreamcast GENERIC_MD kernel now reaches userland :)
		Adding a dummy dev_dreamcast_asic.c (not really useful yet).
		Implementing simple support for Store Queues.
		Beginning on the PVR Tile Accelerator.
20061022	Generalizing the PVR framebuffer to support off-screen drawing,
		multiple bit-depths, etc. (A small speed penalty, but most
		likely worth it.)
		Adding more SH instructions (mulu.w, fcmp/eq, fsub, fmac,
		fschg, and some more); correcting bugs in "fsca" and "float".
20061024	Adding the SH ftrv (matrix * vector) instruction. Marcus
		Comstedt's "tatest" example runs :) (wireframe only).
		Correcting disassembly for SH floating point instructions that
		use the xd* registers.
		Adding the SH fsts instruction.
		In memory_device_dyntrans_access(), only the currently used
		range is now invalidated, and not the entire device range.
20061025	Adding a dummy AVR32 cpu mode skeleton.
20061026	Various Dreamcast updates; beginning on a Maple bus controller.
20061027	Continuing on the Maple bus. A bogus Controller, Keyboard, and
		Mouse can now be detected by NetBSD and KOS homebrew programs.
		Cleaning up the SH4 Timer Management Unit, and beginning on
		SH4 interrupts.
		Implementing the Dreamcast SYSASIC.
20061028	Continuing on the SYSASIC.
		Adding the SH fsqrt instruction.
		memory_sh.c now actually scans the ITLB.
		Fixing a bug in dev_sh4.c, related to associative writes into
		the memory-mapped UTLB array. NetBSD/dreamcast now reaches
		userland stably, and prints the "Terminal type?" message :-]
		Implementing enough of the Dreamcast keyboard to make NetBSD
		accept it for input.
		Enabling SuperH for stable (non-development) builds.
		Adding NetBSD/dreamcast to the documentation, although it
		doesn't support root-on-nfs yet.
20061029	Changing usleep(1) calls in the debugger to to usleep(10000)
		(according to Brian Foley, this makes GXemul run better on
		MacOS X).
		Making the Maple "Controller" do something (enough to barely
		interact with dcircus.elf).
20061030-31	Some progress on the PVR. More test programs start running (but
		with strange output).
		Various other SH4-related updates.
20061102	Various Dreamcast and SH4 updates; more KOS demos run now.
20061104	Adding a skeleton dev_mb8696x.c (the Dreamcast's LAN adapter).
20061105	Continuing on the MB8696x; NetBSD/dreamcast detects it as mbe0.
		Testing for the release.

==============  RELEASE 0.4.3  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26