bool mprv_m = get_field(mstatus, MSTATUS_MPRV) == PRV_M;
bool mprv_s = get_field(mstatus, MSTATUS_MPRV) == PRV_S;
+ reg_t want_perm = store ? (mode_s || (mode_m && mprv_s) ? PTE_SW : PTE_UW) :
+ !fetch ? (mode_s || (mode_m && mprv_s) ? PTE_SR : PTE_UR) :
+ (mode_s ? PTE_SX : PTE_UX);
+
if (vm_disabled || (mode_m && (mprv_m || fetch))) {
// virtual memory is disabled. merely check legality of physical address.
if (addr < memsz) {
pte |= PTE_UR | PTE_SR | PTE_UW | PTE_SW;
}
} else {
- pte = walk(addr, store);
+ pte = walk(addr, want_perm);
}
- reg_t pte_perm = pte & PTE_PERM;
- if (mode_s || (mode_m && mprv_s && !fetch))
- pte_perm = (pte_perm/(PTE_SX/PTE_UX)) & PTE_PERM;
- pte_perm |= pte & PTE_V;
-
- reg_t perm = (fetch ? PTE_UX : store ? PTE_UW : PTE_UR) | PTE_V;
- if(unlikely((pte_perm & perm) != perm))
- {
+ if (!(pte & PTE_V) || !(pte & want_perm)) {
if (fetch)
throw trap_instruction_access_fault(addr);
if (store)
tracer.trace(paddr, bytes, store, fetch);
else
{
- tlb_load_tag[idx] = (pte_perm & PTE_UR) ? expected_tag : -1;
- tlb_store_tag[idx] = (pte_perm & PTE_UW) && store ? expected_tag : -1;
- tlb_insn_tag[idx] = (pte_perm & PTE_UX) ? expected_tag : -1;
+ tlb_load_tag[idx] = (pte & (PTE_UR|PTE_SR)) ? expected_tag : -1;
+ tlb_store_tag[idx] = (pte & (PTE_UW|PTE_SW)) && store ? expected_tag : -1;
+ tlb_insn_tag[idx] = (pte & (PTE_UX|PTE_SX)) ? expected_tag : -1;
tlb_data[idx] = mem + pgbase - (addr & ~(PGSIZE-1));
}
return mem + paddr;
}
-pte_t mmu_t::walk(reg_t addr, bool store)
+pte_t mmu_t::walk(reg_t addr, reg_t perm)
{
reg_t msb_mask = -(reg_t(1) << (VA_BITS-1));
if ((addr & msb_mask) != 0 && (addr & msb_mask) != msb_mask)
base = (*ppte >> PGSHIFT) << PGSHIFT;
} else {
// we've found the PTE. set referenced and possibly dirty bits.
- *ppte |= PTE_R | (store ? PTE_D : 0);
+ if (*ppte & perm) {
+ *ppte |= PTE_R;
+ if (perm & (PTE_SW | PTE_UW))
+ *ppte |= PTE_D;
+ }
// for superpage mappings, make a fake leaf PTE for the TLB's benefit.
reg_t vpn = addr >> PGSHIFT;
reg_t pte = *ppte | ((vpn & ((1<<(ptshift))-1)) << PGSHIFT);
void* refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch);
// perform a page table walk for a given VA; set referenced/dirty bits
- pte_t walk(reg_t addr, bool store);
+ pte_t walk(reg_t addr, reg_t perm);
// translate a virtual address to a physical address
void* translate(reg_t addr, reg_t bytes, bool store, bool fetch)