@@ -166,13 +166,14 @@ FIELD(CR_TLBMISC, EE, 24, 1)
#define EXCP_UNALIGN 6
#define EXCP_UNALIGND 7
#define EXCP_DIV 8
-#define EXCP_SUPERA 9
+#define EXCP_SUPERA_X 9
#define EXCP_SUPERI 10
-#define EXCP_SUPERD 11
-#define EXCP_TLBD 12
-#define EXCP_TLBX 13
-#define EXCP_TLBR 14
-#define EXCP_TLBW 15
+#define EXCP_SUPERA_D 11
+#define EXCP_TLB_X 12
+#define EXCP_TLB_D (0x1000 | EXCP_TLB_X)
+#define EXCP_PERM_X 13
+#define EXCP_PERM_R 14
+#define EXCP_PERM_W 15
#define EXCP_MPUI 16
#define EXCP_MPUD 17
@@ -29,7 +29,8 @@
#include "semihosting/semihost.h"
-static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, bool is_break)
+static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
+ uint32_t tlbmisc_set, bool is_break)
{
CPUNios2State *env = &cpu->env;
CPUState *cs = CPU(cpu);
@@ -48,6 +49,16 @@ static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, bool is_break)
if (cpu->mmu_present) {
new_status |= CR_STATUS_EH;
+
+ /*
+ * There are 4 bits that are always written.
+ * Explicitly clear them, to be set via the argument.
+ */
+ env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
+ CR_TLBMISC_PERM |
+ CR_TLBMISC_BAD |
+ CR_TLBMISC_DBL);
+ env->ctrl[CR_TLBMISC] |= tlbmisc_set;
}
}
@@ -63,13 +74,14 @@ static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, bool is_break)
static void do_iic_irq(Nios2CPU *cpu)
{
- do_exception(cpu, cpu->exception_addr, false);
+ do_exception(cpu, cpu->exception_addr, 0, false);
}
void nios2_cpu_do_interrupt(CPUState *cs)
{
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
+ uint32_t tlbmisc_set = 0;
if (qemu_loglevel_mask(CPU_LOG_INT)) {
const char *name = NULL;
@@ -78,20 +90,21 @@ void nios2_cpu_do_interrupt(CPUState *cs)
case EXCP_IRQ:
name = "interrupt";
break;
- case EXCP_TLBD:
+ case EXCP_TLB_X:
+ case EXCP_TLB_D:
if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
name = "TLB MISS (double)";
} else {
name = "TLB MISS (fast)";
}
break;
- case EXCP_TLBR:
- case EXCP_TLBW:
- case EXCP_TLBX:
+ case EXCP_PERM_R:
+ case EXCP_PERM_W:
+ case EXCP_PERM_X:
name = "TLB PERM";
break;
- case EXCP_SUPERA:
- case EXCP_SUPERD:
+ case EXCP_SUPERA_X:
+ case EXCP_SUPERA_D:
name = "SUPERVISOR (address)";
break;
case EXCP_SUPERI:
@@ -129,38 +142,57 @@ void nios2_cpu_do_interrupt(CPUState *cs)
do_iic_irq(cpu);
break;
- case EXCP_TLBD:
- if ((env->ctrl[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->ctrl[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
- env->ctrl[CR_TLBMISC] |= CR_TLBMISC_WE;
- do_exception(cpu, cpu->fast_tlb_miss_addr, false);
+ case EXCP_TLB_D:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_TLB_X:
+ if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
+ tlbmisc_set |= CR_TLBMISC_DBL;
+ /*
+ * Normally, we don't write to tlbmisc unless !EH,
+ * so do it manually for the double-tlb miss exception.
+ */
+ env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
+ CR_TLBMISC_PERM |
+ CR_TLBMISC_BAD);
+ env->ctrl[CR_TLBMISC] |= tlbmisc_set;
+ do_exception(cpu, cpu->exception_addr, 0, false);
} else {
- env->ctrl[CR_TLBMISC] |= CR_TLBMISC_DBL;
- do_exception(cpu, cpu->exception_addr, false);
+ tlbmisc_set |= CR_TLBMISC_WE;
+ do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false);
}
break;
- case EXCP_TLBR:
- case EXCP_TLBW:
- case EXCP_TLBX:
- if ((env->ctrl[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->ctrl[CR_TLBMISC] |= CR_TLBMISC_WE;
+ case EXCP_PERM_R:
+ case EXCP_PERM_W:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_PERM_X:
+ tlbmisc_set |= CR_TLBMISC_PERM;
+ if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) {
+ tlbmisc_set |= CR_TLBMISC_WE;
}
- do_exception(cpu, cpu->exception_addr, false);
+ do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
+ break;
+
+ case EXCP_SUPERA_D:
+ case EXCP_UNALIGN:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_SUPERA_X:
+ case EXCP_UNALIGND:
+ tlbmisc_set |= CR_TLBMISC_BAD;
+ do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
break;
- case EXCP_SUPERA:
case EXCP_SUPERI:
- case EXCP_SUPERD:
case EXCP_ILLEGAL:
case EXCP_TRAP:
- case EXCP_UNALIGN:
- case EXCP_UNALIGND:
- do_exception(cpu, cpu->exception_addr, false);
+ do_exception(cpu, cpu->exception_addr, 0, false);
break;
case EXCP_BREAK:
- do_exception(cpu, cpu->exception_addr, true);
+ do_exception(cpu, cpu->exception_addr, 0, true);
break;
case EXCP_SEMIHOST:
@@ -215,7 +247,7 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
{
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- unsigned int excp = EXCP_TLBD;
+ unsigned int excp;
target_ulong vaddr, paddr;
Nios2MMULookup lu;
unsigned int hit;
@@ -242,7 +274,8 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (probe) {
return false;
}
- cs->exception_index = EXCP_SUPERA;
+ cs->exception_index = (access_type == MMU_INST_FETCH
+ ? EXCP_SUPERA_X : EXCP_SUPERA_D);
env->ctrl[CR_BADADDR] = address;
cpu_loop_exit_restore(cs, retaddr);
}
@@ -263,8 +296,10 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
/* Permission violation */
- excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR :
- access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX);
+ excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R :
+ access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X);
+ } else {
+ excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D);
}
if (probe) {