--- trunk/src/memory_rw.c 2007/10/08 16:17:48 2 +++ trunk/src/memory_rw.c 2007/10/08 16:18:51 14 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: memory_rw.c,v 1.10 2005/03/01 08:23:55 debug Exp $ + * $Id: memory_rw.c,v 1.64 2005/09/22 09:06:59 debug Exp $ * * Generic memory_rw(), with special hacks for specific CPU families. * @@ -68,45 +68,101 @@ int MEMORY_RW(struct cpu *cpu, struct memory *mem, uint64_t vaddr, unsigned char *data, size_t len, int writeflag, int cache_flags) { +#ifdef MEM_ALPHA + const int offset_mask = 0x1fff; +#else + const int offset_mask = 0xfff; +#endif + #ifndef MEM_USERLAND int ok = 1; #endif uint64_t paddr; int cache, no_exceptions, offset; unsigned char *memblock; -#ifdef BINTRANS +#ifdef MEM_MIPS int bintrans_cached = cpu->machine->bintrans_enable; #endif + int bintrans_device_danger = 0; + no_exceptions = cache_flags & NO_EXCEPTIONS; cache = cache_flags & CACHE_FLAGS_MASK; -#ifdef MEM_PPC - if (cpu->cd.ppc.bits == 32) - vaddr &= 0xffffffff; -#endif +#ifdef MEM_X86 + /* Real-mode wrap-around: */ + if (REAL_MODE && !(cache_flags & PHYSICAL)) { + if ((vaddr & 0xffff) + len > 0x10000) { + /* Do one byte at a time: */ + int res = 0, i; + for (i=0; icd.urisc.wordlen < 64) - mask = ((int64_t)1 << cpu->cd.urisc.wordlen) - 1; - vaddr &= mask; + /* Crossing a page boundary? Then do one byte at a time: */ + if ((vaddr & 0xfff) + len > 0x1000 && !(cache_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, i; + if (writeflag == MEM_WRITE) { + unsigned char tmp; + for (i=0; irunning = 0; + } + return 0; + } + } + } + return res; } -#endif +#endif /* X86 */ #ifdef MEM_MIPS -#ifdef BINTRANS if (bintrans_cached) { if (cache == CACHE_INSTRUCTION) { cpu->cd.mips.pc_bintrans_host_4kpage = NULL; cpu->cd.mips.pc_bintrans_paddr_valid = 0; } } -#endif #endif /* MEM_MIPS */ #ifdef MEM_USERLAND +#ifdef MEM_ALPHA + paddr = vaddr; +#else paddr = vaddr & 0x7fffffff; +#endif goto have_paddr; #endif @@ -130,10 +186,35 @@ if (cache_flags & PHYSICAL || cpu->translate_address == NULL) { paddr = vaddr; + +#ifdef MEM_ALPHA + /* paddr &= 0x1fffffff; For testalpha */ + paddr &= 0x000003ffffffffffULL; +#endif + +#ifdef MEM_IA64 + /* For testia64 */ + paddr &= 0x3fffffff; +#endif + +#ifdef MEM_PPC + if (cpu->cd.ppc.bits == 32) + paddr &= 0xffffffff; +#endif + +#ifdef MEM_SH + paddr &= 0xffffffff; +#endif } else { ok = cpu->translate_address(cpu, vaddr, &paddr, (writeflag? FLAG_WRITEFLAG : 0) + (no_exceptions? FLAG_NOEXCEPTIONS : 0) +#ifdef MEM_X86 + + (cache_flags & NO_SEGMENTATION) +#endif +#ifdef MEM_ARM + + (cache_flags & MEMORY_USER_ACCESS) +#endif + (cache==CACHE_INSTRUCTION? FLAG_INSTR : 0)); /* If the translation caused an exception, or was invalid in some way, we simply return without doing the memory @@ -143,6 +224,22 @@ } +#ifdef MEM_X86 + /* DOS debugging :-) */ + if (!quiet_mode && !(cache_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 + #ifdef MEM_MIPS /* * If correct cache emulation is enabled, and we need to simluate @@ -164,26 +261,22 @@ #endif /* ifndef MEM_USERLAND */ +#if defined(MEM_MIPS) || defined(MEM_USERLAND) have_paddr: +#endif #ifdef MEM_MIPS /* TODO: How about bintrans vs cache emulation? */ -#ifdef BINTRANS if (bintrans_cached) { if (cache == CACHE_INSTRUCTION) { cpu->cd.mips.pc_bintrans_paddr_valid = 1; cpu->cd.mips.pc_bintrans_paddr = paddr; } } -#endif #endif /* MEM_MIPS */ - if (!(cache_flags & PHYSICAL)) - if (no_exceptions) - goto no_exception_access; - #ifndef MEM_USERLAND /* @@ -194,10 +287,35 @@ * to a device to */ if (paddr >= mem->mmap_dev_minaddr && paddr < mem->mmap_dev_maxaddr) { -#ifdef BINTRANS uint64_t orig_paddr = paddr; -#endif int i, start, res; + + /* + * Really really slow, but unfortunately necessary. This is + * to avoid the folowing scenario: + * + * a) offsets 0x000..0x123 are normal memory + * b) offsets 0x124..0x777 are a device + * + * 1) a read is done from offset 0x100. the page is + * added to the bintrans system as a "RAM" page + * 2) a bintranslated read is done from offset 0x200, + * which should access the device, but since the + * entire page is added, it will access non-existant + * RAM instead, without warning. + * + * Setting bintrans_device_danger = 1 on accesses which are + * on _any_ offset on pages that are device mapped avoids + * this problem, but it is probably not very fast. + */ + for (i=0; in_mmapped_devices; i++) + if (paddr >= (mem->dev_baseaddr[i] & ~offset_mask) && + paddr <= ((mem->dev_baseaddr[i] + + mem->dev_length[i] - 1) | offset_mask)) { + bintrans_device_danger = 1; + break; + } + i = start = mem->last_accessed_device; /* Scan through all devices: */ @@ -211,39 +329,42 @@ if (paddr + len > mem->dev_length[i]) len = mem->dev_length[i] - paddr; -#ifdef BINTRANS - if (bintrans_cached && mem->dev_flags[i] & - MEM_BINTRANS_OK) { + if (cpu->update_translation_table != NULL && + mem->dev_flags[i] & MEM_DYNTRANS_OK) { int wf = writeflag == MEM_WRITE? 1 : 0; if (writeflag) { if (paddr < mem-> - dev_bintrans_write_low[i]) + dev_dyntrans_write_low[i]) mem-> - dev_bintrans_write_low - [i] = - paddr & ~0xfff; - if (paddr > mem-> - dev_bintrans_write_high[i]) + dev_dyntrans_write_low + [i] = paddr & + ~offset_mask; + if (paddr >= mem-> + dev_dyntrans_write_high[i]) mem-> - dev_bintrans_write_high - [i] = paddr | 0xfff; + dev_dyntrans_write_high + [i] = paddr | + offset_mask; } if (!(mem->dev_flags[i] & - MEM_BINTRANS_WRITE_OK)) + MEM_DYNTRANS_WRITE_OK)) wf = 0; - update_translation_table(cpu, - vaddr & ~0xfff, - mem->dev_bintrans_data[i] + - (paddr & ~0xfff), - wf, orig_paddr & ~0xfff); + cpu->update_translation_table(cpu, + vaddr & ~offset_mask, + mem->dev_dyntrans_data[i] + + (paddr & ~offset_mask), + wf, orig_paddr & ~offset_mask); } -#endif - res = mem->dev_f[i](cpu, mem, paddr, data, len, - writeflag, mem->dev_extra[i]); + res = 0; + if (!no_exceptions || (mem->dev_flags[i] & + MEM_READING_HAS_NO_SIDE_EFFECTS)) + res = mem->dev_f[i](cpu, mem, paddr, + data, len, writeflag, + mem->dev_extra[i]); #ifdef ENABLE_INSTRUCTION_DELAYS if (res == 0) @@ -253,11 +374,13 @@ ( (abs(res) - 1) * cpu->cd.mips.cpu_type.instrs_per_cycle ); #endif + +#ifndef MEM_X86 /* * If accessing the memory mapped device * failed, then return with a DBE exception. */ - if (res <= 0) { + if (res <= 0 && !no_exceptions) { debug("%s device '%s' addr %08lx " "failed\n", writeflag? "writing to" : "reading from", @@ -268,7 +391,7 @@ #endif return MEMORY_ACCESS_FAILED; } - +#endif goto do_return_ok; } @@ -295,33 +418,6 @@ goto do_return_ok; } break; -#if 0 -/* Remove this, it doesn't work anyway */ - case MMU10K: - /* other cpus: */ - /* - * SUPER-UGLY HACK for SGI-IP32 PROM, R10000: - * K0 bits == 0x3 means uncached... - * - * It seems that during bootup, the SGI-IP32 prom - * stores a return pointers a 0x80000f10, then tests - * memory by writing bit patterns to 0xa0000xxx, and - * then when it's done, reads back the return pointer - * from 0x80000f10. - * - * I need to find the correct way to disconnect the - * cache from the main memory for R10000. (TODO !!!) - */ -/* if ((cpu->cd.mips.coproc[0]->reg[COP0_CONFIG] & 7) == 3) { */ -/* - if (cache == CACHE_DATA && - cpu->r10k_cache_disable_TODO) { - paddr &= ((512*1024)-1); - paddr += 512*1024; - } -*/ - break; -#endif default: /* R4000 etc */ /* TODO */ @@ -332,7 +428,8 @@ /* Outside of physical RAM? */ if (paddr >= mem->physical_max) { - if ((paddr & 0xffff000000ULL) == 0x1f000000) { +#ifdef MEM_MIPS + if ((paddr & 0xffffc00000ULL) == 0x1fc00000) { /* Ok, this is PROM stuff */ } else if ((paddr & 0xfffff00000ULL) == 0x1ff00000) { /* Sprite reads from this area of memory... */ @@ -340,13 +437,27 @@ if (writeflag == MEM_READ) memset(data, 0, len); goto do_return_ok; - } else { - if (paddr >= mem->physical_max + 0 * 1024) { + } else +#endif /* MIPS */ + { + if (paddr >= mem->physical_max) { char *symbol; -#ifdef MEM_MIPS + uint64_t old_pc; uint64_t offset; + +#ifdef MEM_MIPS + old_pc = cpu->cd.mips.pc_last; +#else + /* Default instruction size on most + RISC archs is 32 bits: */ + old_pc = cpu->pc - sizeof(uint32_t); #endif - if (!quiet_mode) { + + /* 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) { @@ -373,35 +484,42 @@ data[i]); debug("}"); } -#ifdef MEM_MIPS + + 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, - cpu->cd.mips.pc_last, &offset); -#else - symbol = "(unimpl for non-MIPS)"; -#endif - -/* TODO: fix! not mips.pc_last for for example ppc */ - - fatal(" paddr=%llx >= physical_max pc=" - "0x%08llx <%s> ]\n", - (long long)paddr, - (long long)cpu->cd.mips.pc_last, - symbol? symbol : "no symbol"); + old_pc, &offset); + fatal(" <%s> ]\n", + symbol? symbol : " no symbol "); } if (cpu->machine->single_step_on_bad_addr) { fatal("[ unimplemented access to " - "0x%016llx, pc = 0x%016llx ]\n", - (long long)paddr, - (long long)cpu->pc); + "0x%llx, pc=0x",(long long)paddr); + if (cpu->is_32bit) + fatal("%08x ]\n", + (int)old_pc); + else + fatal("%016llx ]\n", + (long long)old_pc); single_step = 1; } } if (writeflag == MEM_READ) { +#ifdef MEM_X86 + /* Reading non-existant memory on x86: */ + memset(data, 0xff, len); +#else /* Return all zeroes? (Or 0xff? TODO) */ memset(data, 0, len); +#endif #ifdef MEM_MIPS /* @@ -409,7 +527,8 @@ * an exceptions on an illegal read: */ if (cache != CACHE_NONE && cpu->machine-> - dbe_on_nonexistant_memaccess) { + dbe_on_nonexistant_memaccess && + !no_exceptions) { if (paddr >= mem->physical_max && paddr < mem->physical_max+1048576) mips_cpu_exception(cpu, @@ -429,8 +548,6 @@ #endif /* ifndef MEM_USERLAND */ -no_exception_access: - /* * Uncached access: */ @@ -443,10 +560,9 @@ offset = paddr & ((1 << BITS_PER_MEMBLOCK) - 1); -#ifdef BINTRANS - if (bintrans_cached) - update_translation_table(cpu, vaddr & ~0xfff, - memblock + (offset & ~0xfff), + if (cpu->update_translation_table != NULL && !bintrans_device_danger) + cpu->update_translation_table(cpu, vaddr & ~offset_mask, + memblock + (offset & ~offset_mask), #if 0 cache == CACHE_INSTRUCTION? (writeflag == MEM_WRITE? 1 : 0) @@ -454,34 +570,41 @@ #else writeflag == MEM_WRITE? 1 : 0, #endif - paddr & ~0xfff); -#endif + paddr & ~offset_mask); + + if (writeflag == MEM_WRITE && + cpu->invalidate_code_translation != NULL) + cpu->invalidate_code_translation(cpu, paddr, INVALIDATE_PADDR); if (writeflag == MEM_WRITE) { - if (len == sizeof(uint32_t) && (offset & 3)==0) + /* Ugly optimization, but it works: */ + if (len == sizeof(uint32_t) && (offset & 3)==0 + && ((size_t)data&3)==0) *(uint32_t *)(memblock + offset) = *(uint32_t *)data; else if (len == sizeof(uint8_t)) *(uint8_t *)(memblock + offset) = *(uint8_t *)data; else memcpy(memblock + offset, data, len); } else { - if (len == sizeof(uint32_t) && (offset & 3)==0) + /* 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); +#ifdef MEM_MIPS if (cache == CACHE_INSTRUCTION) { cpu->cd.mips.pc_last_host_4k_page = memblock - + (offset & ~0xfff); -#ifdef BINTRANS + + (offset & ~offset_mask); if (bintrans_cached) { cpu->cd.mips.pc_bintrans_host_4kpage = cpu->cd.mips.pc_last_host_4k_page; } -#endif } +#endif /* MIPS */ }