/[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

Annotation of /upstream/0.3.3.1/src/memory_rw.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (hide annotations)
Mon Oct 8 16:18:14 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 16809 byte(s)
0.3.3.1
1 dpavlin 2 /*
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 dpavlin 6 * $Id: memory_rw.c,v 1.37 2005/06/02 12:31:39 debug Exp $
29 dpavlin 2 *
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 dpavlin 4 int bintrans_device_danger = 0;
80 dpavlin 2 #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 dpavlin 6 #ifdef MEM_ARM
90     vaddr &= 0x0fffffff;
91     #endif
92    
93 dpavlin 4 #ifdef MEM_X86
94 dpavlin 6 /* 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 dpavlin 4
106 dpavlin 6 /* 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 dpavlin 4 return 0;
121 dpavlin 6 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 dpavlin 4 }
147     }
148     }
149 dpavlin 6 return res;
150 dpavlin 4 }
151 dpavlin 6 #endif /* X86 */
152 dpavlin 4
153 dpavlin 2 #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 dpavlin 6 #ifdef MEM_X86
203     + (cache_flags & NO_SEGMENTATION)
204     #endif
205 dpavlin 2 + (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 dpavlin 6 #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 dpavlin 2 #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 dpavlin 4 #if defined(MEM_MIPS) || defined(MEM_USERLAND)
252 dpavlin 2 have_paddr:
253 dpavlin 4 #endif
254 dpavlin 2
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 dpavlin 4
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 dpavlin 2 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 dpavlin 6 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 dpavlin 2
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 dpavlin 6
374     #ifndef MEM_X86
375 dpavlin 2 /*
376     * If accessing the memory mapped device
377     * failed, then return with a DBE exception.
378     */
379 dpavlin 6 if (res <= 0 && !no_exceptions) {
380 dpavlin 2 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 dpavlin 6 #endif
391 dpavlin 2 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 dpavlin 6 #ifdef MEM_MIPS
455     if ((paddr & 0xffffc00000ULL) == 0x1fc00000) {
456 dpavlin 2 /* 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 dpavlin 6 } else
464     #endif /* MIPS */
465     {
466     if (paddr >= mem->physical_max) {
467 dpavlin 2 char *symbol;
468     #ifdef MEM_MIPS
469     uint64_t offset;
470     #endif
471 dpavlin 6 /* 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 dpavlin 2 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 dpavlin 6 #ifdef MEM_X86
530     /* Reading non-existant memory on x86: */
531     memset(data, 0xff, len);
532     #else
533 dpavlin 2 /* Return all zeroes? (Or 0xff? TODO) */
534     memset(data, 0, len);
535 dpavlin 6 #endif
536 dpavlin 2
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 dpavlin 6 dbe_on_nonexistant_memaccess &&
544     !no_exceptions) {
545 dpavlin 2 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 dpavlin 4 if (bintrans_cached && !bintrans_device_danger)
578 dpavlin 2 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 dpavlin 6 #ifdef MEM_MIPS
606 dpavlin 2 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 dpavlin 6 #endif /* MIPS */
617 dpavlin 2 }
618    
619    
620     do_return_ok:
621     return MEMORY_ACCESS_OK;
622     }
623    

  ViewVC Help
Powered by ViewVC 1.1.26