Browse Source

Merge pull request #751 from riscv/fix-hlvx

Fix HLVX
pull/755/head
Andrew Waterman 5 years ago
committed by GitHub
parent
commit
b26b05256f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 38
      riscv/mmu.cc
  2. 26
      riscv/mmu.h

38
riscv/mmu.cc

@ -53,8 +53,8 @@ reg_t mmu_t::translate(reg_t addr, reg_t len, access_type type, uint32_t xlate_f
if (!proc) if (!proc)
return addr; return addr;
bool mxr = get_field(proc->state.mstatus, MSTATUS_MXR);
bool virt = (proc) ? proc->state.v : false; bool virt = (proc) ? proc->state.v : false;
bool hlvx = xlate_flags & RISCV_XLATE_VIRT_HLVX;
reg_t mode = proc->state.prv; reg_t mode = proc->state.prv;
if (type != FETCH) { if (type != FETCH) {
if (!proc->state.debug_mode && get_field(proc->state.mstatus, MSTATUS_MPRV)) { if (!proc->state.debug_mode && get_field(proc->state.mstatus, MSTATUS_MPRV)) {
@ -62,16 +62,13 @@ reg_t mmu_t::translate(reg_t addr, reg_t len, access_type type, uint32_t xlate_f
if (get_field(proc->state.mstatus, MSTATUS_MPV) && mode != PRV_M) if (get_field(proc->state.mstatus, MSTATUS_MPV) && mode != PRV_M)
virt = true; virt = true;
} }
if (!proc->state.debug_mode && (xlate_flags & RISCV_XLATE_VIRT)) { if (xlate_flags & RISCV_XLATE_VIRT) {
virt = true; virt = true;
mode = get_field(proc->state.hstatus, HSTATUS_SPVP); mode = get_field(proc->state.hstatus, HSTATUS_SPVP);
if (type == LOAD && (xlate_flags & RISCV_XLATE_VIRT_MXR)) {
mxr = true;
}
} }
} }
reg_t paddr = walk(addr, type, mode, virt, mxr) | (addr & (PGSIZE-1)); reg_t paddr = walk(addr, type, mode, virt, hlvx) | (addr & (PGSIZE-1));
if (!pmp_ok(paddr, len, type, mode)) if (!pmp_ok(paddr, len, type, mode))
throw_access_exception(virt, addr, type); throw_access_exception(virt, addr, type);
return paddr; return paddr;
@ -150,7 +147,7 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes, uint32_t xlate
memcpy(bytes, host_addr, len); memcpy(bytes, host_addr, len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD)) if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD))
tracer.trace(paddr, len, LOAD); tracer.trace(paddr, len, LOAD);
else else if (xlate_flags == 0)
refill_tlb(addr, paddr, host_addr, LOAD); refill_tlb(addr, paddr, host_addr, LOAD);
} else if (!mmio_load(paddr, len, bytes)) { } else if (!mmio_load(paddr, len, bytes)) {
throw trap_load_access_fault((proc) ? proc->state.v : false, addr, 0, 0); throw trap_load_access_fault((proc) ? proc->state.v : false, addr, 0, 0);
@ -179,7 +176,7 @@ void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes, uint32_
memcpy(host_addr, bytes, len); memcpy(host_addr, bytes, len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE)) if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE))
tracer.trace(paddr, len, STORE); tracer.trace(paddr, len, STORE);
else else if (xlate_flags == 0)
refill_tlb(addr, paddr, host_addr, STORE); refill_tlb(addr, paddr, host_addr, STORE);
} else if (!mmio_store(paddr, len, bytes)) { } else if (!mmio_store(paddr, len, bytes)) {
throw trap_store_access_fault((proc) ? proc->state.v : false, addr, 0, 0); throw trap_store_access_fault((proc) ? proc->state.v : false, addr, 0, 0);
@ -305,7 +302,7 @@ reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len)
return true; return true;
} }
reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_type, bool virt, bool mxr) reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_type, bool virt, bool hlvx)
{ {
if (!virt) if (!virt)
return gpa; return gpa;
@ -314,6 +311,8 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty
if (vm.levels == 0) if (vm.levels == 0)
return gpa; return gpa;
bool mxr = proc->state.mstatus & MSTATUS_MXR;
reg_t base = vm.ptbase; reg_t base = vm.ptbase;
for (int i = vm.levels - 1; i >= 0; i--) { for (int i = vm.levels - 1; i >= 0; i--) {
int ptshift = i * vm.idxbits; int ptshift = i * vm.idxbits;
@ -338,9 +337,9 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty
break; break;
} else if (!(pte & PTE_U)) { } else if (!(pte & PTE_U)) {
break; break;
} else if (type == FETCH ? !(pte & PTE_X) : } else if (type == FETCH || hlvx ? !(pte & PTE_X) :
type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) : type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
!((pte & PTE_R) && (pte & PTE_W))) { !((pte & PTE_R) && (pte & PTE_W))) {
break; break;
} else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) { } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
break; break;
@ -380,16 +379,17 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty
} }
} }
reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool mxr) reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool hlvx)
{ {
reg_t page_mask = (reg_t(1) << PGSHIFT) - 1; reg_t page_mask = (reg_t(1) << PGSHIFT) - 1;
reg_t satp = (virt) ? proc->get_state()->vsatp : proc->get_state()->satp; reg_t satp = (virt) ? proc->get_state()->vsatp : proc->get_state()->satp;
vm_info vm = decode_vm_info(proc->max_xlen, false, mode, satp); vm_info vm = decode_vm_info(proc->max_xlen, false, mode, satp);
if (vm.levels == 0) if (vm.levels == 0)
return s2xlate(addr, addr & ((reg_t(2) << (proc->xlen-1))-1), type, type, virt, mxr) & ~page_mask; // zero-extend from xlen 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 s_mode = mode == PRV_S;
bool sum = get_field(proc->state.mstatus, MSTATUS_SUM); bool sum = (virt ? proc->state.vsstatus : proc->state.mstatus) & MSTATUS_SUM;
bool mxr = (proc->state.mstatus | (virt ? proc->state.vsstatus : 0)) & MSTATUS_MXR;
// verify bits xlen-1:va_bits-1 are all equal // verify bits xlen-1:va_bits-1 are all equal
int va_bits = PGSHIFT + vm.levels * vm.idxbits; int va_bits = PGSHIFT + vm.levels * vm.idxbits;
@ -420,9 +420,9 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool mxr)
break; break;
} else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
break; break;
} else if (type == FETCH ? !(pte & PTE_X) : } else if (type == FETCH || hlvx ? !(pte & PTE_X) :
type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) : type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
!((pte & PTE_R) && (pte & PTE_W))) { !((pte & PTE_R) && (pte & PTE_W))) {
break; break;
} else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) { } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
break; break;
@ -451,7 +451,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool mxr)
| (vpn & ((reg_t(1) << napot_bits) - 1)) | (vpn & ((reg_t(1) << napot_bits) - 1))
| (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
reg_t phys = page_base | (addr & page_mask); reg_t phys = page_base | (addr & page_mask);
return s2xlate(addr, phys, type, type, virt, mxr) & ~page_mask; return s2xlate(addr, phys, type, type, virt, hlvx) & ~page_mask;
} }
} }

26
riscv/mmu.h

@ -62,7 +62,7 @@ public:
~mmu_t(); ~mmu_t();
#define RISCV_XLATE_VIRT (1U << 0) #define RISCV_XLATE_VIRT (1U << 0)
#define RISCV_XLATE_VIRT_MXR (1U << 1) #define RISCV_XLATE_VIRT_HLVX (1U << 1)
inline reg_t misaligned_load(reg_t addr, size_t size, uint32_t xlate_flags) inline reg_t misaligned_load(reg_t addr, size_t size, uint32_t xlate_flags)
{ {
@ -98,19 +98,17 @@ public:
// template for functions that load an aligned value from memory // template for functions that load an aligned value from memory
#define load_func(type, prefix, xlate_flags) \ #define load_func(type, prefix, xlate_flags) \
inline type##_t prefix##_##type(reg_t addr, bool require_alignment = false) { \ inline type##_t prefix##_##type(reg_t addr, bool require_alignment = false) { \
if ((xlate_flags) != 0) \
flush_tlb(); \
if (unlikely(addr & (sizeof(type##_t)-1))) { \ if (unlikely(addr & (sizeof(type##_t)-1))) { \
if (require_alignment) load_reserved_address_misaligned(addr); \ if (require_alignment) load_reserved_address_misaligned(addr); \
else return misaligned_load(addr, sizeof(type##_t), xlate_flags); \ else return misaligned_load(addr, sizeof(type##_t), xlate_flags); \
} \ } \
reg_t vpn = addr >> PGSHIFT; \ reg_t vpn = addr >> PGSHIFT; \
size_t size = sizeof(type##_t); \ size_t size = sizeof(type##_t); \
if (likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) { \ if ((xlate_flags) == 0 && likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) { \
if (proc) READ_MEM(addr, size); \ if (proc) READ_MEM(addr, size); \
return from_target(*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \ return from_target(*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \
} \ } \
if (unlikely(tlb_load_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \ if ((xlate_flags) == 0 && unlikely(tlb_load_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
type##_t data = from_target(*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \ type##_t data = from_target(*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \
if (!matched_trigger) { \ if (!matched_trigger) { \
matched_trigger = trigger_exception(OPERATION_LOAD, addr, data); \ matched_trigger = trigger_exception(OPERATION_LOAD, addr, data); \
@ -123,8 +121,6 @@ public:
target_endian<type##_t> res; \ target_endian<type##_t> res; \
load_slow_path(addr, sizeof(type##_t), (uint8_t*)&res, (xlate_flags)); \ load_slow_path(addr, sizeof(type##_t), (uint8_t*)&res, (xlate_flags)); \
if (proc) READ_MEM(addr, size); \ if (proc) READ_MEM(addr, size); \
if ((xlate_flags) != 0) \
flush_tlb(); \
return from_target(res); \ return from_target(res); \
} }
@ -139,8 +135,8 @@ public:
load_func(uint16, guest_load, RISCV_XLATE_VIRT) load_func(uint16, guest_load, RISCV_XLATE_VIRT)
load_func(uint32, guest_load, RISCV_XLATE_VIRT) load_func(uint32, guest_load, RISCV_XLATE_VIRT)
load_func(uint64, guest_load, RISCV_XLATE_VIRT) load_func(uint64, guest_load, RISCV_XLATE_VIRT)
load_func(uint16, guest_load_x, RISCV_XLATE_VIRT|RISCV_XLATE_VIRT_MXR) load_func(uint16, guest_load_x, RISCV_XLATE_VIRT|RISCV_XLATE_VIRT_HLVX)
load_func(uint32, guest_load_x, RISCV_XLATE_VIRT|RISCV_XLATE_VIRT_MXR) load_func(uint32, guest_load_x, RISCV_XLATE_VIRT|RISCV_XLATE_VIRT_HLVX)
// load value from memory at aligned address; sign extend to register width // load value from memory at aligned address; sign extend to register width
load_func(int8, load, 0) load_func(int8, load, 0)
@ -164,17 +160,15 @@ public:
// template for functions that store an aligned value to memory // template for functions that store an aligned value to memory
#define store_func(type, prefix, xlate_flags) \ #define store_func(type, prefix, xlate_flags) \
void prefix##_##type(reg_t addr, type##_t val) { \ void prefix##_##type(reg_t addr, type##_t val) { \
if ((xlate_flags) != 0) \
flush_tlb(); \
if (unlikely(addr & (sizeof(type##_t)-1))) \ if (unlikely(addr & (sizeof(type##_t)-1))) \
return misaligned_store(addr, val, sizeof(type##_t), xlate_flags); \ return misaligned_store(addr, val, sizeof(type##_t), xlate_flags); \
reg_t vpn = addr >> PGSHIFT; \ reg_t vpn = addr >> PGSHIFT; \
size_t size = sizeof(type##_t); \ size_t size = sizeof(type##_t); \
if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) { \ if ((xlate_flags) == 0 && likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) { \
if (proc) WRITE_MEM(addr, val, size); \ if (proc) WRITE_MEM(addr, val, size); \
*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_target(val); \ *(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_target(val); \
} \ } \
else if (unlikely(tlb_store_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \ else if ((xlate_flags) == 0 && unlikely(tlb_store_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
if (!matched_trigger) { \ if (!matched_trigger) { \
matched_trigger = trigger_exception(OPERATION_STORE, addr, val); \ matched_trigger = trigger_exception(OPERATION_STORE, addr, val); \
if (matched_trigger) \ if (matched_trigger) \
@ -188,8 +182,6 @@ public:
store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&target_val, (xlate_flags)); \ store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&target_val, (xlate_flags)); \
if (proc) WRITE_MEM(addr, val, size); \ if (proc) WRITE_MEM(addr, val, size); \
} \ } \
if ((xlate_flags) != 0) \
flush_tlb(); \
} }
// template for functions that perform an atomic memory operation // template for functions that perform an atomic memory operation
@ -422,10 +414,10 @@ private:
const char* fill_from_mmio(reg_t vaddr, reg_t paddr); const char* fill_from_mmio(reg_t vaddr, reg_t paddr);
// perform a stage2 translation for a given guest address // perform a stage2 translation for a given guest address
reg_t s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_type, bool virt, bool mxr); reg_t s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_type, bool virt, bool hlvx);
// perform a page table walk for a given VA; set referenced/dirty bits // perform a page table walk for a given VA; set referenced/dirty bits
reg_t walk(reg_t addr, access_type type, reg_t prv, bool virt, bool mxr); reg_t walk(reg_t addr, access_type type, reg_t prv, bool virt, bool hlvx);
// handle uncommon cases: TLB misses, page faults, MMIO // handle uncommon cases: TLB misses, page faults, MMIO
tlb_entry_t fetch_slow_path(reg_t addr); tlb_entry_t fetch_slow_path(reg_t addr);

Loading…
Cancel
Save