/* * LatticeMico32 helper routines. * * Copyright (c) 2010-2014 Michael Walle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "qemu/host-utils.h" #include "sysemu/sysemu.h" #include "hw/semihosting/semihost.h" #include "exec/log.h" bool lm32_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; int prot; address &= TARGET_PAGE_MASK; prot = PAGE_BITS; if (env->flags & LM32_FLAG_IGNORE_MSB) { tlb_set_page(cs, address, address & 0x7fffffff, prot, mmu_idx, TARGET_PAGE_SIZE); } else { tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); } return true; } hwaddr lm32_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { LM32CPU *cpu = LM32_CPU(cs); addr &= TARGET_PAGE_MASK; if (cpu->env.flags & LM32_FLAG_IGNORE_MSB) { return addr & 0x7fffffff; } else { return addr; } } void lm32_breakpoint_insert(CPULM32State *env, int idx, target_ulong address) { cpu_breakpoint_insert(env_cpu(env), address, BP_CPU, &env->cpu_breakpoint[idx]); } void lm32_breakpoint_remove(CPULM32State *env, int idx) { if (!env->cpu_breakpoint[idx]) { return; } cpu_breakpoint_remove_by_ref(env_cpu(env), env->cpu_breakpoint[idx]); env->cpu_breakpoint[idx] = NULL; } void lm32_watchpoint_insert(CPULM32State *env, int idx, target_ulong address, lm32_wp_t wp_type) { int flags = 0; switch (wp_type) { case LM32_WP_DISABLED: /* nothing to do */ break; case LM32_WP_READ: flags = BP_CPU | BP_STOP_BEFORE_ACCESS | BP_MEM_READ; break; case LM32_WP_WRITE: flags = BP_CPU | BP_STOP_BEFORE_ACCESS | BP_MEM_WRITE; break; case LM32_WP_READ_WRITE: flags = BP_CPU | BP_STOP_BEFORE_ACCESS | BP_MEM_ACCESS; break; } if (flags != 0) { cpu_watchpoint_insert(env_cpu(env), address, 1, flags, &env->cpu_watchpoint[idx]); } } void lm32_watchpoint_remove(CPULM32State *env, int idx) { if (!env->cpu_watchpoint[idx]) { return; } cpu_watchpoint_remove_by_ref(env_cpu(env), env->cpu_watchpoint[idx]); env->cpu_watchpoint[idx] = NULL; } static bool check_watchpoints(CPULM32State *env) { LM32CPU *cpu = env_archcpu(env); int i; for (i = 0; i < cpu->num_watchpoints; i++) { if (env->cpu_watchpoint[i] && env->cpu_watchpoint[i]->flags & BP_WATCHPOINT_HIT) { return true; } } return false; } void lm32_debug_excp_handler(CPUState *cs) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; CPUBreakpoint *bp; if (cs->watchpoint_hit) { if (cs->watchpoint_hit->flags & BP_CPU) { cs->watchpoint_hit = NULL; if (check_watchpoints(env)) { raise_exception(env, EXCP_WATCHPOINT); } else { cpu_loop_exit_noexc(cs); } } } else { QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { if (bp->pc == env->pc) { if (bp->flags & BP_CPU) { raise_exception(env, EXCP_BREAKPOINT); } break; } } } } void lm32_cpu_do_interrupt(CPUState *cs) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; qemu_log_mask(CPU_LOG_INT, "exception at pc=%x type=%x\n", env->pc, cs->exception_index); switch (cs->exception_index) { case EXCP_SYSTEMCALL: if (unlikely(semihosting_enabled())) { /* do_semicall() returns true if call was handled. Otherwise * do the normal exception handling. */ if (lm32_cpu_do_semihosting(cs)) { env->pc += 4; break; } } /* fall through */ case EXCP_INSN_BUS_ERROR: case EXCP_DATA_BUS_ERROR: case EXCP_DIVIDE_BY_ZERO: case EXCP_IRQ: /* non-debug exceptions */ env->regs[R_EA] = env->pc; env->ie |= (env->ie & IE_IE) ? IE_EIE : 0; env->ie &= ~IE_IE; if (env->dc & DC_RE) { env->pc = env->deba + (cs->exception_index * 32); } else { env->pc = env->eba + (cs->exception_index * 32); } log_cpu_state_mask(CPU_LOG_INT, cs, 0); break; case EXCP_BREAKPOINT: case EXCP_WATCHPOINT: /* debug exceptions */ env->regs[R_BA] = env->pc; env->ie |= (env->ie & IE_IE) ? IE_BIE : 0; env->ie &= ~IE_IE; env->pc = env->deba + (cs->exception_index * 32); log_cpu_state_mask(CPU_LOG_INT, cs, 0); break; default: cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); break; } } bool lm32_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->ie & IE_IE)) { cs->exception_index = EXCP_IRQ; lm32_cpu_do_interrupt(cs); return true; } return false; } /* Some soc ignores the MSB on the address bus. Thus creating a shadow memory * area. As a general rule, 0x00000000-0x7fffffff is cached, whereas * 0x80000000-0xffffffff is not cached and used to access IO devices. */ void cpu_lm32_set_phys_msb_ignore(CPULM32State *env, int value) { if (value) { env->flags |= LM32_FLAG_IGNORE_MSB; } else { env->flags &= ~LM32_FLAG_IGNORE_MSB; } }