You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
561 lines
18 KiB
561 lines
18 KiB
// See LICENSE for license details.
|
|
|
|
#include "config.h"
|
|
#include "mmu.h"
|
|
#include "arith.h"
|
|
#include "simif.h"
|
|
#include "processor.h"
|
|
|
|
mmu_t::mmu_t(simif_t* sim, endianness_t endianness, processor_t* proc)
|
|
: sim(sim), proc(proc),
|
|
#ifdef RISCV_ENABLE_DUAL_ENDIAN
|
|
target_big_endian(endianness == endianness_big),
|
|
#endif
|
|
check_triggers_fetch(false),
|
|
check_triggers_load(false),
|
|
check_triggers_store(false),
|
|
matched_trigger(NULL)
|
|
{
|
|
#ifndef RISCV_ENABLE_DUAL_ENDIAN
|
|
assert(endianness == endianness_little);
|
|
#endif
|
|
flush_tlb();
|
|
yield_load_reservation();
|
|
}
|
|
|
|
mmu_t::~mmu_t()
|
|
{
|
|
}
|
|
|
|
void mmu_t::flush_icache()
|
|
{
|
|
for (size_t i = 0; i < ICACHE_ENTRIES; i++)
|
|
icache[i].tag = -1;
|
|
}
|
|
|
|
void mmu_t::flush_tlb()
|
|
{
|
|
memset(tlb_insn_tag, -1, sizeof(tlb_insn_tag));
|
|
memset(tlb_load_tag, -1, sizeof(tlb_load_tag));
|
|
memset(tlb_store_tag, -1, sizeof(tlb_store_tag));
|
|
|
|
flush_icache();
|
|
}
|
|
|
|
void throw_access_exception(bool virt, reg_t addr, access_type type)
|
|
{
|
|
switch (type) {
|
|
case FETCH: throw trap_instruction_access_fault(virt, addr, 0, 0);
|
|
case LOAD: throw trap_load_access_fault(virt, addr, 0, 0);
|
|
case STORE: throw trap_store_access_fault(virt, addr, 0, 0);
|
|
default: abort();
|
|
}
|
|
}
|
|
|
|
reg_t mmu_t::translate(mem_access_info_t access_info, reg_t len)
|
|
{
|
|
reg_t addr = access_info.vaddr;
|
|
access_type type = access_info.type;
|
|
if (!proc)
|
|
return addr;
|
|
|
|
bool virt = access_info.effective_virt;
|
|
reg_t mode = (reg_t) access_info.effective_priv;
|
|
|
|
reg_t paddr = walk(access_info) | (addr & (PGSIZE-1));
|
|
if (!pmp_ok(paddr, len, type, mode))
|
|
throw_access_exception(virt, addr, type);
|
|
return paddr;
|
|
}
|
|
|
|
tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr)
|
|
{
|
|
auto access_info = generate_access_info(vaddr, FETCH, {false, false, false});
|
|
check_triggers(triggers::OPERATION_EXECUTE, vaddr, access_info.effective_virt);
|
|
|
|
tlb_entry_t result;
|
|
reg_t vpn = vaddr >> PGSHIFT;
|
|
if (unlikely(tlb_insn_tag[vpn % TLB_ENTRIES] != (vpn | TLB_CHECK_TRIGGERS))) {
|
|
reg_t paddr = translate(access_info, sizeof(fetch_temp));
|
|
if (auto host_addr = sim->addr_to_mem(paddr)) {
|
|
result = refill_tlb(vaddr, paddr, host_addr, FETCH);
|
|
} else {
|
|
if (!mmio_fetch(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp))
|
|
throw trap_instruction_access_fault(proc->state.v, vaddr, 0, 0);
|
|
result = {(char*)&fetch_temp - vaddr, paddr - vaddr};
|
|
}
|
|
} else {
|
|
result = tlb_data[vpn % TLB_ENTRIES];
|
|
}
|
|
|
|
check_triggers(triggers::OPERATION_EXECUTE, vaddr, access_info.effective_virt, from_le(*(const uint16_t*)(result.host_offset + vaddr)));
|
|
|
|
return result;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool mmu_t::mmio_ok(reg_t paddr, access_type UNUSED type)
|
|
{
|
|
// Disallow access to debug region when not in debug mode
|
|
if (paddr >= DEBUG_START && paddr <= DEBUG_END && proc && !proc->state.debug_mode)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mmu_t::mmio_fetch(reg_t paddr, size_t len, uint8_t* bytes)
|
|
{
|
|
if (!mmio_ok(paddr, FETCH))
|
|
return false;
|
|
|
|
return sim->mmio_fetch(paddr, len, bytes);
|
|
}
|
|
|
|
bool mmu_t::mmio_load(reg_t paddr, size_t len, uint8_t* bytes)
|
|
{
|
|
return mmio(paddr, len, bytes, LOAD);
|
|
}
|
|
|
|
bool mmu_t::mmio_store(reg_t paddr, size_t len, const uint8_t* bytes)
|
|
{
|
|
return mmio(paddr, len, const_cast<uint8_t*>(bytes), STORE);
|
|
}
|
|
|
|
bool mmu_t::mmio(reg_t paddr, size_t len, uint8_t* bytes, access_type type)
|
|
{
|
|
bool power_of_2 = (len & (len - 1)) == 0;
|
|
bool naturally_aligned = (paddr & (len - 1)) == 0;
|
|
|
|
if (power_of_2 && naturally_aligned) {
|
|
if (!mmio_ok(paddr, type))
|
|
return false;
|
|
|
|
if (type == STORE)
|
|
return sim->mmio_store(paddr, len, bytes);
|
|
else
|
|
return sim->mmio_load(paddr, len, bytes);
|
|
}
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (!mmio(paddr + i, 1, bytes + i, type))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void mmu_t::check_triggers(triggers::operation_t operation, reg_t address, bool virt, std::optional<reg_t> data)
|
|
{
|
|
if (matched_trigger || !proc)
|
|
return;
|
|
|
|
auto match = proc->TM.detect_memory_access_match(operation, address, data);
|
|
|
|
if (match.has_value())
|
|
switch (match->timing) {
|
|
case triggers::TIMING_BEFORE:
|
|
throw triggers::matched_t(operation, address, match->action, virt);
|
|
|
|
case triggers::TIMING_AFTER:
|
|
// We want to take this exception on the next instruction. We check
|
|
// whether to do so in the I$ refill path, so flush the I$.
|
|
flush_icache();
|
|
matched_trigger = new triggers::matched_t(operation, address, match->action, virt);
|
|
}
|
|
}
|
|
|
|
void mmu_t::load_slow_path_intrapage(reg_t len, uint8_t* bytes, mem_access_info_t access_info)
|
|
{
|
|
reg_t addr = access_info.vaddr;
|
|
reg_t vpn = addr >> PGSHIFT;
|
|
if (!access_info.flags.is_special_access() && vpn == (tlb_load_tag[vpn % TLB_ENTRIES] & ~TLB_CHECK_TRIGGERS)) {
|
|
auto host_addr = tlb_data[vpn % TLB_ENTRIES].host_offset + addr;
|
|
memcpy(bytes, host_addr, len);
|
|
return;
|
|
}
|
|
|
|
reg_t paddr = translate(access_info, len);
|
|
|
|
if (access_info.flags.lr && !sim->reservable(paddr)) {
|
|
throw trap_load_access_fault(access_info.effective_virt, addr, 0, 0);
|
|
}
|
|
|
|
if (auto host_addr = sim->addr_to_mem(paddr)) {
|
|
memcpy(bytes, host_addr, len);
|
|
if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD))
|
|
tracer.trace(paddr, len, LOAD);
|
|
else if (!access_info.flags.is_special_access())
|
|
refill_tlb(addr, paddr, host_addr, LOAD);
|
|
|
|
} else if (!mmio_load(paddr, len, bytes)) {
|
|
throw trap_load_access_fault(access_info.effective_virt, addr, 0, 0);
|
|
}
|
|
|
|
if (access_info.flags.lr) {
|
|
load_reservation_address = paddr;
|
|
}
|
|
}
|
|
|
|
void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes, xlate_flags_t xlate_flags)
|
|
{
|
|
auto access_info = generate_access_info(addr, LOAD, xlate_flags);
|
|
check_triggers(triggers::OPERATION_LOAD, addr, access_info.effective_virt);
|
|
|
|
if ((addr & (len - 1)) == 0) {
|
|
load_slow_path_intrapage(len, bytes, access_info);
|
|
} else {
|
|
bool gva = access_info.effective_virt;
|
|
if (!is_misaligned_enabled())
|
|
throw trap_load_address_misaligned(gva, addr, 0, 0);
|
|
|
|
if (access_info.flags.lr)
|
|
throw trap_load_access_fault(gva, addr, 0, 0);
|
|
|
|
reg_t len_page0 = std::min(len, PGSIZE - addr % PGSIZE);
|
|
load_slow_path_intrapage(len_page0, bytes, access_info);
|
|
if (len_page0 != len)
|
|
load_slow_path_intrapage(len - len_page0, bytes + len_page0, access_info.split_misaligned_access(len_page0));
|
|
}
|
|
|
|
check_triggers(triggers::OPERATION_LOAD, addr, access_info.effective_virt, reg_from_bytes(len, bytes));
|
|
}
|
|
|
|
void mmu_t::store_slow_path_intrapage(reg_t len, const uint8_t* bytes, mem_access_info_t access_info, bool actually_store)
|
|
{
|
|
reg_t addr = access_info.vaddr;
|
|
reg_t vpn = addr >> PGSHIFT;
|
|
if (!access_info.flags.is_special_access() && vpn == (tlb_store_tag[vpn % TLB_ENTRIES] & ~TLB_CHECK_TRIGGERS)) {
|
|
if (actually_store) {
|
|
auto host_addr = tlb_data[vpn % TLB_ENTRIES].host_offset + addr;
|
|
memcpy(host_addr, bytes, len);
|
|
}
|
|
return;
|
|
}
|
|
|
|
reg_t paddr = translate(access_info, len);
|
|
|
|
if (actually_store) {
|
|
if (auto host_addr = sim->addr_to_mem(paddr)) {
|
|
memcpy(host_addr, bytes, len);
|
|
if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE))
|
|
tracer.trace(paddr, len, STORE);
|
|
else if (!access_info.flags.is_special_access())
|
|
refill_tlb(addr, paddr, host_addr, STORE);
|
|
} else if (!mmio_store(paddr, len, bytes)) {
|
|
throw trap_store_access_fault(access_info.effective_virt, addr, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes, xlate_flags_t xlate_flags, bool actually_store, bool UNUSED require_alignment)
|
|
{
|
|
auto access_info = generate_access_info(addr, STORE, xlate_flags);
|
|
if (actually_store)
|
|
check_triggers(triggers::OPERATION_STORE, addr, access_info.effective_virt, reg_from_bytes(len, bytes));
|
|
|
|
if (addr & (len - 1)) {
|
|
bool gva = access_info.effective_virt;
|
|
if (!is_misaligned_enabled())
|
|
throw trap_store_address_misaligned(gva, addr, 0, 0);
|
|
|
|
if (require_alignment)
|
|
throw trap_store_access_fault(gva, addr, 0, 0);
|
|
|
|
reg_t len_page0 = std::min(len, PGSIZE - addr % PGSIZE);
|
|
store_slow_path_intrapage(len_page0, bytes, access_info, actually_store);
|
|
if (len_page0 != len)
|
|
store_slow_path_intrapage(len - len_page0, bytes + len_page0, access_info.split_misaligned_access(len_page0), actually_store);
|
|
} else {
|
|
store_slow_path_intrapage(len, bytes, access_info, actually_store);
|
|
}
|
|
}
|
|
|
|
tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_type type)
|
|
{
|
|
reg_t idx = (vaddr >> PGSHIFT) % TLB_ENTRIES;
|
|
reg_t expected_tag = vaddr >> PGSHIFT;
|
|
|
|
tlb_entry_t entry = {host_addr - vaddr, paddr - vaddr};
|
|
|
|
if (in_mprv())
|
|
return entry;
|
|
|
|
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 (pmp_homogeneous(paddr & ~reg_t(PGSIZE - 1), PGSIZE)) {
|
|
if (type == FETCH) tlb_insn_tag[idx] = expected_tag;
|
|
else if (type == STORE) tlb_store_tag[idx] = expected_tag;
|
|
else tlb_load_tag[idx] = expected_tag;
|
|
}
|
|
|
|
tlb_data[idx] = entry;
|
|
return entry;
|
|
}
|
|
|
|
bool mmu_t::pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode)
|
|
{
|
|
if (!proc || proc->n_pmp == 0)
|
|
return true;
|
|
|
|
for (size_t i = 0; i < proc->n_pmp; i++) {
|
|
// Check each 4-byte sector of the access
|
|
bool any_match = false;
|
|
bool all_match = true;
|
|
for (reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) {
|
|
reg_t cur_addr = addr + offset;
|
|
bool match = proc->state.pmpaddr[i]->match4(cur_addr);
|
|
any_match |= match;
|
|
all_match &= match;
|
|
}
|
|
|
|
if (any_match) {
|
|
// If the PMP matches only a strict subset of the access, fail it
|
|
if (!all_match)
|
|
return false;
|
|
|
|
return proc->state.pmpaddr[i]->access_ok(type, mode);
|
|
}
|
|
}
|
|
|
|
// in case matching region is not found
|
|
const bool mseccfg_mml = proc->state.mseccfg->get_mml();
|
|
const bool mseccfg_mmwp = proc->state.mseccfg->get_mmwp();
|
|
return ((mode == PRV_M) && !mseccfg_mmwp
|
|
&& (!mseccfg_mml || ((type == LOAD) || (type == STORE))));
|
|
}
|
|
|
|
reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len)
|
|
{
|
|
if ((addr | len) & (len - 1))
|
|
abort();
|
|
|
|
if (!proc)
|
|
return true;
|
|
|
|
for (size_t i = 0; i < proc->n_pmp; i++)
|
|
if (proc->state.pmpaddr[i]->subset_match(addr, len))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_type, bool virt, bool hlvx)
|
|
{
|
|
if (!virt)
|
|
return gpa;
|
|
|
|
vm_info vm = decode_vm_info(proc->get_const_xlen(), true, 0, proc->get_state()->hgatp->read());
|
|
if (vm.levels == 0)
|
|
return gpa;
|
|
|
|
int maxgpabits = vm.levels * vm.idxbits + vm.widenbits + PGSHIFT;
|
|
reg_t maxgpa = (1ULL << maxgpabits) - 1;
|
|
|
|
bool mxr = proc->state.sstatus->readvirt(false) & MSTATUS_MXR;
|
|
|
|
reg_t base = vm.ptbase;
|
|
if ((gpa & ~maxgpa) == 0) {
|
|
for (int i = vm.levels - 1; i >= 0; i--) {
|
|
int ptshift = i * vm.idxbits;
|
|
int idxbits = (i == (vm.levels - 1)) ? vm.idxbits + vm.widenbits : vm.idxbits;
|
|
reg_t idx = (gpa >> (PGSHIFT + ptshift)) & ((reg_t(1) << idxbits) - 1);
|
|
|
|
// check that physical address of PTE is legal
|
|
auto pte_paddr = base + idx * vm.ptesize;
|
|
reg_t pte = pte_load(pte_paddr, gva, virt, trap_type, vm.ptesize);
|
|
reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT;
|
|
bool pbmte = proc->get_state()->menvcfg->read() & MENVCFG_PBMTE;
|
|
bool hade = proc->get_state()->menvcfg->read() & MENVCFG_HADE;
|
|
|
|
if (pte & PTE_RSVD) {
|
|
break;
|
|
} else if (!proc->extension_enabled(EXT_SVNAPOT) && (pte & PTE_N)) {
|
|
break;
|
|
} else if (!pbmte && (pte & PTE_PBMT)) {
|
|
break;
|
|
} else if ((pte & PTE_PBMT) == PTE_PBMT) {
|
|
break;
|
|
} else if (PTE_TABLE(pte)) { // next level of page table
|
|
if (pte & (PTE_D | PTE_A | PTE_U | PTE_N | PTE_PBMT))
|
|
break;
|
|
base = ppn << PGSHIFT;
|
|
} else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
|
|
break;
|
|
} else if (!(pte & PTE_U)) {
|
|
break;
|
|
} else if (type == FETCH || hlvx ? !(pte & PTE_X) :
|
|
type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
|
|
!((pte & PTE_R) && (pte & PTE_W))) {
|
|
break;
|
|
} else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
|
|
break;
|
|
} else {
|
|
reg_t ad = PTE_A | ((type == STORE) * PTE_D);
|
|
|
|
if ((pte & ad) != ad) {
|
|
if (hade) {
|
|
// set accessed and possibly dirty bits
|
|
pte_store(pte_paddr, pte | ad, gva, virt, type, vm.ptesize);
|
|
} else {
|
|
// take exception if access or possibly dirty bit is not set.
|
|
break;
|
|
}
|
|
}
|
|
|
|
reg_t vpn = gpa >> PGSHIFT;
|
|
reg_t page_mask = (reg_t(1) << PGSHIFT) - 1;
|
|
|
|
int napot_bits = ((pte & PTE_N) ? (ctz(ppn) + 1) : 0);
|
|
if (((pte & PTE_N) && (ppn == 0 || i != 0)) || (napot_bits != 0 && napot_bits != 4))
|
|
break;
|
|
|
|
reg_t page_base = ((ppn & ~((reg_t(1) << napot_bits) - 1))
|
|
| (vpn & ((reg_t(1) << napot_bits) - 1))
|
|
| (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
|
|
return page_base | (gpa & page_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (trap_type) {
|
|
case FETCH: throw trap_instruction_guest_page_fault(gva, gpa >> 2, 0);
|
|
case LOAD: throw trap_load_guest_page_fault(gva, gpa >> 2, 0);
|
|
case STORE: throw trap_store_guest_page_fault(gva, gpa >> 2, 0);
|
|
default: abort();
|
|
}
|
|
}
|
|
|
|
reg_t mmu_t::walk(mem_access_info_t access_info)
|
|
{
|
|
access_type type = access_info.type;
|
|
reg_t addr = access_info.vaddr;
|
|
bool virt = access_info.effective_virt;
|
|
bool hlvx = access_info.flags.hlvx;
|
|
reg_t mode = access_info.effective_priv;
|
|
reg_t page_mask = (reg_t(1) << PGSHIFT) - 1;
|
|
reg_t satp = proc->get_state()->satp->readvirt(virt);
|
|
vm_info vm = decode_vm_info(proc->get_const_xlen(), false, mode, satp);
|
|
if (vm.levels == 0)
|
|
return s2xlate(addr, addr & ((reg_t(2) << (proc->xlen-1))-1), type, type, virt, hlvx) & ~page_mask; // zero-extend from xlen
|
|
|
|
bool s_mode = mode == PRV_S;
|
|
bool sum = proc->state.sstatus->readvirt(virt) & MSTATUS_SUM;
|
|
bool mxr = (proc->state.sstatus->readvirt(false) | proc->state.sstatus->readvirt(virt)) & MSTATUS_MXR;
|
|
|
|
// verify bits xlen-1:va_bits-1 are all equal
|
|
int va_bits = PGSHIFT + vm.levels * vm.idxbits;
|
|
reg_t mask = (reg_t(1) << (proc->xlen - (va_bits-1))) - 1;
|
|
reg_t masked_msbs = (addr >> (va_bits-1)) & mask;
|
|
if (masked_msbs != 0 && masked_msbs != mask)
|
|
vm.levels = 0;
|
|
|
|
reg_t base = vm.ptbase;
|
|
for (int i = vm.levels - 1; i >= 0; i--) {
|
|
int ptshift = i * vm.idxbits;
|
|
reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
|
|
|
|
// check that physical address of PTE is legal
|
|
auto pte_paddr = s2xlate(addr, base + idx * vm.ptesize, LOAD, type, virt, false);
|
|
reg_t pte = pte_load(pte_paddr, addr, virt, type, vm.ptesize);
|
|
reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT;
|
|
bool pbmte = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_PBMTE) : (proc->get_state()->menvcfg->read() & MENVCFG_PBMTE);
|
|
bool hade = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_HADE) : (proc->get_state()->menvcfg->read() & MENVCFG_HADE);
|
|
|
|
if (pte & PTE_RSVD) {
|
|
break;
|
|
} else if (!proc->extension_enabled(EXT_SVNAPOT) && (pte & PTE_N)) {
|
|
break;
|
|
} else if (!pbmte && (pte & PTE_PBMT)) {
|
|
break;
|
|
} else if ((pte & PTE_PBMT) == PTE_PBMT) {
|
|
break;
|
|
} else if (PTE_TABLE(pte)) { // next level of page table
|
|
if (pte & (PTE_D | PTE_A | PTE_U | PTE_N | PTE_PBMT))
|
|
break;
|
|
base = ppn << PGSHIFT;
|
|
} else if ((pte & PTE_U) ? s_mode && (type == FETCH || !sum) : !s_mode) {
|
|
break;
|
|
} else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
|
|
break;
|
|
} else if (type == FETCH || hlvx ? !(pte & PTE_X) :
|
|
type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
|
|
!((pte & PTE_R) && (pte & PTE_W))) {
|
|
break;
|
|
} else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
|
|
break;
|
|
} else {
|
|
reg_t ad = PTE_A | ((type == STORE) * PTE_D);
|
|
|
|
if ((pte & ad) != ad) {
|
|
if (hade) {
|
|
// set accessed and possibly dirty bits.
|
|
pte_store(pte_paddr, pte | ad, addr, virt, type, vm.ptesize);
|
|
} else {
|
|
// take exception if access or possibly dirty bit is not set.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// for superpage or Svnapot NAPOT mappings, make a fake leaf PTE for the TLB's benefit.
|
|
reg_t vpn = addr >> PGSHIFT;
|
|
|
|
int napot_bits = ((pte & PTE_N) ? (ctz(ppn) + 1) : 0);
|
|
if (((pte & PTE_N) && (ppn == 0 || i != 0)) || (napot_bits != 0 && napot_bits != 4))
|
|
break;
|
|
|
|
reg_t page_base = ((ppn & ~((reg_t(1) << napot_bits) - 1))
|
|
| (vpn & ((reg_t(1) << napot_bits) - 1))
|
|
| (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
|
|
reg_t phys = page_base | (addr & page_mask);
|
|
return s2xlate(addr, phys, type, type, virt, hlvx) & ~page_mask;
|
|
}
|
|
}
|
|
|
|
switch (type) {
|
|
case FETCH: throw trap_instruction_page_fault(virt, addr, 0, 0);
|
|
case LOAD: throw trap_load_page_fault(virt, addr, 0, 0);
|
|
case STORE: throw trap_store_page_fault(virt, addr, 0, 0);
|
|
default: abort();
|
|
}
|
|
}
|
|
|
|
void mmu_t::register_memtracer(memtracer_t* t)
|
|
{
|
|
flush_tlb();
|
|
tracer.hook(t);
|
|
}
|
|
|