aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--riscv/mmu.cc126
-rw-r--r--riscv/mmu.h9
-rw-r--r--riscv/processor.cc32
-rw-r--r--riscv/processor.h4
4 files changed, 147 insertions, 24 deletions
diff --git a/riscv/mmu.cc b/riscv/mmu.cc
index 021f587..3e1fc25 100644
--- a/riscv/mmu.cc
+++ b/riscv/mmu.cc
@@ -34,7 +34,17 @@ void mmu_t::flush_tlb()
flush_icache();
}
-reg_t mmu_t::translate(reg_t addr, access_type type)
+static void throw_access_exception(reg_t addr, access_type type)
+{
+ switch (type) {
+ case FETCH: throw trap_instruction_access_fault(addr);
+ case LOAD: throw trap_load_access_fault(addr);
+ case STORE: throw trap_store_access_fault(addr);
+ default: abort();
+ }
+}
+
+reg_t mmu_t::translate(reg_t addr, reg_t len, access_type type)
{
if (!proc)
return addr;
@@ -45,12 +55,15 @@ reg_t mmu_t::translate(reg_t addr, access_type type)
mode = get_field(proc->state.mstatus, MSTATUS_MPP);
}
- return walk(addr, type, mode) | (addr & (PGSIZE-1));
+ reg_t paddr = walk(addr, type, mode) | (addr & (PGSIZE-1));
+ if (!pmp_ok(paddr, type, mode) || !pmp_homogeneous(paddr, len))
+ throw_access_exception(addr, type);
+ return paddr;
}
tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr)
{
- reg_t paddr = translate(vaddr, FETCH);
+ reg_t paddr = translate(vaddr, sizeof(fetch_temp), FETCH);
if (auto host_addr = sim->addr_to_mem(paddr)) {
return refill_tlb(vaddr, paddr, host_addr, FETCH);
@@ -90,7 +103,7 @@ reg_t reg_from_bytes(size_t len, const uint8_t* bytes)
void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
{
- reg_t paddr = translate(addr, LOAD);
+ reg_t paddr = translate(addr, len, LOAD);
if (auto host_addr = sim->addr_to_mem(paddr)) {
memcpy(bytes, host_addr, len);
@@ -112,7 +125,7 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes)
{
- reg_t paddr = translate(addr, STORE);
+ reg_t paddr = translate(addr, len, STORE);
if (!matched_trigger) {
reg_t data = reg_from_bytes(len, bytes);
@@ -149,15 +162,90 @@ tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_
(check_triggers_store && type == STORE))
expected_tag |= TLB_CHECK_TRIGGERS;
- 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;
+ 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_entry_t entry = {host_addr - vaddr, paddr - vaddr};
tlb_data[idx] = entry;
return entry;
}
+reg_t mmu_t::pmp_ok(reg_t addr, access_type type, reg_t mode)
+{
+ if (!proc)
+ return true;
+
+ reg_t base = 0;
+ for (size_t i = 0; i < proc->state.n_pmp; i++) {
+ reg_t tor = proc->state.pmpaddr[i] << PMP_SHIFT;
+ uint8_t cfg = proc->state.pmpcfg[i];
+
+ if (cfg & PMP_A) {
+ bool is_tor = (cfg & PMP_A) == PMP_TOR;
+ bool is_na4 = (cfg & PMP_A) == PMP_NA4;
+
+ reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4);
+ mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
+ bool napot_match = ((addr ^ tor) & mask) == 0;
+ bool tor_match = base <= addr && addr < tor;
+
+ if (is_tor ? tor_match : napot_match) {
+ return
+ (mode == PRV_M && !(cfg & PMP_L)) ||
+ (type == LOAD && (cfg & PMP_R)) ||
+ (type == STORE && (cfg & PMP_W)) ||
+ (type == FETCH && (cfg & PMP_X));
+ }
+ }
+
+ base = tor;
+ }
+
+ return mode == PRV_M;
+}
+
+reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len)
+{
+ if ((addr | len) & (len - 1))
+ abort();
+
+ if (!proc)
+ return true;
+
+ reg_t base = 0;
+ for (size_t i = 0; i < proc->state.n_pmp; i++) {
+ reg_t tor = proc->state.pmpaddr[i] << PMP_SHIFT;
+ uint8_t cfg = proc->state.pmpcfg[i];
+
+ if (cfg & PMP_A) {
+ bool is_tor = (cfg & PMP_A) == PMP_TOR;
+ bool is_na4 = (cfg & PMP_A) == PMP_NA4;
+
+ bool begins_after_lower = addr >= base;
+ bool begins_after_upper = addr >= tor;
+ bool ends_before_lower = (addr & -len) < (base & -len);
+ bool ends_before_upper = (addr & -len) < (tor & -len);
+ bool tor_homogeneous = ends_before_lower || begins_after_upper ||
+ (begins_after_lower && ends_before_upper);
+
+ reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4);
+ mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
+ bool mask_homogeneous = ~(mask << 1) & len;
+ bool napot_homogeneous = mask_homogeneous || ((addr ^ tor) / len) != 0;
+
+ if (!(is_tor ? tor_homogeneous : napot_homogeneous))
+ return false;
+ }
+
+ base = tor;
+ }
+
+ return true;
+}
+
reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
{
vm_info vm = decode_vm_info(proc->max_xlen, mode, proc->get_state()->satp);
@@ -181,9 +269,10 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
// check that physical address of PTE is legal
- auto ppte = sim->addr_to_mem(base + idx * vm.ptesize);
- if (!ppte)
- goto fail_access;
+ auto pte_paddr = base + idx * vm.ptesize;
+ auto ppte = sim->addr_to_mem(pte_paddr);
+ if (!ppte || !pmp_ok(pte_paddr, LOAD, PRV_S))
+ throw_access_exception(addr, type);
reg_t pte = vm.ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte;
reg_t ppn = pte >> PTE_PPN_SHIFT;
@@ -204,7 +293,11 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
reg_t ad = PTE_A | ((type == STORE) * PTE_D);
#ifdef RISCV_ENABLE_DIRTY
// set accessed and possibly dirty bits.
- *(uint32_t*)ppte |= ad;
+ if ((pte & ad) != ad) {
+ if (!pmp_ok(pte_paddr, STORE, PRV_S))
+ throw_access_exception(addr, type);
+ *(uint32_t*)ppte |= ad;
+ }
#else
// take exception if access or possibly dirty bit is not set.
if ((pte & ad) != ad)
@@ -217,21 +310,12 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
}
}
-fail:
switch (type) {
case FETCH: throw trap_instruction_page_fault(addr);
case LOAD: throw trap_load_page_fault(addr);
case STORE: throw trap_store_page_fault(addr);
default: abort();
}
-
-fail_access:
- switch (type) {
- case FETCH: throw trap_instruction_access_fault(addr);
- case LOAD: throw trap_load_access_fault(addr);
- case STORE: throw trap_store_access_fault(addr);
- default: abort();
- }
}
void mmu_t::register_memtracer(memtracer_t* t)
diff --git a/riscv/mmu.h b/riscv/mmu.h
index f66eb00..7617367 100644
--- a/riscv/mmu.h
+++ b/riscv/mmu.h
@@ -187,7 +187,7 @@ public:
inline void acquire_load_reservation(reg_t vaddr)
{
- reg_t paddr = translate(vaddr, LOAD);
+ reg_t paddr = translate(vaddr, 1, LOAD);
if (auto host_addr = sim->addr_to_mem(paddr))
load_reservation_address = refill_tlb(vaddr, paddr, host_addr, LOAD).target_offset + vaddr;
else
@@ -196,7 +196,7 @@ public:
inline bool check_load_reservation(reg_t vaddr)
{
- reg_t paddr = translate(vaddr, STORE);
+ reg_t paddr = translate(vaddr, 1, STORE);
if (auto host_addr = sim->addr_to_mem(paddr))
return load_reservation_address == refill_tlb(vaddr, paddr, host_addr, STORE).target_offset + vaddr;
else
@@ -311,7 +311,7 @@ private:
tlb_entry_t fetch_slow_path(reg_t addr);
void load_slow_path(reg_t addr, reg_t len, uint8_t* bytes);
void store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes);
- reg_t translate(reg_t addr, access_type type);
+ reg_t translate(reg_t addr, reg_t len, access_type type);
// ITLB lookup
inline tlb_entry_t translate_insn_addr(reg_t addr) {
@@ -353,6 +353,9 @@ private:
return new trigger_matched_t(match, operation, address, data);
}
+ reg_t pmp_homogeneous(reg_t addr, reg_t len);
+ reg_t pmp_ok(reg_t addr, access_type type, reg_t mode);
+
bool check_triggers_fetch;
bool check_triggers_load;
bool check_triggers_store;
diff --git a/riscv/processor.cc b/riscv/processor.cc
index 4a7d0ec..6221f8b 100644
--- a/riscv/processor.cc
+++ b/riscv/processor.cc
@@ -333,6 +333,26 @@ void processor_t::set_csr(int which, reg_t val)
reg_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP
| ((ext != NULL) << IRQ_COP);
reg_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP;
+
+ if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.n_pmp) {
+ size_t i = which - CSR_PMPADDR0;
+ bool locked = state.pmpcfg[i] & PMP_L;
+ bool next_locked = i+1 < state.n_pmp && (state.pmpcfg[i+1] & PMP_L);
+ bool next_tor = i+1 < state.n_pmp && (state.pmpcfg[i+1] & PMP_A) == PMP_TOR;
+ if (!locked && !(next_locked && next_tor))
+ state.pmpaddr[i] = val;
+
+ mmu->flush_tlb();
+ }
+
+ if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.n_pmp / 4) {
+ for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8; i++) {
+ if (!(state.pmpcfg[i] & PMP_L))
+ state.pmpcfg[i] = (val >> (8 * (i - i0))) & (PMP_R | PMP_W | PMP_X | PMP_A | PMP_L);
+ }
+ mmu->flush_tlb();
+ }
+
switch (which)
{
case CSR_FFLAGS:
@@ -556,6 +576,18 @@ reg_t processor_t::get_csr(int which)
if (which >= CSR_MHPMEVENT3 && which <= CSR_MHPMEVENT31)
return 0;
+ if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.n_pmp)
+ return state.pmpaddr[which - CSR_PMPADDR0];
+
+ if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.n_pmp / 4) {
+ require((which & ((xlen / 32) - 1)) == 0);
+
+ reg_t res = 0;
+ for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8 && i < state.n_pmp; i++)
+ res |= reg_t(state.pmpcfg[i]) << (8 * (i - i0));
+ return res;
+ }
+
switch (which)
{
case CSR_FFLAGS:
diff --git a/riscv/processor.h b/riscv/processor.h
index fd90ce3..de0be78 100644
--- a/riscv/processor.h
+++ b/riscv/processor.h
@@ -123,6 +123,10 @@ struct state_t
mcontrol_t mcontrol[num_triggers];
reg_t tdata2[num_triggers];
+ static const int n_pmp = 16;
+ uint8_t pmpcfg[n_pmp];
+ reg_t pmpaddr[n_pmp];
+
uint32_t fflags;
uint32_t frm;
bool serialized; // whether timer CSRs are in a well-defined state