1 |
/* |
/* |
2 |
* Copyright (C) 2003-2006 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: memory_rw.c,v 1.95 2006/07/25 21:49:14 debug Exp $ |
* $Id: memory_rw.c,v 1.107 2007/06/12 03:49:11 debug Exp $ |
29 |
* |
* |
30 |
* Generic memory_rw(), with special hacks for specific CPU families. |
* Generic memory_rw(), with special hacks for specific CPU families. |
31 |
* |
* |
33 |
* |
* |
34 |
* MEMORY_RW should be mips_memory_rw |
* MEMORY_RW should be mips_memory_rw |
35 |
* MEM_MIPS should be defined |
* MEM_MIPS should be defined |
36 |
|
* |
37 |
|
* |
38 |
|
* TODO: Cleanup the "ok" variable usage! |
39 |
*/ |
*/ |
40 |
|
|
41 |
|
|
74 |
#endif |
#endif |
75 |
|
|
76 |
#ifndef MEM_USERLAND |
#ifndef MEM_USERLAND |
77 |
int ok = 1; |
int ok = 2; |
78 |
#endif |
#endif |
79 |
uint64_t paddr; |
uint64_t paddr; |
80 |
int cache, no_exceptions, offset; |
int cache, no_exceptions, offset; |
84 |
no_exceptions = misc_flags & NO_EXCEPTIONS; |
no_exceptions = misc_flags & NO_EXCEPTIONS; |
85 |
cache = misc_flags & CACHE_FLAGS_MASK; |
cache = misc_flags & CACHE_FLAGS_MASK; |
86 |
|
|
|
#ifdef MEM_X86 |
|
|
/* Real-mode wrap-around: */ |
|
|
if (REAL_MODE && !(misc_flags & PHYSICAL)) { |
|
|
if ((vaddr & 0xffff) + len > 0x10000) { |
|
|
/* Do one byte at a time: */ |
|
|
int res = 0; |
|
|
size_t i; |
|
|
for (i=0; i<len; i++) |
|
|
res = MEMORY_RW(cpu, mem, vaddr+i, &data[i], 1, |
|
|
writeflag, misc_flags); |
|
|
return res; |
|
|
} |
|
|
} |
|
|
|
|
|
/* Crossing a page boundary? Then do one byte at a time: */ |
|
|
if ((vaddr & 0xfff) + len > 0x1000 && !(misc_flags & PHYSICAL) |
|
|
&& cpu->cd.x86.cr[0] & X86_CR0_PG) { |
|
|
/* For WRITES: Read ALL BYTES FIRST and write them back!!! |
|
|
Then do a write of all the new bytes. This is to make sure |
|
|
than both pages around the boundary are writable so we don't |
|
|
do a partial write. */ |
|
|
int res = 0; |
|
|
size_t i; |
|
|
if (writeflag == MEM_WRITE) { |
|
|
unsigned char tmp; |
|
|
for (i=0; i<len; i++) { |
|
|
res = MEMORY_RW(cpu, mem, vaddr+i, &tmp, 1, |
|
|
MEM_READ, misc_flags); |
|
|
if (!res) |
|
|
return 0; |
|
|
res = MEMORY_RW(cpu, mem, vaddr+i, &tmp, 1, |
|
|
MEM_WRITE, misc_flags); |
|
|
if (!res) |
|
|
return 0; |
|
|
} |
|
|
for (i=0; i<len; i++) { |
|
|
res = MEMORY_RW(cpu, mem, vaddr+i, &data[i], 1, |
|
|
MEM_WRITE, misc_flags); |
|
|
if (!res) |
|
|
return 0; |
|
|
} |
|
|
} else { |
|
|
for (i=0; i<len; i++) { |
|
|
/* Do one byte at a time: */ |
|
|
res = MEMORY_RW(cpu, mem, vaddr+i, &data[i], 1, |
|
|
writeflag, misc_flags); |
|
|
if (!res) { |
|
|
if (cache == CACHE_INSTRUCTION) { |
|
|
fatal("FAILED instruction " |
|
|
"fetch across page boundar" |
|
|
"y: todo. vaddr=0x%08x\n", |
|
|
(int)vaddr); |
|
|
cpu->running = 0; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
} |
|
|
return res; |
|
|
} |
|
|
#endif /* X86 */ |
|
|
|
|
87 |
|
|
88 |
#ifdef MEM_USERLAND |
#ifdef MEM_USERLAND |
89 |
#ifdef MEM_ALPHA |
#ifdef MEM_ALPHA |
98 |
ok = cpu->translate_v2p(cpu, vaddr, &paddr, |
ok = cpu->translate_v2p(cpu, vaddr, &paddr, |
99 |
(writeflag? FLAG_WRITEFLAG : 0) + |
(writeflag? FLAG_WRITEFLAG : 0) + |
100 |
(no_exceptions? FLAG_NOEXCEPTIONS : 0) |
(no_exceptions? FLAG_NOEXCEPTIONS : 0) |
|
#ifdef MEM_X86 |
|
|
+ (misc_flags & NO_SEGMENTATION) |
|
|
#endif |
|
|
#ifdef MEM_ARM |
|
101 |
+ (misc_flags & MEMORY_USER_ACCESS) |
+ (misc_flags & MEMORY_USER_ACCESS) |
|
#endif |
|
102 |
+ (cache==CACHE_INSTRUCTION? FLAG_INSTR : 0)); |
+ (cache==CACHE_INSTRUCTION? FLAG_INSTR : 0)); |
103 |
/* If the translation caused an exception, or was invalid in |
|
104 |
some way, we simply return without doing the memory |
/* |
105 |
access: */ |
* If the translation caused an exception, or was invalid in |
106 |
|
* some way, then simply return without doing the memory |
107 |
|
* access: |
108 |
|
*/ |
109 |
if (!ok) |
if (!ok) |
110 |
return MEMORY_ACCESS_FAILED; |
return MEMORY_ACCESS_FAILED; |
111 |
} |
} |
112 |
|
|
|
|
|
|
#ifdef MEM_X86 |
|
|
/* DOS debugging :-) */ |
|
|
if (!quiet_mode && !(misc_flags & PHYSICAL)) { |
|
|
if (paddr >= 0x400 && paddr <= 0x4ff) |
|
|
debug("{ PC BIOS DATA AREA: %s 0x%x }\n", writeflag == |
|
|
MEM_WRITE? "writing to" : "reading from", |
|
|
(int)paddr); |
|
|
#if 0 |
|
|
if (paddr >= 0xf0000 && paddr <= 0xfffff) |
|
|
debug("{ BIOS ACCESS: %s 0x%x }\n", |
|
|
writeflag == MEM_WRITE? "writing to" : |
|
|
"reading from", (int)paddr); |
|
|
#endif |
|
|
} |
|
|
#endif |
|
113 |
#endif /* !MEM_USERLAND */ |
#endif /* !MEM_USERLAND */ |
114 |
|
|
115 |
|
|
124 |
uint64_t orig_paddr = paddr; |
uint64_t orig_paddr = paddr; |
125 |
int i, start, end, res; |
int i, start, end, res; |
126 |
|
|
127 |
|
#if 0 |
128 |
|
|
129 |
|
TODO: The correct solution for this is to add RAM devices _around_ the |
130 |
|
dangerous device. The solution below incurs a slowdown for _everything_, |
131 |
|
not just the device in question. |
132 |
|
|
133 |
/* |
/* |
134 |
* Really really slow, but unfortunately necessary. This is |
* Really really slow, but unfortunately necessary. This is |
135 |
* to avoid the folowing scenario: |
* to avoid the folowing scenario: |
151 |
* TODO: Convert this into a quick (multi-level, 64-bit) |
* TODO: Convert this into a quick (multi-level, 64-bit) |
152 |
* address space lookup, to find dangerous pages. |
* address space lookup, to find dangerous pages. |
153 |
*/ |
*/ |
|
#if 1 |
|
154 |
for (i=0; i<mem->n_mmapped_devices; i++) |
for (i=0; i<mem->n_mmapped_devices; i++) |
155 |
if (paddr >= (mem->dev_baseaddr[i] & ~offset_mask) && |
if (paddr >= (mem->devices[i].baseaddr & ~offset_mask)&& |
156 |
paddr <= ((mem->dev_endaddr[i]-1) | offset_mask)) { |
paddr <= ((mem->devices[i].endaddr-1)|offset_mask)){ |
157 |
dyntrans_device_danger = 1; |
dyntrans_device_danger = 1; |
158 |
break; |
break; |
159 |
} |
} |
164 |
|
|
165 |
/* Scan through all devices: */ |
/* Scan through all devices: */ |
166 |
do { |
do { |
167 |
if (paddr >= mem->dev_baseaddr[i] && |
if (paddr >= mem->devices[i].baseaddr && |
168 |
paddr < mem->dev_endaddr[i]) { |
paddr < mem->devices[i].endaddr) { |
169 |
/* Found a device, let's access it: */ |
/* Found a device, let's access it: */ |
170 |
mem->last_accessed_device = i; |
mem->last_accessed_device = i; |
171 |
|
|
172 |
paddr -= mem->dev_baseaddr[i]; |
paddr -= mem->devices[i].baseaddr; |
173 |
if (paddr + len > mem->dev_length[i]) |
if (paddr + len > mem->devices[i].length) |
174 |
len = mem->dev_length[i] - paddr; |
len = mem->devices[i].length - paddr; |
175 |
|
|
176 |
if (cpu->update_translation_table != NULL && |
if (cpu->update_translation_table != NULL && |
177 |
!(ok & MEMORY_NOT_FULL_PAGE) && |
!(ok & MEMORY_NOT_FULL_PAGE) && |
178 |
mem->dev_flags[i] & DM_DYNTRANS_OK) { |
mem->devices[i].flags & DM_DYNTRANS_OK) { |
179 |
int wf = writeflag == MEM_WRITE? 1 : 0; |
int wf = writeflag == MEM_WRITE? 1 : 0; |
180 |
unsigned char *host_addr; |
unsigned char *host_addr; |
181 |
|
|
182 |
if (!(mem->dev_flags[i] & |
if (!(mem->devices[i].flags & |
183 |
DM_DYNTRANS_WRITE_OK)) |
DM_DYNTRANS_WRITE_OK)) |
184 |
wf = 0; |
wf = 0; |
185 |
|
|
186 |
if (writeflag && wf) { |
if (writeflag && wf) { |
187 |
if (paddr < mem-> |
if (paddr < mem->devices[i]. |
188 |
dev_dyntrans_write_low[i]) |
dyntrans_write_low) |
189 |
mem-> |
mem->devices[i]. |
190 |
dev_dyntrans_write_low |
dyntrans_write_low = |
191 |
[i] = paddr & |
paddr &~offset_mask; |
192 |
~offset_mask; |
if (paddr >= mem->devices[i]. |
193 |
if (paddr >= mem-> |
dyntrans_write_high) |
194 |
dev_dyntrans_write_high[i]) |
mem->devices[i]. |
195 |
mem-> |
dyntrans_write_high = |
196 |
dev_dyntrans_write_high |
paddr | offset_mask; |
|
[i] = paddr | |
|
|
offset_mask; |
|
197 |
} |
} |
198 |
|
|
199 |
if (mem->dev_flags[i] & |
if (mem->devices[i].flags & |
200 |
DM_EMULATED_RAM) { |
DM_EMULATED_RAM) { |
201 |
/* MEM_WRITE to force the page |
/* MEM_WRITE to force the page |
202 |
to be allocated, if it |
to be allocated, if it |
203 |
wasn't already */ |
wasn't already */ |
204 |
uint64_t *pp = (uint64_t *) |
uint64_t *pp = (uint64_t *)mem-> |
205 |
mem->dev_dyntrans_data[i]; |
devices[i].dyntrans_data; |
206 |
uint64_t p = orig_paddr - *pp; |
uint64_t p = orig_paddr - *pp; |
207 |
host_addr = |
host_addr = |
208 |
memory_paddr_to_hostaddr( |
memory_paddr_to_hostaddr( |
209 |
mem, p & ~offset_mask, |
mem, p & ~offset_mask, |
210 |
MEM_WRITE); |
MEM_WRITE); |
211 |
} else { |
} else { |
212 |
host_addr = |
host_addr = mem->devices[i]. |
213 |
mem->dev_dyntrans_data[i] + |
dyntrans_data + |
214 |
(paddr & ~offset_mask); |
(paddr & ~offset_mask); |
215 |
} |
} |
216 |
|
|
220 |
} |
} |
221 |
|
|
222 |
res = 0; |
res = 0; |
223 |
if (!no_exceptions || (mem->dev_flags[i] & |
if (!no_exceptions || (mem->devices[i].flags & |
224 |
DM_READS_HAVE_NO_SIDE_EFFECTS)) |
DM_READS_HAVE_NO_SIDE_EFFECTS)) |
225 |
res = mem->dev_f[i](cpu, mem, paddr, |
res = mem->devices[i].f(cpu, mem, paddr, |
226 |
data, len, writeflag, |
data, len, writeflag, |
227 |
mem->dev_extra[i]); |
mem->devices[i].extra); |
228 |
|
|
229 |
if (res == 0) |
if (res == 0) |
230 |
res = -1; |
res = -1; |
231 |
|
|
|
#ifndef MEM_X86 |
|
232 |
/* |
/* |
233 |
* If accessing the memory mapped device |
* If accessing the memory mapped device |
234 |
* failed, then return with a DBE exception. |
* failed, then return with a DBE exception. |
237 |
debug("%s device '%s' addr %08lx " |
debug("%s device '%s' addr %08lx " |
238 |
"failed\n", writeflag? |
"failed\n", writeflag? |
239 |
"writing to" : "reading from", |
"writing to" : "reading from", |
240 |
mem->dev_name[i], (long)paddr); |
mem->devices[i].name, (long)paddr); |
241 |
#ifdef MEM_MIPS |
#ifdef MEM_MIPS |
242 |
mips_cpu_exception(cpu, EXCEPTION_DBE, |
mips_cpu_exception(cpu, |
243 |
|
cache == CACHE_INSTRUCTION? |
244 |
|
EXCEPTION_IBE : EXCEPTION_DBE, |
245 |
0, vaddr, 0, 0, 0, 0); |
0, vaddr, 0, 0, 0, 0); |
246 |
#endif |
#endif |
247 |
return MEMORY_ACCESS_FAILED; |
return MEMORY_ACCESS_FAILED; |
248 |
} |
} |
|
#endif |
|
249 |
goto do_return_ok; |
goto do_return_ok; |
250 |
} |
} |
251 |
|
|
252 |
if (paddr < mem->dev_baseaddr[i]) |
if (paddr < mem->devices[i].baseaddr) |
253 |
end = i - 1; |
end = i - 1; |
254 |
if (paddr >= mem->dev_endaddr[i]) |
if (paddr >= mem->devices[i].endaddr) |
255 |
start = i + 1; |
start = i + 1; |
256 |
i = (start + end) >> 1; |
i = (start + end) >> 1; |
257 |
} while (start <= end); |
} while (start <= end); |
296 |
} else |
} else |
297 |
#endif /* MIPS */ |
#endif /* MIPS */ |
298 |
{ |
{ |
299 |
if (paddr >= mem->physical_max) { |
if (paddr >= mem->physical_max && !no_exceptions) |
300 |
uint64_t offset, old_pc = cpu->pc; |
memory_warn_about_unimplemented_addr |
301 |
char *symbol; |
(cpu, mem, writeflag, paddr, data, len); |
|
|
|
|
/* This allows for example OS kernels to probe |
|
|
memory a few KBs past the end of memory, |
|
|
without giving too many warnings. */ |
|
|
if (!quiet_mode && !no_exceptions && paddr >= |
|
|
mem->physical_max + 0x40000) { |
|
|
fatal("[ memory_rw(): writeflag=%i ", |
|
|
writeflag); |
|
|
if (writeflag) { |
|
|
unsigned int i; |
|
|
debug("data={", writeflag); |
|
|
if (len > 16) { |
|
|
int start2 = len-16; |
|
|
for (i=0; i<16; i++) |
|
|
debug("%s%02x", |
|
|
i?",":"", |
|
|
data[i]); |
|
|
debug(" .. "); |
|
|
if (start2 < 16) |
|
|
start2 = 16; |
|
|
for (i=start2; i<len; |
|
|
i++) |
|
|
debug("%s%02x", |
|
|
i?",":"", |
|
|
data[i]); |
|
|
} else |
|
|
for (i=0; i<len; i++) |
|
|
debug("%s%02x", |
|
|
i?",":"", |
|
|
data[i]); |
|
|
debug("}"); |
|
|
} |
|
|
|
|
|
fatal(" paddr=0x%llx >= physical_max" |
|
|
"; pc=", (long long)paddr); |
|
|
if (cpu->is_32bit) |
|
|
fatal("0x%08x",(int)old_pc); |
|
|
else |
|
|
fatal("0x%016llx", |
|
|
(long long)old_pc); |
|
|
symbol = get_symbol_name( |
|
|
&cpu->machine->symbol_context, |
|
|
old_pc, &offset); |
|
|
fatal(" <%s> ]\n", |
|
|
symbol? symbol : " no symbol "); |
|
|
} |
|
|
} |
|
302 |
|
|
303 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
|
#ifdef MEM_X86 |
|
|
/* Reading non-existant memory on x86: */ |
|
|
memset(data, 0xff, len); |
|
|
#else |
|
304 |
/* Return all zeroes? (Or 0xff? TODO) */ |
/* Return all zeroes? (Or 0xff? TODO) */ |
305 |
memset(data, 0, len); |
memset(data, 0, len); |
|
#endif |
|
306 |
|
|
307 |
|
#if 0 |
308 |
|
/* |
309 |
|
* NOTE: This code prevents a PROM image from a real 5000/200 from booting. |
310 |
|
* I think I introduced it because it was how some guest OS (NetBSD?) detected |
311 |
|
* the amount of RAM on some machine. |
312 |
|
* |
313 |
|
* TODO: Figure out if it is not needed anymore, and remove it completely. |
314 |
|
*/ |
315 |
#ifdef MEM_MIPS |
#ifdef MEM_MIPS |
316 |
/* |
/* |
317 |
* For real data/instruction accesses, cause |
* For real data/instruction accesses, cause |
318 |
* an exceptions on an illegal read: |
* an exceptions on an illegal read: |
319 |
*/ |
*/ |
320 |
if (cache != CACHE_NONE && cpu->machine-> |
if (cache != CACHE_NONE && !no_exceptions && |
321 |
dbe_on_nonexistant_memaccess && |
paddr >= mem->physical_max && |
322 |
!no_exceptions) { |
paddr < mem->physical_max+1048576) { |
323 |
if (paddr >= mem->physical_max && |
mips_cpu_exception(cpu, |
324 |
paddr < mem->physical_max+1048576) |
EXCEPTION_DBE, 0, vaddr, 0, |
325 |
mips_cpu_exception(cpu, |
0, 0, 0); |
|
EXCEPTION_DBE, 0, vaddr, 0, |
|
|
0, 0, 0); |
|
326 |
} |
} |
327 |
#endif /* MEM_MIPS */ |
#endif /* MEM_MIPS */ |
328 |
|
#endif |
329 |
} |
} |
330 |
|
|
331 |
/* Hm? Shouldn't there be a DBE exception for |
/* Hm? Shouldn't there be a DBE exception for |
365 |
&& (cpu->cd.mips.cpu_type.mmu_model != MMU3K || |
&& (cpu->cd.mips.cpu_type.mmu_model != MMU3K || |
366 |
!(cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & MIPS1_ISOL_CACHES)) |
!(cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & MIPS1_ISOL_CACHES)) |
367 |
#endif |
#endif |
|
#ifndef MEM_MIPS |
|
|
/* && !(misc_flags & MEMORY_USER_ACCESS) */ |
|
368 |
#ifndef MEM_USERLAND |
#ifndef MEM_USERLAND |
369 |
&& !(ok & MEMORY_NOT_FULL_PAGE) |
&& !(ok & MEMORY_NOT_FULL_PAGE) |
370 |
#endif |
#endif |
|
#endif |
|
371 |
&& !no_exceptions) |
&& !no_exceptions) |
372 |
cpu->update_translation_table(cpu, vaddr & ~offset_mask, |
cpu->update_translation_table(cpu, vaddr & ~offset_mask, |
373 |
memblock, (misc_flags & MEMORY_USER_ACCESS) | |
memblock, (misc_flags & MEMORY_USER_ACCESS) | |
374 |
#if !defined(MEM_MIPS) && !defined(MEM_USERLAND) |
#if !defined(MEM_USERLAND) |
375 |
(cache == CACHE_INSTRUCTION? |
(cache == CACHE_INSTRUCTION? |
376 |
(writeflag == MEM_WRITE? 1 : 0) : ok - 1), |
(writeflag == MEM_WRITE? 1 : 0) : ok - 1), |
377 |
#else |
#else |
379 |
#endif |
#endif |
380 |
paddr & ~offset_mask); |
paddr & ~offset_mask); |
381 |
|
|
382 |
/* Invalidate code translations for the page we are writing to. */ |
/* |
383 |
if (writeflag == MEM_WRITE && cpu->invalidate_code_translation != NULL) |
* If writing, or if mapping a page where writing is ok later on, |
384 |
|
* then invalidate code translations for the (physical) page address: |
385 |
|
*/ |
386 |
|
|
387 |
|
if ((writeflag == MEM_WRITE |
388 |
|
#if !defined(MEM_USERLAND) |
389 |
|
|| (ok == 2 && cache == CACHE_DATA) |
390 |
|
#endif |
391 |
|
) && cpu->invalidate_code_translation != NULL) |
392 |
cpu->invalidate_code_translation(cpu, paddr, INVALIDATE_PADDR); |
cpu->invalidate_code_translation(cpu, paddr, INVALIDATE_PADDR); |
393 |
|
|
394 |
if ((paddr&((1<<BITS_PER_MEMBLOCK)-1)) + len > (1<<BITS_PER_MEMBLOCK)) { |
if ((paddr&((1<<BITS_PER_MEMBLOCK)-1)) + len > (1<<BITS_PER_MEMBLOCK)) { |
396 |
exit(1); |
exit(1); |
397 |
} |
} |
398 |
|
|
399 |
if (writeflag == MEM_WRITE) { |
/* And finally, read or write the data: */ |
400 |
/* Ugly optimization, but it works: */ |
if (writeflag == MEM_WRITE) |
401 |
if (len == sizeof(uint32_t) && (offset & 3)==0 |
memcpy(memblock + offset, data, len); |
402 |
&& ((size_t)data&3)==0) |
else |
403 |
*(uint32_t *)(memblock + offset) = *(uint32_t *)data; |
memcpy(data, memblock + offset, len); |
|
else if (len == sizeof(uint8_t)) |
|
|
*(uint8_t *)(memblock + offset) = *(uint8_t *)data; |
|
|
else |
|
|
memcpy(memblock + offset, data, len); |
|
|
} else { |
|
|
/* Ugly optimization, but it works: */ |
|
|
if (len == sizeof(uint32_t) && (offset & 3)==0 |
|
|
&& ((size_t)data&3)==0) |
|
|
*(uint32_t *)data = *(uint32_t *)(memblock + offset); |
|
|
else if (len == sizeof(uint8_t)) |
|
|
*(uint8_t *)data = *(uint8_t *)(memblock + offset); |
|
|
else |
|
|
memcpy(data, memblock + offset, len); |
|
|
} |
|
|
|
|
404 |
|
|
405 |
do_return_ok: |
do_return_ok: |
406 |
return MEMORY_ACCESS_OK; |
return MEMORY_ACCESS_OK; |