|
|
|
@ -34,7 +34,6 @@ 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 = 0; |
|
|
|
reg_t mstatus = proc ? proc->state.mstatus : 0; |
|
|
|
|
|
|
|
bool vm_disabled = get_field(mstatus, MSTATUS_VM) == VM_MBARE; |
|
|
|
@ -43,52 +42,48 @@ void* mmu_t::refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch) |
|
|
|
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); |
|
|
|
|
|
|
|
reg_t pgbase; |
|
|
|
if (vm_disabled || (mode_m && (mprv_m || fetch))) { |
|
|
|
pgbase = addr & -PGSIZE; |
|
|
|
// 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; |
|
|
|
} |
|
|
|
if (addr >= memsz) |
|
|
|
pgbase = -1; |
|
|
|
} else { |
|
|
|
pte = walk(addr, want_perm); |
|
|
|
} |
|
|
|
|
|
|
|
if (!(pte & PTE_V) || !(pte & want_perm)) { |
|
|
|
if (fetch) |
|
|
|
throw trap_instruction_access_fault(addr); |
|
|
|
if (store) |
|
|
|
throw trap_store_access_fault(addr); |
|
|
|
throw trap_load_access_fault(addr); |
|
|
|
pgbase = walk(addr, mode_s || (mode_m && mprv_s), store, fetch); |
|
|
|
} |
|
|
|
|
|
|
|
reg_t pgoff = addr & (PGSIZE-1); |
|
|
|
reg_t pgbase = pte >> PGSHIFT << PGSHIFT; |
|
|
|
reg_t paddr = pgbase + pgoff; |
|
|
|
|
|
|
|
if (pgbase == reg_t(-1)) { |
|
|
|
if (fetch) throw trap_instruction_access_fault(addr); |
|
|
|
else if (store) throw trap_store_access_fault(addr); |
|
|
|
else throw trap_load_access_fault(addr); |
|
|
|
} |
|
|
|
|
|
|
|
if (unlikely(tracer.interested_in_range(pgbase, pgbase + PGSIZE, store, fetch))) |
|
|
|
tracer.trace(paddr, bytes, store, fetch); |
|
|
|
else |
|
|
|
{ |
|
|
|
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; |
|
|
|
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 (fetch) tlb_insn_tag[idx] = expected_tag; |
|
|
|
else if (store) tlb_store_tag[idx] = expected_tag; |
|
|
|
else tlb_load_tag[idx] = expected_tag; |
|
|
|
|
|
|
|
tlb_data[idx] = mem + pgbase - (addr & ~(PGSIZE-1)); |
|
|
|
} |
|
|
|
|
|
|
|
return mem + paddr; |
|
|
|
} |
|
|
|
|
|
|
|
pte_t mmu_t::walk(reg_t addr, reg_t perm) |
|
|
|
pte_t mmu_t::walk(reg_t addr, bool supervisor, bool store, bool fetch) |
|
|
|
{ |
|
|
|
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
|
|
|
|
return -1; // address isn't properly sign-extended
|
|
|
|
|
|
|
|
reg_t base = proc->get_state()->sptbr; |
|
|
|
|
|
|
|
@ -99,33 +94,34 @@ pte_t mmu_t::walk(reg_t addr, reg_t perm) |
|
|
|
// check that physical address of PTE is legal
|
|
|
|
reg_t pte_addr = base + idx*sizeof(pte_t); |
|
|
|
if (pte_addr >= memsz) |
|
|
|
return 0; |
|
|
|
return -1; |
|
|
|
|
|
|
|
pte_t* ppte = (pte_t*)(mem+pte_addr); |
|
|
|
reg_t ppn = *ppte >> PTE_PPN_SHIFT; |
|
|
|
|
|
|
|
if (!(*ppte & PTE_V)) { // invalid mapping
|
|
|
|
return 0; |
|
|
|
} else if (*ppte & PTE_T) { // next level of page table
|
|
|
|
base = (*ppte >> PGSHIFT) << PGSHIFT; |
|
|
|
if ((*ppte & PTE_TYPE) == PTE_TYPE_TABLE) { // next level of page table
|
|
|
|
base = ppn << PGSHIFT; |
|
|
|
} else { |
|
|
|
// we've found the PTE. set referenced and possibly dirty bits.
|
|
|
|
if (*ppte & perm) { |
|
|
|
*ppte |= PTE_R; |
|
|
|
if (perm & (PTE_SW | PTE_UW)) |
|
|
|
*ppte |= PTE_D; |
|
|
|
} |
|
|
|
// we've found the PTE. check the permissions.
|
|
|
|
if (!PTE_CHECK_PERM(*ppte, supervisor, store, fetch)) |
|
|
|
return -1; |
|
|
|
// set referenced and possibly dirty bits.
|
|
|
|
*ppte |= PTE_R; |
|
|
|
if (store) |
|
|
|
*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); |
|
|
|
reg_t addr = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; |
|
|
|
|
|
|
|
// check that physical address is legal
|
|
|
|
if (((pte >> PGSHIFT) << PGSHIFT) >= memsz) |
|
|
|
return 0; |
|
|
|
if (addr >= memsz) |
|
|
|
return -1; |
|
|
|
|
|
|
|
return pte; |
|
|
|
return addr; |
|
|
|
} |
|
|
|
} |
|
|
|
return 0; |
|
|
|
|
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
void mmu_t::register_memtracer(memtracer_t* t) |
|
|
|
|