target/riscv: Fix PMP propagation for tlb

Only the pmp index that be checked by pmp_hart_has_privs can be used
by pmp_get_tlb_size to avoid an error pmp index.

Before modification, we may use an error pmp index. For example,
we check address 0x4fc, and the size 0x4 in pmp_hart_has_privs. If there
is an pmp rule, valid range is [0x4fc, 0x500), then pmp_hart_has_privs
will return true;

However, this checked pmp index is discarded as pmp_hart_has_privs
return bool value. In pmp_is_range_in_tlb, it will traverse all pmp
rules. The tlb_sa will be 0x0, and tlb_ea will be 0xfff. If there is
a pmp rule [0x10, 0x14), it will be misused as it is legal in
pmp_get_tlb_size.

As we have already known the correct pmp index, just remove the
remove the pmp_is_range_in_tlb and get tlb size directly from
pmp_get_tlb_size.

Signed-off-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-Id: <20221012060016.30856-1-zhiwei_liu@linux.alibaba.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
LIU Zhiwei 2022-10-12 14:00:16 +08:00 committed by Alistair Francis
parent d1852caab1
commit 824cac681c
3 changed files with 42 additions and 70 deletions

View File

@ -706,24 +706,26 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
int mode) int mode)
{ {
pmp_priv_t pmp_priv; pmp_priv_t pmp_priv;
target_ulong tlb_size_pmp = 0; int pmp_index = -1;
if (!riscv_feature(env, RISCV_FEATURE_PMP)) { if (!riscv_feature(env, RISCV_FEATURE_PMP)) {
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return TRANSLATE_SUCCESS; return TRANSLATE_SUCCESS;
} }
if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv, pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type,
mode)) { &pmp_priv, mode);
if (pmp_index < 0) {
*prot = 0; *prot = 0;
return TRANSLATE_PMP_FAIL; return TRANSLATE_PMP_FAIL;
} }
*prot = pmp_priv_to_page_prot(pmp_priv); *prot = pmp_priv_to_page_prot(pmp_priv);
if (tlb_size != NULL) { if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) {
if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) { target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
*tlb_size = tlb_size_pmp; target_ulong tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1;
}
*tlb_size = pmp_get_tlb_size(env, pmp_index, tlb_sa, tlb_ea);
} }
return TRANSLATE_SUCCESS; return TRANSLATE_SUCCESS;

View File

@ -292,8 +292,11 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr,
/* /*
* Check if the address has required RWX privs to complete desired operation * Check if the address has required RWX privs to complete desired operation
* Return PMP rule index if a pmp rule match
* Return MAX_RISCV_PMPS if default match
* Return negtive value if no match
*/ */
bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs, target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
target_ulong mode) target_ulong mode)
{ {
@ -305,8 +308,10 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
/* Short cut if no rules */ /* Short cut if no rules */
if (0 == pmp_get_num_rules(env)) { if (0 == pmp_get_num_rules(env)) {
return pmp_hart_has_privs_default(env, addr, size, privs, if (pmp_hart_has_privs_default(env, addr, size, privs,
allowed_privs, mode); allowed_privs, mode)) {
ret = MAX_RISCV_PMPS;
}
} }
if (size == 0) { if (size == 0) {
@ -333,7 +338,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
if ((s + e) == 1) { if ((s + e) == 1) {
qemu_log_mask(LOG_GUEST_ERROR, qemu_log_mask(LOG_GUEST_ERROR,
"pmp violation - access is partially inside\n"); "pmp violation - access is partially inside\n");
ret = 0; ret = -1;
break; break;
} }
@ -436,18 +441,22 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
} }
} }
ret = ((privs & *allowed_privs) == privs); if ((privs & *allowed_privs) == privs) {
ret = i;
}
break; break;
} }
} }
/* No rule matched */ /* No rule matched */
if (ret == -1) { if (ret == -1) {
return pmp_hart_has_privs_default(env, addr, size, privs, if (pmp_hart_has_privs_default(env, addr, size, privs,
allowed_privs, mode); allowed_privs, mode)) {
ret = MAX_RISCV_PMPS;
}
} }
return ret == 1 ? true : false; return ret;
} }
/* /*
@ -586,64 +595,25 @@ target_ulong mseccfg_csr_read(CPURISCVState *env)
* Calculate the TLB size if the start address or the end address of * Calculate the TLB size if the start address or the end address of
* PMP entry is presented in the TLB page. * PMP entry is presented in the TLB page.
*/ */
static target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index, target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
target_ulong tlb_sa, target_ulong tlb_ea) target_ulong tlb_sa, target_ulong tlb_ea)
{ {
target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa; target_ulong pmp_sa = env->pmp_state.addr[pmp_index].sa;
target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea; target_ulong pmp_ea = env->pmp_state.addr[pmp_index].ea;
if (pmp_sa >= tlb_sa && pmp_ea <= tlb_ea) { if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
return pmp_ea - pmp_sa + 1; return TARGET_PAGE_SIZE;
} } else {
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) {
/* /*
* At this point we have a tlb_size that is the smallest possible size * At this point we have a tlb_size that is the smallest possible size
* That fits within a TARGET_PAGE_SIZE and the PMP region. * That fits within a TARGET_PAGE_SIZE and the PMP region.
* *
* If the size is less then TARGET_PAGE_SIZE we drop the size to 1. * If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
* This means the result isn't cached in the TLB and is only used for * This means the result isn't cached in the TLB and is only used for
* a single translation. * a single translation.
*/ */
if (*tlb_size < TARGET_PAGE_SIZE) { return 1;
*tlb_size = 1;
}
return true;
} }
return false;
} }
/* /*

View File

@ -72,11 +72,11 @@ target_ulong mseccfg_csr_read(CPURISCVState *env);
void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
target_ulong val); target_ulong val);
target_ulong pmpaddr_csr_read(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, int pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs, target_ulong size, pmp_priv_t privs, pmp_priv_t *allowed_privs,
target_ulong mode); target_ulong mode);
bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa, target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
target_ulong *tlb_size); target_ulong tlb_sa, target_ulong tlb_ea);
void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index); void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index);
void pmp_update_rule_nums(CPURISCVState *env); void pmp_update_rule_nums(CPURISCVState *env);
uint32_t pmp_get_num_rules(CPURISCVState *env); uint32_t pmp_get_num_rules(CPURISCVState *env);