diff options
author | Andrew Waterman <waterman@cs.berkeley.edu> | 2015-03-12 17:32:43 -0700 |
---|---|---|
committer | Andrew Waterman <waterman@cs.berkeley.edu> | 2015-03-12 17:32:43 -0700 |
commit | ea58df801f36605b462783a61b5266bdd9a40eb0 (patch) | |
tree | ec6c4736f29e9f2021a9e448946e0a0169639cc1 /riscv/mmu.cc | |
parent | 9af855a28e7dc4009ad31312840ca6f9396d572e (diff) | |
download | riscv-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.cc | 102 |
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) |