target/riscv: Implement AIA xiselect and xireg CSRs
The AIA specification defines [m|s|vs]iselect and [m|s|vs]ireg CSRs which allow indirect access to interrupt priority arrays and per-HART IMSIC registers. This patch implements AIA xiselect and xireg CSRs. Signed-off-by: Anup Patel <anup.patel@wdc.com> Signed-off-by: Anup Patel <anup@brainfault.org> Reviewed-by: Frank Chang <frank.chang@sifive.com> Message-id: 20220204174700.534953-15-anup@brainfault.org Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
parent
c7de92b4e8
commit
d1ceff405a
@ -196,6 +196,10 @@ struct CPURISCVState {
|
||||
uint8_t miprio[64];
|
||||
uint8_t siprio[64];
|
||||
|
||||
/* AIA CSRs */
|
||||
target_ulong miselect;
|
||||
target_ulong siselect;
|
||||
|
||||
/* Hypervisor CSRs */
|
||||
target_ulong hstatus;
|
||||
target_ulong hedeleg;
|
||||
@ -229,6 +233,9 @@ struct CPURISCVState {
|
||||
target_ulong vstval;
|
||||
target_ulong vsatp;
|
||||
|
||||
/* AIA VS-mode CSRs */
|
||||
target_ulong vsiselect;
|
||||
|
||||
target_ulong mtval2;
|
||||
target_ulong mtinst;
|
||||
|
||||
|
@ -931,6 +931,169 @@ static int read_mtopi(CPURISCVState *env, int csrno, target_ulong *val)
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno)
|
||||
{
|
||||
if (!riscv_cpu_virt_enabled(env)) {
|
||||
return csrno;
|
||||
}
|
||||
|
||||
switch (csrno) {
|
||||
case CSR_SISELECT:
|
||||
return CSR_VSISELECT;
|
||||
case CSR_SIREG:
|
||||
return CSR_VSIREG;
|
||||
default:
|
||||
return csrno;
|
||||
};
|
||||
}
|
||||
|
||||
static int rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val,
|
||||
target_ulong new_val, target_ulong wr_mask)
|
||||
{
|
||||
target_ulong *iselect;
|
||||
|
||||
/* Translate CSR number for VS-mode */
|
||||
csrno = aia_xlate_vs_csrno(env, csrno);
|
||||
|
||||
/* Find the iselect CSR based on CSR number */
|
||||
switch (csrno) {
|
||||
case CSR_MISELECT:
|
||||
iselect = &env->miselect;
|
||||
break;
|
||||
case CSR_SISELECT:
|
||||
iselect = &env->siselect;
|
||||
break;
|
||||
case CSR_VSISELECT:
|
||||
iselect = &env->vsiselect;
|
||||
break;
|
||||
default:
|
||||
return RISCV_EXCP_ILLEGAL_INST;
|
||||
};
|
||||
|
||||
if (val) {
|
||||
*val = *iselect;
|
||||
}
|
||||
|
||||
wr_mask &= ISELECT_MASK;
|
||||
if (wr_mask) {
|
||||
*iselect = (*iselect & ~wr_mask) | (new_val & wr_mask);
|
||||
}
|
||||
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static int rmw_iprio(target_ulong xlen,
|
||||
target_ulong iselect, uint8_t *iprio,
|
||||
target_ulong *val, target_ulong new_val,
|
||||
target_ulong wr_mask, int ext_irq_no)
|
||||
{
|
||||
int i, firq, nirqs;
|
||||
target_ulong old_val;
|
||||
|
||||
if (iselect < ISELECT_IPRIO0 || ISELECT_IPRIO15 < iselect) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (xlen != 32 && iselect & 0x1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nirqs = 4 * (xlen / 32);
|
||||
firq = ((iselect - ISELECT_IPRIO0) / (xlen / 32)) * (nirqs);
|
||||
|
||||
old_val = 0;
|
||||
for (i = 0; i < nirqs; i++) {
|
||||
old_val |= ((target_ulong)iprio[firq + i]) << (IPRIO_IRQ_BITS * i);
|
||||
}
|
||||
|
||||
if (val) {
|
||||
*val = old_val;
|
||||
}
|
||||
|
||||
if (wr_mask) {
|
||||
new_val = (old_val & ~wr_mask) | (new_val & wr_mask);
|
||||
for (i = 0; i < nirqs; i++) {
|
||||
/*
|
||||
* M-level and S-level external IRQ priority always read-only
|
||||
* zero. This means default priority order is always preferred
|
||||
* for M-level and S-level external IRQs.
|
||||
*/
|
||||
if ((firq + i) == ext_irq_no) {
|
||||
continue;
|
||||
}
|
||||
iprio[firq + i] = (new_val >> (IPRIO_IRQ_BITS * i)) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val,
|
||||
target_ulong new_val, target_ulong wr_mask)
|
||||
{
|
||||
bool virt;
|
||||
uint8_t *iprio;
|
||||
int ret = -EINVAL;
|
||||
target_ulong priv, isel, vgein;
|
||||
|
||||
/* Translate CSR number for VS-mode */
|
||||
csrno = aia_xlate_vs_csrno(env, csrno);
|
||||
|
||||
/* Decode register details from CSR number */
|
||||
virt = false;
|
||||
switch (csrno) {
|
||||
case CSR_MIREG:
|
||||
iprio = env->miprio;
|
||||
isel = env->miselect;
|
||||
priv = PRV_M;
|
||||
break;
|
||||
case CSR_SIREG:
|
||||
iprio = env->siprio;
|
||||
isel = env->siselect;
|
||||
priv = PRV_S;
|
||||
break;
|
||||
case CSR_VSIREG:
|
||||
iprio = env->hviprio;
|
||||
isel = env->vsiselect;
|
||||
priv = PRV_S;
|
||||
virt = true;
|
||||
break;
|
||||
default:
|
||||
goto done;
|
||||
};
|
||||
|
||||
/* Find the selected guest interrupt file */
|
||||
vgein = (virt) ? get_field(env->hstatus, HSTATUS_VGEIN) : 0;
|
||||
|
||||
if (ISELECT_IPRIO0 <= isel && isel <= ISELECT_IPRIO15) {
|
||||
/* Local interrupt priority registers not available for VS-mode */
|
||||
if (!virt) {
|
||||
ret = rmw_iprio(riscv_cpu_mxl_bits(env),
|
||||
isel, iprio, val, new_val, wr_mask,
|
||||
(priv == PRV_M) ? IRQ_M_EXT : IRQ_S_EXT);
|
||||
}
|
||||
} else if (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST) {
|
||||
/* IMSIC registers only available when machine implements it. */
|
||||
if (env->aia_ireg_rmw_fn[priv]) {
|
||||
/* Selected guest interrupt file should not be zero */
|
||||
if (virt && (!vgein || env->geilen < vgein)) {
|
||||
goto done;
|
||||
}
|
||||
/* Call machine specific IMSIC register emulation */
|
||||
ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv],
|
||||
AIA_MAKE_IREG(isel, priv, virt, vgein,
|
||||
riscv_cpu_mxl_bits(env)),
|
||||
val, new_val, wr_mask);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (ret) {
|
||||
return (riscv_cpu_virt_enabled(env) && virt) ?
|
||||
RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST;
|
||||
}
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException read_mtvec(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
@ -2760,6 +2923,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_MTVAL] = { "mtval", any, read_mtval, write_mtval },
|
||||
[CSR_MIP] = { "mip", any, NULL, NULL, rmw_mip },
|
||||
|
||||
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
|
||||
[CSR_MISELECT] = { "miselect", aia_any, NULL, NULL, rmw_xiselect },
|
||||
[CSR_MIREG] = { "mireg", aia_any, NULL, NULL, rmw_xireg },
|
||||
|
||||
/* Machine-Level Interrupts (AIA) */
|
||||
[CSR_MTOPI] = { "mtopi", aia_any, read_mtopi },
|
||||
|
||||
@ -2792,6 +2959,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
/* Supervisor Protection and Translation */
|
||||
[CSR_SATP] = { "satp", smode, read_satp, write_satp },
|
||||
|
||||
/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
|
||||
[CSR_SISELECT] = { "siselect", aia_smode, NULL, NULL, rmw_xiselect },
|
||||
[CSR_SIREG] = { "sireg", aia_smode, NULL, NULL, rmw_xireg },
|
||||
|
||||
/* Supervisor-Level Interrupts (AIA) */
|
||||
[CSR_STOPI] = { "stopi", aia_smode, read_stopi },
|
||||
|
||||
@ -2833,6 +3004,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 },
|
||||
[CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 },
|
||||
|
||||
/*
|
||||
* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA)
|
||||
*/
|
||||
[CSR_VSISELECT] = { "vsiselect", aia_hmode, NULL, NULL, rmw_xiselect },
|
||||
[CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg },
|
||||
|
||||
/* VS-Level Interrupts (H-extension with AIA) */
|
||||
[CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi },
|
||||
|
||||
|
@ -103,6 +103,7 @@ static const VMStateDescription vmstate_hyper = {
|
||||
VMSTATE_UINTTL(env.vscause, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.vstval, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.vsatp, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.vsiselect, RISCVCPU),
|
||||
|
||||
VMSTATE_UINTTL(env.mtval2, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.mtinst, RISCVCPU),
|
||||
@ -272,6 +273,8 @@ const VMStateDescription vmstate_riscv_cpu = {
|
||||
VMSTATE_UINTTL(env.mepc, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.mcause, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.mtval, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.miselect, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.siselect, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.scounteren, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.mcounteren, RISCVCPU),
|
||||
VMSTATE_UINTTL(env.sscratch, RISCVCPU),
|
||||
|
Loading…
Reference in New Issue
Block a user