diff options
author | edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-05-02 22:16:17 +0000 |
---|---|---|
committer | edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-05-02 22:16:17 +0000 |
commit | b41f7df0189dbda34be3944a48db3b98348e4bc6 (patch) | |
tree | b6c2840eabbfce1f272c47e754686af9e9473403 /target-cris/mmu.c | |
parent | ff56ff7a07fe8fbcc4e9f74972d8399ca1ab8051 (diff) |
CRIS updates:
* Support both the I and D MMUs and improve the accuracy of the MMU model.
* Handle the automatic user/kernel stack pointer switching when leaving or entering user mode.
* Move the CCS evaluation into helper funcs.
* Make sure user-mode cannot change flags only writeable in kernel mode.
* More conversion of the translator into TCG.
* Handle exceptions while in a delayslot.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4299 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-cris/mmu.c')
-rw-r--r-- | target-cris/mmu.c | 158 |
1 files changed, 103 insertions, 55 deletions
diff --git a/target-cris/mmu.c b/target-cris/mmu.c index 84a1747e5f..ac711fb983 100644 --- a/target-cris/mmu.c +++ b/target-cris/mmu.c @@ -73,11 +73,30 @@ static inline void set_field(uint32_t *dst, unsigned int val, val <<= offset; val &= mask; - D(printf ("val=%x mask=%x dst=%x\n", val, mask, *dst)); *dst &= ~(mask); *dst |= val; } +static void dump_tlb(CPUState *env, int mmu) +{ + int set; + int idx; + uint32_t hi, lo, tlb_vpn, tlb_pfn; + + for (set = 0; set < 4; set++) { + for (idx = 0; idx < 16; idx++) { + lo = env->tlbsets[mmu][set][idx].lo; + hi = env->tlbsets[mmu][set][idx].hi; + tlb_vpn = EXTRACT_FIELD(hi, 13, 31); + tlb_pfn = EXTRACT_FIELD(lo, 13, 31); + + printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n", + set, idx, hi, lo, tlb_vpn, tlb_pfn); + } + } +} + +/* rw 0 = read, 1 = write, 2 = exec. */ static int cris_mmu_translate_page(struct cris_mmu_result_t *res, CPUState *env, uint32_t vaddr, int rw, int usermode) @@ -88,53 +107,63 @@ static int cris_mmu_translate_page(struct cris_mmu_result_t *res, uint32_t tlb_vpn, tlb_pfn = 0; int tlb_pid, tlb_g, tlb_v, tlb_k, tlb_w, tlb_x; int cfg_v, cfg_k, cfg_w, cfg_x; - int i, match = 0; + int set, match = 0; uint32_t r_cause; uint32_t r_cfg; int rwcause; - int update_sel = 0; + int mmu = 1; /* Data mmu is default. */ + int vect_base; r_cause = env->sregs[SFR_R_MM_CAUSE]; r_cfg = env->sregs[SFR_RW_MM_CFG]; - rwcause = rw ? CRIS_MMU_ERR_WRITE : CRIS_MMU_ERR_READ; + + switch (rw) { + case 2: rwcause = CRIS_MMU_ERR_EXEC; mmu = 0; break; + case 1: rwcause = CRIS_MMU_ERR_WRITE; break; + default: + case 0: rwcause = CRIS_MMU_ERR_READ; break; + } + + /* I exception vectors 4 - 7, D 8 - 11. */ + vect_base = (mmu + 1) * 4; vpage = vaddr >> 13; - idx = vpage & 15; /* We know the index which to check on each set. Scan both I and D. */ #if 0 - for (i = 0; i < 4; i++) { - int j; - for (j = 0; j < 16; j++) { - lo = env->tlbsets[1][i][j].lo; - hi = env->tlbsets[1][i][j].hi; + for (set = 0; set < 4; set++) { + for (idx = 0; idx < 16; idx++) { + lo = env->tlbsets[mmu][set][idx].lo; + hi = env->tlbsets[mmu][set][idx].hi; tlb_vpn = EXTRACT_FIELD(hi, 13, 31); tlb_pfn = EXTRACT_FIELD(lo, 13, 31); printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n", - i, j, hi, lo, tlb_vpn, tlb_pfn); + set, idx, hi, lo, tlb_vpn, tlb_pfn); } } #endif - for (i = 0; i < 4; i++) + + idx = vpage & 15; + for (set = 0; set < 4; set++) { - lo = env->tlbsets[1][i][idx].lo; - hi = env->tlbsets[1][i][idx].hi; + lo = env->tlbsets[mmu][set][idx].lo; + hi = env->tlbsets[mmu][set][idx].hi; tlb_vpn = EXTRACT_FIELD(hi, 13, 31); tlb_pfn = EXTRACT_FIELD(lo, 13, 31); - D(printf ("TLB[%d][%d] tlbv=%x vpage=%x -> pfn=%x\n", - i, idx, tlb_vpn, vpage, tlb_pfn)); + D(printf("TLB[%d][%d] v=%x vpage=%x -> pfn=%x lo=%x hi=%x\n", + i, idx, tlb_vpn, vpage, tlb_pfn, lo, hi)); if (tlb_vpn == vpage) { match = 1; break; } } + res->bf_vec = vect_base; if (match) { - cfg_w = EXTRACT_FIELD(r_cfg, 19, 19); cfg_k = EXTRACT_FIELD(r_cfg, 18, 18); cfg_x = EXTRACT_FIELD(r_cfg, 17, 17); @@ -158,54 +187,67 @@ static int cris_mmu_translate_page(struct cris_mmu_result_t *res, set_exception_vector(0x0a, d_mmu_access); set_exception_vector(0x0b, d_mmu_write); */ - if (cfg_v && !tlb_v) { - printf ("tlb: invalid\n"); - set_field(&r_cause, rwcause, 8, 9); + if (!tlb_g + && tlb_pid != (env->pregs[PR_PID] & 0xff)) { + D(printf ("tlb: wrong pid %x %x pc=%x\n", + tlb_pid, env->pregs[PR_PID], env->pc)); match = 0; - res->bf_vec = 0x9; - update_sel = 1; - } - else if (!tlb_g - && tlb_pid != 0xff - && tlb_pid != env->pregs[PR_PID] - && cfg_w && !tlb_w) { - printf ("tlb: wrong pid\n"); + res->bf_vec = vect_base; + } else if (rw == 1 && cfg_w && !tlb_w) { + D(printf ("tlb: write protected %x lo=%x\n", + vaddr, lo)); match = 0; - res->bf_vec = 0xa; - } - else if (rw && cfg_w && !tlb_w) { - printf ("tlb: write protected\n"); + res->bf_vec = vect_base + 3; + } else if (cfg_v && !tlb_v) { + D(printf ("tlb: invalid %x\n", vaddr)); + set_field(&r_cause, rwcause, 8, 9); match = 0; - res->bf_vec = 0xb; + res->bf_vec = vect_base + 1; } - } else - update_sel = 1; - if (update_sel) { - /* miss. */ - env->sregs[SFR_RW_MM_TLB_SEL] = 0; - D(printf ("tlb: miss %x vp=%x\n", - env->sregs[SFR_RW_MM_TLB_SEL], vpage & 15)); - set_field(&env->sregs[SFR_RW_MM_TLB_SEL], vpage & 15, 0, 4); - set_field(&env->sregs[SFR_RW_MM_TLB_SEL], 0, 4, 5); - res->bf_vec = 0x8; + res->prot = 0; + if (match) { + res->prot |= PAGE_READ; + if (tlb_w) + res->prot |= PAGE_WRITE; + if (tlb_x) + res->prot |= PAGE_EXEC; + } + else + D(dump_tlb(env, mmu)); + + env->sregs[SFR_RW_MM_TLB_HI] = hi; + env->sregs[SFR_RW_MM_TLB_LO] = lo; } if (!match) { - set_field(&r_cause, rwcause, 8, 9); + /* miss. */ + idx = vpage & 15; + set = 0; + + /* Update RW_MM_TLB_SEL. */ + env->sregs[SFR_RW_MM_TLB_SEL] = 0; + set_field(&env->sregs[SFR_RW_MM_TLB_SEL], idx, 0, 4); + set_field(&env->sregs[SFR_RW_MM_TLB_SEL], set, 4, 5); + + /* Update RW_MM_CAUSE. */ + set_field(&r_cause, rwcause, 8, 2); set_field(&r_cause, vpage, 13, 19); set_field(&r_cause, env->pregs[PR_PID], 0, 8); env->sregs[SFR_R_MM_CAUSE] = r_cause; + D(printf("refill vaddr=%x pc=%x\n", vaddr, env->pc)); } - D(printf ("%s mtch=%d pc=%x va=%x vpn=%x tlbvpn=%x pfn=%x pid=%x" - " %x cause=%x sel=%x r13=%x\n", - __func__, match, env->pc, + + + D(printf ("%s rw=%d mtch=%d pc=%x va=%x vpn=%x tlbvpn=%x pfn=%x pid=%x" + " %x cause=%x sel=%x sp=%x %x %x\n", + __func__, rw, match, env->pc, vaddr, vpage, tlb_vpn, tlb_pfn, tlb_pid, env->pregs[PR_PID], r_cause, env->sregs[SFR_RW_MM_TLB_SEL], - env->regs[13])); + env->regs[R_SP], env->pregs[PR_USP], env->ksp)); res->pfn = tlb_pfn; return !match; @@ -236,10 +278,17 @@ int cris_mmu_translate(struct cris_mmu_result_t *res, int seg; int miss = 0; int is_user = mmu_idx == MMU_USER_IDX; + uint32_t old_srs; + + old_srs= env->pregs[PR_SRS]; + + /* rw == 2 means exec, map the access to the insn mmu. */ + env->pregs[PR_SRS] = rw == 2 ? 1 : 2; if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) { res->phy = vaddr; - return 0; + res->prot = PAGE_BITS; + goto done; } seg = vaddr >> 28; @@ -251,17 +300,16 @@ int cris_mmu_translate(struct cris_mmu_result_t *res, base = cris_mmu_translate_seg(env, seg); phy = base | (0x0fffffff & vaddr); res->phy = phy; + res->prot = PAGE_BITS; } else { miss = cris_mmu_translate_page(res, env, vaddr, rw, is_user); - if (!miss) { - phy &= 8191; - phy |= (res->pfn << 13); - res->phy = phy; - } + phy = (res->pfn << 13); + res->phy = phy; } - D(printf ("miss=%d v=%x -> p=%x\n", miss, vaddr, phy)); + done: + env->pregs[PR_SRS] = old_srs; return miss; } #endif |