diff --git a/qemu/header_gen.py b/qemu/header_gen.py index 929081db..5f5654d5 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -7300,6 +7300,7 @@ riscv_symbols = ( 'helper_vcompress_vm_w', 'helper_vcompress_vm_d', 'pmp_hart_has_privs', + 'pmp_is_range_in_tlb', 'pmpaddr_csr_read', 'pmpaddr_csr_write', 'pmpcfg_csr_read', diff --git a/qemu/riscv32.h b/qemu/riscv32.h index c472d27f..ad441703 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -4736,6 +4736,7 @@ #define helper_vcompress_vm_w helper_vcompress_vm_w_riscv32 #define helper_vcompress_vm_d helper_vcompress_vm_d_riscv32 #define pmp_hart_has_privs pmp_hart_has_privs_riscv32 +#define pmp_is_range_in_tlb pmp_is_range_in_tlb_riscv32 #define pmpaddr_csr_read pmpaddr_csr_read_riscv32 #define pmpaddr_csr_write pmpaddr_csr_write_riscv32 #define pmpcfg_csr_read pmpcfg_csr_read_riscv32 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index b4bb0c8f..8265c3cf 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -4736,6 +4736,7 @@ #define helper_vcompress_vm_w helper_vcompress_vm_w_riscv64 #define helper_vcompress_vm_d helper_vcompress_vm_d_riscv64 #define pmp_hart_has_privs pmp_hart_has_privs_riscv64 +#define pmp_is_range_in_tlb pmp_is_range_in_tlb_riscv64 #define pmpaddr_csr_read pmpaddr_csr_read_riscv64 #define pmpaddr_csr_write pmpaddr_csr_write_riscv64 #define pmpcfg_csr_read pmpcfg_csr_read_riscv64 diff --git a/qemu/target/riscv/cpu_helper.c b/qemu/target/riscv/cpu_helper.c index d0dd9da8..5044934a 100644 --- a/qemu/target/riscv/cpu_helper.c +++ b/qemu/target/riscv/cpu_helper.c @@ -684,6 +684,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool first_stage_error = true; int ret = TRANSLATE_FAIL; int mode = mmu_idx; + target_ulong tlb_size = 0; env->guest_phys_fault_addr = 0; @@ -775,8 +776,13 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } if (ret == TRANSLATE_SUCCESS) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, - prot, mmu_idx, TARGET_PAGE_SIZE); + if (pmp_is_range_in_tlb(env, pa & TARGET_PAGE_MASK, &tlb_size)) { + tlb_set_page(cs, address & ~(tlb_size - 1), pa & ~(tlb_size - 1), + prot, mmu_idx, tlb_size); + } else { + tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); + } return true; } else if (probe) { return false; diff --git a/qemu/target/riscv/pmp.c b/qemu/target/riscv/pmp.c index 330e9a32..dcf16492 100644 --- a/qemu/target/riscv/pmp.c +++ b/qemu/target/riscv/pmp.c @@ -383,4 +383,56 @@ target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) } } +/* + * Calculate the TLB size if the start address or the end address of + * PMP entry is presented in thie TLB page. + */ +static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index, + target_ulong tlb_sa, target_ulong tlb_ea) +{ + target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa; + target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea; + + if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) { + return pmp_ea - pmp_sa + 1; + } + + if (pmp_sa >= tlb_sa && pmp_sa <= tlb_ea && pmp_ea >= tlb_ea) { + return tlb_ea - pmp_sa + 1; + } + + if (pmp_ea <= tlb_ea && pmp_ea >= tlb_sa && pmp_sa <= tlb_sa) { + return pmp_ea - tlb_sa + 1; + } + + return 0; +} + +/* + * Check is there a PMP entry which range covers this page. If so, + * try to find the minimum granularity for the TLB size. + */ +bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa, + target_ulong *tlb_size) +{ + int i; + target_ulong val; + target_ulong tlb_ea = (tlb_sa + TARGET_PAGE_SIZE - 1); + + for (i = 0; i < MAX_RISCV_PMPS; i++) { + val = pmp_get_tlb_size(env, i, tlb_sa, tlb_ea); + if (val) { + if (*tlb_size == 0 || *tlb_size > val) { + *tlb_size = val; + } + } + } + + if (*tlb_size != 0) { + return true; + } + + return false; +} + #endif diff --git a/qemu/target/riscv/pmp.h b/qemu/target/riscv/pmp.h index 8e197931..6a8f0728 100644 --- a/qemu/target/riscv/pmp.h +++ b/qemu/target/riscv/pmp.h @@ -60,5 +60,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, target_ulong size, pmp_priv_t priv, target_ulong mode); +bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa, + target_ulong *tlb_size); #endif