target/riscv: Implementation of enhanced PMP (ePMP)

This commit adds support for ePMP v0.9.1.

The ePMP spec can be found in:
https://docs.google.com/document/d/1Mh_aiHYxemL0umN3GTTw8vsbmzHZ_nxZXgjgOUzbvc8

Signed-off-by: Hongzheng-Li <Ethan.Lee.QNL@gmail.com>
Signed-off-by: Hou Weiying <weiying_hou@outlook.com>
Signed-off-by: Myriad-Dreamin <camiyoru@gmail.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Message-id: fef23b885f9649a4d54e7c98b168bdec5d297bb1.1618812899.git.alistair.francis@wdc.com
[ Changes by AF:
 - Rebase on master
 - Update to latest spec
 - Use a switch case to handle ePMP MML permissions
 - Fix a few bugs
]
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Hou Weiying 2021-04-19 16:17:11 +10:00 committed by Alistair Francis
parent 2582a95c3c
commit ae39e4ce19

View File

@ -90,11 +90,42 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index)
static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)
{
if (pmp_index < MAX_RISCV_PMPS) {
bool locked = true;
if (riscv_feature(env, RISCV_FEATURE_EPMP)) {
/* mseccfg.RLB is set */
if (MSECCFG_RLB_ISSET(env)) {
locked = false;
}
/* mseccfg.MML is not set */
if (!MSECCFG_MML_ISSET(env) && !pmp_is_locked(env, pmp_index)) {
locked = false;
}
/* mseccfg.MML is set */
if (MSECCFG_MML_ISSET(env)) {
/* not adding execute bit */
if ((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) {
locked = false;
}
/* shared region and not adding X bit */
if ((val & PMP_LOCK) != PMP_LOCK &&
(val & 0x7) != (PMP_WRITE | PMP_EXEC)) {
locked = false;
}
}
} else {
if (!pmp_is_locked(env, pmp_index)) {
locked = false;
}
}
if (locked) {
qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n");
} else {
env->pmp_state.pmp[pmp_index].cfg_reg = val;
pmp_update_rule(env, pmp_index);
} else {
qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n");
}
} else {
qemu_log_mask(LOG_GUEST_ERROR,
@ -217,6 +248,32 @@ static bool pmp_hart_has_privs_default(CPURISCVState *env, target_ulong addr,
{
bool ret;
if (riscv_feature(env, RISCV_FEATURE_EPMP)) {
if (MSECCFG_MMWP_ISSET(env)) {
/*
* The Machine Mode Whitelist Policy (mseccfg.MMWP) is set
* so we default to deny all, even for M-mode.
*/
*allowed_privs = 0;
return false;
} else if (MSECCFG_MML_ISSET(env)) {
/*
* The Machine Mode Lockdown (mseccfg.MML) bit is set
* so we can only execute code in M-mode with an applicable
* rule. Other modes are disabled.
*/
if (mode == PRV_M && !(privs & PMP_EXEC)) {
ret = true;
*allowed_privs = PMP_READ | PMP_WRITE;
} else {
ret = false;
*allowed_privs = 0;
}
return ret;
}
}
if ((!riscv_feature(env, RISCV_FEATURE_PMP)) || (mode == PRV_M)) {
/*
* Privileged spec v1.10 states if HW doesn't implement any PMP entry
@ -294,14 +351,95 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
/*
* If the PMP entry is not off and the address is in range, do the priv
* check
* Convert the PMP permissions to match the truth table in the
* ePMP spec.
*/
const uint8_t epmp_operation =
((env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) >> 4) |
((env->pmp_state.pmp[i].cfg_reg & PMP_READ) << 2) |
(env->pmp_state.pmp[i].cfg_reg & PMP_WRITE) |
((env->pmp_state.pmp[i].cfg_reg & PMP_EXEC) >> 2);
if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) {
/*
* If the PMP entry is not off and the address is in range,
* do the priv check
*/
if (!MSECCFG_MML_ISSET(env)) {
/*
* If mseccfg.MML Bit is not set, do pmp priv check
* This will always apply to regular PMP.
*/
*allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
if ((mode != PRV_M) || pmp_is_locked(env, i)) {
*allowed_privs &= env->pmp_state.pmp[i].cfg_reg;
}
} else {
/*
* If mseccfg.MML Bit set, do the enhanced pmp priv check
*/
if (mode == PRV_M) {
switch (epmp_operation) {
case 0:
case 1:
case 4:
case 5:
case 6:
case 7:
case 8:
*allowed_privs = 0;
break;
case 2:
case 3:
case 14:
*allowed_privs = PMP_READ | PMP_WRITE;
break;
case 9:
case 10:
*allowed_privs = PMP_EXEC;
break;
case 11:
case 13:
*allowed_privs = PMP_READ | PMP_EXEC;
break;
case 12:
case 15:
*allowed_privs = PMP_READ;
break;
}
} else {
switch (epmp_operation) {
case 0:
case 8:
case 9:
case 12:
case 13:
case 14:
*allowed_privs = 0;
break;
case 1:
case 10:
case 11:
*allowed_privs = PMP_EXEC;
break;
case 2:
case 4:
case 15:
*allowed_privs = PMP_READ;
break;
case 3:
case 6:
*allowed_privs = PMP_READ | PMP_WRITE;
break;
case 5:
*allowed_privs = PMP_READ | PMP_EXEC;
break;
case 7:
*allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
break;
}
}
}
ret = ((privs & *allowed_privs) == privs);
break;