aboutsummaryrefslogtreecommitdiff
path: root/riscv/mmu.cc
diff options
context:
space:
mode:
authorAndrew Waterman <waterman@cs.berkeley.edu>2015-03-12 17:32:43 -0700
committerAndrew Waterman <waterman@cs.berkeley.edu>2015-03-12 17:32:43 -0700
commitea58df801f36605b462783a61b5266bdd9a40eb0 (patch)
treeec6c4736f29e9f2021a9e448946e0a0169639cc1 /riscv/mmu.cc
parent9af855a28e7dc4009ad31312840ca6f9396d572e (diff)
downloadriscv-isa-sim-ea58df801f36605b462783a61b5266bdd9a40eb0.zip
riscv-isa-sim-ea58df801f36605b462783a61b5266bdd9a40eb0.tar.gz
riscv-isa-sim-ea58df801f36605b462783a61b5266bdd9a40eb0.tar.bz2
Update to new privileged spec
Sorry, everyone.
Diffstat (limited to 'riscv/mmu.cc')
-rw-r--r--riscv/mmu.cc102
1 files changed, 55 insertions, 47 deletions
diff --git a/riscv/mmu.cc b/riscv/mmu.cc
index 2e5de5b..779383d 100644
--- a/riscv/mmu.cc
+++ b/riscv/mmu.cc
@@ -34,10 +34,29 @@ void* mmu_t::refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch)
reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
reg_t expected_tag = addr >> PGSHIFT;
- reg_t pte = walk(addr);
+ reg_t pte = 0;
+ reg_t mstatus = proc ? proc->state.mstatus : 0;
+
+ bool vm_disabled = get_field(mstatus, MSTATUS_VM) == VM_MBARE;
+ bool mode_m = get_field(mstatus, MSTATUS_PRV) == PRV_M;
+ bool mode_s = get_field(mstatus, MSTATUS_PRV) == PRV_S;
+ bool mprv_m = get_field(mstatus, MSTATUS_MPRV) == PRV_M;
+ bool mprv_s = get_field(mstatus, MSTATUS_MPRV) == PRV_S;
+
+ if (vm_disabled || (mode_m && (mprv_m || fetch))) {
+ // virtual memory is disabled. merely check legality of physical address.
+ if (addr < memsz) {
+ // produce a fake PTE for the TLB's benefit.
+ pte = PTE_V | PTE_UX | PTE_SX | ((addr >> PGSHIFT) << PGSHIFT);
+ if (vm_disabled || !(mode_m && !mprv_m))
+ pte |= PTE_UR | PTE_SR | PTE_UW | PTE_SW;
+ }
+ } else {
+ pte = walk(addr);
+ }
reg_t pte_perm = pte & PTE_PERM;
- if (proc == NULL || (proc->state.sr & SR_S))
+ if (mode_s || (mode_m && mprv_s && !fetch))
pte_perm = (pte_perm/(PTE_SX/PTE_UX)) & PTE_PERM;
pte_perm |= pte & PTE_V;
@@ -70,53 +89,42 @@ void* mmu_t::refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch)
pte_t mmu_t::walk(reg_t addr)
{
- pte_t pte = 0;
-
- // the address must be a canonical sign-extended VA_BITS-bit number
- int shift = 8*sizeof(reg_t) - VA_BITS;
- if (((sreg_t)addr << shift >> shift) != (sreg_t)addr)
- ;
- else if (proc == NULL || !(proc->state.sr & SR_VM))
- {
- if(addr < memsz)
- pte = PTE_V | PTE_PERM | ((addr >> PGSHIFT) << PGSHIFT);
- }
- else
- {
- reg_t base = proc->get_state()->ptbr;
- reg_t ptd;
-
- int ptshift = (LEVELS-1)*PTIDXBITS;
- for(reg_t i = 0; i < LEVELS; i++, ptshift -= PTIDXBITS)
- {
- reg_t idx = (addr >> (PGSHIFT+ptshift)) & ((1<<PTIDXBITS)-1);
-
- reg_t pte_addr = base + idx*sizeof(pte_t);
- if(pte_addr >= memsz)
- break;
-
- ptd = *(pte_t*)(mem+pte_addr);
-
- if (!(ptd & PTE_V)) // invalid mapping
- break;
- else if (ptd & PTE_T) // next level of page table
- base = (ptd >> PGSHIFT) << PGSHIFT;
- else // the actual PTE
- {
- // if this PTE is from a larger PT, fake a leaf
- // PTE so the TLB will work right
- reg_t vpn = addr >> PGSHIFT;
- ptd |= (vpn & ((1<<(ptshift))-1)) << PGSHIFT;
-
- // fault if physical addr is out of range
- if (((ptd >> PGSHIFT) << PGSHIFT) < memsz)
- pte = ptd;
- break;
- }
+ reg_t msb_mask = -(reg_t(1) << (VA_BITS-1));
+ if ((addr & msb_mask) != 0 && (addr & msb_mask) != msb_mask)
+ return 0; // address isn't properly sign-extended
+
+ reg_t base = proc->get_state()->sptbr;
+ reg_t ptd;
+
+ int ptshift = (LEVELS-1)*PTIDXBITS;
+ for (reg_t i = 0; i < LEVELS; i++, ptshift -= PTIDXBITS) {
+ reg_t idx = (addr >> (PGSHIFT+ptshift)) & ((1<<PTIDXBITS)-1);
+
+ // check that physical address of PTE is legal
+ reg_t pte_addr = base + idx*sizeof(pte_t);
+ if (pte_addr >= memsz)
+ return 0;
+
+ ptd = *(pte_t*)(mem+pte_addr);
+
+ if (!(ptd & PTE_V)) { // invalid mapping
+ return 0;
+ } else if (ptd & PTE_T) { // next level of page table
+ base = (ptd >> PGSHIFT) << PGSHIFT;
+ } else {
+ // we've found the PTE.
+ // for superpage mappings, make a fake leaf PTE for the TLB's benefit.
+ reg_t vpn = addr >> PGSHIFT;
+ ptd |= (vpn & ((1<<(ptshift))-1)) << PGSHIFT;
+
+ // check that physical address is legal
+ if (((ptd >> PGSHIFT) << PGSHIFT) >= memsz)
+ return 0;
+
+ return ptd;
}
}
-
- return pte;
+ return 0;
}
void mmu_t::register_memtracer(memtracer_t* t)