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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5 - (show annotations)
Mon Oct 8 16:18:06 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 14709 byte(s)
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