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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (show annotations)
Mon Oct 8 16:18:00 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 14709 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.707 2005/04/27 16:37:33 debug Exp $
20050408	Some minor updates to the wdc. Linux now doesn't complain
		anymore if a disk is non-present.
20050409	Various minor fixes (a bintrans bug, and some other things).
		The wdc seems to work with Playstation2 emulation, but there
		is a _long_ annoying delay when disks are detected.
		Fixing a really important bintrans bug (when devices and RAM
		are mixed within 4KB pages), which was triggered with
		NetBSD/playstation2 kernels.
20050410	Adding a dummy dev_ps2_ether (just so that NetBSD doesn't
		complain as much during bootup).
		Symbols starting with '$' are now ignored.
		Renaming dev_ps2_ohci.c to dev_ohci.c, etc.
20050411	Moving the bintrans-cache-isolation check from cpu_mips.c to
		cpu_mips_coproc.c. (I thought this would give a speedup, but
		it's not noticable.)
		Better playstation2 sbus interrupt code.
		Skip ahead many ticks if the count register is read manually.
		(This increases the speed of delay-loops that simply read
		the count register.)
20050412	Updates to the playstation2 timer/interrupt code.
		Some other minor updates.
20050413	NetBSD/cobalt runs from a disk image :-) including userland;
		updating the documentation on how to install NetBSD/cobalt
		using NetBSD/pmax (!).
		Some minor bintrans updates (no real speed improvement) and
		other minor updates (playstation2 now uses the -o options).
20050414	Adding a dummy x86 (and AMD64) mode.
20050415	Adding some (32-bit and 16-bit) x86 instructions.
		Adding some initial support for non-SCSI, non-IDE floppy
		images. (The x86 mode can boot from these, more or less.)
		Moving the devices/ and include/ directories to src/devices/
		and src/include/, respectively.
20050416	Continuing on the x86 stuff. (Adding pc_bios.c and some simple
		support for software interrupts in 16-bit mode.)
20050417	Ripping out most of the x86 instruction decoding stuff, trying
		to rewrite it in a cleaner way.
		Disabling some of the least working CPU families in the
		configure script (sparc, x86, alpha, hppa), so that they are
		not enabled by default.
20050418	Trying to fix the bug which caused problems when turning on
		and off bintrans interactively, by flushing the bintrans cache
		whenever bintrans is manually (re)enabled.
20050419	Adding the 'lswi' ppc instruction.
		Minor updates to the x86 instruction decoding.
20050420	Renaming x86 register name indices from R_xx to X86_R_xx (this
		makes building on Tru64 nicer).
20050422	Adding a check for duplicate MIPS TLB entries on tlbwr/tlbwi.
20050427	Adding screenshots to guestoses.html.
		Some minor fixes and testing for the next release.

==============  RELEASE 0.3.2  ==============


1 /*
2 * Copyright (C) 2003-2005 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: memory_rw.c,v 1.16 2005/04/19 01:24:35 debug Exp $
29 *
30 * Generic memory_rw(), with special hacks for specific CPU families.
31 *
32 * Example for inclusion from memory_mips.c:
33 *
34 * MEMORY_RW should be mips_memory_rw
35 * MEM_MIPS should be defined
36 */
37
38
39 /*
40 * memory_rw():
41 *
42 * Read or write data from/to memory.
43 *
44 * cpu the cpu doing the read/write
45 * mem the memory object to use
46 * vaddr the virtual address
47 * data a pointer to the data to be written to memory, or
48 * a placeholder for data when reading from memory
49 * len the length of the 'data' buffer
50 * writeflag set to MEM_READ or MEM_WRITE
51 * cache_flags CACHE_{NONE,DATA,INSTRUCTION} | other flags
52 *
53 * If the address indicates access to a memory mapped device, that device'
54 * read/write access function is called.
55 *
56 * If instruction latency/delay support is enabled, then
57 * cpu->instruction_delay is increased by the number of instruction to
58 * delay execution.
59 *
60 * This function should not be called with cpu == NULL.
61 *
62 * Returns one of the following:
63 * MEMORY_ACCESS_FAILED
64 * MEMORY_ACCESS_OK
65 *
66 * (MEMORY_ACCESS_FAILED is 0.)
67 */
68 int MEMORY_RW(struct cpu *cpu, struct memory *mem, uint64_t vaddr,
69 unsigned char *data, size_t len, int writeflag, int cache_flags)
70 {
71 #ifndef MEM_USERLAND
72 int ok = 1;
73 #endif
74 uint64_t paddr;
75 int cache, no_exceptions, offset;
76 unsigned char *memblock;
77 #ifdef BINTRANS
78 int bintrans_cached = cpu->machine->bintrans_enable;
79 int bintrans_device_danger = 0;
80 #endif
81 no_exceptions = cache_flags & NO_EXCEPTIONS;
82 cache = cache_flags & CACHE_FLAGS_MASK;
83
84 #ifdef MEM_PPC
85 if (cpu->cd.ppc.bits == 32)
86 vaddr &= 0xffffffff;
87 #endif
88
89 #ifdef MEM_X86
90 if (cpu->cd.x86.bits == 32) {
91 if ((vaddr >> 32) == 0xffffffff)
92 vaddr &= 0xffffffff;
93
94 /* TODO: Actual address translation */
95 if ((vaddr >> 32) == 0) {
96 vaddr &= 0x0fffffff;
97
98 if (cpu->cd.x86.mode == 16) {
99 vaddr = (cpu->cd.x86.cursegment<<4) +
100 (vaddr & 0xffff);
101 /* TODO: A20 stuff */
102 if ((vaddr & 0xffff) + len > 0x10000) {
103 fatal("x86 memory access crossing"
104 " segment boundary: TODO\n");
105 cpu->running = 0;
106 return 0;
107 }
108 }
109 }
110 }
111 #endif
112
113 #ifdef MEM_URISC
114 {
115 uint64_t mask = (uint64_t) -1;
116 if (cpu->cd.urisc.wordlen < 64)
117 mask = ((int64_t)1 << cpu->cd.urisc.wordlen) - 1;
118 vaddr &= mask;
119 }
120 #endif
121
122 #ifdef MEM_MIPS
123 #ifdef BINTRANS
124 if (bintrans_cached) {
125 if (cache == CACHE_INSTRUCTION) {
126 cpu->cd.mips.pc_bintrans_host_4kpage = NULL;
127 cpu->cd.mips.pc_bintrans_paddr_valid = 0;
128 }
129 }
130 #endif
131 #endif /* MEM_MIPS */
132
133 #ifdef MEM_USERLAND
134 paddr = vaddr & 0x7fffffff;
135 goto have_paddr;
136 #endif
137
138 #ifndef MEM_USERLAND
139 #ifdef MEM_MIPS
140 /*
141 * For instruction fetch, are we on the same page as the last
142 * instruction we fetched?
143 *
144 * NOTE: There's no need to check this stuff here if this address
145 * is known to be in host ram, as it's done at instruction fetch
146 * time in cpu.c! Only check if _host_4k_page == NULL.
147 */
148 if (cache == CACHE_INSTRUCTION &&
149 cpu->cd.mips.pc_last_host_4k_page == NULL &&
150 (vaddr & ~0xfff) == cpu->cd.mips.pc_last_virtual_page) {
151 paddr = cpu->cd.mips.pc_last_physical_page | (vaddr & 0xfff);
152 goto have_paddr;
153 }
154 #endif /* MEM_MIPS */
155
156 if (cache_flags & PHYSICAL || cpu->translate_address == NULL) {
157 paddr = vaddr;
158 } else {
159 ok = cpu->translate_address(cpu, vaddr, &paddr,
160 (writeflag? FLAG_WRITEFLAG : 0) +
161 (no_exceptions? FLAG_NOEXCEPTIONS : 0)
162 + (cache==CACHE_INSTRUCTION? FLAG_INSTR : 0));
163 /* If the translation caused an exception, or was invalid in
164 some way, we simply return without doing the memory
165 access: */
166 if (!ok)
167 return MEMORY_ACCESS_FAILED;
168 }
169
170
171 #ifdef MEM_MIPS
172 /*
173 * If correct cache emulation is enabled, and we need to simluate
174 * cache misses even from the instruction cache, we can't run directly
175 * from a host page. :-/
176 */
177 #if defined(ENABLE_CACHE_EMULATION) && defined(ENABLE_INSTRUCTION_DELAYS)
178 #else
179 if (cache == CACHE_INSTRUCTION) {
180 cpu->cd.mips.pc_last_virtual_page = vaddr & ~0xfff;
181 cpu->cd.mips.pc_last_physical_page = paddr & ~0xfff;
182 cpu->cd.mips.pc_last_host_4k_page = NULL;
183
184 /* _last_host_4k_page will be set to 1 further down,
185 if the page is actually in host ram */
186 }
187 #endif
188 #endif /* MEM_MIPS */
189 #endif /* ifndef MEM_USERLAND */
190
191
192 #if defined(MEM_MIPS) || defined(MEM_USERLAND)
193 have_paddr:
194 #endif
195
196
197 #ifdef MEM_MIPS
198 /* TODO: How about bintrans vs cache emulation? */
199 #ifdef BINTRANS
200 if (bintrans_cached) {
201 if (cache == CACHE_INSTRUCTION) {
202 cpu->cd.mips.pc_bintrans_paddr_valid = 1;
203 cpu->cd.mips.pc_bintrans_paddr = paddr;
204 }
205 }
206 #endif
207 #endif /* MEM_MIPS */
208
209
210 if (!(cache_flags & PHYSICAL))
211 if (no_exceptions)
212 goto no_exception_access;
213
214
215 #ifndef MEM_USERLAND
216 /*
217 * Memory mapped device?
218 *
219 * TODO: this is utterly slow.
220 * TODO2: if paddr<base, but len enough, then we should write
221 * to a device to
222 */
223 if (paddr >= mem->mmap_dev_minaddr && paddr < mem->mmap_dev_maxaddr) {
224 #ifdef BINTRANS
225 uint64_t orig_paddr = paddr;
226 #endif
227 int i, start, res;
228
229 #ifdef BINTRANS
230 /*
231 * Really really slow, but unfortunately necessary. This is
232 * to avoid the folowing scenario:
233 *
234 * a) offsets 0x000..0x123 are normal memory
235 * b) offsets 0x124..0x777 are a device
236 *
237 * 1) a read is done from offset 0x100. the page is
238 * added to the bintrans system as a "RAM" page
239 * 2) a bintranslated read is done from offset 0x200,
240 * which should access the device, but since the
241 * entire page is added, it will access non-existant
242 * RAM instead, without warning.
243 *
244 * Setting bintrans_device_danger = 1 on accesses which are
245 * on _any_ offset on pages that are device mapped avoids
246 * this problem, but it is probably not very fast.
247 */
248 if (bintrans_cached) {
249 for (i=0; i<mem->n_mmapped_devices; i++)
250 if (paddr >= (mem->dev_baseaddr[i] & ~0xfff) &&
251 paddr <= ((mem->dev_baseaddr[i] +
252 mem->dev_length[i] - 1) | 0xfff)) {
253 bintrans_device_danger = 1;
254 break;
255 }
256 }
257 #endif
258
259 i = start = mem->last_accessed_device;
260
261 /* Scan through all devices: */
262 do {
263 if (paddr >= mem->dev_baseaddr[i] &&
264 paddr < mem->dev_baseaddr[i] + mem->dev_length[i]) {
265 /* Found a device, let's access it: */
266 mem->last_accessed_device = i;
267
268 paddr -= mem->dev_baseaddr[i];
269 if (paddr + len > mem->dev_length[i])
270 len = mem->dev_length[i] - paddr;
271
272 #ifdef BINTRANS
273 if (bintrans_cached && mem->dev_flags[i] &
274 MEM_BINTRANS_OK) {
275 int wf = writeflag == MEM_WRITE? 1 : 0;
276
277 if (writeflag) {
278 if (paddr < mem->
279 dev_bintrans_write_low[i])
280 mem->
281 dev_bintrans_write_low
282 [i] =
283 paddr & ~0xfff;
284 if (paddr > mem->
285 dev_bintrans_write_high[i])
286 mem->
287 dev_bintrans_write_high
288 [i] = paddr | 0xfff;
289 }
290
291 if (!(mem->dev_flags[i] &
292 MEM_BINTRANS_WRITE_OK))
293 wf = 0;
294
295 update_translation_table(cpu,
296 vaddr & ~0xfff,
297 mem->dev_bintrans_data[i] +
298 (paddr & ~0xfff),
299 wf, orig_paddr & ~0xfff);
300 }
301 #endif
302
303 res = mem->dev_f[i](cpu, mem, paddr, data, len,
304 writeflag, mem->dev_extra[i]);
305
306 #ifdef ENABLE_INSTRUCTION_DELAYS
307 if (res == 0)
308 res = -1;
309
310 cpu->cd.mips.instruction_delay +=
311 ( (abs(res) - 1) *
312 cpu->cd.mips.cpu_type.instrs_per_cycle );
313 #endif
314 /*
315 * If accessing the memory mapped device
316 * failed, then return with a DBE exception.
317 */
318 if (res <= 0) {
319 debug("%s device '%s' addr %08lx "
320 "failed\n", writeflag?
321 "writing to" : "reading from",
322 mem->dev_name[i], (long)paddr);
323 #ifdef MEM_MIPS
324 mips_cpu_exception(cpu, EXCEPTION_DBE,
325 0, vaddr, 0, 0, 0, 0);
326 #endif
327 return MEMORY_ACCESS_FAILED;
328 }
329
330 goto do_return_ok;
331 }
332
333 i ++;
334 if (i == mem->n_mmapped_devices)
335 i = 0;
336 } while (i != start);
337 }
338
339
340 #ifdef MEM_MIPS
341 /*
342 * Data and instruction cache emulation:
343 */
344
345 switch (cpu->cd.mips.cpu_type.mmu_model) {
346 case MMU3K:
347 /* if not uncached addess (TODO: generalize this) */
348 if (!(cache_flags & PHYSICAL) && cache != CACHE_NONE &&
349 !((vaddr & 0xffffffffULL) >= 0xa0000000ULL &&
350 (vaddr & 0xffffffffULL) <= 0xbfffffffULL)) {
351 if (memory_cache_R3000(cpu, cache, paddr,
352 writeflag, len, data))
353 goto do_return_ok;
354 }
355 break;
356 #if 0
357 /* Remove this, it doesn't work anyway */
358 case MMU10K:
359 /* other cpus: */
360 /*
361 * SUPER-UGLY HACK for SGI-IP32 PROM, R10000:
362 * K0 bits == 0x3 means uncached...
363 *
364 * It seems that during bootup, the SGI-IP32 prom
365 * stores a return pointers a 0x80000f10, then tests
366 * memory by writing bit patterns to 0xa0000xxx, and
367 * then when it's done, reads back the return pointer
368 * from 0x80000f10.
369 *
370 * I need to find the correct way to disconnect the
371 * cache from the main memory for R10000. (TODO !!!)
372 */
373 /* if ((cpu->cd.mips.coproc[0]->reg[COP0_CONFIG] & 7) == 3) { */
374 /*
375 if (cache == CACHE_DATA &&
376 cpu->r10k_cache_disable_TODO) {
377 paddr &= ((512*1024)-1);
378 paddr += 512*1024;
379 }
380 */
381 break;
382 #endif
383 default:
384 /* R4000 etc */
385 /* TODO */
386 ;
387 }
388 #endif /* MEM_MIPS */
389
390
391 /* Outside of physical RAM? */
392 if (paddr >= mem->physical_max) {
393 if ((paddr & 0xffff000000ULL) == 0x1f000000) {
394 /* Ok, this is PROM stuff */
395 } else if ((paddr & 0xfffff00000ULL) == 0x1ff00000) {
396 /* Sprite reads from this area of memory... */
397 /* TODO: is this still correct? */
398 if (writeflag == MEM_READ)
399 memset(data, 0, len);
400 goto do_return_ok;
401 } else {
402 if (paddr >= mem->physical_max + 0 * 1024) {
403 char *symbol;
404 #ifdef MEM_MIPS
405 uint64_t offset;
406 #endif
407 if (!quiet_mode) {
408 fatal("[ memory_rw(): writeflag=%i ",
409 writeflag);
410 if (writeflag) {
411 unsigned int i;
412 debug("data={", writeflag);
413 if (len > 16) {
414 int start2 = len-16;
415 for (i=0; i<16; i++)
416 debug("%s%02x",
417 i?",":"",
418 data[i]);
419 debug(" .. ");
420 if (start2 < 16)
421 start2 = 16;
422 for (i=start2; i<len;
423 i++)
424 debug("%s%02x",
425 i?",":"",
426 data[i]);
427 } else
428 for (i=0; i<len; i++)
429 debug("%s%02x",
430 i?",":"",
431 data[i]);
432 debug("}");
433 }
434 #ifdef MEM_MIPS
435 symbol = get_symbol_name(
436 &cpu->machine->symbol_context,
437 cpu->cd.mips.pc_last, &offset);
438 #else
439 symbol = "(unimpl for non-MIPS)";
440 #endif
441
442 /* TODO: fix! not mips.pc_last for for example ppc */
443
444 fatal(" paddr=%llx >= physical_max pc="
445 "0x%08llx <%s> ]\n",
446 (long long)paddr,
447 (long long)cpu->cd.mips.pc_last,
448 symbol? symbol : "no symbol");
449 }
450
451 if (cpu->machine->single_step_on_bad_addr) {
452 fatal("[ unimplemented access to "
453 "0x%016llx, pc = 0x%016llx ]\n",
454 (long long)paddr,
455 (long long)cpu->pc);
456 single_step = 1;
457 }
458 }
459
460 if (writeflag == MEM_READ) {
461 /* Return all zeroes? (Or 0xff? TODO) */
462 memset(data, 0, len);
463
464 #ifdef MEM_MIPS
465 /*
466 * For real data/instruction accesses, cause
467 * an exceptions on an illegal read:
468 */
469 if (cache != CACHE_NONE && cpu->machine->
470 dbe_on_nonexistant_memaccess) {
471 if (paddr >= mem->physical_max &&
472 paddr < mem->physical_max+1048576)
473 mips_cpu_exception(cpu,
474 EXCEPTION_DBE, 0, vaddr, 0,
475 0, 0, 0);
476 }
477 #endif /* MEM_MIPS */
478 }
479
480 /* Hm? Shouldn't there be a DBE exception for
481 invalid writes as well? TODO */
482
483 goto do_return_ok;
484 }
485 }
486
487 #endif /* ifndef MEM_USERLAND */
488
489
490 no_exception_access:
491
492 /*
493 * Uncached access:
494 */
495 memblock = memory_paddr_to_hostaddr(mem, paddr, writeflag);
496 if (memblock == NULL) {
497 if (writeflag == MEM_READ)
498 memset(data, 0, len);
499 goto do_return_ok;
500 }
501
502 offset = paddr & ((1 << BITS_PER_MEMBLOCK) - 1);
503
504 #ifdef BINTRANS
505 if (bintrans_cached && !bintrans_device_danger)
506 update_translation_table(cpu, vaddr & ~0xfff,
507 memblock + (offset & ~0xfff),
508 #if 0
509 cache == CACHE_INSTRUCTION?
510 (writeflag == MEM_WRITE? 1 : 0)
511 : ok - 1,
512 #else
513 writeflag == MEM_WRITE? 1 : 0,
514 #endif
515 paddr & ~0xfff);
516 #endif
517
518 if (writeflag == MEM_WRITE) {
519 if (len == sizeof(uint32_t) && (offset & 3)==0)
520 *(uint32_t *)(memblock + offset) = *(uint32_t *)data;
521 else if (len == sizeof(uint8_t))
522 *(uint8_t *)(memblock + offset) = *(uint8_t *)data;
523 else
524 memcpy(memblock + offset, data, len);
525 } else {
526 if (len == sizeof(uint32_t) && (offset & 3)==0)
527 *(uint32_t *)data = *(uint32_t *)(memblock + offset);
528 else if (len == sizeof(uint8_t))
529 *(uint8_t *)data = *(uint8_t *)(memblock + offset);
530 else
531 memcpy(data, memblock + offset, len);
532
533 if (cache == CACHE_INSTRUCTION) {
534 cpu->cd.mips.pc_last_host_4k_page = memblock
535 + (offset & ~0xfff);
536 #ifdef BINTRANS
537 if (bintrans_cached) {
538 cpu->cd.mips.pc_bintrans_host_4kpage =
539 cpu->cd.mips.pc_last_host_4k_page;
540 }
541 #endif
542 }
543 }
544
545
546 do_return_ok:
547 return MEMORY_ACCESS_OK;
548 }
549

  ViewVC Help
Powered by ViewVC 1.1.26