--- trunk/src/memory_rw.c 2007/10/08 16:18:51 14 +++ trunk/src/memory_rw.c 2007/10/08 16:22:11 40 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2005 Anders Gavare. All rights reserved. + * Copyright (C) 2003-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: memory_rw.c,v 1.64 2005/09/22 09:06:59 debug Exp $ + * $Id: memory_rw.c,v 1.102 2007/04/16 15:13:44 debug Exp $ * * Generic memory_rw(), with special hacks for specific CPU families. * @@ -48,15 +48,11 @@ * a placeholder for data when reading from memory * len the length of the 'data' buffer * writeflag set to MEM_READ or MEM_WRITE - * cache_flags CACHE_{NONE,DATA,INSTRUCTION} | other flags + * misc_flags CACHE_{NONE,DATA,INSTRUCTION} | other flags * * If the address indicates access to a memory mapped device, that device' * read/write access function is called. * - * If instruction latency/delay support is enabled, then - * cpu->instruction_delay is increased by the number of instruction to - * delay execution. - * * This function should not be called with cpu == NULL. * * Returns one of the following: @@ -66,7 +62,7 @@ * (MEMORY_ACCESS_FAILED is 0.) */ int MEMORY_RW(struct cpu *cpu, struct memory *mem, uint64_t vaddr, - unsigned char *data, size_t len, int writeflag, int cache_flags) + unsigned char *data, size_t len, int writeflag, int misc_flags) { #ifdef MEM_ALPHA const int offset_mask = 0x1fff; @@ -80,82 +76,11 @@ uint64_t paddr; int cache, no_exceptions, offset; unsigned char *memblock; -#ifdef MEM_MIPS - int bintrans_cached = cpu->machine->bintrans_enable; -#endif - int bintrans_device_danger = 0; + int dyntrans_device_danger = 0; - no_exceptions = cache_flags & NO_EXCEPTIONS; - cache = cache_flags & CACHE_FLAGS_MASK; + no_exceptions = misc_flags & NO_EXCEPTIONS; + cache = misc_flags & CACHE_FLAGS_MASK; -#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; i 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 /* X86 */ - -#ifdef MEM_MIPS - if (bintrans_cached) { - if (cache == CACHE_INSTRUCTION) { - cpu->cd.mips.pc_bintrans_host_4kpage = NULL; - cpu->cd.mips.pc_bintrans_paddr_valid = 0; - } - } -#endif /* MEM_MIPS */ #ifdef MEM_USERLAND #ifdef MEM_ALPHA @@ -163,132 +88,46 @@ #else paddr = vaddr & 0x7fffffff; #endif - goto have_paddr; -#endif - -#ifndef MEM_USERLAND -#ifdef MEM_MIPS - /* - * For instruction fetch, are we on the same page as the last - * instruction we fetched? - * - * NOTE: There's no need to check this stuff here if this address - * is known to be in host ram, as it's done at instruction fetch - * time in cpu.c! Only check if _host_4k_page == NULL. - */ - if (cache == CACHE_INSTRUCTION && - cpu->cd.mips.pc_last_host_4k_page == NULL && - (vaddr & ~0xfff) == cpu->cd.mips.pc_last_virtual_page) { - paddr = cpu->cd.mips.pc_last_physical_page | (vaddr & 0xfff); - goto have_paddr; - } -#endif /* MEM_MIPS */ - - if (cache_flags & PHYSICAL || cpu->translate_address == NULL) { +#else /* !MEM_USERLAND */ + if (misc_flags & PHYSICAL || cpu->translate_v2p == 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, + ok = cpu->translate_v2p(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) + + (misc_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 - access: */ + + /* + * If the translation caused an exception, or was invalid in + * some way, then simply return without doing the memory + * access: + */ if (!ok) return MEMORY_ACCESS_FAILED; } - -#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 - * cache misses even from the instruction cache, we can't run directly - * from a host page. :-/ - */ -#if defined(ENABLE_CACHE_EMULATION) && defined(ENABLE_INSTRUCTION_DELAYS) -#else - if (cache == CACHE_INSTRUCTION) { - cpu->cd.mips.pc_last_virtual_page = vaddr & ~0xfff; - cpu->cd.mips.pc_last_physical_page = paddr & ~0xfff; - cpu->cd.mips.pc_last_host_4k_page = NULL; - - /* _last_host_4k_page will be set to 1 further down, - if the page is actually in host ram */ - } -#endif -#endif /* MEM_MIPS */ -#endif /* ifndef MEM_USERLAND */ - - -#if defined(MEM_MIPS) || defined(MEM_USERLAND) -have_paddr: -#endif - - -#ifdef MEM_MIPS - /* TODO: How about bintrans vs cache emulation? */ - if (bintrans_cached) { - if (cache == CACHE_INSTRUCTION) { - cpu->cd.mips.pc_bintrans_paddr_valid = 1; - cpu->cd.mips.pc_bintrans_paddr = paddr; - } - } -#endif /* MEM_MIPS */ - +#endif /* !MEM_USERLAND */ #ifndef MEM_USERLAND /* * Memory mapped device? * - * TODO: this is utterly slow. - * TODO2: if paddr= mem->mmap_dev_minaddr && paddr < mem->mmap_dev_maxaddr) { uint64_t orig_paddr = paddr; - int i, start, res; + int i, start, end, res; + +#if 0 + +TODO: The correct solution for this is to add RAM devices _around_ the +dangerous device. The solution below incurs a slowdown for _everything_, +not just the device in question. /* * Really really slow, but unfortunately necessary. This is @@ -298,84 +137,97 @@ * 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, + * added to the dyntrans system as a "RAM" page + * 2) a dyntranslated 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 + * Setting dyntrans_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. + * + * TODO: Convert this into a quick (multi-level, 64-bit) + * address space lookup, to find dangerous pages. */ 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; + if (paddr >= (mem->devices[i].baseaddr & ~offset_mask)&& + paddr <= ((mem->devices[i].endaddr-1)|offset_mask)){ + dyntrans_device_danger = 1; break; } +#endif - i = start = mem->last_accessed_device; + start = 0; end = mem->n_mmapped_devices - 1; + i = mem->last_accessed_device; /* Scan through all devices: */ do { - if (paddr >= mem->dev_baseaddr[i] && - paddr < mem->dev_baseaddr[i] + mem->dev_length[i]) { + if (paddr >= mem->devices[i].baseaddr && + paddr < mem->devices[i].endaddr) { /* Found a device, let's access it: */ mem->last_accessed_device = i; - paddr -= mem->dev_baseaddr[i]; - if (paddr + len > mem->dev_length[i]) - len = mem->dev_length[i] - paddr; + paddr -= mem->devices[i].baseaddr; + if (paddr + len > mem->devices[i].length) + len = mem->devices[i].length - paddr; if (cpu->update_translation_table != NULL && - mem->dev_flags[i] & MEM_DYNTRANS_OK) { + !(ok & MEMORY_NOT_FULL_PAGE) && + mem->devices[i].flags & DM_DYNTRANS_OK) { int wf = writeflag == MEM_WRITE? 1 : 0; + unsigned char *host_addr; - if (writeflag) { - if (paddr < mem-> - dev_dyntrans_write_low[i]) - mem-> - dev_dyntrans_write_low - [i] = paddr & - ~offset_mask; - if (paddr >= mem-> - dev_dyntrans_write_high[i]) - mem-> - dev_dyntrans_write_high - [i] = paddr | - offset_mask; + if (!(mem->devices[i].flags & + DM_DYNTRANS_WRITE_OK)) + wf = 0; + + if (writeflag && wf) { + if (paddr < mem->devices[i]. + dyntrans_write_low) + mem->devices[i]. + dyntrans_write_low = + paddr &~offset_mask; + if (paddr >= mem->devices[i]. + dyntrans_write_high) + mem->devices[i]. + dyntrans_write_high = + paddr | offset_mask; } - if (!(mem->dev_flags[i] & - MEM_DYNTRANS_WRITE_OK)) - wf = 0; + if (mem->devices[i].flags & + DM_EMULATED_RAM) { + /* MEM_WRITE to force the page + to be allocated, if it + wasn't already */ + uint64_t *pp = (uint64_t *)mem-> + devices[i].dyntrans_data; + uint64_t p = orig_paddr - *pp; + host_addr = + memory_paddr_to_hostaddr( + mem, p & ~offset_mask, + MEM_WRITE); + } else { + host_addr = mem->devices[i]. + dyntrans_data + + (paddr & ~offset_mask); + } cpu->update_translation_table(cpu, - vaddr & ~offset_mask, - mem->dev_dyntrans_data[i] + - (paddr & ~offset_mask), + vaddr & ~offset_mask, host_addr, wf, orig_paddr & ~offset_mask); } res = 0; - if (!no_exceptions || (mem->dev_flags[i] & - MEM_READING_HAS_NO_SIDE_EFFECTS)) - res = mem->dev_f[i](cpu, mem, paddr, + if (!no_exceptions || (mem->devices[i].flags & + DM_READS_HAVE_NO_SIDE_EFFECTS)) + res = mem->devices[i].f(cpu, mem, paddr, data, len, writeflag, - mem->dev_extra[i]); + mem->devices[i].extra); -#ifdef ENABLE_INSTRUCTION_DELAYS if (res == 0) res = -1; - cpu->cd.mips.instruction_delay += - ( (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. @@ -384,21 +236,22 @@ debug("%s device '%s' addr %08lx " "failed\n", writeflag? "writing to" : "reading from", - mem->dev_name[i], (long)paddr); + mem->devices[i].name, (long)paddr); #ifdef MEM_MIPS mips_cpu_exception(cpu, EXCEPTION_DBE, 0, vaddr, 0, 0, 0, 0); #endif return MEMORY_ACCESS_FAILED; } -#endif goto do_return_ok; } - i ++; - if (i == mem->n_mmapped_devices) - i = 0; - } while (i != start); + if (paddr < mem->devices[i].baseaddr) + end = i - 1; + if (paddr >= mem->devices[i].endaddr) + start = i + 1; + i = (start + end) >> 1; + } while (start <= end); } @@ -410,7 +263,7 @@ switch (cpu->cd.mips.cpu_type.mmu_model) { case MMU3K: /* if not uncached addess (TODO: generalize this) */ - if (!(cache_flags & PHYSICAL) && cache != CACHE_NONE && + if (!(misc_flags & PHYSICAL) && cache != CACHE_NONE && !((vaddr & 0xffffffffULL) >= 0xa0000000ULL && (vaddr & 0xffffffffULL) <= 0xbfffffffULL)) { if (memory_cache_R3000(cpu, cache, paddr, @@ -440,86 +293,13 @@ } else #endif /* MIPS */ { - if (paddr >= mem->physical_max) { - char *symbol; - 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 - - /* 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= 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 "); - } - - if (cpu->machine->single_step_on_bad_addr) { - fatal("[ unimplemented access to " - "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 (paddr >= mem->physical_max && !no_exceptions) + memory_warn_about_unimplemented_addr + (cpu, mem, writeflag, paddr, data, len); 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 /* @@ -550,63 +330,69 @@ /* * Uncached access: + * + * 1) Translate the physical address to a host address. + * + * 2) Insert this virtual->physical->host translation into the + * fast translation arrays (using update_translation_table()). + * + * 3) If this was a Write, then invalidate any code translations + * in that page. */ - memblock = memory_paddr_to_hostaddr(mem, paddr, writeflag); + memblock = memory_paddr_to_hostaddr(mem, paddr & ~offset_mask, + writeflag); if (memblock == NULL) { if (writeflag == MEM_READ) memset(data, 0, len); goto do_return_ok; } - offset = paddr & ((1 << BITS_PER_MEMBLOCK) - 1); + offset = paddr & offset_mask; - if (cpu->update_translation_table != NULL && !bintrans_device_danger) + if (cpu->update_translation_table != NULL && !dyntrans_device_danger +#ifdef MEM_MIPS + /* Ugly hack for R2000/R3000 caches: */ + && (cpu->cd.mips.cpu_type.mmu_model != MMU3K || + !(cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & MIPS1_ISOL_CACHES)) +#endif +#ifndef MEM_MIPS +/* && !(misc_flags & MEMORY_USER_ACCESS) */ +#ifndef MEM_USERLAND + && !(ok & MEMORY_NOT_FULL_PAGE) +#endif +#endif + && !no_exceptions) cpu->update_translation_table(cpu, vaddr & ~offset_mask, - memblock + (offset & ~offset_mask), -#if 0 - cache == CACHE_INSTRUCTION? - (writeflag == MEM_WRITE? 1 : 0) - : ok - 1, + memblock, (misc_flags & MEMORY_USER_ACCESS) | +#if !defined(MEM_MIPS) && !defined(MEM_USERLAND) + (cache == CACHE_INSTRUCTION? + (writeflag == MEM_WRITE? 1 : 0) : ok - 1), #else - writeflag == MEM_WRITE? 1 : 0, + (writeflag == MEM_WRITE? 1 : 0), #endif paddr & ~offset_mask); - if (writeflag == MEM_WRITE && - cpu->invalidate_code_translation != NULL) + /* + * If writing, or if mapping a page where writing is ok later on, + * then invalidate code translations for the (physical) page address: + */ + if ((writeflag == MEM_WRITE +#if !defined(MEM_USERLAND) + || ok == 2 +#endif + ) && cpu->invalidate_code_translation != NULL) cpu->invalidate_code_translation(cpu, paddr, INVALIDATE_PADDR); - if (writeflag == MEM_WRITE) { - /* 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 { - /* 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 & ~offset_mask); - if (bintrans_cached) { - cpu->cd.mips.pc_bintrans_host_4kpage = - cpu->cd.mips.pc_last_host_4k_page; - } - } -#endif /* MIPS */ + if ((paddr&((1< (1<