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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (show annotations)
Mon Oct 8 16:18:14 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 16809 byte(s)
0.3.3.1
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.37 2005/06/02 12:31:39 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_ARM
90 vaddr &= 0x0fffffff;
91 #endif
92
93 #ifdef MEM_X86
94 /* Real-mode wrap-around: */
95 if (REAL_MODE && !(cache_flags & PHYSICAL)) {
96 if ((vaddr & 0xffff) + len > 0x10000) {
97 /* Do one byte at a time: */
98 int res = 0, i;
99 for (i=0; i<len; i++)
100 res = MEMORY_RW(cpu, mem, vaddr+i, &data[i], 1,
101 writeflag, cache_flags);
102 return res;
103 }
104 }
105
106 /* Crossing a page boundary? Then do one byte at a time: */
107 if ((vaddr & 0xfff) + len > 0x1000 && !(cache_flags & PHYSICAL)
108 && cpu->cd.x86.cr[0] & X86_CR0_PG) {
109 /* For WRITES: Read ALL BYTES FIRST and write them back!!!
110 Then do a write of all the new bytes. This is to make sure
111 than both pages around the boundary are writable so we don't
112 do a partial write. */
113 int res = 0, i;
114 if (writeflag == MEM_WRITE) {
115 unsigned char tmp;
116 for (i=0; i<len; i++) {
117 res = MEMORY_RW(cpu, mem, vaddr+i, &tmp, 1,
118 MEM_READ, cache_flags);
119 if (!res)
120 return 0;
121 res = MEMORY_RW(cpu, mem, vaddr+i, &tmp, 1,
122 MEM_WRITE, cache_flags);
123 if (!res)
124 return 0;
125 }
126 for (i=0; i<len; i++) {
127 res = MEMORY_RW(cpu, mem, vaddr+i, &data[i], 1,
128 MEM_WRITE, cache_flags);
129 if (!res)
130 return 0;
131 }
132 } else {
133 for (i=0; i<len; i++) {
134 /* Do one byte at a time: */
135 res = MEMORY_RW(cpu, mem, vaddr+i, &data[i], 1,
136 writeflag, cache_flags);
137 if (!res) {
138 if (cache == CACHE_INSTRUCTION) {
139 fatal("FAILED instruction "
140 "fetch across page boundar"
141 "y: todo. vaddr=0x%08x\n",
142 (int)vaddr);
143 cpu->running = 0;
144 }
145 return 0;
146 }
147 }
148 }
149 return res;
150 }
151 #endif /* X86 */
152
153 #ifdef MEM_URISC
154 {
155 uint64_t mask = (uint64_t) -1;
156 if (cpu->cd.urisc.wordlen < 64)
157 mask = ((int64_t)1 << cpu->cd.urisc.wordlen) - 1;
158 vaddr &= mask;
159 }
160 #endif
161
162 #ifdef MEM_MIPS
163 #ifdef BINTRANS
164 if (bintrans_cached) {
165 if (cache == CACHE_INSTRUCTION) {
166 cpu->cd.mips.pc_bintrans_host_4kpage = NULL;
167 cpu->cd.mips.pc_bintrans_paddr_valid = 0;
168 }
169 }
170 #endif
171 #endif /* MEM_MIPS */
172
173 #ifdef MEM_USERLAND
174 paddr = vaddr & 0x7fffffff;
175 goto have_paddr;
176 #endif
177
178 #ifndef MEM_USERLAND
179 #ifdef MEM_MIPS
180 /*
181 * For instruction fetch, are we on the same page as the last
182 * instruction we fetched?
183 *
184 * NOTE: There's no need to check this stuff here if this address
185 * is known to be in host ram, as it's done at instruction fetch
186 * time in cpu.c! Only check if _host_4k_page == NULL.
187 */
188 if (cache == CACHE_INSTRUCTION &&
189 cpu->cd.mips.pc_last_host_4k_page == NULL &&
190 (vaddr & ~0xfff) == cpu->cd.mips.pc_last_virtual_page) {
191 paddr = cpu->cd.mips.pc_last_physical_page | (vaddr & 0xfff);
192 goto have_paddr;
193 }
194 #endif /* MEM_MIPS */
195
196 if (cache_flags & PHYSICAL || cpu->translate_address == NULL) {
197 paddr = vaddr;
198 } else {
199 ok = cpu->translate_address(cpu, vaddr, &paddr,
200 (writeflag? FLAG_WRITEFLAG : 0) +
201 (no_exceptions? FLAG_NOEXCEPTIONS : 0)
202 #ifdef MEM_X86
203 + (cache_flags & NO_SEGMENTATION)
204 #endif
205 + (cache==CACHE_INSTRUCTION? FLAG_INSTR : 0));
206 /* If the translation caused an exception, or was invalid in
207 some way, we simply return without doing the memory
208 access: */
209 if (!ok)
210 return MEMORY_ACCESS_FAILED;
211 }
212
213
214 #ifdef MEM_X86
215 /* DOS debugging :-) */
216 if (!quiet_mode && !(cache_flags & PHYSICAL)) {
217 if (paddr >= 0x400 && paddr <= 0x4ff)
218 debug("{ PC BIOS DATA AREA: %s 0x%x }\n", writeflag ==
219 MEM_WRITE? "writing to" : "reading from",
220 (int)paddr);
221 #if 0
222 if (paddr >= 0xf0000 && paddr <= 0xfffff)
223 debug("{ BIOS ACCESS: %s 0x%x }\n",
224 writeflag == MEM_WRITE? "writing to" :
225 "reading from", (int)paddr);
226 #endif
227 }
228 #endif
229
230 #ifdef MEM_MIPS
231 /*
232 * If correct cache emulation is enabled, and we need to simluate
233 * cache misses even from the instruction cache, we can't run directly
234 * from a host page. :-/
235 */
236 #if defined(ENABLE_CACHE_EMULATION) && defined(ENABLE_INSTRUCTION_DELAYS)
237 #else
238 if (cache == CACHE_INSTRUCTION) {
239 cpu->cd.mips.pc_last_virtual_page = vaddr & ~0xfff;
240 cpu->cd.mips.pc_last_physical_page = paddr & ~0xfff;
241 cpu->cd.mips.pc_last_host_4k_page = NULL;
242
243 /* _last_host_4k_page will be set to 1 further down,
244 if the page is actually in host ram */
245 }
246 #endif
247 #endif /* MEM_MIPS */
248 #endif /* ifndef MEM_USERLAND */
249
250
251 #if defined(MEM_MIPS) || defined(MEM_USERLAND)
252 have_paddr:
253 #endif
254
255
256 #ifdef MEM_MIPS
257 /* TODO: How about bintrans vs cache emulation? */
258 #ifdef BINTRANS
259 if (bintrans_cached) {
260 if (cache == CACHE_INSTRUCTION) {
261 cpu->cd.mips.pc_bintrans_paddr_valid = 1;
262 cpu->cd.mips.pc_bintrans_paddr = paddr;
263 }
264 }
265 #endif
266 #endif /* MEM_MIPS */
267
268
269
270 #ifndef MEM_USERLAND
271 /*
272 * Memory mapped device?
273 *
274 * TODO: this is utterly slow.
275 * TODO2: if paddr<base, but len enough, then we should write
276 * to a device to
277 */
278 if (paddr >= mem->mmap_dev_minaddr && paddr < mem->mmap_dev_maxaddr) {
279 #ifdef BINTRANS
280 uint64_t orig_paddr = paddr;
281 #endif
282 int i, start, res;
283
284 #ifdef BINTRANS
285 /*
286 * Really really slow, but unfortunately necessary. This is
287 * to avoid the folowing scenario:
288 *
289 * a) offsets 0x000..0x123 are normal memory
290 * b) offsets 0x124..0x777 are a device
291 *
292 * 1) a read is done from offset 0x100. the page is
293 * added to the bintrans system as a "RAM" page
294 * 2) a bintranslated read is done from offset 0x200,
295 * which should access the device, but since the
296 * entire page is added, it will access non-existant
297 * RAM instead, without warning.
298 *
299 * Setting bintrans_device_danger = 1 on accesses which are
300 * on _any_ offset on pages that are device mapped avoids
301 * this problem, but it is probably not very fast.
302 */
303 if (bintrans_cached) {
304 for (i=0; i<mem->n_mmapped_devices; i++)
305 if (paddr >= (mem->dev_baseaddr[i] & ~0xfff) &&
306 paddr <= ((mem->dev_baseaddr[i] +
307 mem->dev_length[i] - 1) | 0xfff)) {
308 bintrans_device_danger = 1;
309 break;
310 }
311 }
312 #endif
313
314 i = start = mem->last_accessed_device;
315
316 /* Scan through all devices: */
317 do {
318 if (paddr >= mem->dev_baseaddr[i] &&
319 paddr < mem->dev_baseaddr[i] + mem->dev_length[i]) {
320 /* Found a device, let's access it: */
321 mem->last_accessed_device = i;
322
323 paddr -= mem->dev_baseaddr[i];
324 if (paddr + len > mem->dev_length[i])
325 len = mem->dev_length[i] - paddr;
326
327 #ifdef BINTRANS
328 if (bintrans_cached && mem->dev_flags[i] &
329 MEM_BINTRANS_OK) {
330 int wf = writeflag == MEM_WRITE? 1 : 0;
331
332 if (writeflag) {
333 if (paddr < mem->
334 dev_bintrans_write_low[i])
335 mem->
336 dev_bintrans_write_low
337 [i] =
338 paddr & ~0xfff;
339 if (paddr > mem->
340 dev_bintrans_write_high[i])
341 mem->
342 dev_bintrans_write_high
343 [i] = paddr | 0xfff;
344 }
345
346 if (!(mem->dev_flags[i] &
347 MEM_BINTRANS_WRITE_OK))
348 wf = 0;
349
350 update_translation_table(cpu,
351 vaddr & ~0xfff,
352 mem->dev_bintrans_data[i] +
353 (paddr & ~0xfff),
354 wf, orig_paddr & ~0xfff);
355 }
356 #endif
357
358 res = 0;
359 if (!no_exceptions || (mem->dev_flags[i] &
360 MEM_READING_HAS_NO_SIDE_EFFECTS))
361 res = mem->dev_f[i](cpu, mem, paddr,
362 data, len, writeflag,
363 mem->dev_extra[i]);
364
365 #ifdef ENABLE_INSTRUCTION_DELAYS
366 if (res == 0)
367 res = -1;
368
369 cpu->cd.mips.instruction_delay +=
370 ( (abs(res) - 1) *
371 cpu->cd.mips.cpu_type.instrs_per_cycle );
372 #endif
373
374 #ifndef MEM_X86
375 /*
376 * If accessing the memory mapped device
377 * failed, then return with a DBE exception.
378 */
379 if (res <= 0 && !no_exceptions) {
380 debug("%s device '%s' addr %08lx "
381 "failed\n", writeflag?
382 "writing to" : "reading from",
383 mem->dev_name[i], (long)paddr);
384 #ifdef MEM_MIPS
385 mips_cpu_exception(cpu, EXCEPTION_DBE,
386 0, vaddr, 0, 0, 0, 0);
387 #endif
388 return MEMORY_ACCESS_FAILED;
389 }
390 #endif
391 goto do_return_ok;
392 }
393
394 i ++;
395 if (i == mem->n_mmapped_devices)
396 i = 0;
397 } while (i != start);
398 }
399
400
401 #ifdef MEM_MIPS
402 /*
403 * Data and instruction cache emulation:
404 */
405
406 switch (cpu->cd.mips.cpu_type.mmu_model) {
407 case MMU3K:
408 /* if not uncached addess (TODO: generalize this) */
409 if (!(cache_flags & PHYSICAL) && cache != CACHE_NONE &&
410 !((vaddr & 0xffffffffULL) >= 0xa0000000ULL &&
411 (vaddr & 0xffffffffULL) <= 0xbfffffffULL)) {
412 if (memory_cache_R3000(cpu, cache, paddr,
413 writeflag, len, data))
414 goto do_return_ok;
415 }
416 break;
417 #if 0
418 /* Remove this, it doesn't work anyway */
419 case MMU10K:
420 /* other cpus: */
421 /*
422 * SUPER-UGLY HACK for SGI-IP32 PROM, R10000:
423 * K0 bits == 0x3 means uncached...
424 *
425 * It seems that during bootup, the SGI-IP32 prom
426 * stores a return pointers a 0x80000f10, then tests
427 * memory by writing bit patterns to 0xa0000xxx, and
428 * then when it's done, reads back the return pointer
429 * from 0x80000f10.
430 *
431 * I need to find the correct way to disconnect the
432 * cache from the main memory for R10000. (TODO !!!)
433 */
434 /* if ((cpu->cd.mips.coproc[0]->reg[COP0_CONFIG] & 7) == 3) { */
435 /*
436 if (cache == CACHE_DATA &&
437 cpu->r10k_cache_disable_TODO) {
438 paddr &= ((512*1024)-1);
439 paddr += 512*1024;
440 }
441 */
442 break;
443 #endif
444 default:
445 /* R4000 etc */
446 /* TODO */
447 ;
448 }
449 #endif /* MEM_MIPS */
450
451
452 /* Outside of physical RAM? */
453 if (paddr >= mem->physical_max) {
454 #ifdef MEM_MIPS
455 if ((paddr & 0xffffc00000ULL) == 0x1fc00000) {
456 /* Ok, this is PROM stuff */
457 } else if ((paddr & 0xfffff00000ULL) == 0x1ff00000) {
458 /* Sprite reads from this area of memory... */
459 /* TODO: is this still correct? */
460 if (writeflag == MEM_READ)
461 memset(data, 0, len);
462 goto do_return_ok;
463 } else
464 #endif /* MIPS */
465 {
466 if (paddr >= mem->physical_max) {
467 char *symbol;
468 #ifdef MEM_MIPS
469 uint64_t offset;
470 #endif
471 /* This allows for example OS kernels to probe
472 memory a few KBs past the end of memory,
473 without giving too many warnings. */
474 if (!quiet_mode && paddr >=
475 mem->physical_max + 0x40000) {
476 fatal("[ memory_rw(): writeflag=%i ",
477 writeflag);
478 if (writeflag) {
479 unsigned int i;
480 debug("data={", writeflag);
481 if (len > 16) {
482 int start2 = len-16;
483 for (i=0; i<16; i++)
484 debug("%s%02x",
485 i?",":"",
486 data[i]);
487 debug(" .. ");
488 if (start2 < 16)
489 start2 = 16;
490 for (i=start2; i<len;
491 i++)
492 debug("%s%02x",
493 i?",":"",
494 data[i]);
495 } else
496 for (i=0; i<len; i++)
497 debug("%s%02x",
498 i?",":"",
499 data[i]);
500 debug("}");
501 }
502 #ifdef MEM_MIPS
503 symbol = get_symbol_name(
504 &cpu->machine->symbol_context,
505 cpu->cd.mips.pc_last, &offset);
506 #else
507 symbol = "(unimpl for non-MIPS)";
508 #endif
509
510 /* TODO: fix! not mips.pc_last for for example ppc */
511
512 fatal(" paddr=%llx >= physical_max pc="
513 "0x%08llx <%s> ]\n",
514 (long long)paddr,
515 (long long)cpu->cd.mips.pc_last,
516 symbol? symbol : "no symbol");
517 }
518
519 if (cpu->machine->single_step_on_bad_addr) {
520 fatal("[ unimplemented access to "
521 "0x%016llx, pc = 0x%016llx ]\n",
522 (long long)paddr,
523 (long long)cpu->pc);
524 single_step = 1;
525 }
526 }
527
528 if (writeflag == MEM_READ) {
529 #ifdef MEM_X86
530 /* Reading non-existant memory on x86: */
531 memset(data, 0xff, len);
532 #else
533 /* Return all zeroes? (Or 0xff? TODO) */
534 memset(data, 0, len);
535 #endif
536
537 #ifdef MEM_MIPS
538 /*
539 * For real data/instruction accesses, cause
540 * an exceptions on an illegal read:
541 */
542 if (cache != CACHE_NONE && cpu->machine->
543 dbe_on_nonexistant_memaccess &&
544 !no_exceptions) {
545 if (paddr >= mem->physical_max &&
546 paddr < mem->physical_max+1048576)
547 mips_cpu_exception(cpu,
548 EXCEPTION_DBE, 0, vaddr, 0,
549 0, 0, 0);
550 }
551 #endif /* MEM_MIPS */
552 }
553
554 /* Hm? Shouldn't there be a DBE exception for
555 invalid writes as well? TODO */
556
557 goto do_return_ok;
558 }
559 }
560
561 #endif /* ifndef MEM_USERLAND */
562
563
564 /*
565 * Uncached access:
566 */
567 memblock = memory_paddr_to_hostaddr(mem, paddr, writeflag);
568 if (memblock == NULL) {
569 if (writeflag == MEM_READ)
570 memset(data, 0, len);
571 goto do_return_ok;
572 }
573
574 offset = paddr & ((1 << BITS_PER_MEMBLOCK) - 1);
575
576 #ifdef BINTRANS
577 if (bintrans_cached && !bintrans_device_danger)
578 update_translation_table(cpu, vaddr & ~0xfff,
579 memblock + (offset & ~0xfff),
580 #if 0
581 cache == CACHE_INSTRUCTION?
582 (writeflag == MEM_WRITE? 1 : 0)
583 : ok - 1,
584 #else
585 writeflag == MEM_WRITE? 1 : 0,
586 #endif
587 paddr & ~0xfff);
588 #endif
589
590 if (writeflag == MEM_WRITE) {
591 if (len == sizeof(uint32_t) && (offset & 3)==0)
592 *(uint32_t *)(memblock + offset) = *(uint32_t *)data;
593 else if (len == sizeof(uint8_t))
594 *(uint8_t *)(memblock + offset) = *(uint8_t *)data;
595 else
596 memcpy(memblock + offset, data, len);
597 } else {
598 if (len == sizeof(uint32_t) && (offset & 3)==0)
599 *(uint32_t *)data = *(uint32_t *)(memblock + offset);
600 else if (len == sizeof(uint8_t))
601 *(uint8_t *)data = *(uint8_t *)(memblock + offset);
602 else
603 memcpy(data, memblock + offset, len);
604
605 #ifdef MEM_MIPS
606 if (cache == CACHE_INSTRUCTION) {
607 cpu->cd.mips.pc_last_host_4k_page = memblock
608 + (offset & ~0xfff);
609 #ifdef BINTRANS
610 if (bintrans_cached) {
611 cpu->cd.mips.pc_bintrans_host_4kpage =
612 cpu->cd.mips.pc_last_host_4k_page;
613 }
614 #endif
615 }
616 #endif /* MIPS */
617 }
618
619
620 do_return_ok:
621 return MEMORY_ACCESS_OK;
622 }
623

  ViewVC Help
Powered by ViewVC 1.1.26