memory_rw.cc Source File

Back to the index.

memory_rw.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2003-2014 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  * Generic memory_rw(), with special hacks for specific CPU families.
29  *
30  * Example for inclusion from memory_mips.c:
31  *
32  * MEMORY_RW should be mips_memory_rw
33  * MEM_MIPS should be defined
34  *
35  *
36  * TODO: Cleanup the "ok" variable usage!
37  */
38 
39 
40 /*
41  * memory_rw():
42  *
43  * Read or write data from/to memory.
44  *
45  * cpu the cpu doing the read/write
46  * mem the memory object to use
47  * vaddr the virtual address
48  * data a pointer to the data to be written to memory, or
49  * a placeholder for data when reading from memory
50  * len the length of the 'data' buffer
51  * writeflag set to MEM_READ or MEM_WRITE
52  * misc_flags CACHE_{NONE,DATA,INSTRUCTION} | other flags
53  *
54  * If the address indicates access to a memory mapped device, that device'
55  * read/write access function is called.
56  *
57  * This function should not be called with cpu == NULL.
58  *
59  * Returns one of the following:
60  * MEMORY_ACCESS_FAILED
61  * MEMORY_ACCESS_OK
62  *
63  * (MEMORY_ACCESS_FAILED is 0.)
64  */
65 int MEMORY_RW(struct cpu *cpu, struct memory *mem, uint64_t vaddr,
66  unsigned char *data, size_t len, int writeflag, int misc_flags)
67 {
68 #ifdef MEM_ALPHA
69  const int offset_mask = 0x1fff;
70 #else
71  const int offset_mask = 0xfff;
72 #endif
73 
74  int ok = 2;
75  uint64_t paddr;
76  int cache, no_exceptions, offset;
77  unsigned char *memblock;
78  int dyntrans_device_danger = 0;
79 
80  no_exceptions = misc_flags & NO_EXCEPTIONS;
81  cache = misc_flags & CACHE_FLAGS_MASK;
82 
83 
84  if (misc_flags & PHYSICAL || cpu->translate_v2p == NULL) {
85  paddr = vaddr;
86  } else {
87  ok = cpu->translate_v2p(cpu, vaddr, &paddr,
88  (writeflag? FLAG_WRITEFLAG : 0) +
89  (no_exceptions? FLAG_NOEXCEPTIONS : 0)
90  + (misc_flags & MEMORY_USER_ACCESS)
91  + (cache==CACHE_INSTRUCTION? FLAG_INSTR : 0));
92 
93  /*
94  * If the translation caused an exception, or was invalid in
95  * some way, then simply return without doing the memory
96  * access:
97  */
98  if (!ok)
99  return MEMORY_ACCESS_FAILED;
100  }
101 
102 #if 0
103  /*
104  * For quick-and-dirty debugging of 32-bit code, typically
105  * unknown ROM code: Write out a summary of accessed memory ranges.
106  */
107  if (cpu->running && paddr < 0x00300000)
108  {
109  static int swriteflag = -1, lastswriteflag;
110  static int32_t start = -1, stop = -1;
111  static int32_t laststart = -1, laststop = -1;
112 
113  if (start == -1)
114  {
115  start = stop = paddr;
116  swriteflag = writeflag;
117  }
118  else
119  {
120  if (paddr == stop + len && writeflag == swriteflag)
121  {
122  stop = paddr;
123  }
124  else
125  {
126  if (start != laststart || stop != laststop || swriteflag != lastswriteflag)
127  {
128  printf("%s %08x -- %08x %i-bit\n",
129  swriteflag ? "WRITE" : "READ ",
130  (int)start, (int)(stop + len - 1), (int)len*8);
131  }
132 
133  laststart = start; laststop = stop; lastswriteflag = swriteflag;
134 
135  start = stop = paddr;
136  swriteflag = writeflag;
137  }
138  }
139 
140  // Make sure the access is not put into quick lookup tables:
141  ok |= MEMORY_NOT_FULL_PAGE;
142  }
143 #endif
144 
145 #if 0
146  /*
147  * For quick-and-dirty test to see which memory addresses are in use,
148  * making a "waterfall spectrum"...
149  */
150  if (cpu->running && (paddr < 0x200000 ||
151  (paddr >= 0x0c000000 && paddr < 0x0d000000)))
152  {
153  int64_t xsize = 1920, ysize = 1080;
154  static int y = 0;
155  static int count = 0;
156  static uint8_t* buf = NULL;
157 
158  if (buf == NULL)
159  {
160  buf = (uint8_t*)malloc(xsize * ysize*3);
161  memset(buf, 0xff, xsize * ysize*3);
162  }
163 
164  uint64_t paddr2 = paddr - 0x0c000000;
165 
166  if (paddr < 0x200000)
167  paddr2 = paddr + 16*1048576;
168 
169  int64_t max = 16*1048576 + (2*1048576);
170 
171  int64_t x = xsize * paddr2 / max;
172 
173  if (x >= 0 && x < xsize)
174  {
175  if (writeflag)
176  {
177  int c = buf[(x+y*xsize)*3+0];
178  if (c == 255)
179  c = 128;
180  else if (c > 0)
181  {
182  c = c - 8;
183  }
184 
185  buf[(x+y*xsize)*3+1] = c;
186  buf[(x+y*xsize)*3+2] = c;
187  }
188  else
189  {
190  int c = buf[(x+y*xsize)*3+1];
191  if (c == 255)
192  c = 128;
193  else if (c > 0)
194  {
195  c = c - 8;
196  }
197 
198  buf[(x+y*xsize)*3+0] = c;
199  buf[(x+y*xsize)*3+2] = c;
200  }
201  }
202 
203  for (int meg = 0; meg < 18; ++meg)
204  buf[((xsize * (meg * 0x100000LL) / max) +y*xsize)*3+1] = 64;
205 
206  count ++;
207  if (count >= 8192)
208  {
209  count = 0;
210  y ++;
211 
212  if (y >= ysize)
213  {
214  static int n = 0;
215  char name[40];
216  snprintf(name, sizeof(name), "memory_%05i.ppm", n++);
217  FILE* f = fopen(name, "w");
218  printf("writing out %s\n", name);
219  fprintf(f, "P6\n%i %i\n255\n", (int)xsize, (int)ysize);
220  fwrite((char*)buf, 1, (int)(xsize*ysize*3), f);
221  fclose(f);
222  memset(buf, 0xff, xsize*ysize);
223  y = 0;
224  }
225  }
226 
227  // Make sure the access is not put into quick lookup tables:
228  ok |= MEMORY_NOT_FULL_PAGE;
229  }
230 #endif
231 
232  /*
233  * Memory mapped device?
234  *
235  * TODO: if paddr < base, but len enough, then the device should
236  * still be written to!
237  */
238  if (paddr >= mem->mmap_dev_minaddr && paddr < mem->mmap_dev_maxaddr) {
239  uint64_t orig_paddr = paddr;
240  int i, start, end, res;
241 
242 #if 0
243 
244 TODO: The correct solution for this is to add RAM devices _around_ the
245 dangerous device. The solution below incurs a slowdown for _everything_,
246 not just the device in question.
247 
248  /*
249  * Really really slow, but unfortunately necessary. This is
250  * to avoid the folowing scenario:
251  *
252  * a) offsets 0x000..0x123 are normal memory
253  * b) offsets 0x124..0x777 are a device
254  *
255  * 1) a read is done from offset 0x100. the page is
256  * added to the dyntrans system as a "RAM" page
257  * 2) a dyntranslated read is done from offset 0x200,
258  * which should access the device, but since the
259  * entire page is added, it will access non-existant
260  * RAM instead, without warning.
261  *
262  * Setting dyntrans_device_danger = 1 on accesses which are
263  * on _any_ offset on pages that are device mapped avoids
264  * this problem, but it is probably not very fast.
265  *
266  * TODO: Convert this into a quick (multi-level, 64-bit)
267  * address space lookup, to find dangerous pages.
268  */
269  for (i=0; i<mem->n_mmapped_devices; i++)
270  if (paddr >= (mem->devices[i].baseaddr & ~offset_mask)&&
271  paddr <= ((mem->devices[i].endaddr-1)|offset_mask)){
272  dyntrans_device_danger = 1;
273  break;
274  }
275 #endif
276 
277  start = 0; end = mem->n_mmapped_devices - 1;
278  i = mem->last_accessed_device;
279 
280  /* Scan through all devices: */
281  do {
282  if (paddr >= mem->devices[i].baseaddr &&
283  paddr < mem->devices[i].endaddr) {
284  /* Found a device, let's access it: */
285  mem->last_accessed_device = i;
286 
287  paddr -= mem->devices[i].baseaddr;
288  if (paddr + len > mem->devices[i].length)
289  len = mem->devices[i].length - paddr;
290 
291  if (cpu->update_translation_table != NULL &&
292  !(ok & MEMORY_NOT_FULL_PAGE) &&
293  mem->devices[i].flags & DM_DYNTRANS_OK) {
294  int wf = writeflag == MEM_WRITE? 1 : 0;
295  unsigned char *host_addr;
296 
297  if (!(mem->devices[i].flags &
299  wf = 0;
300 
301  if (writeflag && wf) {
302  if (paddr < mem->devices[i].
303  dyntrans_write_low)
304  mem->devices[i].
305  dyntrans_write_low =
306  paddr &~offset_mask;
307  if (paddr >= mem->devices[i].
308  dyntrans_write_high)
309  mem->devices[i].
310  dyntrans_write_high =
311  paddr | offset_mask;
312  }
313 
314  if (mem->devices[i].flags &
315  DM_EMULATED_RAM) {
316  /* MEM_WRITE to force the page
317  to be allocated, if it
318  wasn't already */
319  uint64_t *pp = (uint64_t *)mem->
320  devices[i].dyntrans_data;
321  uint64_t p = orig_paddr - *pp;
322  host_addr =
324  mem, p & ~offset_mask,
325  MEM_WRITE);
326  } else {
327  host_addr = mem->devices[i].
328  dyntrans_data +
329  (paddr & ~offset_mask);
330  }
331 
333  vaddr & ~offset_mask, host_addr,
334  wf, orig_paddr & ~offset_mask);
335  }
336 
337  res = 0;
338  if (!no_exceptions || (mem->devices[i].flags &
340  res = mem->devices[i].f(cpu, mem, paddr,
341  data, len, writeflag,
342  mem->devices[i].extra);
343 
344  if (res == 0)
345  res = -1;
346 
347  /*
348  * If accessing the memory mapped device
349  * failed, then return with an exception.
350  * (Architecture specific.)
351  */
352  if (res <= 0 && !no_exceptions) {
353  debug("[ %s device '%s' addr %08lx "
354  "failed ]\n", writeflag?
355  "writing to" : "reading from",
356  mem->devices[i].name, (long)paddr);
357 #ifdef MEM_MIPS
359  cache == CACHE_INSTRUCTION?
361  0, vaddr, 0, 0, 0, 0);
362 #endif
363 #ifdef MEM_M88K
364  /* TODO: This is enough for
365  OpenBSD/mvme88k's badaddr()
366  implementation... but the
367  faulting address should probably
368  be included somewhere too! */
372 #endif
373  return MEMORY_ACCESS_FAILED;
374  }
375  goto do_return_ok;
376  }
377 
378  if (paddr < mem->devices[i].baseaddr)
379  end = i - 1;
380  if (paddr >= mem->devices[i].endaddr)
381  start = i + 1;
382  i = (start + end) >> 1;
383  } while (start <= end);
384  }
385 
386 
387 #ifdef MEM_MIPS
388  /*
389  * Data and instruction cache emulation:
390  */
391 
392  switch (cpu->cd.mips.cpu_type.mmu_model) {
393  case MMU3K:
394  /* if not uncached addess (TODO: generalize this) */
395  if (!(misc_flags & PHYSICAL) && cache != CACHE_NONE &&
396  !((vaddr & 0xffffffffULL) >= 0xa0000000ULL &&
397  (vaddr & 0xffffffffULL) <= 0xbfffffffULL)) {
398  if (memory_cache_R3000(cpu, cache, paddr,
399  writeflag, len, data))
400  goto do_return_ok;
401  }
402  break;
403  default:
404  /* R4000 etc */
405  /* TODO */
406  ;
407  }
408 #endif /* MEM_MIPS */
409 
410 
411  /* Outside of physical RAM? */
412  if (paddr >= mem->physical_max) {
413 #ifdef MEM_MIPS
414  if ((paddr & 0xffffc00000ULL) == 0x1fc00000) {
415  /* Ok, this is PROM stuff */
416  } else if ((paddr & 0xfffff00000ULL) == 0x1ff00000) {
417  /* Sprite reads from this area of memory... */
418  /* TODO: is this still correct? */
419  if (writeflag == MEM_READ)
420  memset(data, 0, len);
421  goto do_return_ok;
422  } else
423 #endif /* MIPS */
424  {
425  if (paddr >= mem->physical_max && !no_exceptions)
427  (cpu, mem, writeflag, paddr, data, len);
428 
429  if (writeflag == MEM_READ) {
430  /* Return all zeroes? (Or 0xff? TODO) */
431  memset(data, 0, len);
432 
433 #if 0
434 /*
435  * NOTE: This code prevents a PROM image from a real 5000/200 from booting.
436  * I think I introduced it because it was how some guest OS (NetBSD?) detected
437  * the amount of RAM on some machine.
438  *
439  * TODO: Figure out if it is not needed anymore, and remove it completely.
440  */
441 #ifdef MEM_MIPS
442  /*
443  * For real data/instruction accesses, cause
444  * an exceptions on an illegal read:
445  */
446  if (cache != CACHE_NONE && !no_exceptions &&
447  paddr >= mem->physical_max &&
448  paddr < mem->physical_max+1048576) {
450  EXCEPTION_DBE, 0, vaddr, 0,
451  0, 0, 0);
452  }
453 #endif /* MEM_MIPS */
454 #endif
455  }
456 
457  /* Hm? Shouldn't there be a DBE exception for
458  invalid writes as well? TODO */
459 
460  goto do_return_ok;
461  }
462  }
463 
464 
465  /*
466  * Uncached access:
467  *
468  * 1) Translate the physical address to a host address.
469  *
470  * 2) Insert this virtual->physical->host translation into the
471  * fast translation arrays (using update_translation_table()).
472  *
473  * 3) If this was a Write, then invalidate any code translations
474  * in that page.
475  */
476  memblock = memory_paddr_to_hostaddr(mem, paddr & ~offset_mask,
477  writeflag);
478  if (memblock == NULL) {
479  if (writeflag == MEM_READ)
480  memset(data, 0, len);
481  goto do_return_ok;
482  }
483 
484  offset = paddr & offset_mask;
485 
486  if (cpu->update_translation_table != NULL && !dyntrans_device_danger
487 #ifdef MEM_MIPS
488  /* Ugly hack for R2000/R3000 caches: */
489  && (cpu->cd.mips.cpu_type.mmu_model != MMU3K ||
491 #endif
492  && !(ok & MEMORY_NOT_FULL_PAGE)
493  && !no_exceptions)
494  cpu->update_translation_table(cpu, vaddr & ~offset_mask,
495  memblock, (misc_flags & MEMORY_USER_ACCESS) |
496  (cache == CACHE_INSTRUCTION?
497  (writeflag == MEM_WRITE? 1 : 0) : ok - 1),
498  paddr & ~offset_mask);
499 
500  /*
501  * If writing, or if mapping a page where writing is ok later on,
502  * then invalidate code translations for the (physical) page address:
503  */
504 
505  if ((writeflag == MEM_WRITE
506  || (ok == 2 && cache == CACHE_DATA)
507  ) && cpu->invalidate_code_translation != NULL)
509 
510  if ((paddr&((1<<BITS_PER_MEMBLOCK)-1)) + len > (1<<BITS_PER_MEMBLOCK)) {
511  printf("Write over memblock boundary?\n");
512  exit(1);
513  }
514 
515  /* And finally, read or write the data: */
516  if (writeflag == MEM_WRITE)
517  memcpy(memblock + offset, data, len);
518  else
519  memcpy(data, memblock + offset, len);
520 
521 do_return_ok:
522  return MEMORY_ACCESS_OK;
523 }
524 
memory::n_mmapped_devices
int n_mmapped_devices
Definition: memory.h:81
DM_EMULATED_RAM
#define DM_EMULATED_RAM
Definition: memory.h:134
mips_cpu::cpu_type
struct mips_cpu_type_def cpu_type
Definition: cpu_mips.h:206
mips_coproc::reg
uint64_t reg[N_MIPS_COPROC_REGS]
Definition: cpu_mips.h:102
data
u_short data
Definition: siireg.h:79
memory_device::name
const char * name
Definition: memory.h:54
mips_cpu::coproc
struct mips_coproc * coproc[N_MIPS_COPROCS]
Definition: cpu_mips.h:219
f
void f(int s, int func, int only_name)
Definition: generate_arm_r.c:45
cpu::running
uint8_t running
Definition: cpu.h:353
MEM_MIPS
#define MEM_MIPS
Definition: tmp_mips_tail.cc:39
memory_device::extra
void * extra
Definition: memory.h:58
memory
Definition: memory.h:75
debug
#define debug
Definition: dev_adb.cc:57
M88K_EXCEPTION_INSTRUCTION_ACCESS
#define M88K_EXCEPTION_INSTRUCTION_ACCESS
Definition: M88K_CPUComponent.h:285
M88K_EXCEPTION_DATA_ACCESS
#define M88K_EXCEPTION_DATA_ACCESS
Definition: M88K_CPUComponent.h:286
memory_device::f
int(* f)(struct cpu *, struct memory *, uint64_t, unsigned char *, size_t, int, void *)
Definition: memory.h:56
MEM_READ
#define MEM_READ
Definition: memory.h:116
cpu::update_translation_table
void(* update_translation_table)(struct cpu *, uint64_t vaddr_page, unsigned char *host_page, int writeflag, uint64_t paddr_page)
Definition: cpu.h:371
MIPS1_ISOL_CACHES
#define MIPS1_ISOL_CACHES
Definition: mips_cpuregs.h:180
memory_device::length
uint64_t length
Definition: memory.h:51
cpu::mips
struct mips_cpu mips
Definition: cpu.h:443
memory_device::baseaddr
uint64_t baseaddr
Definition: memory.h:49
memory_device::endaddr
uint64_t endaddr
Definition: memory.h:50
MEM_WRITE
#define MEM_WRITE
Definition: memory.h:117
memory::devices
struct memory_device * devices
Definition: memory.h:88
cpu::invalidate_code_translation
void(* invalidate_code_translation)(struct cpu *, uint64_t paddr, int flags)
Definition: cpu.h:376
memory_cache_R3000
int memory_cache_R3000(struct cpu *cpu, int cache, uint64_t paddr, int writeflag, size_t len, unsigned char *data)
Definition: memory_mips.cc:43
DM_DYNTRANS_WRITE_OK
#define DM_DYNTRANS_WRITE_OK
Definition: memory.h:132
MEMORY_ACCESS_OK
#define MEMORY_ACCESS_OK
Definition: memory.h:141
PHYSICAL
#define PHYSICAL
Definition: memory.h:126
mips_cpu_type_def::mmu_model
int mmu_model
Definition: MIPS_CPUComponent.h:124
CACHE_FLAGS_MASK
#define CACHE_FLAGS_MASK
Definition: memory.h:124
MEMORY_USER_ACCESS
#define MEMORY_USER_ACCESS
Definition: memory.h:127
CACHE_NONE
#define CACHE_NONE
Definition: memory.h:123
cpu::cd
union cpu::@1 cd
memory::mmap_dev_minaddr
uint64_t mmap_dev_minaddr
Definition: memory.h:85
EXCEPTION_DBE
#define EXCEPTION_DBE
Definition: cop0.h:190
CACHE_INSTRUCTION
#define CACHE_INSTRUCTION
Definition: memory.h:122
MMU3K
#define MMU3K
Definition: mips_cpu_types.h:44
CACHE_DATA
#define CACHE_DATA
Definition: memory.h:121
m88k_exception
void m88k_exception(struct cpu *cpu, int vector, int is_trap)
Definition: cpu_m88k.cc:648
BITS_PER_MEMBLOCK
#define BITS_PER_MEMBLOCK
Definition: memory.h:92
memory_warn_about_unimplemented_addr
void memory_warn_about_unimplemented_addr(struct cpu *cpu, struct memory *mem, int writeflag, uint64_t paddr, uint8_t *data, size_t len)
Definition: memory.cc:594
MEMORY_ACCESS_FAILED
#define MEMORY_ACCESS_FAILED
Definition: memory.h:140
cpu::translate_v2p
int(* translate_v2p)(struct cpu *, uint64_t vaddr, uint64_t *return_paddr, int flags)
Definition: cpu.h:369
memory::last_accessed_device
int last_accessed_device
Definition: memory.h:82
MEMORY_RW
int MEMORY_RW(struct cpu *cpu, struct memory *mem, uint64_t vaddr, unsigned char *data, size_t len, int writeflag, int misc_flags)
Definition: memory_rw.cc:65
NO_EXCEPTIONS
#define NO_EXCEPTIONS
Definition: memory.h:125
DM_READS_HAVE_NO_SIDE_EFFECTS
#define DM_READS_HAVE_NO_SIDE_EFFECTS
Definition: memory.h:133
mips_cpu_exception
void mips_cpu_exception(struct cpu *cpu, int exccode, int tlb, uint64_t vaddr, int coproc_nr, uint64_t vaddr_vpn2, int vaddr_asid, int x_64)
Definition: cpu_mips.cc:1719
EXCEPTION_IBE
#define EXCEPTION_IBE
Definition: cop0.h:189
memory::physical_max
uint64_t physical_max
Definition: memory.h:76
INVALIDATE_PADDR
#define INVALIDATE_PADDR
Definition: cpu.h:479
cpu
Definition: cpu.h:326
MEMORY_NOT_FULL_PAGE
#define MEMORY_NOT_FULL_PAGE
Definition: memory.h:143
DM_DYNTRANS_OK
#define DM_DYNTRANS_OK
Definition: memory.h:131
FLAG_NOEXCEPTIONS
#define FLAG_NOEXCEPTIONS
Definition: memory.h:137
FLAG_WRITEFLAG
#define FLAG_WRITEFLAG
Definition: memory.h:136
FLAG_INSTR
#define FLAG_INSTR
Definition: memory.h:138
memory_paddr_to_hostaddr
unsigned char * memory_paddr_to_hostaddr(struct memory *mem, uint64_t paddr, int writeflag)
Definition: memory.cc:495
COP0_STATUS
#define COP0_STATUS
Definition: cop0.h:109
memory_device::flags
int flags
Definition: memory.h:52

Generated on Tue Mar 24 2020 14:04:48 for GXemul by doxygen 1.8.17