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

Annotation of /trunk/src/memory_rw.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (hide 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 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 4 * $Id: memory_rw.c,v 1.16 2005/04/19 01:24:35 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 4 #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 dpavlin 2 #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 dpavlin 4 #if defined(MEM_MIPS) || defined(MEM_USERLAND)
193 dpavlin 2 have_paddr:
194 dpavlin 4 #endif
195 dpavlin 2
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 dpavlin 4
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 dpavlin 2 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 dpavlin 4 if (bintrans_cached && !bintrans_device_danger)
506 dpavlin 2 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