add openrisc MMU support. Signed-off-by: Jia Liu <pro...@gmail.com> --- target-openrisc/cpu.h | 82 ++++++++++++++++- target-openrisc/mmu.c | 199 +++++++++++++++++++++++++++++++++++++++++- target-openrisc/mmu_helper.c | 20 +++++ 3 files changed, 298 insertions(+), 3 deletions(-)
diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 24d82f8..779c889 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -55,6 +55,9 @@ struct CPUOpenriscState; (reg) |= ((v & 0x1f) << 2);\ } while (0) +/* Internel flags, delay slot flag */ +#define D_FLAG 1 + /* Verison Register */ #define SPR_VR 0x12000001 #define SPR_CPUCFGR 0x12000001 @@ -92,6 +95,25 @@ enum { FPCSR_DZF = (1 << 11), }; +/* Exceptions indices */ +enum { + EXCP_RESET = 0x1, + EXCP_BUSERR = 0x2, + EXCP_DPF = 0x3, + EXCP_IPF = 0x4, + EXCP_TICK = 0x5, + EXCP_ALIGN = 0x6, + EXCP_ILLEGAL = 0x7, + EXCP_INT = 0x8, + EXCP_DTLBMISS = 0x9, + EXCP_ITLBMISS = 0xa, + EXCP_RANGE = 0xb, + EXCP_SYSCALL = 0xc, + EXCP_FPE = 0xd, + EXCP_TRAP = 0xe, + EXCP_NR, +}; + /* Supervisor register */ enum { SR_SM = 1, @@ -114,6 +136,39 @@ enum { SR_SCE = (1<<17), }; +enum { + DTLB_WAYS = 1, + DTLB_SIZE = 64, + DTLB_MASK = (DTLB_SIZE-1), + ITLB_WAYS = 1, + ITLB_SIZE = 64, + ITLB_MASK = (ITLB_SIZE-1), +}; + +/* TLB prot */ +enum { + URE = (1<<6), + UWE = (1<<7), + SRE = (1<<8), + SWE = (1<<9), + + SXE = (1<<6), + UXE = (1<<7), +}; + +/* check if tlb available */ +enum { + TLBRET_INVALID = -3, + TLBRET_NOMATCH = -2, + TLBRET_BADADDR = -1, + TLBRET_MATCH = 0 +}; + +typedef struct tlb_entry { + uint32_t mr; + uint32_t tr; +} tlb_entry; + typedef struct CPUOpenriscState CPUOpenriscState; struct CPUOpenriscState { target_ulong gpr[32]; /* General registers */ @@ -123,9 +178,21 @@ struct CPUOpenriscState { target_ulong epcr; /* Exception PC register */ target_ulong eear; /* Exception EA register */ uint32_t esr; /* Exception supervisor register */ +#if !defined(CONFIG_USER_ONLY) + tlb_entry itlb[ITLB_WAYS][ITLB_SIZE]; + tlb_entry dtlb[DTLB_WAYS][DTLB_SIZE]; +#endif CPU_COMMON +#if !defined(CONFIG_USER_ONLY) + int (*map_address_code)(struct CPUOpenriscState *env, + target_phys_addr_t *physical, int *prot, + target_ulong address, int rw); + int (*map_address_data)(struct CPUOpenriscState *env, + target_phys_addr_t *physical, int *prot, + target_ulong address, int rw); +#endif uint32_t fpcsr; /* Float register */ target_ulong pc; /* Program counter */ target_ulong npc; /* Next PC */ @@ -187,10 +254,13 @@ OpenriscCPU *cpu_openrisc_init(const char *cpu_model); int cpu_openrisc_exec(CPUOpenriscState *s); void do_interrupt(CPUOpenriscState *env); void openrisc_translate_init(void); +int cpu_openrisc_handle_mmu_fault(CPUOpenriscState *env, target_ulong address, + int rw, int mmu_idx); #define cpu_list cpu_openrisc_list #define cpu_exec cpu_openrisc_exec #define cpu_gen_code cpu_openrisc_gen_code +#define cpu_handle_mmu_fault cpu_openrisc_handle_mmu_fault #define CPU_SAVE_VERSION 1 @@ -201,6 +271,10 @@ void cpu_openrisc_pic_reset(CPUOpenriscState *env); void openrisc_mmu_init(CPUOpenriscState *env); int get_phys_nommu(CPUOpenriscState *env, target_phys_addr_t *physical, int *prot, target_ulong address, int rw); +int get_phys_code(CPUOpenriscState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw); +int get_phys_data(CPUOpenriscState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw); #endif static inline CPUOpenriscState *cpu_init(const char *cpu_model) @@ -220,12 +294,16 @@ static inline void cpu_get_tb_cpu_state(CPUOpenriscState *env, { *pc = env->pc; *cs_base = 0; - *flags = 0; + /* D_FLAG -- branch insrtuction exception */ + *flags = (env->flags&D_FLAG); } static inline int cpu_mmu_index(CPUOpenriscState *env) { - return 0; + if (!(env->sr & SR_IME)) { + return MMU_NOMMU_IDX; + } + return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX; } static inline bool cpu_has_work(CPUOpenriscState *env) diff --git a/target-openrisc/mmu.c b/target-openrisc/mmu.c index 4f0312f..2403b77 100644 --- a/target-openrisc/mmu.c +++ b/target-openrisc/mmu.c @@ -27,13 +27,210 @@ #endif #if !defined(CONFIG_USER_ONLY) +/* no MMU emulation */ +int get_phys_nommu(CPUOpenriscState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw) +{ + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; + return TLBRET_MATCH; +} + +int get_phys_code(CPUOpenriscState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw) +{ + int vpn = address >> TARGET_PAGE_BITS; + int idx = vpn & ITLB_MASK; + int right = 0; + + if ((env->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + return TLBRET_NOMATCH; + } + if (!(env->itlb[0][idx].mr & 1)) { + return TLBRET_INVALID; + } + + if (env->sr & SR_SM) { /* supervisor mode */ + if (env->itlb[0][idx].tr & SXE) { + right |= PAGE_EXEC; + } + } else { + if (env->itlb[0][idx].tr & UXE) { + right |= PAGE_EXEC; + } + } + + if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { + return TLBRET_BADADDR; + } + + *physical = (env->itlb[0][idx].tr & TARGET_PAGE_MASK) | + (address & (TARGET_PAGE_SIZE-1)); + *prot = right; + return TLBRET_MATCH; +} + +int get_phys_data(CPUOpenriscState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw) +{ + int vpn = address >> TARGET_PAGE_BITS; + int idx = vpn & DTLB_MASK; + int right = 0; + + if ((env->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + return TLBRET_NOMATCH; + } + if (!(env->dtlb[0][idx].mr & 1)) { + return TLBRET_INVALID; + } + + if (env->sr & SR_SM) { /* supervisor mode */ + if (env->dtlb[0][idx].tr & SRE) { + right |= PAGE_READ; + } + if (env->dtlb[0][idx].tr & SWE) { + right |= PAGE_WRITE; + } + } else { + if (env->dtlb[0][idx].tr & URE) { + right |= PAGE_READ; + } + if (env->dtlb[0][idx].tr & UWE) { + right |= PAGE_WRITE; + } + } + + if ((rw & 0) && ((right & PAGE_READ) == 0)) { + return TLBRET_BADADDR; + } + if ((rw & 1) && ((right & PAGE_WRITE) == 0)) { + return TLBRET_BADADDR; + } + + *physical = (env->dtlb[0][idx].tr & TARGET_PAGE_MASK) | + (address & (TARGET_PAGE_SIZE-1)); + *prot = right; + return TLBRET_MATCH; +} + +static int get_physical_address(CPUOpenriscState *env, + target_phys_addr_t *physical, + int *prot, target_ulong address, + int rw) +{ + int ret = TLBRET_MATCH; + + /* [0x0000--0x2000]: unmapped */ + if (address < 0x2000 && (env->sr & SR_SM)) { + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; + return ret; + } + + if (rw == 2) { /* ITLB */ + *physical = 0; + ret = env->map_address_code(env, physical, + prot, address, rw); + } else { /* DTLB */ + ret = env->map_address_data(env, physical, + prot, address, rw); + } + + return ret; +} +#endif + +static void raise_mmu_exception(CPUOpenriscState *env, target_ulong address, + int rw, int tlb_error) +{ + int exception = 0; + + switch (tlb_error) { + default: + if (rw == 2) { + exception = EXCP_IPF; + } else { + exception = EXCP_DPF; + } + break; +#if !defined(CONFIG_USER_ONLY) + case TLBRET_BADADDR: + if (rw == 2) { + exception = EXCP_IPF; + } else { + exception = EXCP_DPF; + } + break; + case TLBRET_INVALID: + case TLBRET_NOMATCH: + /* No TLB match for a mapped address */ + if (rw == 2) { + exception = EXCP_ITLBMISS; + } else { + exception = EXCP_DTLBMISS; + } + break; +#endif + } + + env->exception_index = exception; + env->eear = address; +} + +#if !defined(CONFIG_USER_ONLY) +int cpu_openrisc_handle_mmu_fault(CPUOpenriscState *env, + target_ulong address, int rw, int mmu_idx) +{ + int ret = 0; + target_phys_addr_t physical = 0; + int prot = 0; + + ret = get_physical_address(env, &physical, &prot, + address, rw); + + if (ret == TLBRET_MATCH) { + tlb_set_page(env, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; + } else if (ret < 0) { + raise_mmu_exception(env, address, rw, ret); + ret = 1; + } + + return ret; +} +#else +int cpu_openrisc_handle_mmu_fault(CPUOpenriscState *env, + target_ulong address, int rw, int mmu_idx) +{ + int ret = 0; + + raise_mmu_exception(env, address, rw, ret); + ret = 1; + + return ret; +} +#endif + +#if !defined(CONFIG_USER_ONLY) target_phys_addr_t cpu_get_phys_page_debug(CPUOpenriscState *env, target_ulong addr) { - return 0; + target_phys_addr_t phys_addr; + int prot; + + if (get_physical_address(env, &phys_addr, &prot, addr, 0)) { + return -1; + } + return phys_addr; } void openrisc_mmu_init(CPUOpenriscState *env) { + env->map_address_code = &get_phys_nommu; + env->map_address_data = &get_phys_nommu; + memset(env->dtlb, 0, sizeof(tlb_entry)*DTLB_SIZE*DTLB_WAYS); + memset(env->itlb, 0, sizeof(tlb_entry)*ITLB_SIZE*ITLB_WAYS); } #endif diff --git a/target-openrisc/mmu_helper.c b/target-openrisc/mmu_helper.c index 8c658ff..1fbd6a4 100644 --- a/target-openrisc/mmu_helper.c +++ b/target-openrisc/mmu_helper.c @@ -39,5 +39,25 @@ void tlb_fill(CPUOpenriscState *env, target_ulong addr, int is_write, int mmu_idx, uintptr_t retaddr) { + TranslationBlock *tb; + unsigned long pc; + int ret; + + ret = cpu_openrisc_handle_mmu_fault(env, addr, is_write, mmu_idx); + + if (ret) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we + have a virtual CPU fault */ + cpu_restore_state(tb, env, pc); + } + } + /* Raise Exception... */ + cpu_loop_exit(env); + } } #endif -- 1.7.9.5