aboutsummaryrefslogtreecommitdiff
path: root/target-cris/mmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-cris/mmu.c')
-rw-r--r--target-cris/mmu.c165
1 files changed, 142 insertions, 23 deletions
diff --git a/target-cris/mmu.c b/target-cris/mmu.c
index 5fa5215bc2..84a1747e5f 100644
--- a/target-cris/mmu.c
+++ b/target-cris/mmu.c
@@ -30,6 +30,7 @@
#include "mmu.h"
#include "exec-all.h"
+#define D(x)
static int cris_mmu_enabled(uint32_t rw_gc_cfg)
{
@@ -60,7 +61,22 @@ static uint32_t cris_mmu_translate_seg(CPUState *env, int seg)
}
/* Used by the tlb decoder. */
#define EXTRACT_FIELD(src, start, end) \
- (((src) >> start) & ((1 << (end - start + 1)) - 1))
+ (((src) >> start) & ((1 << (end - start + 1)) - 1))
+
+static inline void set_field(uint32_t *dst, unsigned int val,
+ unsigned int offset, unsigned int width)
+{
+ uint32_t mask;
+
+ mask = (1 << width) - 1;
+ mask <<= offset;
+ val <<= offset;
+
+ val &= mask;
+ D(printf ("val=%x mask=%x dst=%x\n", val, mask, *dst));
+ *dst &= ~(mask);
+ *dst |= val;
+}
static int cris_mmu_translate_page(struct cris_mmu_result_t *res,
CPUState *env, uint32_t vaddr,
@@ -69,46 +85,149 @@ static int cris_mmu_translate_page(struct cris_mmu_result_t *res,
unsigned int vpage;
unsigned int idx;
uint32_t lo, hi;
- uint32_t vpn, pfn = 0, pid, fg, fv, fk, fw, fx;
+ 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;
+ uint32_t r_cause;
+ uint32_t r_cfg;
+ int rwcause;
+ int update_sel = 0;
+
+ 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;
vpage = vaddr >> 13;
- idx = vpage & 31;
- vpage >>= 4;
+ 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;
+ 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);
+ }
+ }
+#endif
for (i = 0; i < 4; i++)
{
- lo = env->tlbsets[0][i][idx].lo;
- hi = env->tlbsets[0][i][idx].hi;
+ lo = env->tlbsets[1][i][idx].lo;
+ hi = env->tlbsets[1][i][idx].hi;
- vpn = EXTRACT_FIELD(hi, 13, 31);
- pid = EXTRACT_FIELD(hi, 0, 7);
+ tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
+ tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
- if (vpn == vpage
- && pid == env->pregs[PR_PID]) {
+ D(printf ("TLB[%d][%d] tlbv=%x vpage=%x -> pfn=%x\n",
+ i, idx, tlb_vpn, vpage, tlb_pfn));
+ if (tlb_vpn == vpage) {
match = 1;
break;
}
}
if (match) {
- pfn = EXTRACT_FIELD(lo, 13, 31);
- fg = EXTRACT_FIELD(lo, 4, 4);
- fv = EXTRACT_FIELD(lo, 3, 3);
- fk = EXTRACT_FIELD(lo, 2, 2);
- fw = EXTRACT_FIELD(lo, 1, 1);
- fx = EXTRACT_FIELD(lo, 0, 0);
+
+ cfg_w = EXTRACT_FIELD(r_cfg, 19, 19);
+ cfg_k = EXTRACT_FIELD(r_cfg, 18, 18);
+ cfg_x = EXTRACT_FIELD(r_cfg, 17, 17);
+ cfg_v = EXTRACT_FIELD(r_cfg, 16, 16);
+
+ tlb_pid = EXTRACT_FIELD(hi, 0, 7);
+ tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
+ tlb_g = EXTRACT_FIELD(lo, 4, 4);
+ tlb_v = EXTRACT_FIELD(lo, 3, 3);
+ tlb_k = EXTRACT_FIELD(lo, 2, 2);
+ tlb_w = EXTRACT_FIELD(lo, 1, 1);
+ tlb_x = EXTRACT_FIELD(lo, 0, 0);
+
+ /*
+ set_exception_vector(0x04, i_mmu_refill);
+ set_exception_vector(0x05, i_mmu_invalid);
+ set_exception_vector(0x06, i_mmu_access);
+ set_exception_vector(0x07, i_mmu_execute);
+ set_exception_vector(0x08, d_mmu_refill);
+ set_exception_vector(0x09, d_mmu_invalid);
+ 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);
+ 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");
+ match = 0;
+ res->bf_vec = 0xa;
+ }
+ else if (rw && cfg_w && !tlb_w) {
+ printf ("tlb: write protected\n");
+ match = 0;
+ res->bf_vec = 0xb;
+ }
+ } 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;
+ }
+
+ if (!match) {
+ set_field(&r_cause, rwcause, 8, 9);
+ 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;
}
- printf ("%s match=%d vaddr=%x vpage=%x vpn=%x pfn=%x pid=%x %x\n",
- __func__, match,
- vaddr, vpage,
- vpn, pfn, pid, env->pregs[PR_PID]);
- res->pfn = pfn;
+ 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,
+ vaddr, vpage,
+ tlb_vpn, tlb_pfn, tlb_pid,
+ env->pregs[PR_PID],
+ r_cause,
+ env->sregs[SFR_RW_MM_TLB_SEL],
+ env->regs[13]));
+
+ res->pfn = tlb_pfn;
return !match;
}
+/* Give us the vaddr corresponding to the latest TLB update. */
+target_ulong cris_mmu_tlb_latest_update(CPUState *env, uint32_t new_lo)
+{
+ uint32_t sel = env->sregs[SFR_RW_MM_TLB_SEL];
+ uint32_t vaddr;
+ uint32_t hi;
+ int set;
+ int idx;
+
+ idx = EXTRACT_FIELD(sel, 0, 4);
+ set = EXTRACT_FIELD(sel, 4, 5);
+
+ hi = env->tlbsets[1][set][idx].hi;
+ vaddr = EXTRACT_FIELD(hi, 13, 31);
+ return vaddr << TARGET_PAGE_BITS;
+}
+
int cris_mmu_translate(struct cris_mmu_result_t *res,
CPUState *env, uint32_t vaddr,
int rw, int mmu_idx)
@@ -116,7 +235,7 @@ int cris_mmu_translate(struct cris_mmu_result_t *res,
uint32_t phy = vaddr;
int seg;
int miss = 0;
- int is_user = mmu_idx == MMU_USER_IDX;
+ int is_user = mmu_idx == MMU_USER_IDX;
if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) {
res->phy = vaddr;
@@ -142,7 +261,7 @@ int cris_mmu_translate(struct cris_mmu_result_t *res,
res->phy = phy;
}
}
-// printf ("miss=%d v=%x -> p=%x\n", miss, vaddr, phy);
+ D(printf ("miss=%d v=%x -> p=%x\n", miss, vaddr, phy));
return miss;
}
#endif