/[gxemul]/trunk/src/devices/dev_sgi_ip32.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/devices/dev_sgi_ip32.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 24 - (show annotations)
Mon Oct 8 16:19:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 32564 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 /*
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: dev_sgi_ip32.c,v 1.45 2006/03/04 12:38:48 debug Exp $
29 *
30 * SGI IP32 devices.
31 *
32 * o) CRIME
33 * o) MACE
34 * o) MACE PCI bus
35 * o) mec (ethernet)
36 * o) ust (unknown device)
37 * o) mte (memory transfer engine? details unknown)
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "bus_pci.h"
45 #include "console.h"
46 #include "cpu.h"
47 #include "devices.h"
48 #include "emul.h"
49 #include "machine.h"
50 #include "memory.h"
51 #include "misc.h"
52 #include "net.h"
53
54 #include "crimereg.h"
55
56 #include "if_mecreg.h"
57
58
59 #define CRIME_TICKSHIFT 14
60 #define CRIME_SPEED_MUL_FACTOR 1
61 #define CRIME_SPEED_DIV_FACTOR 1
62
63 struct macepci_data {
64 struct pci_data *pci_data;
65 uint32_t reg[DEV_MACEPCI_LENGTH / 4];
66 };
67
68
69 /*
70 * dev_crime_tick():
71 *
72 * This function simply updates CRIME_TIME each tick.
73 *
74 * The names DIV and MUL may be a bit confusing. Increasing the
75 * MUL factor will result in an OS running on the emulated machine
76 * detecting a faster CPU. Increasing the DIV factor will result
77 * in a slower detected CPU.
78 *
79 * A R10000 is detected as running at
80 * CRIME_SPEED_FACTOR * 66 MHz. (TODO: this is not correct anymore)
81 */
82 void dev_crime_tick(struct cpu *cpu, void *extra)
83 {
84 int j, carry, old, new, add_byte;
85 uint64_t what_to_add = (1<<CRIME_TICKSHIFT)
86 * CRIME_SPEED_DIV_FACTOR / CRIME_SPEED_MUL_FACTOR;
87 struct crime_data *d = extra;
88
89 j = 0;
90 carry = 0;
91 while (j < 8) {
92 old = d->reg[CRIME_TIME + 7 - j];
93 add_byte = what_to_add >> ((int64_t)j * 8);
94 add_byte &= 255;
95 new = old + add_byte + carry;
96 d->reg[CRIME_TIME + 7 - j] = new & 255;
97 if (new >= 256)
98 carry = 1;
99 else
100 carry = 0;
101 j++;
102 }
103 }
104
105
106 /*
107 * dev_crime_access():
108 */
109 DEVICE_ACCESS(crime)
110 {
111 struct crime_data *d = extra;
112 uint64_t idata = 0;
113 size_t i;
114
115 if (writeflag == MEM_WRITE)
116 idata = memory_readmax64(cpu, data, len);
117
118 /*
119 * Set crime version/revision:
120 *
121 * This might not be the most elegant or correct solution, but it
122 * seems that the IP32 PROM likes 0x11 for machines without graphics,
123 * and 0xa1 for machines with graphics.
124 *
125 * NetBSD 2.0 complains about "unknown" crime for 0x11, but I guess
126 * that's something one has to live with. (TODO?)
127 */
128 d->reg[4] = 0x00; d->reg[5] = 0x00; d->reg[6] = 0x00;
129 d->reg[7] = d->use_fb? 0xa1 : 0x11;
130
131 /*
132 * Amount of memory. Bit 8 of bank control set ==> 128MB instead
133 * of 32MB per bank (?)
134 *
135 * When the bank control registers contain the same value as the
136 * previous one, that bank is not valid. (?)
137 */
138 d->reg[CRM_MEM_BANK_CTRL0 + 6] = 0; /* lowbit set=128MB, clear=32MB */
139 d->reg[CRM_MEM_BANK_CTRL0 + 7] = 0; /* address * 32MB */
140 d->reg[CRM_MEM_BANK_CTRL1 + 6] = 0; /* lowbit set=128MB, clear=32MB */
141 d->reg[CRM_MEM_BANK_CTRL1 + 7] = 1; /* address * 32MB */
142
143 if (relative_addr >= CRIME_TIME && relative_addr < CRIME_TIME+8) {
144 if (writeflag == MEM_READ)
145 memcpy(data, &d->reg[relative_addr], len);
146 return 1;
147 }
148
149 if (writeflag == MEM_WRITE)
150 memcpy(&d->reg[relative_addr], data, len);
151 else
152 memcpy(data, &d->reg[relative_addr], len);
153
154 if ((relative_addr >= 0x18 && relative_addr <= 0x1f) ||
155 (relative_addr+len-1 >= 0x18 && relative_addr+len-1 <= 0x1f)) {
156 /*
157 * Force interrupt re-assertion:
158 *
159 * NOTE: Ugly hack. Hopefully CRMERR is never used.
160 */
161 #if 0
162 /*
163 No. If this is enabled, the mec bugs out on either NetBSD or OpenBSD.
164 TODO.
165 */
166 cpu_interrupt_ack(cpu, 8); /* CRM_INT_CRMERR); */
167 #endif
168 }
169
170 switch (relative_addr) {
171 case CRIME_CONTROL: /* 0x008 */
172 /* TODO: 64-bit write to CRIME_CONTROL, but some things
173 (such as NetBSD 1.6.2) write to 0x00c! */
174 if (writeflag == MEM_WRITE) {
175 /*
176 * 0x200 = watchdog timer (according to NetBSD)
177 * 0x800 = "reboot" used by the IP32 PROM
178 */
179 if (idata & 0x200) {
180 idata &= ~0x200;
181 }
182 if (idata & 0x800) {
183 int j;
184
185 /* This is used by the IP32 PROM's
186 "reboot" command: */
187 for (j=0; j<cpu->machine->ncpus; j++)
188 cpu->machine->cpus[j]->running = 0;
189 cpu->machine->
190 exit_without_entering_debugger = 1;
191 idata &= ~0x800;
192 }
193 if (idata != 0)
194 fatal("[ CRIME_CONTROL: unimplemented "
195 "control 0x%016llx ]\n", (long long)idata);
196 }
197 break;
198 #if 0
199 case CRIME_INTSTAT: /* 0x010, Current interrupt status */
200 case 0x14:
201 case CRIME_INTMASK: /* 0x018, Current interrupt mask */
202 case 0x1c:
203 case 0x34:
204 /* don't dump debug info for these */
205 break;
206 #endif
207 default:
208 if (writeflag==MEM_READ) {
209 debug("[ crime: read from 0x%x, len=%i:",
210 (int)relative_addr, len);
211 for (i=0; i<len; i++)
212 debug(" %02x", data[i]);
213 debug(" ]\n");
214 } else {
215 debug("[ crime: write to 0x%x:", (int)relative_addr);
216 for (i=0; i<len; i++)
217 debug(" %02x", data[i]);
218 debug(" (len=%i) ]\n", len);
219 }
220 }
221
222 return 1;
223 }
224
225
226 /*
227 * dev_crime_init():
228 */
229 struct crime_data *dev_crime_init(struct machine *machine, struct memory *mem,
230 uint64_t baseaddr, int irq_nr, int use_fb)
231 {
232 struct crime_data *d;
233
234 d = malloc(sizeof(struct crime_data));
235 if (d == NULL) {
236 fprintf(stderr, "out of memory\n");
237 exit(1);
238 }
239 memset(d, 0, sizeof(struct crime_data));
240 d->irq_nr = irq_nr;
241 d->use_fb = use_fb;
242
243 memory_device_register(mem, "crime", baseaddr, DEV_CRIME_LENGTH,
244 dev_crime_access, d, DM_DEFAULT, NULL);
245 machine_add_tickfunction(machine, dev_crime_tick, d,
246 CRIME_TICKSHIFT, 0.0);
247
248 return d;
249 }
250
251
252 /****************************************************************************/
253
254
255 /*
256 * dev_mace_access():
257 */
258 DEVICE_ACCESS(mace)
259 {
260 size_t i;
261 struct mace_data *d = extra;
262
263 if (writeflag == MEM_WRITE)
264 memcpy(&d->reg[relative_addr], data, len);
265 else
266 memcpy(data, &d->reg[relative_addr], len);
267
268 if ((relative_addr >= 0x18 && relative_addr <= 0x1f) ||
269 (relative_addr+len-1 >= 0x18 && relative_addr+len-1 <= 0x1f))
270 cpu_interrupt_ack(cpu, 8); /* CRM_INT_CRMERR); */
271
272 switch (relative_addr) {
273 #if 0
274 case 0x10: /* Current interrupt assertions */
275 case 0x14:
276 /* don't dump debug info for these */
277 if (writeflag == MEM_WRITE) {
278 fatal("[ NOTE/TODO: WRITE to mace intr: "
279 "reladdr=0x%x data=", (int)relative_addr);
280 for (i=0; i<len; i++)
281 fatal(" %02x", data[i]);
282 fatal(" (len=%i) ]\n", len);
283 }
284 break;
285 case 0x18: /* Interrupt mask */
286 case 0x1c:
287 /* don't dump debug info for these */
288 break;
289 #endif
290 default:
291 if (writeflag == MEM_READ) {
292 debug("[ mace: read from 0x%x:", (int)relative_addr);
293 for (i=0; i<len; i++)
294 debug(" %02x", data[i]);
295 debug(" (len=%i) ]\n", len);
296 } else {
297 debug("[ mace: write to 0x%x:", (int)relative_addr);
298 for (i=0; i<len; i++)
299 debug(" %02x", data[i]);
300 debug(" (len=%i) ]\n", len);
301 }
302 }
303
304 return 1;
305 }
306
307
308 /*
309 * dev_mace_init():
310 */
311 struct mace_data *dev_mace_init(struct memory *mem, uint64_t baseaddr,
312 int irqnr)
313 {
314 struct mace_data *d;
315
316 d = malloc(sizeof(struct mace_data));
317 if (d == NULL) {
318 fprintf(stderr, "out of memory\n");
319 exit(1);
320 }
321 memset(d, 0, sizeof(struct mace_data));
322 d->irqnr = irqnr;
323
324 memory_device_register(mem, "mace", baseaddr, DEV_MACE_LENGTH,
325 dev_mace_access, d, DM_DEFAULT, NULL);
326
327 return d;
328 }
329
330
331 /****************************************************************************/
332
333
334 /*
335 * dev_macepci_access():
336 */
337 DEVICE_ACCESS(macepci)
338 {
339 struct macepci_data *d = (struct macepci_data *) extra;
340 uint64_t idata = 0, odata=0;
341 int regnr, res = 1, bus, dev, func, pcireg;
342
343 if (writeflag == MEM_WRITE)
344 idata = memory_readmax64(cpu, data, len);
345
346 regnr = relative_addr / sizeof(uint32_t);
347
348 /* Read from/write to the macepci: */
349 switch (relative_addr) {
350
351 case 0x00: /* Error address */
352 if (writeflag == MEM_WRITE) {
353 } else {
354 odata = 0;
355 }
356 break;
357
358 case 0x04: /* Error flags */
359 if (writeflag == MEM_WRITE) {
360 } else {
361 odata = 0x06;
362 }
363 break;
364
365 case 0x0c: /* Revision number */
366 if (writeflag == MEM_WRITE) {
367 } else {
368 odata = 0x01;
369 }
370 break;
371
372 case 0xcf8: /* PCI ADDR */
373 bus_pci_decompose_1(idata, &bus, &dev, &func, &pcireg);
374 bus_pci_setaddr(cpu, d->pci_data, bus, dev, func, pcireg);
375 break;
376
377 case 0xcfc: /* PCI DATA */
378 bus_pci_data_access(cpu, d->pci_data, writeflag == MEM_READ?
379 &odata : &idata, len, writeflag);
380 break;
381
382 default:
383 if (writeflag == MEM_WRITE) {
384 debug("[ macepci: unimplemented write to address "
385 "0x%x, data=0x%02x ]\n",
386 (int)relative_addr, (int)idata);
387 } else {
388 debug("[ macepci: unimplemented read from address "
389 "0x%x ]\n", (int)relative_addr);
390 }
391 }
392
393 if (writeflag == MEM_READ)
394 memory_writemax64(cpu, data, len, odata);
395
396 return res;
397 }
398
399
400 /*
401 * dev_macepci_init():
402 */
403 struct pci_data *dev_macepci_init(struct machine *machine,
404 struct memory *mem, uint64_t baseaddr, int pciirq)
405 {
406 struct macepci_data *d = malloc(sizeof(struct macepci_data));
407 if (d == NULL) {
408 fprintf(stderr, "out of memory\n");
409 exit(1);
410 }
411 memset(d, 0, sizeof(struct macepci_data));
412
413 d->pci_data = bus_pci_init(machine, pciirq, 0,0, 0,0,0, 0,0,0);
414
415 memory_device_register(mem, "macepci", baseaddr, DEV_MACEPCI_LENGTH,
416 dev_macepci_access, (void *)d, DM_DEFAULT, NULL);
417
418 return d->pci_data;
419 }
420
421
422 /****************************************************************************/
423
424
425 /*
426 * SGI "mec" ethernet. Used in SGI-IP32.
427 *
428 * Study http://www.openbsd.org/cgi-bin/cvsweb/src/sys/arch/sgi/dev/if_mec.c
429 * and/or NetBSD. TODO:
430 *
431 * x) tx and rx interrupts/ring/slot stuff
432 */
433
434 #define MEC_TICK_SHIFT 14
435
436 #define MAX_TX_PACKET_LEN 1700
437 #define N_RX_ADDRESSES 16
438
439 struct sgi_mec_data {
440 uint64_t reg[DEV_SGI_MEC_LENGTH / sizeof(uint64_t)];
441
442 int irq_nr;
443 unsigned char macaddr[6];
444
445 unsigned char cur_tx_packet[MAX_TX_PACKET_LEN];
446 int cur_tx_packet_len;
447
448 unsigned char *cur_rx_packet;
449 int cur_rx_packet_len;
450
451 uint64_t rx_addr[N_RX_ADDRESSES];
452 int cur_rx_addr_index_write;
453 int cur_rx_addr_index;
454 };
455
456
457 /*
458 * mec_reset():
459 */
460 static void mec_reset(struct sgi_mec_data *d)
461 {
462 if (d->cur_rx_packet != NULL)
463 free(d->cur_rx_packet);
464
465 memset(d->reg, 0, sizeof(d->reg));
466 }
467
468
469 /*
470 * mec_control_write():
471 */
472 static void mec_control_write(struct cpu *cpu, struct sgi_mec_data *d,
473 uint64_t x)
474 {
475 if (x & MEC_MAC_CORE_RESET) {
476 debug("[ sgi_mec: CORE RESET ]\n");
477 mec_reset(d);
478 }
479 }
480
481
482 /*
483 * mec_try_rx():
484 */
485 static int mec_try_rx(struct cpu *cpu, struct sgi_mec_data *d)
486 {
487 uint64_t base;
488 unsigned char data[8];
489 int i, res, retval = 0;
490
491 base = d->rx_addr[d->cur_rx_addr_index];
492 if (base & 0xfff)
493 fatal("[ mec_try_rx(): WARNING! lowest bits of base are "
494 "non-zero (0x%3x). TODO ]\n", (int)(base & 0xfff));
495 base &= 0xfffff000ULL;
496 if (base == 0)
497 goto skip;
498
499 /* printf("rx base = 0x%016llx\n", (long long)base); */
500
501 /* Read an rx descriptor from memory: */
502 res = cpu->memory_rw(cpu, cpu->mem, base,
503 &data[0], sizeof(data), MEM_READ, PHYSICAL);
504 if (!res)
505 return 0;
506
507 #if 0
508 printf("{ mec: rxdesc %i: ", d->cur_rx_addr_index);
509 for (i=0; i<sizeof(data); i++) {
510 if ((i & 3) == 0)
511 printf(" ");
512 printf("%02x", data[i]);
513 }
514 printf(" }\n");
515 #endif
516
517 /* Is this descriptor already in use? */
518 if (data[0] & 0x80) {
519 /* printf("INTERRUPT for base = 0x%x\n", (int)base); */
520 goto skip_and_advance;
521 }
522
523 if (d->cur_rx_packet == NULL &&
524 net_ethernet_rx_avail(cpu->machine->emul->net, d))
525 net_ethernet_rx(cpu->machine->emul->net, d,
526 &d->cur_rx_packet, &d->cur_rx_packet_len);
527
528 if (d->cur_rx_packet == NULL)
529 goto skip;
530
531 /* Copy the packet data: */
532 /* printf("RX: "); */
533 for (i=0; i<d->cur_rx_packet_len; i++) {
534 res = cpu->memory_rw(cpu, cpu->mem, base + 32 + i + 2,
535 d->cur_rx_packet + i, 1, MEM_WRITE, PHYSICAL);
536 /* printf(" %02x", d->cur_rx_packet[i]); */
537 }
538 /* printf("\n"); */
539
540 #if 0
541 printf("RX: %i bytes, index %i, base = 0x%x\n",
542 d->cur_rx_packet_len, d->cur_rx_addr_index, (int)base);
543 #endif
544
545 /* 4 bytes of CRC at the end. Hm. TODO */
546 d->cur_rx_packet_len += 4;
547
548 memset(data, 0, sizeof(data));
549 data[6] = (d->cur_rx_packet_len >> 8) & 255;
550 data[7] = d->cur_rx_packet_len & 255;
551 /* TODO: lots of bits :-) */
552 data[4] = 0x04; /* match MAC */
553 data[0] = 0x80; /* 0x80 = received. */
554 res = cpu->memory_rw(cpu, cpu->mem, base,
555 &data[0], sizeof(data), MEM_WRITE, PHYSICAL);
556
557 /* Free the packet from memory: */
558 free(d->cur_rx_packet);
559 d->cur_rx_packet = NULL;
560
561 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] |= MEC_INT_RX_THRESHOLD;
562 skip_and_advance:
563 d->cur_rx_addr_index ++;
564 d->cur_rx_addr_index %= N_RX_ADDRESSES;
565 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] &= ~MEC_INT_RX_MCL_FIFO_ALIAS;
566 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] |=
567 (d->cur_rx_addr_index & 0x1f) << 8;
568 retval = 1;
569
570 skip:
571 return retval;
572 }
573
574
575 /*
576 * mec_try_tx():
577 */
578 static int mec_try_tx(struct cpu *cpu, struct sgi_mec_data *d)
579 {
580 uint64_t base, addr, dma_base;
581 int tx_ring_ptr, ringread, ringwrite, res, i, j;
582 unsigned char data[32];
583 int len, start_offset, dma_ptr_nr, dma_len;
584
585 base = d->reg[MEC_TX_RING_BASE / sizeof(uint64_t)];
586 tx_ring_ptr = d->reg[MEC_TX_RING_PTR / sizeof(uint64_t)];
587
588 if (base == 0)
589 return 0;
590
591 /* printf("base = 0x%016llx\n", base); */
592
593 ringread = tx_ring_ptr & MEC_TX_RING_READ_PTR;
594 ringwrite = tx_ring_ptr & MEC_TX_RING_WRITE_PTR;
595 ringread >>= 16;
596 /* All done? Then abort. */
597 if (ringread == ringwrite)
598 return 0;
599
600 tx_ring_ptr &= MEC_TX_RING_READ_PTR;
601 tx_ring_ptr >>= 16;
602
603 /* Each tx descriptor (+ buffer) is 128 bytes: */
604 addr = base + tx_ring_ptr*128;
605 res = cpu->memory_rw(cpu, cpu->mem, addr,
606 &data[0], sizeof(data), MEM_READ, PHYSICAL);
607 if (!res)
608 return 0;
609
610 /* Is this packet transmitted already? */
611 if (data[0] & 0x80) {
612 fatal("[ mec_try_tx: tx_ring_ptr = %i, already"
613 " transmitted? ]\n", tx_ring_ptr);
614 goto advance_tx;
615 }
616
617 len = data[6] * 256 + data[7];
618 start_offset = data[5] & 0x7f;
619
620 /* Is this packet empty? Then don't transmit. */
621 if (len == 0)
622 return 0;
623
624 /* Hm. Is len one too little? TODO */
625 len ++;
626
627 #if 0
628 printf("{ mec: txdesc %i: ", tx_ring_ptr);
629 for (i=0; i<sizeof(data); i++) {
630 if ((i & 3) == 0)
631 printf(" ");
632 printf("%02x", data[i]);
633 }
634 printf(" }\n");
635 #endif
636 dma_ptr_nr = 0;
637
638 j = 0;
639 d->cur_tx_packet_len = len;
640
641 for (i=start_offset; i<start_offset+len; i++) {
642 unsigned char ch;
643
644 if ((i & 0x7f) == 0x00)
645 break;
646
647 res = cpu->memory_rw(cpu, cpu->mem, addr + i,
648 &ch, sizeof(ch), MEM_READ, PHYSICAL);
649 /* printf(" %02x", ch); */
650
651 d->cur_tx_packet[j++] = ch;
652 if (j >= MAX_TX_PACKET_LEN) {
653 fatal("[ mec_try_tx: packet too large? ]\n");
654 break;
655 }
656 }
657 /* printf("\n"); */
658
659 if (j < len) {
660 /* Continue with DMA: */
661 for (;;) {
662 dma_ptr_nr ++;
663 if (dma_ptr_nr >= 4)
664 break;
665 if (!(data[4] & (0x01 << dma_ptr_nr)))
666 break;
667 dma_base = (data[dma_ptr_nr * 8 + 4] << 24)
668 + (data[dma_ptr_nr * 8 + 5] << 16)
669 + (data[dma_ptr_nr * 8 + 6] << 8)
670 + (data[dma_ptr_nr * 8 + 7]);
671 dma_base &= 0xfffffff8ULL;
672 dma_len = (data[dma_ptr_nr * 8 + 2] << 8)
673 + (data[dma_ptr_nr * 8 + 3]) + 1;
674
675 /* printf("dma_base = %08x, dma_len = %i\n",
676 (int)dma_base, dma_len); */
677
678 while (dma_len > 0) {
679 unsigned char ch;
680 res = cpu->memory_rw(cpu, cpu->mem, dma_base,
681 &ch, sizeof(ch), MEM_READ, PHYSICAL);
682 /* printf(" %02x", ch); */
683
684 d->cur_tx_packet[j++] = ch;
685 if (j >= MAX_TX_PACKET_LEN) {
686 fatal("[ mec_try_tx: packet too large?"
687 " ]\n");
688 break;
689 }
690 dma_base ++;
691 dma_len --;
692 }
693 }
694 }
695
696 if (j < len)
697 fatal("[ mec_try_tx: not enough data? ]\n");
698
699 net_ethernet_tx(cpu->machine->emul->net, d,
700 d->cur_tx_packet, d->cur_tx_packet_len);
701
702 /* see openbsd's if_mec.c for details */
703 if (data[4] & 0x01) {
704 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] |=
705 MEC_INT_TX_PACKET_SENT;
706 }
707 memset(data, 0, 6); /* last 2 bytes are len */
708 data[0] = 0x80;
709 data[5] = 0x80;
710
711 res = cpu->memory_rw(cpu, cpu->mem, addr,
712 &data[0], sizeof(data), MEM_WRITE, PHYSICAL);
713
714 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] |= MEC_INT_TX_EMPTY;
715 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] |= MEC_INT_TX_PACKET_SENT;
716
717 advance_tx:
718 /* Advance the ring Read ptr. */
719 tx_ring_ptr = d->reg[MEC_TX_RING_PTR / sizeof(uint64_t)];
720 ringread = tx_ring_ptr & MEC_TX_RING_READ_PTR;
721 ringwrite = tx_ring_ptr & MEC_TX_RING_WRITE_PTR;
722
723 ringread = (ringread >> 16) + 1;
724 ringread &= 63;
725 ringread <<= 16;
726
727 d->reg[MEC_TX_RING_PTR / sizeof(uint64_t)] =
728 (ringwrite & MEC_TX_RING_WRITE_PTR) |
729 (ringread & MEC_TX_RING_READ_PTR);
730
731 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] &=
732 ~MEC_INT_TX_RING_BUFFER_ALIAS;
733 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] |=
734 (d->reg[MEC_TX_RING_PTR / sizeof(uint64_t)] &
735 MEC_INT_TX_RING_BUFFER_ALIAS);
736
737 return 1;
738 }
739
740
741 /*
742 * dev_sgi_mec_tick():
743 */
744 void dev_sgi_mec_tick(struct cpu *cpu, void *extra)
745 {
746 struct sgi_mec_data *d = (struct sgi_mec_data *) extra;
747 int n = 0;
748
749 while (mec_try_tx(cpu, d))
750 ;
751
752 while (mec_try_rx(cpu, d) && n < 16)
753 n++;
754
755 /* Interrupts: (TODO: only when enabled) */
756 if (d->reg[MEC_INT_STATUS / sizeof(uint64_t)] & MEC_INT_STATUS_MASK) {
757 #if 0
758 printf("[%02x]", (int)(d->reg[MEC_INT_STATUS /
759 sizeof(uint64_t)] & MEC_INT_STATUS_MASK));
760 fflush(stdout);
761 #endif
762 cpu_interrupt(cpu, d->irq_nr);
763 } else
764 cpu_interrupt_ack(cpu, d->irq_nr);
765 }
766
767
768 /*
769 * dev_sgi_mec_access():
770 */
771 DEVICE_ACCESS(sgi_mec)
772 {
773 struct sgi_mec_data *d = (struct sgi_mec_data *) extra;
774 uint64_t idata = 0, odata = 0;
775 int regnr;
776
777 if (writeflag == MEM_WRITE)
778 idata = memory_readmax64(cpu, data, len);
779
780 regnr = relative_addr / sizeof(uint64_t);
781
782 /* Treat most registers as read/write, by default. */
783 if (writeflag == MEM_WRITE) {
784 switch (relative_addr) {
785 case MEC_INT_STATUS: /* 0x08 */
786 /* Clear bits on write: (This is just a guess) */
787 d->reg[regnr] = (d->reg[regnr] & ~0xff)
788 | ((d->reg[regnr] & ~idata) & 0xff);
789 break;
790 case MEC_TX_RING_PTR: /* 0x30 */
791 idata &= MEC_TX_RING_WRITE_PTR;
792 d->reg[regnr] = (d->reg[regnr] &
793 ~MEC_TX_RING_WRITE_PTR) | idata;
794 /* TODO */
795 break;
796 default:
797 d->reg[regnr] = idata;
798 }
799 } else
800 odata = d->reg[regnr];
801
802 switch (relative_addr) {
803 case MEC_MAC_CONTROL: /* 0x00 */
804 if (writeflag)
805 mec_control_write(cpu, d, idata);
806 else {
807 /* Fake "revision 1": */
808 odata &= ~MEC_MAC_REVISION;
809 odata |= 1 << MEC_MAC_REVISION_SHIFT;
810 }
811 break;
812 case MEC_INT_STATUS: /* 0x08 */
813 if (writeflag)
814 debug("[ sgi_mec: write to MEC_INT_STATUS: "
815 "0x%016llx ]\n", (long long)idata);
816 break;
817 case MEC_DMA_CONTROL: /* 0x10 */
818 if (writeflag) {
819 debug("[ sgi_mec: write to MEC_DMA_CONTROL: "
820 "0x%016llx ]\n", (long long)idata);
821 if (!(idata & MEC_DMA_TX_INT_ENABLE)) {
822 /* This should apparently stop the
823 TX Empty interrupt. */
824 d->reg[MEC_INT_STATUS / sizeof(uint64_t)] &=
825 ~MEC_INT_TX_EMPTY;
826 }
827 }
828 break;
829 case MEC_TX_ALIAS: /* 0x20 */
830 if (writeflag) {
831 debug("[ sgi_mec: write to MEC_TX_ALIAS: "
832 "0x%016llx ]\n", (long long)idata);
833 } else {
834 debug("[ sgi_mec: read from MEC_TX_ALIAS: "
835 "0x%016llx ]\n", (long long)idata);
836 odata = d->reg[MEC_TX_RING_PTR / sizeof(uint64_t)];
837 }
838 break;
839 case MEC_RX_ALIAS: /* 0x28 */
840 if (writeflag)
841 debug("[ sgi_mec: write to MEC_RX_ALIAS: "
842 "0x%016llx ]\n", (long long)idata);
843 break;
844 case MEC_TX_RING_PTR: /* 0x30 */
845 if (writeflag)
846 debug("[ sgi_mec: write to MEC_TX_RING_PTR: "
847 "0x%016llx ]\n", (long long)idata);
848 break;
849 case MEC_PHY_DATA: /* 0x64 */
850 if (writeflag)
851 fatal("[ sgi_mec: write to MEC_PHY_DATA: "
852 "0x%016llx ]\n", (long long)idata);
853 else
854 odata = 0; /* ? */
855 break;
856 case MEC_PHY_ADDRESS: /* 0x6c */
857 if (writeflag)
858 debug("[ sgi_mec: write to MEC_PHY_ADDRESS: "
859 "0x%016llx ]\n", (long long)idata);
860 break;
861 case MEC_PHY_READ_INITIATE: /* 0x70 */
862 if (writeflag)
863 debug("[ sgi_mec: write to MEC_PHY_READ_INITIATE: "
864 "0x%016llx ]\n", (long long)idata);
865 break;
866 case 0x74:
867 if (writeflag)
868 debug("[ sgi_mec: write to 0x74: 0x%016llx ]\n",
869 (long long)idata);
870 else
871 debug("[ sgi_mec: read from 0x74 ]\n");
872 break;
873 case MEC_STATION: /* 0xa0 */
874 if (writeflag)
875 debug("[ sgi_mec: setting the MAC address to "
876 "%02x:%02x:%02x:%02x:%02x:%02x ]\n",
877 (idata >> 40) & 255, (idata >> 32) & 255,
878 (idata >> 24) & 255, (idata >> 16) & 255,
879 (idata >> 8) & 255, (idata >> 0) & 255);
880 break;
881 case MEC_STATION_ALT: /* 0xa8 */
882 if (writeflag)
883 debug("[ sgi_mec: setting the ALTERNATIVE MAC address"
884 " to %02x:%02x:%02x:%02x:%02x:%02x ]\n",
885 (idata >> 40) & 255, (idata >> 32) & 255,
886 (idata >> 24) & 255, (idata >> 16) & 255,
887 (idata >> 8) & 255, (idata >> 0) & 255);
888 break;
889 case MEC_MULTICAST: /* 0xb0 */
890 if (writeflag)
891 debug("[ sgi_mec: write to MEC_MULTICAST: "
892 "0x%016llx ]\n", (long long)idata);
893 break;
894 case MEC_TX_RING_BASE: /* 0xb8 */
895 if (writeflag)
896 debug("[ sgi_mec: write to MEC_TX_RING_BASE: "
897 "0x%016llx ]\n", (long long)idata);
898 break;
899 case MEC_MCL_RX_FIFO: /* 0x100 */
900 if (writeflag) {
901 debug("[ sgi_mec: write to MEC_MCL_RX_FIFO: 0x"
902 "%016llx ]\n", (long long)idata);
903 d->rx_addr[d->cur_rx_addr_index_write] = idata;
904 d->cur_rx_addr_index_write ++;
905 d->cur_rx_addr_index_write %= N_RX_ADDRESSES;
906 }
907 break;
908 default:
909 if (writeflag == MEM_WRITE)
910 fatal("[ sgi_mec: unimplemented write to address"
911 " 0x%llx, data=0x%016llx ]\n",
912 (long long)relative_addr, (long long)idata);
913 else
914 fatal("[ sgi_mec: unimplemented read from address"
915 " 0x%llx ]\n", (long long)relative_addr);
916 }
917
918 if (writeflag == MEM_READ)
919 memory_writemax64(cpu, data, len, odata);
920
921 dev_sgi_mec_tick(cpu, extra);
922
923 return 1;
924 }
925
926
927 /*
928 * dev_sgi_mec_init():
929 */
930 void dev_sgi_mec_init(struct machine *machine, struct memory *mem,
931 uint64_t baseaddr, int irq_nr, unsigned char *macaddr)
932 {
933 char *name2;
934 size_t nlen = 55;
935 struct sgi_mec_data *d = malloc(sizeof(struct sgi_mec_data));
936
937 if (d == NULL) {
938 fprintf(stderr, "out of memory\n");
939 exit(1);
940 }
941 memset(d, 0, sizeof(struct sgi_mec_data));
942 d->irq_nr = irq_nr;
943 memcpy(d->macaddr, macaddr, 6);
944
945 mec_reset(d);
946
947 name2 = malloc(nlen);
948 if (name2 == NULL) {
949 fprintf(stderr, "out of memory in dev_sgi_mec_init()\n");
950 exit(1);
951 }
952 snprintf(name2, nlen, "mec [%02x:%02x:%02x:%02x:%02x:%02x]",
953 d->macaddr[0], d->macaddr[1], d->macaddr[2],
954 d->macaddr[3], d->macaddr[4], d->macaddr[5]);
955
956 memory_device_register(mem, name2, baseaddr,
957 DEV_SGI_MEC_LENGTH, dev_sgi_mec_access, (void *)d,
958 DM_DEFAULT, NULL);
959
960 machine_add_tickfunction(machine, dev_sgi_mec_tick, d,
961 MEC_TICK_SHIFT, 0.0);
962
963 net_add_nic(machine->emul->net, d, macaddr);
964 }
965
966
967 /****************************************************************************/
968
969
970 struct sgi_ust_data {
971 uint64_t reg[DEV_SGI_UST_LENGTH / sizeof(uint64_t)];
972 };
973
974
975 /*
976 * dev_sgi_ust_access():
977 */
978 DEVICE_ACCESS(sgi_ust)
979 {
980 struct sgi_ust_data *d = (struct sgi_ust_data *) extra;
981 uint64_t idata = 0, odata = 0;
982 int regnr;
983
984 idata = memory_readmax64(cpu, data, len);
985 regnr = relative_addr / sizeof(uint64_t);
986
987 /* Treat all registers as read/write, by default. */
988 if (writeflag == MEM_WRITE)
989 d->reg[regnr] = idata;
990 else
991 odata = d->reg[regnr];
992
993 switch (relative_addr) {
994 case 0:
995 d->reg[regnr] += 0x2710;
996 break;
997 default:
998 if (writeflag == MEM_WRITE)
999 debug("[ sgi_ust: unimplemented write to "
1000 "address 0x%llx, data=0x%016llx ]\n",
1001 (long long)relative_addr, (long long)idata);
1002 else
1003 debug("[ sgi_ust: unimplemented read from address"
1004 " 0x%llx ]\n", (long long)relative_addr);
1005 }
1006
1007 if (writeflag == MEM_READ)
1008 memory_writemax64(cpu, data, len, odata);
1009
1010 return 1;
1011 }
1012
1013
1014 /*
1015 * dev_sgi_ust_init():
1016 */
1017 void dev_sgi_ust_init(struct memory *mem, uint64_t baseaddr)
1018 {
1019 struct sgi_ust_data *d = malloc(sizeof(struct sgi_ust_data));
1020 if (d == NULL) {
1021 fprintf(stderr, "out of memory\n");
1022 exit(1);
1023 }
1024 memset(d, 0, sizeof(struct sgi_ust_data));
1025
1026 memory_device_register(mem, "sgi_ust", baseaddr,
1027 DEV_SGI_UST_LENGTH, dev_sgi_ust_access, (void *)d,
1028 DM_DEFAULT, NULL);
1029 }
1030
1031
1032 /****************************************************************************/
1033
1034
1035 /*
1036 * SGI "mte". This device seems to be an accelerator for copying/clearing
1037 * memory. Used by (at least) the SGI O2 PROM.
1038 *
1039 * Actually, it seems to be used for graphics output as well. (?)
1040 * The O2's PROM uses it to output graphics.
1041 */
1042 /* #define debug fatal */
1043 /* #define MTE_DEBUG */
1044 #define ZERO_CHUNK_LEN 4096
1045
1046 struct sgi_mte_data {
1047 uint32_t reg[DEV_SGI_MTE_LENGTH / sizeof(uint32_t)];
1048 };
1049
1050 /*
1051 * dev_sgi_mte_access():
1052 */
1053 DEVICE_ACCESS(sgi_mte)
1054 {
1055 struct sgi_mte_data *d = (struct sgi_mte_data *) extra;
1056 uint64_t first_addr, last_addr, zerobuflen, fill_addr, fill_len;
1057 unsigned char zerobuf[ZERO_CHUNK_LEN];
1058 uint64_t idata = 0, odata = 0;
1059 int regnr;
1060
1061 idata = memory_readmax64(cpu, data, len);
1062 regnr = relative_addr / sizeof(uint32_t);
1063
1064 /*
1065 * Treat all registers as read/write, by default. Sometimes these
1066 * are accessed as 32-bit words, sometimes as 64-bit words.
1067 */
1068 if (len != 4) {
1069 if (writeflag == MEM_WRITE) {
1070 d->reg[regnr] = idata >> 32;
1071 d->reg[regnr+1] = idata;
1072 } else
1073 odata = ((uint64_t)d->reg[regnr] << 32) +
1074 d->reg[regnr+1];
1075 }
1076
1077 if (writeflag == MEM_WRITE)
1078 d->reg[regnr] = idata;
1079 else
1080 odata = d->reg[regnr];
1081
1082 #ifdef MTE_DEBUG
1083 if (writeflag == MEM_WRITE && relative_addr >= 0x2000 &&
1084 relative_addr < 0x3000)
1085 fatal("[ MTE: 0x%08x: 0x%016llx ]\n", (int)relative_addr,
1086 (long long)idata);
1087 #endif
1088
1089 /*
1090 * I've not found any docs about this 'mte' device at all, so this is
1091 * just a guess. The mte seems to be used for copying and zeroing
1092 * chunks of memory.
1093 *
1094 * write to 0x3030, data=0x00000000003da000 ] <-- first address
1095 * write to 0x3038, data=0x00000000003f9fff ] <-- last address
1096 * write to 0x3018, data=0x0000000000000000 ] <-- what to fill?
1097 * write to 0x3008, data=0x00000000ffffffff ] <-- ?
1098 * write to 0x3800, data=0x0000000000000011 ] <-- operation
1099 * (0x11 = zerofill)
1100 *
1101 * write to 0x1700, data=0x80001ea080001ea1 <-- also containing the
1102 * write to 0x1708, data=0x80001ea280001ea3 address to fill (?)
1103 * write to 0x1710, data=0x80001ea480001ea5
1104 * ...
1105 * write to 0x1770, data=0x80001e9c80001e9d
1106 * write to 0x1778, data=0x80001e9e80001e9f
1107 */
1108 switch (relative_addr) {
1109
1110 /* No warnings for these: */
1111 case 0x3030:
1112 case 0x3038:
1113 break;
1114
1115 /* Unknown, but no warning: */
1116 case 0x4000:
1117 case 0x3018:
1118 case 0x3008:
1119 case 0x1700:
1120 case 0x1708:
1121 case 0x1710:
1122 case 0x1718:
1123 case 0x1720:
1124 case 0x1728:
1125 case 0x1730:
1126 case 0x1738:
1127 case 0x1740:
1128 case 0x1748:
1129 case 0x1750:
1130 case 0x1758:
1131 case 0x1760:
1132 case 0x1768:
1133 case 0x1770:
1134 case 0x1778:
1135 break;
1136
1137 /* Graphics stuff? No warning: */
1138 case 0x2018:
1139 case 0x2060:
1140 case 0x2070:
1141 case 0x2074:
1142 case 0x20c0:
1143 case 0x20c4:
1144 case 0x20d0:
1145 case 0x21b0:
1146 case 0x21b8:
1147 break;
1148
1149 /* Perform graphics operation: */
1150 case 0x21f8:
1151 {
1152 uint32_t op = d->reg[0x2060 / sizeof(uint32_t)];
1153 uint32_t color = d->reg[0x20d0 / sizeof(uint32_t)]&255;
1154 uint32_t x1 = (d->reg[0x2070 / sizeof(uint32_t)]
1155 >> 16) & 0xfff;
1156 uint32_t y1 = d->reg[0x2070 / sizeof(uint32_t)]& 0xfff;
1157 uint32_t x2 = (d->reg[0x2074 / sizeof(uint32_t)]
1158 >> 16) & 0xfff;
1159 uint32_t y2 = d->reg[0x2074 / sizeof(uint32_t)]& 0xfff;
1160 uint32_t y;
1161
1162 op >>= 24;
1163
1164 switch (op) {
1165 case 1: /* Unknown. Used after drawing bitmaps? */
1166 break;
1167 case 3: /* Fill: */
1168 if (x2 < x1) {
1169 int tmp = x1; x1 = x2; x2 = tmp;
1170 }
1171 if (y2 < y1) {
1172 int tmp = y1; y1 = y2; y2 = tmp;
1173 }
1174 for (y=y1; y<=y2; y++) {
1175 unsigned char buf[1280];
1176 int length = x2-x1+1;
1177 int addr = (x1 + y*1280);
1178 if (length < 1)
1179 length = 1;
1180 memset(buf, color, length);
1181 if (x1 < 1280 && y < 1024)
1182 cpu->memory_rw(cpu, cpu->mem,
1183 0x38000000 + addr, buf,
1184 length, MEM_WRITE,
1185 NO_EXCEPTIONS | PHYSICAL);
1186 }
1187 break;
1188
1189 default:fatal("\n--- MTE OP %i color 0x%02x: %i,%i - "
1190 "%i,%i\n\n", op, color, x1,y1, x2,y2);
1191 }
1192 }
1193 break;
1194
1195 case 0x29f0:
1196 /* Pixel output: */
1197 {
1198 uint32_t data = d->reg[0x20c4 / sizeof(uint32_t)];
1199 uint32_t color = d->reg[0x20d0 / sizeof(uint32_t)]&255;
1200 uint32_t x1 = (d->reg[0x2070 / sizeof(uint32_t)]
1201 >> 16) & 0xfff;
1202 uint32_t y1 = d->reg[0x2070 / sizeof(uint32_t)]& 0xfff;
1203 uint32_t x2 = (d->reg[0x2074 / sizeof(uint32_t)]
1204 >> 16) & 0xfff;
1205 uint32_t y2 = d->reg[0x2074 / sizeof(uint32_t)]& 0xfff;
1206 size_t x, y;
1207
1208 if (x2 < x1) {
1209 int tmp = x1; x1 = x2; x2 = tmp;
1210 }
1211 if (y2 < y1) {
1212 int tmp = y1; y1 = y2; y2 = tmp;
1213 }
1214 if (x2-x1 <= 15)
1215 data <<= 16;
1216
1217 x=x1; y=y1;
1218 while (x <= x2 && y <= y2) {
1219 unsigned char buf = color;
1220 int addr = x + y*1280;
1221 int bit_set = data & 0x80000000UL;
1222 data <<= 1;
1223 if (x < 1280 && y < 1024 && bit_set)
1224 cpu->memory_rw(cpu, cpu->mem,
1225 0x38000000 + addr, &buf,1,MEM_WRITE,
1226 NO_EXCEPTIONS | PHYSICAL);
1227 x++;
1228 if (x > x2) {
1229 x = x1;
1230 y++;
1231 }
1232 }
1233 }
1234 break;
1235
1236
1237 /* Operations: */
1238 case 0x3800:
1239 if (writeflag == MEM_WRITE) {
1240 switch (idata) {
1241 case 0x11: /* zerofill */
1242 first_addr = d->reg[0x3030 / sizeof(uint32_t)];
1243 last_addr = d->reg[0x3038 / sizeof(uint32_t)];
1244 zerobuflen = last_addr - first_addr + 1;
1245 debug("[ sgi_mte: zerofill: first = 0x%016llx,"
1246 " last = 0x%016llx, length = 0x%llx ]\n",
1247 (long long)first_addr, (long long)
1248 last_addr, (long long)zerobuflen);
1249
1250 /* TODO: is there a better way to
1251 implement this? */
1252 memset(zerobuf, 0, sizeof(zerobuf));
1253 fill_addr = first_addr;
1254 while (zerobuflen != 0) {
1255 if (zerobuflen > sizeof(zerobuf))
1256 fill_len = sizeof(zerobuf);
1257 else
1258 fill_len = zerobuflen;
1259 cpu->memory_rw(cpu, mem, fill_addr,
1260 zerobuf, fill_len, MEM_WRITE,
1261 NO_EXCEPTIONS | PHYSICAL);
1262 fill_addr += fill_len;
1263 zerobuflen -= sizeof(zerobuf);
1264 }
1265
1266 break;
1267 default:
1268 fatal("[ sgi_mte: UNKNOWN operation "
1269 "0x%x ]\n", idata);
1270 }
1271 }
1272 break;
1273 default:
1274 if (writeflag == MEM_WRITE)
1275 debug("[ sgi_mte: unimplemented write to "
1276 "address 0x%llx, data=0x%016llx ]\n",
1277 (long long)relative_addr, (long long)idata);
1278 else
1279 debug("[ sgi_mte: unimplemented read from address"
1280 " 0x%llx ]\n", (long long)relative_addr);
1281 }
1282
1283 if (writeflag == MEM_READ)
1284 memory_writemax64(cpu, data, len, odata);
1285
1286 return 1;
1287 }
1288
1289
1290 /*
1291 * dev_sgi_mte_init():
1292 */
1293 void dev_sgi_mte_init(struct memory *mem, uint64_t baseaddr)
1294 {
1295 struct sgi_mte_data *d = malloc(sizeof(struct sgi_mte_data));
1296 if (d == NULL) {
1297 fprintf(stderr, "out of memory\n");
1298 exit(1);
1299 }
1300 memset(d, 0, sizeof(struct sgi_mte_data));
1301
1302 memory_device_register(mem, "sgi_mte", baseaddr, DEV_SGI_MTE_LENGTH,
1303 dev_sgi_mte_access, (void *)d, DM_DEFAULT, NULL);
1304 }
1305

  ViewVC Help
Powered by ViewVC 1.1.26