+/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr <
+ tb[1].tc_ptr. Return NULL if not found */
+TranslationBlock *tb_find_pc(unsigned long tc_ptr)
+{
+ int m_min, m_max, m;
+ unsigned long v;
+ TranslationBlock *tb;
+
+ if (nb_tbs <= 0)
+ return NULL;
+ if (tc_ptr < (unsigned long)code_gen_buffer ||
+ tc_ptr >= (unsigned long)code_gen_ptr)
+ return NULL;
+ /* binary search (cf Knuth) */
+ m_min = 0;
+ m_max = nb_tbs - 1;
+ while (m_min <= m_max) {
+ m = (m_min + m_max) >> 1;
+ tb = &tbs[m];
+ v = (unsigned long)tb->tc_ptr;
+ if (v == tc_ptr)
+ return tb;
+ else if (tc_ptr < v) {
+ m_max = m - 1;
+ } else {
+ m_min = m + 1;
+ }
+ }
+ return &tbs[m_max];
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb);
+
+static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n)
+{
+ TranslationBlock *tb1, *tb_next, **ptb;
+ unsigned int n1;
+
+ tb1 = tb->jmp_next[n];
+ if (tb1 != NULL) {
+ /* find head of list */
+ for(;;) {
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == 2)
+ break;
+ tb1 = tb1->jmp_next[n1];
+ }
+ /* we are now sure now that tb jumps to tb1 */
+ tb_next = tb1;
+
+ /* remove tb from the jmp_first list */
+ ptb = &tb_next->jmp_first;
+ for(;;) {
+ tb1 = *ptb;
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == n && tb1 == tb)
+ break;
+ ptb = &tb1->jmp_next[n1];
+ }
+ *ptb = tb->jmp_next[n];
+ tb->jmp_next[n] = NULL;
+
+ /* suppress the jump to next tb in generated code */
+ tb_reset_jump(tb, n);
+
+ /* suppress jumps in the tb on which we could have jumped */
+ tb_reset_jump_recursive(tb_next);
+ }
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb)
+{
+ tb_reset_jump_recursive2(tb, 0);
+ tb_reset_jump_recursive2(tb, 1);
+}
+
+#if defined(TARGET_HAS_ICE)
+static void breakpoint_invalidate(CPUState *env, target_ulong pc)
+{
+ target_ulong phys_addr;
+
+ phys_addr = cpu_get_phys_page_debug(env, pc);
+ tb_invalidate_phys_page_range(phys_addr, phys_addr + 1, 0);
+}
+#endif
+
+/* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a
+ breakpoint is reached */
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
+{
+#if defined(TARGET_HAS_ICE)
+ int i;
+
+ for(i = 0; i < env->nb_breakpoints; i++) {
+ if (env->breakpoints[i] == pc)
+ return 0;
+ }
+
+ if (env->nb_breakpoints >= MAX_BREAKPOINTS)
+ return -1;
+ env->breakpoints[env->nb_breakpoints++] = pc;
+
+ breakpoint_invalidate(env, pc);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/* remove a breakpoint */
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
+{
+#if defined(TARGET_HAS_ICE)
+ int i;
+ for(i = 0; i < env->nb_breakpoints; i++) {
+ if (env->breakpoints[i] == pc)
+ goto found;
+ }
+ return -1;
+ found:
+ env->nb_breakpoints--;
+ if (i < env->nb_breakpoints)
+ env->breakpoints[i] = env->breakpoints[env->nb_breakpoints];
+
+ breakpoint_invalidate(env, pc);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/* enable or disable single step mode. EXCP_DEBUG is returned by the
+ CPU loop after each instruction */
+void cpu_single_step(CPUState *env, int enabled)
+{
+#if defined(TARGET_HAS_ICE)
+ if (env->singlestep_enabled != enabled) {
+ env->singlestep_enabled = enabled;
+ /* must flush all the translated code to avoid inconsistancies */
+ /* XXX: only flush what is necessary */
+ tb_flush(env);
+ }
+#endif
+}
+
+/* enable or disable low levels log */
+void cpu_set_log(int log_flags)
+{
+ loglevel = log_flags;
+ if (loglevel && !logfile) {
+ logfile = fopen(logfilename, "w");
+ if (!logfile) {
+ perror(logfilename);
+ _exit(1);
+ }
+#if !defined(CONFIG_SOFTMMU)
+ /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
+ {
+ static uint8_t logfile_buf[4096];
+ setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
+ }
+#else
+ setvbuf(logfile, NULL, _IOLBF, 0);
+#endif
+ }
+}
+
+void cpu_set_log_filename(const char *filename)
+{
+ logfilename = strdup(filename);
+}
+
+/* mask must never be zero, except for A20 change call */
+void cpu_interrupt(CPUState *env, int mask)
+{
+ TranslationBlock *tb;
+ static int interrupt_lock;
+
+ env->interrupt_request |= mask;
+ /* if the cpu is currently executing code, we must unlink it and
+ all the potentially executing TB */
+ tb = env->current_tb;
+ if (tb && !testandset(&interrupt_lock)) {
+ env->current_tb = NULL;
+ tb_reset_jump_recursive(tb);
+ interrupt_lock = 0;
+ }
+}
+
+void cpu_reset_interrupt(CPUState *env, int mask)
+{
+ env->interrupt_request &= ~mask;
+}
+
+CPULogItem cpu_log_items[] = {
+ { CPU_LOG_TB_OUT_ASM, "out_asm",
+ "show generated host assembly code for each compiled TB" },
+ { CPU_LOG_TB_IN_ASM, "in_asm",
+ "show target assembly code for each compiled TB" },
+ { CPU_LOG_TB_OP, "op",
+ "show micro ops for each compiled TB (only usable if 'in_asm' used)" },
+#ifdef TARGET_I386
+ { CPU_LOG_TB_OP_OPT, "op_opt",
+ "show micro ops after optimization for each compiled TB" },
+#endif
+ { CPU_LOG_INT, "int",
+ "show interrupts/exceptions in short format" },
+ { CPU_LOG_EXEC, "exec",
+ "show trace before each executed TB (lots of logs)" },
+ { CPU_LOG_TB_CPU, "cpu",
+ "show CPU state before bloc translation" },
+#ifdef TARGET_I386
+ { CPU_LOG_PCALL, "pcall",
+ "show protected mode far calls/returns/exceptions" },
+#endif
+#ifdef DEBUG_IOPORT
+ { CPU_LOG_IOPORT, "ioport",
+ "show all i/o ports accesses" },
+#endif
+ { 0, NULL, NULL },
+};
+
+static int cmp1(const char *s1, int n, const char *s2)
+{
+ if (strlen(s2) != n)
+ return 0;
+ return memcmp(s1, s2, n) == 0;
+}
+
+/* takes a comma separated list of log masks. Return 0 if error. */
+int cpu_str_to_log_mask(const char *str)
+{
+ CPULogItem *item;
+ int mask;
+ const char *p, *p1;
+
+ p = str;
+ mask = 0;
+ for(;;) {
+ p1 = strchr(p, ',');
+ if (!p1)
+ p1 = p + strlen(p);
+ if(cmp1(p,p1-p,"all")) {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ mask |= item->mask;
+ }
+ } else {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ if (cmp1(p, p1 - p, item->name))
+ goto found;
+ }
+ return 0;
+ }
+ found:
+ mask |= item->mask;
+ if (*p1 != ',')
+ break;
+ p = p1 + 1;
+ }
+ return mask;
+}
+
+void cpu_abort(CPUState *env, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "qemu: fatal: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+#ifdef TARGET_I386
+ cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
+#else
+ cpu_dump_state(env, stderr, fprintf, 0);
+#endif
+ va_end(ap);
+ abort();
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* NOTE: if flush_global is true, also flush global entries (not
+ implemented yet) */
+void tlb_flush(CPUState *env, int flush_global)
+{
+ int i;
+
+#if defined(DEBUG_TLB)
+ printf("tlb_flush:\n");
+#endif
+ /* must reset current TB so that interrupts cannot modify the
+ links while we are modifying them */
+ env->current_tb = NULL;
+
+ for(i = 0; i < CPU_TLB_SIZE; i++) {
+ env->tlb_read[0][i].address = -1;
+ env->tlb_write[0][i].address = -1;
+ env->tlb_read[1][i].address = -1;
+ env->tlb_write[1][i].address = -1;
+ }
+
+ virt_page_flush();
+ memset (tb_hash, 0, CODE_GEN_HASH_SIZE * sizeof (void *));
+
+#if !defined(CONFIG_SOFTMMU)
+ munmap((void *)MMAP_AREA_START, MMAP_AREA_END - MMAP_AREA_START);
+#endif
+#ifdef USE_KQEMU
+ if (env->kqemu_enabled) {
+ kqemu_flush(env, flush_global);
+ }
+#endif
+ tlb_flush_count++;
+}
+
+static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
+{
+ if (addr == (tlb_entry->address &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK)))
+ tlb_entry->address = -1;
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+ int i, n;
+ VirtPageDesc *vp;
+ PageDesc *p;
+ TranslationBlock *tb;
+
+#if defined(DEBUG_TLB)
+ printf("tlb_flush_page: 0x%08x\n", addr);
+#endif
+ /* must reset current TB so that interrupts cannot modify the
+ links while we are modifying them */
+ env->current_tb = NULL;
+
+ addr &= TARGET_PAGE_MASK;
+ i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ tlb_flush_entry(&env->tlb_read[0][i], addr);
+ tlb_flush_entry(&env->tlb_write[0][i], addr);
+ tlb_flush_entry(&env->tlb_read[1][i], addr);
+ tlb_flush_entry(&env->tlb_write[1][i], addr);
+
+ /* remove from the virtual pc hash table all the TB at this
+ virtual address */
+
+ vp = virt_page_find(addr >> TARGET_PAGE_BITS);
+ if (vp && vp->valid_tag == virt_valid_tag) {
+ p = page_find(vp->phys_addr >> TARGET_PAGE_BITS);
+ if (p) {
+ /* we remove all the links to the TBs in this virtual page */
+ tb = p->first_tb;
+ while (tb != NULL) {
+ n = (long)tb & 3;
+ tb = (TranslationBlock *)((long)tb & ~3);
+ if ((tb->pc & TARGET_PAGE_MASK) == addr ||
+ ((tb->pc + tb->size - 1) & TARGET_PAGE_MASK) == addr) {
+ tb_invalidate(tb);
+ }
+ tb = tb->page_next[n];
+ }
+ }
+ vp->valid_tag = 0;
+ }
+
+#if !defined(CONFIG_SOFTMMU)
+ if (addr < MMAP_AREA_END)
+ munmap((void *)addr, TARGET_PAGE_SIZE);
+#endif
+#ifdef USE_KQEMU
+ if (env->kqemu_enabled) {
+ kqemu_flush_page(env, addr);
+ }
+#endif
+}
+
+static inline void tlb_protect_code1(CPUTLBEntry *tlb_entry, target_ulong addr)
+{
+ if (addr == (tlb_entry->address &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK)) &&
+ (tlb_entry->address & ~TARGET_PAGE_MASK) != IO_MEM_CODE &&
+ (tlb_entry->address & ~TARGET_PAGE_MASK) != IO_MEM_ROM) {
+ tlb_entry->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_CODE;
+ }
+}
+
+/* update the TLBs so that writes to code in the virtual page 'addr'
+ can be detected */
+static void tlb_protect_code(CPUState *env, target_ulong addr)
+{
+ int i;
+
+ addr &= TARGET_PAGE_MASK;
+ i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ tlb_protect_code1(&env->tlb_write[0][i], addr);
+ tlb_protect_code1(&env->tlb_write[1][i], addr);
+#if !defined(CONFIG_SOFTMMU)
+ /* NOTE: as we generated the code for this page, it is already at
+ least readable */
+ if (addr < MMAP_AREA_END)
+ mprotect((void *)addr, TARGET_PAGE_SIZE, PROT_READ);
+#endif
+}
+
+static inline void tlb_unprotect_code2(CPUTLBEntry *tlb_entry,
+ unsigned long phys_addr)
+{
+ if ((tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_CODE &&
+ ((tlb_entry->address & TARGET_PAGE_MASK) + tlb_entry->addend) == phys_addr) {
+ tlb_entry->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY;
+ }
+}
+
+/* update the TLB so that writes in physical page 'phys_addr' are no longer
+ tested self modifying code */
+static void tlb_unprotect_code_phys(CPUState *env, unsigned long phys_addr, target_ulong vaddr)
+{
+ int i;
+
+ phys_addr &= TARGET_PAGE_MASK;
+ phys_addr += (long)phys_ram_base;
+ i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ tlb_unprotect_code2(&env->tlb_write[0][i], phys_addr);
+ tlb_unprotect_code2(&env->tlb_write[1][i], phys_addr);
+}
+
+static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry,
+ unsigned long start, unsigned long length)
+{
+ unsigned long addr;
+ if ((tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+ addr = (tlb_entry->address & TARGET_PAGE_MASK) + tlb_entry->addend;
+ if ((addr - start) < length) {
+ tlb_entry->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY;
+ }
+ }
+}
+
+void cpu_physical_memory_reset_dirty(target_ulong start, target_ulong end,
+ int dirty_flags)
+{
+ CPUState *env;
+ unsigned long length, start1;
+ int i, mask, len;
+ uint8_t *p;
+
+ start &= TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+
+ length = end - start;
+ if (length == 0)
+ return;
+ mask = ~dirty_flags;
+ p = phys_ram_dirty + (start >> TARGET_PAGE_BITS);
+ len = length >> TARGET_PAGE_BITS;
+ for(i = 0; i < len; i++)
+ p[i] &= mask;
+
+ env = cpu_single_env;
+ /* we modify the TLB cache so that the dirty bit will be set again
+ when accessing the range */
+ start1 = start + (unsigned long)phys_ram_base;
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_reset_dirty_range(&env->tlb_write[0][i], start1, length);
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_reset_dirty_range(&env->tlb_write[1][i], start1, length);
+
+#if !defined(CONFIG_SOFTMMU)
+ /* XXX: this is expensive */
+ {
+ VirtPageDesc *p;
+ int j;
+ target_ulong addr;
+
+ for(i = 0; i < L1_SIZE; i++) {
+ p = l1_virt_map[i];
+ if (p) {
+ addr = i << (TARGET_PAGE_BITS + L2_BITS);
+ for(j = 0; j < L2_SIZE; j++) {
+ if (p->valid_tag == virt_valid_tag &&
+ p->phys_addr >= start && p->phys_addr < end &&
+ (p->prot & PROT_WRITE)) {
+ if (addr < MMAP_AREA_END) {
+ mprotect((void *)addr, TARGET_PAGE_SIZE,
+ p->prot & ~PROT_WRITE);
+ }
+ }
+ addr += TARGET_PAGE_SIZE;
+ p++;
+ }
+ }
+ }
+ }
+#endif
+}
+
+static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry,
+ unsigned long start)
+{
+ unsigned long addr;
+ if ((tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_NOTDIRTY) {
+ addr = (tlb_entry->address & TARGET_PAGE_MASK) + tlb_entry->addend;
+ if (addr == start) {
+ tlb_entry->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_RAM;
+ }
+ }
+}
+
+/* update the TLB corresponding to virtual page vaddr and phys addr
+ addr so that it is no longer dirty */
+static inline void tlb_set_dirty(unsigned long addr, target_ulong vaddr)
+{
+ CPUState *env = cpu_single_env;
+ int i;
+
+ phys_ram_dirty[(addr - (unsigned long)phys_ram_base) >> TARGET_PAGE_BITS] = 0xff;
+
+ addr &= TARGET_PAGE_MASK;
+ i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ tlb_set_dirty1(&env->tlb_write[0][i], addr);
+ tlb_set_dirty1(&env->tlb_write[1][i], addr);
+}
+
+/* add a new TLB entry. At most one entry for a given virtual address
+ is permitted. Return 0 if OK or 2 if the page could not be mapped
+ (can only happen in non SOFTMMU mode for I/O pages or pages
+ conflicting with the host address space). */
+int tlb_set_page(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int is_user, int is_softmmu)
+{
+ PhysPageDesc *p;
+ unsigned long pd;
+ TranslationBlock *first_tb;
+ unsigned int index;
+ target_ulong address;
+ unsigned long addend;
+ int ret;
+
+ p = phys_page_find(paddr >> TARGET_PAGE_BITS);
+ first_tb = NULL;
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ PageDesc *p1;
+ pd = p->phys_offset;
+ if ((pd & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) {
+ /* NOTE: we also allocate the page at this stage */
+ p1 = page_find_alloc(pd >> TARGET_PAGE_BITS);
+ first_tb = p1->first_tb;
+ }
+ }
+#if defined(DEBUG_TLB)
+ printf("tlb_set_page: vaddr=0x%08x paddr=0x%08x prot=%x u=%d c=%d smmu=%d pd=0x%08x\n",
+ vaddr, paddr, prot, is_user, (first_tb != NULL), is_softmmu, pd);
+#endif
+
+ ret = 0;
+#if !defined(CONFIG_SOFTMMU)
+ if (is_softmmu)
+#endif
+ {
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) {
+ /* IO memory case */
+ address = vaddr | pd;
+ addend = paddr;
+ } else {
+ /* standard memory */
+ address = vaddr;
+ addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK);
+ }
+
+ index = (vaddr >> 12) & (CPU_TLB_SIZE - 1);
+ addend -= vaddr;
+ if (prot & PAGE_READ) {
+ env->tlb_read[is_user][index].address = address;
+ env->tlb_read[is_user][index].addend = addend;
+ } else {
+ env->tlb_read[is_user][index].address = -1;
+ env->tlb_read[is_user][index].addend = -1;
+ }
+ if (prot & PAGE_WRITE) {
+ if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM) {
+ /* ROM: access is ignored (same as unassigned) */
+ env->tlb_write[is_user][index].address = vaddr | IO_MEM_ROM;
+ env->tlb_write[is_user][index].addend = addend;
+ } else
+ /* XXX: the PowerPC code seems not ready to handle
+ self modifying code with DCBI */
+#if defined(TARGET_HAS_SMC) || 1
+ if (first_tb) {
+ /* if code is present, we use a specific memory
+ handler. It works only for physical memory access */
+ env->tlb_write[is_user][index].address = vaddr | IO_MEM_CODE;
+ env->tlb_write[is_user][index].addend = addend;
+ } else
+#endif
+ if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM &&
+ !cpu_physical_memory_is_dirty(pd)) {
+ env->tlb_write[is_user][index].address = vaddr | IO_MEM_NOTDIRTY;
+ env->tlb_write[is_user][index].addend = addend;
+ } else {
+ env->tlb_write[is_user][index].address = address;
+ env->tlb_write[is_user][index].addend = addend;
+ }
+ } else {
+ env->tlb_write[is_user][index].address = -1;
+ env->tlb_write[is_user][index].addend = -1;
+ }
+ }
+#if !defined(CONFIG_SOFTMMU)
+ else {
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) {
+ /* IO access: no mapping is done as it will be handled by the
+ soft MMU */
+ if (!(env->hflags & HF_SOFTMMU_MASK))
+ ret = 2;
+ } else {
+ void *map_addr;
+
+ if (vaddr >= MMAP_AREA_END) {
+ ret = 2;
+ } else {
+ if (prot & PROT_WRITE) {
+ if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM ||
+#if defined(TARGET_HAS_SMC) || 1
+ first_tb ||
+#endif
+ ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM &&
+ !cpu_physical_memory_is_dirty(pd))) {
+ /* ROM: we do as if code was inside */
+ /* if code is present, we only map as read only and save the
+ original mapping */
+ VirtPageDesc *vp;
+
+ vp = virt_page_find_alloc(vaddr >> TARGET_PAGE_BITS);
+ vp->phys_addr = pd;
+ vp->prot = prot;
+ vp->valid_tag = virt_valid_tag;
+ prot &= ~PAGE_WRITE;
+ }
+ }
+ map_addr = mmap((void *)vaddr, TARGET_PAGE_SIZE, prot,
+ MAP_SHARED | MAP_FIXED, phys_ram_fd, (pd & TARGET_PAGE_MASK));
+ if (map_addr == MAP_FAILED) {
+ cpu_abort(env, "mmap failed when mapped physical address 0x%08x to virtual address 0x%08x\n",
+ paddr, vaddr);
+ }
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+/* called from signal handler: invalidate the code and unprotect the
+ page. Return TRUE if the fault was succesfully handled. */
+int page_unprotect(unsigned long addr, unsigned long pc, void *puc)
+{
+#if !defined(CONFIG_SOFTMMU)
+ VirtPageDesc *vp;
+
+#if defined(DEBUG_TLB)
+ printf("page_unprotect: addr=0x%08x\n", addr);
+#endif
+ addr &= TARGET_PAGE_MASK;
+
+ /* if it is not mapped, no need to worry here */
+ if (addr >= MMAP_AREA_END)
+ return 0;
+ vp = virt_page_find(addr >> TARGET_PAGE_BITS);
+ if (!vp)
+ return 0;
+ /* NOTE: in this case, validate_tag is _not_ tested as it
+ validates only the code TLB */
+ if (vp->valid_tag != virt_valid_tag)
+ return 0;
+ if (!(vp->prot & PAGE_WRITE))
+ return 0;
+#if defined(DEBUG_TLB)
+ printf("page_unprotect: addr=0x%08x phys_addr=0x%08x prot=%x\n",
+ addr, vp->phys_addr, vp->prot);
+#endif
+ if (mprotect((void *)addr, TARGET_PAGE_SIZE, vp->prot) < 0)
+ cpu_abort(cpu_single_env, "error mprotect addr=0x%lx prot=%d\n",
+ (unsigned long)addr, vp->prot);
+ /* set the dirty bit */
+ phys_ram_dirty[vp->phys_addr >> TARGET_PAGE_BITS] = 0xff;
+ /* flush the code inside */
+ tb_invalidate_phys_page(vp->phys_addr, pc, puc);
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+#else
+
+void tlb_flush(CPUState *env, int flush_global)
+{
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+}
+
+int tlb_set_page(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int is_user, int is_softmmu)
+{
+ return 0;
+}
+
+/* dump memory mappings */
+void page_dump(FILE *f)
+{
+ unsigned long start, end;
+ int i, j, prot, prot1;
+ PageDesc *p;
+
+ fprintf(f, "%-8s %-8s %-8s %s\n",
+ "start", "end", "size", "prot");
+ start = -1;
+ end = -1;
+ prot = 0;
+ for(i = 0; i <= L1_SIZE; i++) {
+ if (i < L1_SIZE)
+ p = l1_map[i];
+ else
+ p = NULL;
+ for(j = 0;j < L2_SIZE; j++) {
+ if (!p)
+ prot1 = 0;
+ else
+ prot1 = p[j].flags;
+ if (prot1 != prot) {
+ end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS);
+ if (start != -1) {
+ fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
+ start, end, end - start,
+ prot & PAGE_READ ? 'r' : '-',
+ prot & PAGE_WRITE ? 'w' : '-',
+ prot & PAGE_EXEC ? 'x' : '-');
+ }
+ if (prot1 != 0)
+ start = end;
+ else
+ start = -1;
+ prot = prot1;
+ }
+ if (!p)
+ break;
+ }
+ }
+}
+
+int page_get_flags(unsigned long address)
+{
+ PageDesc *p;
+
+ p = page_find(address >> TARGET_PAGE_BITS);
+ if (!p)
+ return 0;
+ return p->flags;
+}
+
+/* modify the flags of a page and invalidate the code if
+ necessary. The flag PAGE_WRITE_ORG is positionned automatically
+ depending on PAGE_WRITE */
+void page_set_flags(unsigned long start, unsigned long end, int flags)
+{
+ PageDesc *p;
+ unsigned long addr;
+
+ start = start & TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+ if (flags & PAGE_WRITE)
+ flags |= PAGE_WRITE_ORG;
+ spin_lock(&tb_lock);
+ for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
+ p = page_find_alloc(addr >> TARGET_PAGE_BITS);
+ /* if the write protection is set, then we invalidate the code
+ inside */
+ if (!(p->flags & PAGE_WRITE) &&
+ (flags & PAGE_WRITE) &&
+ p->first_tb) {
+ tb_invalidate_phys_page(addr, 0, NULL);
+ }
+ p->flags = flags;
+ }
+ spin_unlock(&tb_lock);
+}
+