Patchwork [v7,03/16] target-or32: Add MMU support

login
register
mail settings
Submitter Jia Liu
Date June 27, 2012, 9:54 a.m.
Message ID <1340790854-15580-4-git-send-email-proljc@gmail.com>
Download mbox | patch
Permalink /patch/167585/
State New
Headers show

Comments

Jia Liu - June 27, 2012, 9:54 a.m.
Add OpenRISC MMU support.

Signed-off-by: Jia Liu <proljc@gmail.com>
---
 target-openrisc/cpu.h        |   75 +++++++++++++++-
 target-openrisc/mmu.c        |  199 +++++++++++++++++++++++++++++++++++++++++-
 target-openrisc/mmu_helper.c |   20 +++++
 3 files changed, 292 insertions(+), 2 deletions(-)

Patch

diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h
index 58c8081..dbb3f17 100644
--- a/target-openrisc/cpu.h
+++ b/target-openrisc/cpu.h
@@ -43,6 +43,12 @@  struct CPUOpenRISCState;
 
 #define NB_MMU_MODES    3
 
+enum {
+    MMU_NOMMU_IDX = 0,
+    MMU_SUPERVISOR_IDX = 1,
+    MMU_USER_IDX = 2,
+};
+
 #define TARGET_PAGE_BITS 13
 
 #define TARGET_PHYS_ADDR_SPACE_BITS 32
@@ -206,6 +212,54 @@  enum {
     TTMR_M  = (3 << 30),
 };
 
+/* TLB size */
+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 OpenRISCTLBEntry {
+    uint32_t mr;
+    uint32_t tr;
+} OpenRISCTLBEntry;
+
+#ifndef CONFIG_USER_ONLY
+typedef struct CPUOpenRISCTLBContext {
+    OpenRISCTLBEntry itlb[ITLB_WAYS][ITLB_SIZE];
+    OpenRISCTLBEntry dtlb[DTLB_WAYS][DTLB_SIZE];
+
+    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);
+} CPUOpenRISCTLBContext;
+#endif
+
 typedef struct CPUOpenRISCState CPUOpenRISCState;
 struct CPUOpenRISCState {
     target_ulong gpr[32];     /* General registers */
@@ -240,6 +294,8 @@  struct CPUOpenRISCState {
     CPU_COMMON
 
 #ifndef CONFIG_USER_ONLY
+    CPUOpenRISCTLBContext * tlb;
+
     struct QEMUTimer *timer;
     uint32_t ttmr;          /* Timer tick mode register */
     uint32_t ttcr;          /* Timer tick count register */
@@ -301,19 +357,33 @@  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
 
 #ifndef CONFIG_USER_ONLY
 void cpu_openrisc_pic_reset(CPUOpenRISCState *env);
+void cpu_openrisc_store_picsr(CPUOpenRISCState *env, uint32_t value);
+void cpu_openrisc_store_picmr(CPUOpenRISCState *env, uint32_t value);
 
 void cpu_openrisc_timer_reset(CPUOpenRISCState *env);
+void cpu_openrisc_store_count(CPUOpenRISCState *env, target_ulong count);
+void cpu_openrisc_store_compare(CPUOpenRISCState *env, target_ulong value);
+uint32_t cpu_openrisc_get_count(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)
@@ -339,7 +409,10 @@  static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env,
 
 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 cd82b7a..ecef2e1 100644
--- a/target-openrisc/mmu.c
+++ b/target-openrisc/mmu.c
@@ -27,13 +27,210 @@ 
 #endif
 
 #ifndef CONFIG_USER_ONLY
+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->tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
+        return TLBRET_NOMATCH;
+    }
+    if (!(env->tlb->itlb[0][idx].mr & 1)) {
+        return TLBRET_INVALID;
+    }
+
+    if (env->sr & SR_SM) { /* supervisor mode */
+        if (env->tlb->itlb[0][idx].tr & SXE) {
+            right |= PAGE_EXEC;
+        }
+    } else {
+        if (env->tlb->itlb[0][idx].tr & UXE) {
+            right |= PAGE_EXEC;
+        }
+    }
+
+    if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
+        return TLBRET_BADADDR;
+    }
+
+    *physical = (env->tlb->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->tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
+        return TLBRET_NOMATCH;
+    }
+    if (!(env->tlb->dtlb[0][idx].mr & 1)) {
+        return TLBRET_INVALID;
+    }
+
+    if (env->sr & SR_SM) { /* supervisor mode */
+        if (env->tlb->dtlb[0][idx].tr & SRE) {
+            right |= PAGE_READ;
+        }
+        if (env->tlb->dtlb[0][idx].tr & SWE) {
+            right |= PAGE_WRITE;
+        }
+    } else {
+        if (env->tlb->dtlb[0][idx].tr & URE) {
+            right |= PAGE_READ;
+        }
+        if (env->tlb->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->tlb->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->tlb->map_address_code(env, physical,
+                                         prot, address, rw);
+    } else {          /* DTLB */
+        ret = env->tlb->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;
+#ifndef 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;
+}
+
+#ifndef 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
+
+#ifndef CONFIG_USER_ONLY
 target_phys_addr_t cpu_get_phys_page_debug(CPUOpenRISCState *env,
                                            target_ulong addr)
 {
-    return addr;
+    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->tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext));
+
+    env->tlb->map_address_code = &get_phys_nommu;
+    env->tlb->map_address_data = &get_phys_nommu;
 }
 #endif
diff --git a/target-openrisc/mmu_helper.c b/target-openrisc/mmu_helper.c
index f526ba8..29387e3 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