qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v2 04/17] Openrisc: add MMU support


From: Jia Liu
Subject: [Qemu-devel] [PATCH v2 04/17] Openrisc: add MMU support
Date: Sun, 27 May 2012 13:32:46 +0800

add openrisc MMU support.

Signed-off-by: Jia Liu <address@hidden>
---
 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]