Browse Source

Add PMP support

pull/242/head
Andrew Waterman 8 years ago
committed by Andrew Waterman
parent
commit
55ef17645d
  1. 118
      riscv/mmu.cc
  2. 9
      riscv/mmu.h
  3. 32
      riscv/processor.cc
  4. 4
      riscv/processor.h

118
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 (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.
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)

9
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;

32
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:

4
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

Loading…
Cancel
Save