diff options
-rw-r--r-- | riscv/mmu.cc | 126 | ||||
-rw-r--r-- | riscv/mmu.h | 9 | ||||
-rw-r--r-- | riscv/processor.cc | 32 | ||||
-rw-r--r-- | riscv/processor.h | 4 |
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 |