/[gxemul]/trunk/src/emul.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/src/emul.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 24 - (hide annotations)
Mon Oct 8 16:19:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 40656 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1256 2006/06/23 20:43:44 debug Exp $
20060219	Various minor updates. Removing the old MIPS16 skeleton code,
		because it will need to be rewritten for dyntrans anyway.
20060220-22	Removing the non-working dyntrans backend support.
		Continuing on the 64-bit dyntrans virtual memory generalization.
20060223	More work on the 64-bit vm generalization.
20060225	Beginning on MIPS dyntrans load/store instructions.
		Minor PPC updates (64-bit load/store, etc).
		Fixes for the variable-instruction-length framework, some
		minor AVR updates (a simple Hello World program works!).
		Beginning on a skeleton for automatically generating documen-
		tation (for devices etc.).
20060226	PPC updates (adding some more 64-bit instructions, etc).
		AVR updates (more instructions).
		FINALLY found and fixed the zs bug, making NetBSD/macppc
		accept the serial console.
20060301	Adding more AVR instructions.
20060304	Continuing on AVR-related stuff. Beginning on a framework for
		cycle-accurate device emulation. Adding an experimental "PAL
		TV" device (just a dummy so far).
20060305	Adding more AVR instructions.
		Adding a dummy epcom serial controller (for TS7200 emulation).
20060310	Removing the emul() command from configuration files, so only
		net() and machine() are supported.
		Minor progress on the MIPS dyntrans rewrite.
20060311	Continuing on the MIPS dyntrans rewrite (adding more
		instructions, etc).
20060315	Adding more instructions (sllv, srav, srlv, bgtz[l], blez[l],
		beql, bnel, slti[u], various loads and stores).
20060316	Removing the ALWAYS_SIGNEXTEND_32 option, since it was rarely
		used.
		Adding more MIPS dyntrans instructions, and fixing bugs.
20060318	Implementing fast loads/stores for MIPS dyntrans (big/little
		endian, 32-bit and 64-bit modes).
20060320	Making MIPS dyntrans the default configure option; use
		"--enable-oldmips" to use the old bintrans system.
		Adding MIPS dyntrans dmult[u]; minor updates.
20060322	Continuing... adding some more instructions.
		Adding a simple skeleton for demangling C++ "_ZN" symbols.
20060323	Moving src/debugger.c into a new directory (src/debugger/).
20060324	Fixing the hack used to load PPC ELFs (useful for relocated
		Linux/ppc kernels), and adding a dummy G3 machine mode.
20060325-26	Beginning to experiment with GDB remote serial protocol
		connections; adding a -G command line option for selecting
		which TCP port to listen to.
20060330	Beginning a major cleanup to replace things like "0x%016llx"
		with more correct "0x%016"PRIx64, etc.
		Continuing on the GDB remote serial protocol support.
20060331	More cleanup, and some minor GDB remote progress.
20060402	Adding a hack to the configure script, to allow compilation
		on systems that lack PRIx64 etc.
20060406	Removing the temporary FreeBSD/arm hack in dev_ns16550.c and
		replacing it with a better fix from Olivier Houchard.
20060407	A remote debugger (gdb or ddd) can now start and stop the
		emulator using the GDB remote serial protocol, and registers
		and memory can be read. MIPS only for now.
20060408	More GDB progress: single-stepping also works, and also adding
		support for ARM, PowerPC, and Alpha targets.
		Continuing on the delay-slot-across-page-boundary issue.
20060412	Minor update: beginning to add support for the SPARC target
		to the remote GDB functionality.
20060414	Various MIPS updates: adding more instructions for dyntrans
		(eret, add), and making some exceptions work. Fixing a bug
		in dmult[u].
		Implementing the first SPARC instructions (sethi, or).
20060415	Adding "magic trap" instructions so that PROM calls can be
		software emulated in MIPS dyntrans.
		Adding more MIPS dyntrans instructions (ddiv, dadd) and
		fixing another bug in dmult.
20060416	More MIPS dyntrans progress: adding [d]addi, movn, movz, dsllv,
		rfi, an ugly hack for supporting R2000/R3000 style faked caches,
		preliminary interrupt support, and various other updates and
		bugfixes.
20060417	Adding more SPARC instructions (add, sub, sll[x], sra[x],
		srl[x]), and useful SPARC header definitions.
		Adding the first (trivial) x86/AMD64 dyntrans instructions (nop,
		cli/sti, stc/clc, std/cld, simple mov, inc ax). Various other
		x86 updates related to variable instruction length stuff.
		Adding unaligned loads/stores to the MIPS dyntrans mode (but
		still using the pre-dyntrans (slow) imlementation).
20060419	Fixing a MIPS dyntrans exception-in-delay-slot bug.
		Removing the old "show opcode statistics" functionality, since
		it wasn't really useful and isn't implemented for dyntrans.
		Single-stepping (or running with instruction trace) now looks
		ok with dyntrans with delay-slot architectures.
20060420	Minor hacks (removing the -B command line option when compiled
		for non-bintrans, and some other very minor updates).
		Adding (slow) MIPS dyntrans load-linked/store-conditional.
20060422	Applying fixes for bugs discovered by Nils Weller's nwcc
		(static DEC memmap => now per machine, and adding an extern
		keyword in cpu_arm_instr.c).
		Finally found one of the MIPS dyntrans bugs that I've been
		looking for (copy/paste spelling error BIG vs LITTLE endian in
		cpu_mips_instr_loadstore.c for 16-bit fast stores).
		FINALLY found the major MIPS dyntrans bug: slti vs sltiu
		signed/unsigned code in cpu_mips_instr.c. :-)
		Adding more MIPS dyntrans instructions (lwc1, swc1, bgezal[l],
		ctc1, tlt[u], tge[u], tne, beginning on rdhwr).
		NetBSD/hpcmips can now reach userland when using dyntrans :-)
		Adding some more x86 dyntrans instructions.
		Finally removed the old Alpha-specific virtual memory code,
		and replaced it with the generic 64-bit version.
		Beginning to add disassembly support for SPECIAL3 MIPS opcodes.
20060423	Continuing on the delay-slot-across-page-boundary issue;
		adding an end_of_page2 ic slot (like I had planned before, but
		had removed for some reason).
		Adding a quick-and-dirty fallback to legacy coprocessor 1
		code (i.e. skipping dyntrans implementation for now).
		NetBSD/hpcmips and NetBSD/pmax (when running on an emulated
		R4400) can now be installed and run. :-)  (Many bugs left
		to fix, though.)
		Adding more MIPS dyntrans instructions: madd[u], msub[u].
		Cleaning up the SPECIAL2 vs R5900/TX79/C790 "MMI" opcode
		maps somewhat (disassembly and dyntrans instruction decoding).
20060424	Adding an isa_revision field to mips_cpu_types.h, and making
		sure that SPECIAL3 opcodes cause Reserved Instruction
		exceptions on MIPS32/64 revisions lower than 2.
		Adding the SPARC 'ba', 'call', 'jmpl/retl', 'and', and 'xor'
		instructions.
20060425	Removing the -m command line option ("run at most x 
		instructions") and -T ("single_step_on_bad_addr"), because
		they never worked correctly with dyntrans anyway.
		Freshening up the man page.
20060428	Adding more MIPS dyntrans instructions: bltzal[l], idle.
		Enabling MIPS dyntrans compare interrupts.
20060429	FINALLY found the weird dyntrans bug, causing NetBSD etc. to
		behave strangely: some floating point code (conditional
		coprocessor branches) could not be reused from the old
		non-dyntrans code. The "quick-and-dirty fallback" only appeared
		to work. Fixing by implementing bc1* for MIPS dyntrans.
		More MIPS instructions: [d]sub, sdc1, ldc1, dmtc1, dmfc1, cfc0.
		Freshening up MIPS floating point disassembly appearance.
20060430	Continuing on C790/R5900/TX79 disassembly; implementing 128-bit
		"por" and "pextlw".
20060504	Disabling -u (userland emulation) unless compiled as unstable
		development version.
		Beginning on freshening up the testmachine include files,
		to make it easier to reuse those files (placing them in
		src/include/testmachine/), and beginning on a set of "demos"
		or "tutorials" for the testmachine functionality.
		Minor updates to the MIPS GDB remote protocol stub.
		Refreshing doc/experiments.html and gdb_remote.html.
		Enabling Alpha emulation in the stable release configuration,
		even though no guest OSes for Alpha can run yet.
20060505	Adding a generic 'settings' object, which will contain
		references to settable variables (which will later be possible
		to access using the debugger).
20060506	Updating dev_disk and corresponding demo/documentation (and
		switching from SCSI to IDE disk types, so it actually works
		with current test machines :-).
20060510	Adding a -D_LARGEFILE_SOURCE hack for 64-bit Linux hosts,
		so that fseeko() doesn't give a warning.
		Updating the section about how dyntrans works (the "runnable
		IR") in doc/intro.html.
		Instruction updates (some x64=1 checks, some more R5900
		dyntrans stuff: better mul/mult separation from MIPS32/64,
		adding ei and di).
		Updating MIPS cpuregs.h to a newer one (from NetBSD).
		Adding more MIPS dyntrans instructions: deret, ehb.
20060514	Adding disassembly and beginning implementation of SPARC wr
		and wrpr instructions.
20060515	Adding a SUN SPARC machine mode, with dummy SS20 and Ultra1
		machines. Adding the 32-bit "rd psr" instruction.
20060517	Disassembly support for the general SPARC rd instruction.
		Partial implementation of the cmp (subcc) instruction.
		Some other minor updates (making sure that R5900 processors
		start up with the EIE bit enabled, otherwise Linux/playstation2
		receives no interrupts).
20060519	Minor MIPS updates/cleanups.
20060521	Moving the MeshCube machine into evbmips; this seems to work
		reasonably well with a snapshot of a NetBSD MeshCube kernel.
		Cleanup/fix of MIPS config0 register initialization.
20060529	Minor MIPS fixes, including a sign-extension fix to the
		unaligned load/store code, which makes NetBSD/pmax on R3000
		work better with dyntrans. (Ultrix and Linux/DECstation still
		don't work, though.)
20060530	Minor updates to the Alpha machine mode: adding an AlphaBook
		mode, an LCA bus (forwarding accesses to an ISA bus), etc.
20060531	Applying a bugfix for the MIPS dyntrans sc[d] instruction from
		Ondrej Palkovsky. (Many thanks.)
20060601	Minifix to allow ARM immediate msr instruction to not give
		an error for some valid values.
		More Alpha updates.
20060602	Some minor Alpha updates.
20060603	Adding the Alpha cmpbge instruction. NetBSD/alpha prints its
		first boot messages :-) on an emulated Alphabook 1.
20060612	Minor updates; adding a dev_ether.h include file for the
		testmachine ether device. Continuing the hunt for the dyntrans
		bug which makes Linux and Ultrix on DECstation behave
		strangely... FINALLY found it! It seems to be related to
		invalidation of the translation cache, on tlbw{r,i}. There
		also seems to be some remaining interrupt-related problems.
20060614	Correcting the implementation of ldc1/sdc1 for MIPS dyntrans
		(so that it uses 16 32-bit registers if the FR bit in the
		status register is not set).
20060616	REMOVING BINTRANS COMPLETELY!
		Removing the old MIPS interpretation mode.
		Removing the MFHILO_DELAY and instruction delay stuff, because
		they wouldn't work with dyntrans anyway.
20060617	Some documentation updates (adding "NetBSD-archive" to some
		URLs, and new Debian/DECstation installation screenshots).
		Removing the "tracenull" and "enable-caches" configure options.
		Improving MIPS dyntrans performance somewhat (only invalidate
		translations if necessary, on writes to the entryhi register,
		instead of doing it for all cop0 writes).
20060618	More cleanup after the removal of the old MIPS emulation.
		Trying to fix the MIPS dyntrans performance bugs/bottlenecks;
		only semi-successful so far (for R3000).
20060620	Minor update to allow clean compilation again on Tru64/Alpha.
20060622	MIPS cleanup and fixes (removing the pc_last stuff, which
		doesn't make sense with dyntrans anyway, and fixing a cross-
		page-delay-slot-with-exception case in end_of_page).
		Removing the old max_random_cycles_per_chunk stuff, and the
		concept of cycles vs instructions for MIPS emulation.
		FINALLY found and fixed the bug which caused NetBSD/pmax
		clocks to behave strangely (it was a load to the zero register,
		which was treated as a NOP; now it is treated as a load to a
		dummy scratch register).
20060623	Increasing the dyntrans chunk size back to
		N_SAFE_DYNTRANS_LIMIT, instead of N_SAFE_DYNTRANS_LIMIT/2.
		Preparing for a quick release, even though there are known
		bugs, and performance for non-R3000 MIPS emulation is very
		poor. :-/
		Reverting to half the dyntrans chunk size again, because
		NetBSD/cats seemed less stable with full size chunks. :(
		NetBSD/sgimips 3.0 can now run :-)  (With release 0.3.8, only
		NetBSD/sgimips 2.1 worked, not 3.0.)

==============  RELEASE 0.4.0  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26