spec bump
[riscv-isa-sim.git] / riscv / mmu.cc
index dee41a9e54aa2854fee40a6d69cd749066f689f5..878d849d7ecaed30521bb8053a23389500743a2b 100644 (file)
@@ -5,7 +5,11 @@
 #include "processor.h"
 
 mmu_t::mmu_t(sim_t* sim, processor_t* proc)
- : sim(sim), proc(proc)
+ : sim(sim), proc(proc),
+  check_triggers_fetch(false),
+  check_triggers_load(false),
+  check_triggers_store(false),
+  matched_trigger(NULL)
 {
   flush_tlb();
 }
@@ -35,11 +39,9 @@ reg_t mmu_t::translate(reg_t addr, access_type type)
     return addr;
 
   reg_t mode = proc->state.prv;
-  bool pum = false;
   if (type != FETCH) {
-    if (get_field(proc->state.mstatus, MSTATUS_MPRV))
+    if (!proc->state.dcsr.cause && get_field(proc->state.mstatus, MSTATUS_MPRV))
       mode = get_field(proc->state.mstatus, MSTATUS_MPP);
-    pum = (mode == PRV_S && get_field(proc->state.mstatus, MSTATUS_PUM));
   }
   if (get_field(proc->state.mstatus, MSTATUS_VM) == VM_MBARE)
     mode = PRV_M;
@@ -48,7 +50,7 @@ reg_t mmu_t::translate(reg_t addr, access_type type)
     reg_t msb_mask = (reg_t(2) << (proc->xlen-1))-1; // zero-extend from xlen
     return addr & msb_mask;
   }
-  return walk(addr, type, mode > PRV_U, pum) | (addr & (PGSIZE-1));
+  return walk(addr, type, mode) | (addr & (PGSIZE-1));
 }
 
 const uint16_t* mmu_t::fetch_slow_path(reg_t vaddr)
@@ -71,9 +73,36 @@ const uint16_t* mmu_t::fetch_slow_path(reg_t vaddr)
   }
 }
 
+reg_t reg_from_bytes(size_t len, const uint8_t* bytes)
+{
+  switch (len) {
+    case 1:
+      return bytes[0];
+    case 2:
+      return bytes[0] |
+        (((reg_t) bytes[1]) << 8);
+    case 4:
+      return bytes[0] |
+        (((reg_t) bytes[1]) << 8) |
+        (((reg_t) bytes[2]) << 16) |
+        (((reg_t) bytes[3]) << 24);
+    case 8:
+      return bytes[0] |
+        (((reg_t) bytes[1]) << 8) |
+        (((reg_t) bytes[2]) << 16) |
+        (((reg_t) bytes[3]) << 24) |
+        (((reg_t) bytes[4]) << 32) |
+        (((reg_t) bytes[5]) << 40) |
+        (((reg_t) bytes[6]) << 48) |
+        (((reg_t) bytes[7]) << 56);
+  }
+  abort();
+}
+
 void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
 {
   reg_t paddr = translate(addr, LOAD);
+
   if (sim->addr_is_mem(paddr)) {
     memcpy(bytes, sim->addr_to_mem(paddr), len);
     if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD))
@@ -83,11 +112,26 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
   } else if (!sim->mmio_load(paddr, len, bytes)) {
     throw trap_load_access_fault(addr);
   }
+
+  if (!matched_trigger) {
+    reg_t data = reg_from_bytes(len, bytes);
+    matched_trigger = trigger_exception(OPERATION_LOAD, addr, data);
+    if (matched_trigger)
+      throw *matched_trigger;
+  }
 }
 
 void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes)
 {
   reg_t paddr = translate(addr, STORE);
+
+  if (!matched_trigger) {
+    reg_t data = reg_from_bytes(len, bytes);
+    matched_trigger = trigger_exception(OPERATION_STORE, addr, data);
+    if (matched_trigger)
+      throw *matched_trigger;
+  }
+
   if (sim->addr_is_mem(paddr)) {
     memcpy(sim->addr_to_mem(paddr), bytes, len);
     if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE))
@@ -104,9 +148,17 @@ void mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, access_type type)
   reg_t idx = (vaddr >> PGSHIFT) % TLB_ENTRIES;
   reg_t expected_tag = vaddr >> PGSHIFT;
 
-  if (tlb_load_tag[idx] != expected_tag) tlb_load_tag[idx] = -1;
-  if (tlb_store_tag[idx] != expected_tag) tlb_store_tag[idx] = -1;
-  if (tlb_insn_tag[idx] != expected_tag) tlb_insn_tag[idx] = -1;
+  if ((tlb_load_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag)
+    tlb_load_tag[idx] = -1;
+  if ((tlb_store_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag)
+    tlb_store_tag[idx] = -1;
+  if ((tlb_insn_tag[idx] & ~TLB_CHECK_TRIGGERS) != expected_tag)
+    tlb_insn_tag[idx] = -1;
+
+  if ((check_triggers_fetch && type == FETCH) ||
+      (check_triggers_load && type == LOAD) ||
+      (check_triggers_store && type == STORE))
+    expected_tag |= TLB_CHECK_TRIGGERS;
 
   if (type == FETCH) tlb_insn_tag[idx] = expected_tag;
   else if (type == STORE) tlb_store_tag[idx] = expected_tag;
@@ -115,7 +167,7 @@ void mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, access_type type)
   tlb_data[idx] = sim->addr_to_mem(paddr) - vaddr;
 }
 
-reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
+reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
 {
   int levels, ptidxbits, ptesize;
   switch (get_field(proc->get_state()->mstatus, MSTATUS_VM))
@@ -126,6 +178,10 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
     default: abort();
   }
 
+  bool supervisor = mode == PRV_S;
+  bool pum = get_field(proc->state.mstatus, MSTATUS_PUM);
+  bool mxr = get_field(proc->state.mstatus, MSTATUS_MXR);
+
   // verify bits xlen-1:va_bits-1 are all equal
   int va_bits = PGSHIFT + levels * ptidxbits;
   reg_t mask = (reg_t(1) << (proc->xlen - (va_bits-1))) - 1;
@@ -149,16 +205,21 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
 
     if (PTE_TABLE(pte)) { // next level of page table
       base = ppn << PGSHIFT;
-    } else if (pum && PTE_CHECK_PERM(pte, 0, type == STORE, type == FETCH)) {
+    } else if ((pte & PTE_U) ? supervisor && pum : !supervisor) {
+      break;
+    } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
       break;
-    } else if (!PTE_CHECK_PERM(pte, supervisor, type == STORE, type == FETCH)) {
+    } else if (type == FETCH ? !(pte & PTE_X) :
+               type == LOAD ?  !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
+                               !((pte & PTE_R) && (pte & PTE_W))) {
       break;
     } else {
-      // set referenced and possibly dirty bits.
-      *(uint32_t*)ppte |= PTE_R | ((type == STORE) * PTE_D);
+      // set accessed and possibly dirty bits.
+      *(uint32_t*)ppte |= PTE_A | ((type == STORE) * PTE_D);
       // for superpage mappings, make a fake leaf PTE for the TLB's benefit.
       reg_t vpn = addr >> PGSHIFT;
-      return (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
+      reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
+      return value;
     }
   }