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)
{
pmp_priv_t pmp_priv;
target_ulong tlb_size_pmp = 0;
int pmp_index = -1;
if (!riscv_feature(env, RISCV_FEATURE_PMP)) {
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return TRANSLATE_SUCCESS;
}
if (!pmp_hart_has_privs(env, addr, size, 1 << access_type, &pmp_priv,
mode)) {
pmp_index = pmp_hart_has_privs(env, addr, size, 1 << access_type,
&pmp_priv, mode);
if (pmp_index < 0) {
*prot = 0;
return TRANSLATE_PMP_FAIL;
}
*prot = pmp_priv_to_page_prot(pmp_priv);
if (tlb_size != NULL) {
if (pmp_is_range_in_tlb(env, addr & ~(*tlb_size - 1), &tlb_size_pmp)) {
*tlb_size = tlb_size_pmp;
}
if ((tlb_size != NULL) && pmp_index != MAX_RISCV_PMPS) {
target_ulong tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1);
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;

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
* 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 mode)
{
@ -305,8 +308,10 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
/* Short cut if no rules */
if (0 == pmp_get_num_rules(env)) {
return pmp_hart_has_privs_default(env, addr, size, privs,
allowed_privs, mode);
if (pmp_hart_has_privs_default(env, addr, size, privs,
allowed_privs, mode)) {
ret = MAX_RISCV_PMPS;
}
}
if (size == 0) {
@ -333,7 +338,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
if ((s + e) == 1) {
qemu_log_mask(LOG_GUEST_ERROR,
"pmp violation - access is partially inside\n");
ret = 0;
ret = -1;
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;
}
}
/* No rule matched */
if (ret == -1) {
return pmp_hart_has_privs_default(env, addr, size, privs,
allowed_privs, mode);
if (pmp_hart_has_privs_default(env, addr, size, privs,
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
* PMP entry is presented in the 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_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) {
if (pmp_sa <= tlb_sa && pmp_ea >= tlb_ea) {
return TARGET_PAGE_SIZE;
} else {
/*
* 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.
*
* 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
* a single translation.
*/
if (*tlb_size < TARGET_PAGE_SIZE) {
*tlb_size = 1;
}
return true;
* 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.
*
* 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
* a single translation.
*/
return 1;
}
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,
target_ulong val);
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 mode);
bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
target_ulong *tlb_size);
target_ulong pmp_get_tlb_size(CPURISCVState *env, int pmp_index,
target_ulong tlb_sa, target_ulong tlb_ea);
void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index);
void pmp_update_rule_nums(CPURISCVState *env);
uint32_t pmp_get_num_rules(CPURISCVState *env);