summary refs log tree commit diff stats
path: root/target-cris/mmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-cris/mmu.c')
-rw-r--r--target-cris/mmu.c158
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