mirror of
https://github.com/torvalds/linux.git
synced 2026-06-01 02:53:36 +02:00
Merge branch 'kvm-arm64/el2-reg-visibility' into kvmarm/next
* kvm-arm64/el2-reg-visibility:
: Fixes to EL2 register visibility, courtesy of Marc Zyngier
:
: - Expose EL2 VGICv3 registers via the VGIC attributes accessor, not the
: KVM_{GET,SET}_ONE_REG ioctls
:
: - Condition visibility of FGT registers on the presence of FEAT_FGT in
: the VM
KVM: arm64: selftest: vgic-v3: Add basic GICv3 sysreg userspace access test
KVM: arm64: Enforce the sorting of the GICv3 system register table
KVM: arm64: Clarify the check for reset callback in check_sysreg_table()
KVM: arm64: vgic-v3: Fix ordering of ICH_HCR_EL2
KVM: arm64: Document registers exposed via KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS
KVM: arm64: selftests: get-reg-list: Add base EL2 registers
KVM: arm64: selftests: get-reg-list: Simplify feature dependency
KVM: arm64: Advertise FGT2 registers to userspace
KVM: arm64: Condition FGT registers on feature availability
KVM: arm64: Expose GICv3 EL2 registers via KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS
KVM: arm64: Let GICv3 save/restore honor visibility attribute
KVM: arm64: Define helper for ICH_VTR_EL2
KVM: arm64: Define constant value for ICC_SRE_EL2
KVM: arm64: Don't advertise ICH_*_EL2 registers through GET_ONE_REG
KVM: arm64: Make RVBAR_EL2 accesses UNDEF
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
commit
a7f49a9bf4
|
|
@ -202,16 +202,69 @@ Groups:
|
|||
KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS accesses the CPU interface registers for the
|
||||
CPU specified by the mpidr field.
|
||||
|
||||
CPU interface registers access is not implemented for AArch32 mode.
|
||||
Error -ENXIO is returned when accessed in AArch32 mode.
|
||||
The available registers are:
|
||||
|
||||
=============== ====================================================
|
||||
ICC_PMR_EL1
|
||||
ICC_BPR0_EL1
|
||||
ICC_AP0R0_EL1
|
||||
ICC_AP0R1_EL1 when the host implements at least 6 bits of priority
|
||||
ICC_AP0R2_EL1 when the host implements 7 bits of priority
|
||||
ICC_AP0R3_EL1 when the host implements 7 bits of priority
|
||||
ICC_AP1R0_EL1
|
||||
ICC_AP1R1_EL1 when the host implements at least 6 bits of priority
|
||||
ICC_AP1R2_EL1 when the host implements 7 bits of priority
|
||||
ICC_AP1R3_EL1 when the host implements 7 bits of priority
|
||||
ICC_BPR1_EL1
|
||||
ICC_CTLR_EL1
|
||||
ICC_SRE_EL1
|
||||
ICC_IGRPEN0_EL1
|
||||
ICC_IGRPEN1_EL1
|
||||
=============== ====================================================
|
||||
|
||||
When EL2 is available for the guest, these registers are also available:
|
||||
|
||||
============= ====================================================
|
||||
ICH_AP0R0_EL2
|
||||
ICH_AP0R1_EL2 when the host implements at least 6 bits of priority
|
||||
ICH_AP0R2_EL2 when the host implements 7 bits of priority
|
||||
ICH_AP0R3_EL2 when the host implements 7 bits of priority
|
||||
ICH_AP1R0_EL2
|
||||
ICH_AP1R1_EL2 when the host implements at least 6 bits of priority
|
||||
ICH_AP1R2_EL2 when the host implements 7 bits of priority
|
||||
ICH_AP1R3_EL2 when the host implements 7 bits of priority
|
||||
ICH_HCR_EL2
|
||||
ICC_SRE_EL2
|
||||
ICH_VTR_EL2
|
||||
ICH_VMCR_EL2
|
||||
ICH_LR0_EL2
|
||||
ICH_LR1_EL2
|
||||
ICH_LR2_EL2
|
||||
ICH_LR3_EL2
|
||||
ICH_LR4_EL2
|
||||
ICH_LR5_EL2
|
||||
ICH_LR6_EL2
|
||||
ICH_LR7_EL2
|
||||
ICH_LR8_EL2
|
||||
ICH_LR9_EL2
|
||||
ICH_LR10_EL2
|
||||
ICH_LR11_EL2
|
||||
ICH_LR12_EL2
|
||||
ICH_LR13_EL2
|
||||
ICH_LR14_EL2
|
||||
ICH_LR15_EL2
|
||||
============= ====================================================
|
||||
|
||||
CPU interface registers are only described using the AArch64
|
||||
encoding.
|
||||
|
||||
Errors:
|
||||
|
||||
======= =====================================================
|
||||
-ENXIO Getting or setting this register is not yet supported
|
||||
======= =================================================
|
||||
-ENXIO Getting or setting this register is not supported
|
||||
-EBUSY VCPU is running
|
||||
-EINVAL Invalid mpidr or register value supplied
|
||||
======= =====================================================
|
||||
======= =================================================
|
||||
|
||||
|
||||
KVM_DEV_ARM_VGIC_GRP_NR_IRQS
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ static bool get_el2_to_el1_mapping(unsigned int reg,
|
|||
PURE_EL2_SYSREG( HACR_EL2 );
|
||||
PURE_EL2_SYSREG( VTTBR_EL2 );
|
||||
PURE_EL2_SYSREG( VTCR_EL2 );
|
||||
PURE_EL2_SYSREG( RVBAR_EL2 );
|
||||
PURE_EL2_SYSREG( TPIDR_EL2 );
|
||||
PURE_EL2_SYSREG( HPFAR_EL2 );
|
||||
PURE_EL2_SYSREG( HCRX_EL2 );
|
||||
|
|
@ -534,8 +533,7 @@ static bool access_gic_sre(struct kvm_vcpu *vcpu,
|
|||
return ignore_write(vcpu, p);
|
||||
|
||||
if (p->Op1 == 4) { /* ICC_SRE_EL2 */
|
||||
p->regval = (ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE |
|
||||
ICC_SRE_EL1_DIB | ICC_SRE_EL1_DFB);
|
||||
p->regval = KVM_ICC_SRE_EL2;
|
||||
} else { /* ICC_SRE_EL1 */
|
||||
p->regval = vcpu->arch.vgic_cpu.vgic_v3.vgic_sre;
|
||||
}
|
||||
|
|
@ -774,6 +772,12 @@ static u64 reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|||
return mpidr;
|
||||
}
|
||||
|
||||
static unsigned int hidden_visibility(const struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
return REG_HIDDEN;
|
||||
}
|
||||
|
||||
static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
|
|
@ -2335,6 +2339,10 @@ static bool bad_redir_trap(struct kvm_vcpu *vcpu,
|
|||
EL2_REG_FILTERED(name, acc, rst, v, el2_visibility)
|
||||
|
||||
#define EL2_REG_VNCR(name, rst, v) EL2_REG(name, bad_vncr_trap, rst, v)
|
||||
#define EL2_REG_VNCR_FILT(name, vis) \
|
||||
EL2_REG_FILTERED(name, bad_vncr_trap, reset_val, 0, vis)
|
||||
#define EL2_REG_VNCR_GICv3(name) \
|
||||
EL2_REG_VNCR_FILT(name, hidden_visibility)
|
||||
#define EL2_REG_REDIR(name, rst, v) EL2_REG(name, bad_redir_trap, rst, v)
|
||||
|
||||
/*
|
||||
|
|
@ -2538,11 +2546,7 @@ static bool access_gic_vtr(struct kvm_vcpu *vcpu,
|
|||
if (p->is_write)
|
||||
return write_to_read_only(vcpu, p, r);
|
||||
|
||||
p->regval = kvm_vgic_global_state.ich_vtr_el2;
|
||||
p->regval &= ~(ICH_VTR_EL2_DVIM |
|
||||
ICH_VTR_EL2_A3V |
|
||||
ICH_VTR_EL2_IDbits);
|
||||
p->regval |= ICH_VTR_EL2_nV4;
|
||||
p->regval = kvm_get_guest_vtr_el2();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2613,6 +2617,26 @@ static unsigned int tcr2_el2_visibility(const struct kvm_vcpu *vcpu,
|
|||
return __el2_visibility(vcpu, rd, tcr2_visibility);
|
||||
}
|
||||
|
||||
static unsigned int fgt2_visibility(const struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
if (el2_visibility(vcpu, rd) == 0 &&
|
||||
kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, FGT, FGT2))
|
||||
return 0;
|
||||
|
||||
return REG_HIDDEN;
|
||||
}
|
||||
|
||||
static unsigned int fgt_visibility(const struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
if (el2_visibility(vcpu, rd) == 0 &&
|
||||
kvm_has_feat(vcpu->kvm, ID_AA64MMFR0_EL1, FGT, IMP))
|
||||
return 0;
|
||||
|
||||
return REG_HIDDEN;
|
||||
}
|
||||
|
||||
static unsigned int s1pie_visibility(const struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
|
|
@ -3352,8 +3376,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||
EL2_REG(MDCR_EL2, access_mdcr, reset_mdcr, 0),
|
||||
EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1),
|
||||
EL2_REG_VNCR(HSTR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(HFGRTR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(HFGWTR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR_FILT(HFGRTR_EL2, fgt_visibility),
|
||||
EL2_REG_VNCR_FILT(HFGWTR_EL2, fgt_visibility),
|
||||
EL2_REG_VNCR(HFGITR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(HACR_EL2, reset_val, 0),
|
||||
|
||||
|
|
@ -3373,9 +3397,14 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||
vncr_el2_visibility),
|
||||
|
||||
{ SYS_DESC(SYS_DACR32_EL2), undef_access, reset_unknown, DACR32_EL2 },
|
||||
EL2_REG_VNCR(HDFGRTR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(HDFGWTR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(HAFGRTR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR_FILT(HDFGRTR2_EL2, fgt2_visibility),
|
||||
EL2_REG_VNCR_FILT(HDFGWTR2_EL2, fgt2_visibility),
|
||||
EL2_REG_VNCR_FILT(HFGRTR2_EL2, fgt2_visibility),
|
||||
EL2_REG_VNCR_FILT(HFGWTR2_EL2, fgt2_visibility),
|
||||
EL2_REG_VNCR_FILT(HDFGRTR_EL2, fgt_visibility),
|
||||
EL2_REG_VNCR_FILT(HDFGWTR_EL2, fgt_visibility),
|
||||
EL2_REG_VNCR_FILT(HAFGRTR_EL2, fgt_visibility),
|
||||
EL2_REG_VNCR_FILT(HFGITR2_EL2, fgt2_visibility),
|
||||
EL2_REG_REDIR(SPSR_EL2, reset_val, 0),
|
||||
EL2_REG_REDIR(ELR_EL2, reset_val, 0),
|
||||
{ SYS_DESC(SYS_SP_EL1), access_sp_el1},
|
||||
|
|
@ -3417,44 +3446,44 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|||
{ SYS_DESC(SYS_MPAMVPM7_EL2), undef_access },
|
||||
|
||||
EL2_REG(VBAR_EL2, access_rw, reset_val, 0),
|
||||
EL2_REG(RVBAR_EL2, access_rw, reset_val, 0),
|
||||
{ SYS_DESC(SYS_RVBAR_EL2), undef_access },
|
||||
{ SYS_DESC(SYS_RMR_EL2), undef_access },
|
||||
EL2_REG_VNCR(VDISR_EL2, reset_unknown, 0),
|
||||
|
||||
EL2_REG_VNCR(ICH_AP0R0_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_AP0R1_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_AP0R2_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_AP0R3_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_AP1R0_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_AP1R1_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_AP1R2_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_AP1R3_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP0R0_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP0R1_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP0R2_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP0R3_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP1R0_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP1R1_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP1R2_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_AP1R3_EL2),
|
||||
|
||||
{ SYS_DESC(SYS_ICC_SRE_EL2), access_gic_sre },
|
||||
|
||||
EL2_REG_VNCR(ICH_HCR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR_GICv3(ICH_HCR_EL2),
|
||||
{ SYS_DESC(SYS_ICH_VTR_EL2), access_gic_vtr },
|
||||
{ SYS_DESC(SYS_ICH_MISR_EL2), access_gic_misr },
|
||||
{ SYS_DESC(SYS_ICH_EISR_EL2), access_gic_eisr },
|
||||
{ SYS_DESC(SYS_ICH_ELRSR_EL2), access_gic_elrsr },
|
||||
EL2_REG_VNCR(ICH_VMCR_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR_GICv3(ICH_VMCR_EL2),
|
||||
|
||||
EL2_REG_VNCR(ICH_LR0_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR1_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR2_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR3_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR4_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR5_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR6_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR7_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR8_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR9_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR10_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR11_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR12_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR13_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR14_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR(ICH_LR15_EL2, reset_val, 0),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR0_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR1_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR2_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR3_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR4_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR5_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR6_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR7_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR8_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR9_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR10_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR11_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR12_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR13_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR14_EL2),
|
||||
EL2_REG_VNCR_GICv3(ICH_LR15_EL2),
|
||||
|
||||
EL2_REG(CONTEXTIDR_EL2, access_rw, reset_val, 0),
|
||||
EL2_REG(TPIDR_EL2, access_rw, reset_val, 0),
|
||||
|
|
@ -4323,12 +4352,12 @@ static const struct sys_reg_desc cp15_64_regs[] = {
|
|||
};
|
||||
|
||||
static bool check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
|
||||
bool is_32)
|
||||
bool reset_check)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!is_32 && table[i].reg && !table[i].reset) {
|
||||
if (reset_check && table[i].reg && !table[i].reset) {
|
||||
kvm_err("sys_reg table %pS entry %d (%s) lacks reset\n",
|
||||
&table[i], i, table[i].name);
|
||||
return false;
|
||||
|
|
@ -5317,18 +5346,22 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu)
|
|||
|
||||
int __init kvm_sys_reg_table_init(void)
|
||||
{
|
||||
const struct sys_reg_desc *gicv3_regs;
|
||||
bool valid = true;
|
||||
unsigned int i;
|
||||
unsigned int i, sz;
|
||||
int ret = 0;
|
||||
|
||||
/* Make sure tables are unique and in order. */
|
||||
valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), false);
|
||||
valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), true);
|
||||
valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true);
|
||||
valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true);
|
||||
valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true);
|
||||
valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), true);
|
||||
valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), false);
|
||||
valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), false);
|
||||
valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), false);
|
||||
valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), false);
|
||||
valid &= check_sysreg_table(sys_insn_descs, ARRAY_SIZE(sys_insn_descs), false);
|
||||
|
||||
gicv3_regs = vgic_v3_get_sysreg_table(&sz);
|
||||
valid &= check_sysreg_table(gicv3_regs, sz, false);
|
||||
|
||||
if (!valid)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -297,6 +297,91 @@ static int get_gic_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int set_gic_ich_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 val)
|
||||
{
|
||||
__vcpu_assign_sys_reg(vcpu, r->reg, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_gic_ich_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 *val)
|
||||
{
|
||||
*val = __vcpu_sys_reg(vcpu, r->reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_gic_ich_apr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 val)
|
||||
{
|
||||
u8 idx = r->Op2 & 3;
|
||||
|
||||
if (idx > vgic_v3_max_apr_idx(vcpu))
|
||||
return -EINVAL;
|
||||
|
||||
return set_gic_ich_reg(vcpu, r, val);
|
||||
}
|
||||
|
||||
static int get_gic_ich_apr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 *val)
|
||||
{
|
||||
u8 idx = r->Op2 & 3;
|
||||
|
||||
if (idx > vgic_v3_max_apr_idx(vcpu))
|
||||
return -EINVAL;
|
||||
|
||||
return get_gic_ich_reg(vcpu, r, val);
|
||||
}
|
||||
|
||||
static int set_gic_icc_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 val)
|
||||
{
|
||||
if (val != KVM_ICC_SRE_EL2)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_gic_icc_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 *val)
|
||||
{
|
||||
*val = KVM_ICC_SRE_EL2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_gic_ich_vtr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 val)
|
||||
{
|
||||
if (val != kvm_get_guest_vtr_el2())
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_gic_ich_vtr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
u64 *val)
|
||||
{
|
||||
*val = kvm_get_guest_vtr_el2();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int el2_visibility(const struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
return vcpu_has_nv(vcpu) ? 0 : REG_HIDDEN;
|
||||
}
|
||||
|
||||
#define __EL2_REG(r, acc, i) \
|
||||
{ \
|
||||
SYS_DESC(SYS_ ## r), \
|
||||
.get_user = get_gic_ ## acc, \
|
||||
.set_user = set_gic_ ## acc, \
|
||||
.reg = i, \
|
||||
.visibility = el2_visibility, \
|
||||
}
|
||||
|
||||
#define EL2_REG(r, acc) __EL2_REG(r, acc, r)
|
||||
|
||||
#define EL2_REG_RO(r, acc) __EL2_REG(r, acc, 0)
|
||||
|
||||
static const struct sys_reg_desc gic_v3_icc_reg_descs[] = {
|
||||
{ SYS_DESC(SYS_ICC_PMR_EL1),
|
||||
.set_user = set_gic_pmr, .get_user = get_gic_pmr, },
|
||||
|
|
@ -328,8 +413,42 @@ static const struct sys_reg_desc gic_v3_icc_reg_descs[] = {
|
|||
.set_user = set_gic_grpen0, .get_user = get_gic_grpen0, },
|
||||
{ SYS_DESC(SYS_ICC_IGRPEN1_EL1),
|
||||
.set_user = set_gic_grpen1, .get_user = get_gic_grpen1, },
|
||||
EL2_REG(ICH_AP0R0_EL2, ich_apr),
|
||||
EL2_REG(ICH_AP0R1_EL2, ich_apr),
|
||||
EL2_REG(ICH_AP0R2_EL2, ich_apr),
|
||||
EL2_REG(ICH_AP0R3_EL2, ich_apr),
|
||||
EL2_REG(ICH_AP1R0_EL2, ich_apr),
|
||||
EL2_REG(ICH_AP1R1_EL2, ich_apr),
|
||||
EL2_REG(ICH_AP1R2_EL2, ich_apr),
|
||||
EL2_REG(ICH_AP1R3_EL2, ich_apr),
|
||||
EL2_REG_RO(ICC_SRE_EL2, icc_sre),
|
||||
EL2_REG(ICH_HCR_EL2, ich_reg),
|
||||
EL2_REG_RO(ICH_VTR_EL2, ich_vtr),
|
||||
EL2_REG(ICH_VMCR_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR0_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR1_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR2_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR3_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR4_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR5_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR6_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR7_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR8_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR9_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR10_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR11_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR12_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR13_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR14_EL2, ich_reg),
|
||||
EL2_REG(ICH_LR15_EL2, ich_reg),
|
||||
};
|
||||
|
||||
const struct sys_reg_desc *vgic_v3_get_sysreg_table(unsigned int *sz)
|
||||
{
|
||||
*sz = ARRAY_SIZE(gic_v3_icc_reg_descs);
|
||||
return gic_v3_icc_reg_descs;
|
||||
}
|
||||
|
||||
static u64 attr_to_id(u64 attr)
|
||||
{
|
||||
return ARM64_SYS_REG(FIELD_GET(KVM_REG_ARM_VGIC_SYSREG_OP0_MASK, attr),
|
||||
|
|
@ -341,8 +460,12 @@ static u64 attr_to_id(u64 attr)
|
|||
|
||||
int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
|
||||
{
|
||||
if (get_reg_by_id(attr_to_id(attr->attr), gic_v3_icc_reg_descs,
|
||||
ARRAY_SIZE(gic_v3_icc_reg_descs)))
|
||||
const struct sys_reg_desc *r;
|
||||
|
||||
r = get_reg_by_id(attr_to_id(attr->attr), gic_v3_icc_reg_descs,
|
||||
ARRAY_SIZE(gic_v3_icc_reg_descs));
|
||||
|
||||
if (r && !sysreg_hidden(vcpu, r))
|
||||
return 0;
|
||||
|
||||
return -ENXIO;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,24 @@
|
|||
KVM_REG_ARM_VGIC_SYSREG_CRM_MASK | \
|
||||
KVM_REG_ARM_VGIC_SYSREG_OP2_MASK)
|
||||
|
||||
#define KVM_ICC_SRE_EL2 (ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE | \
|
||||
ICC_SRE_EL1_DIB | ICC_SRE_EL1_DFB)
|
||||
#define KVM_ICH_VTR_EL2_RES0 (ICH_VTR_EL2_DVIM | \
|
||||
ICH_VTR_EL2_A3V | \
|
||||
ICH_VTR_EL2_IDbits)
|
||||
#define KVM_ICH_VTR_EL2_RES1 ICH_VTR_EL2_nV4
|
||||
|
||||
static inline u64 kvm_get_guest_vtr_el2(void)
|
||||
{
|
||||
u64 vtr;
|
||||
|
||||
vtr = kvm_vgic_global_state.ich_vtr_el2;
|
||||
vtr &= ~KVM_ICH_VTR_EL2_RES0;
|
||||
vtr |= KVM_ICH_VTR_EL2_RES1;
|
||||
|
||||
return vtr;
|
||||
}
|
||||
|
||||
/*
|
||||
* As per Documentation/virt/kvm/devices/arm-vgic-its.rst,
|
||||
* below macros are defined for ITS table entry encoding.
|
||||
|
|
@ -297,6 +315,7 @@ int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
|||
int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu,
|
||||
struct kvm_device_attr *attr, bool is_write);
|
||||
int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
|
||||
const struct sys_reg_desc *vgic_v3_get_sysreg_table(unsigned int *sz);
|
||||
int vgic_v3_line_level_info_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
u32 intid, u32 *val);
|
||||
int kvm_register_vgic_device(unsigned long type);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@
|
|||
#include "test_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
#define SYS_REG(r) ARM64_SYS_REG(sys_reg_Op0(SYS_ ## r), \
|
||||
sys_reg_Op1(SYS_ ## r), \
|
||||
sys_reg_CRn(SYS_ ## r), \
|
||||
sys_reg_CRm(SYS_ ## r), \
|
||||
sys_reg_Op2(SYS_ ## r))
|
||||
|
||||
struct feature_id_reg {
|
||||
__u64 reg;
|
||||
__u64 id_reg;
|
||||
|
|
@ -22,43 +28,41 @@ struct feature_id_reg {
|
|||
__u64 feat_min;
|
||||
};
|
||||
|
||||
static struct feature_id_reg feat_id_regs[] = {
|
||||
{
|
||||
ARM64_SYS_REG(3, 0, 2, 0, 3), /* TCR2_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */
|
||||
0,
|
||||
1
|
||||
},
|
||||
{
|
||||
ARM64_SYS_REG(3, 0, 10, 2, 2), /* PIRE0_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */
|
||||
8,
|
||||
1
|
||||
},
|
||||
{
|
||||
ARM64_SYS_REG(3, 0, 10, 2, 3), /* PIR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */
|
||||
8,
|
||||
1
|
||||
},
|
||||
{
|
||||
ARM64_SYS_REG(3, 0, 10, 2, 4), /* POR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */
|
||||
16,
|
||||
1
|
||||
},
|
||||
{
|
||||
ARM64_SYS_REG(3, 3, 10, 2, 4), /* POR_EL0 */
|
||||
ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */
|
||||
16,
|
||||
1
|
||||
},
|
||||
{
|
||||
KVM_ARM64_SYS_REG(SYS_SCTLR2_EL1),
|
||||
KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR3_EL1),
|
||||
ID_AA64MMFR3_EL1_SCTLRX_SHIFT,
|
||||
ID_AA64MMFR3_EL1_SCTLRX_IMP
|
||||
#define FEAT(id, f, v) \
|
||||
.id_reg = SYS_REG(id), \
|
||||
.feat_shift = id ## _ ## f ## _SHIFT, \
|
||||
.feat_min = id ## _ ## f ## _ ## v
|
||||
|
||||
#define REG_FEAT(r, id, f, v) \
|
||||
{ \
|
||||
.reg = SYS_REG(r), \
|
||||
FEAT(id, f, v) \
|
||||
}
|
||||
|
||||
static struct feature_id_reg feat_id_regs[] = {
|
||||
REG_FEAT(TCR2_EL1, ID_AA64MMFR3_EL1, TCRX, IMP),
|
||||
REG_FEAT(TCR2_EL2, ID_AA64MMFR3_EL1, TCRX, IMP),
|
||||
REG_FEAT(PIRE0_EL1, ID_AA64MMFR3_EL1, S1PIE, IMP),
|
||||
REG_FEAT(PIRE0_EL2, ID_AA64MMFR3_EL1, S1PIE, IMP),
|
||||
REG_FEAT(PIR_EL1, ID_AA64MMFR3_EL1, S1PIE, IMP),
|
||||
REG_FEAT(PIR_EL2, ID_AA64MMFR3_EL1, S1PIE, IMP),
|
||||
REG_FEAT(POR_EL1, ID_AA64MMFR3_EL1, S1POE, IMP),
|
||||
REG_FEAT(POR_EL0, ID_AA64MMFR3_EL1, S1POE, IMP),
|
||||
REG_FEAT(POR_EL2, ID_AA64MMFR3_EL1, S1POE, IMP),
|
||||
REG_FEAT(HCRX_EL2, ID_AA64MMFR1_EL1, HCX, IMP),
|
||||
REG_FEAT(HFGRTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP),
|
||||
REG_FEAT(HFGWTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP),
|
||||
REG_FEAT(HFGITR_EL2, ID_AA64MMFR0_EL1, FGT, IMP),
|
||||
REG_FEAT(HDFGRTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP),
|
||||
REG_FEAT(HDFGWTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP),
|
||||
REG_FEAT(HAFGRTR_EL2, ID_AA64MMFR0_EL1, FGT, IMP),
|
||||
REG_FEAT(HFGRTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2),
|
||||
REG_FEAT(HFGWTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2),
|
||||
REG_FEAT(HFGITR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2),
|
||||
REG_FEAT(HDFGRTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2),
|
||||
REG_FEAT(HDFGWTR2_EL2, ID_AA64MMFR0_EL1, FGT, FGT2),
|
||||
REG_FEAT(ZCR_EL2, ID_AA64PFR0_EL1, SVE, IMP),
|
||||
REG_FEAT(SCTLR2_EL1, ID_AA64MMFR3_EL1, SCTLRX, IMP),
|
||||
};
|
||||
|
||||
bool filter_reg(__u64 reg)
|
||||
|
|
@ -693,6 +697,60 @@ static __u64 pauth_generic_regs[] = {
|
|||
ARM64_SYS_REG(3, 0, 2, 3, 1), /* APGAKEYHI_EL1 */
|
||||
};
|
||||
|
||||
static __u64 el2_regs[] = {
|
||||
SYS_REG(VPIDR_EL2),
|
||||
SYS_REG(VMPIDR_EL2),
|
||||
SYS_REG(SCTLR_EL2),
|
||||
SYS_REG(ACTLR_EL2),
|
||||
SYS_REG(HCR_EL2),
|
||||
SYS_REG(MDCR_EL2),
|
||||
SYS_REG(CPTR_EL2),
|
||||
SYS_REG(HSTR_EL2),
|
||||
SYS_REG(HFGRTR_EL2),
|
||||
SYS_REG(HFGWTR_EL2),
|
||||
SYS_REG(HFGITR_EL2),
|
||||
SYS_REG(HACR_EL2),
|
||||
SYS_REG(ZCR_EL2),
|
||||
SYS_REG(HCRX_EL2),
|
||||
SYS_REG(TTBR0_EL2),
|
||||
SYS_REG(TTBR1_EL2),
|
||||
SYS_REG(TCR_EL2),
|
||||
SYS_REG(TCR2_EL2),
|
||||
SYS_REG(VTTBR_EL2),
|
||||
SYS_REG(VTCR_EL2),
|
||||
SYS_REG(VNCR_EL2),
|
||||
SYS_REG(HDFGRTR2_EL2),
|
||||
SYS_REG(HDFGWTR2_EL2),
|
||||
SYS_REG(HFGRTR2_EL2),
|
||||
SYS_REG(HFGWTR2_EL2),
|
||||
SYS_REG(HDFGRTR_EL2),
|
||||
SYS_REG(HDFGWTR_EL2),
|
||||
SYS_REG(HAFGRTR_EL2),
|
||||
SYS_REG(HFGITR2_EL2),
|
||||
SYS_REG(SPSR_EL2),
|
||||
SYS_REG(ELR_EL2),
|
||||
SYS_REG(AFSR0_EL2),
|
||||
SYS_REG(AFSR1_EL2),
|
||||
SYS_REG(ESR_EL2),
|
||||
SYS_REG(FAR_EL2),
|
||||
SYS_REG(HPFAR_EL2),
|
||||
SYS_REG(MAIR_EL2),
|
||||
SYS_REG(PIRE0_EL2),
|
||||
SYS_REG(PIR_EL2),
|
||||
SYS_REG(POR_EL2),
|
||||
SYS_REG(AMAIR_EL2),
|
||||
SYS_REG(VBAR_EL2),
|
||||
SYS_REG(CONTEXTIDR_EL2),
|
||||
SYS_REG(TPIDR_EL2),
|
||||
SYS_REG(CNTVOFF_EL2),
|
||||
SYS_REG(CNTHCTL_EL2),
|
||||
SYS_REG(CNTHP_CTL_EL2),
|
||||
SYS_REG(CNTHP_CVAL_EL2),
|
||||
SYS_REG(CNTHV_CTL_EL2),
|
||||
SYS_REG(CNTHV_CVAL_EL2),
|
||||
SYS_REG(SP_EL2),
|
||||
};
|
||||
|
||||
#define BASE_SUBLIST \
|
||||
{ "base", .regs = base_regs, .regs_n = ARRAY_SIZE(base_regs), }
|
||||
#define VREGS_SUBLIST \
|
||||
|
|
@ -719,6 +777,14 @@ static __u64 pauth_generic_regs[] = {
|
|||
.regs = pauth_generic_regs, \
|
||||
.regs_n = ARRAY_SIZE(pauth_generic_regs), \
|
||||
}
|
||||
#define EL2_SUBLIST \
|
||||
{ \
|
||||
.name = "EL2", \
|
||||
.capability = KVM_CAP_ARM_EL2, \
|
||||
.feature = KVM_ARM_VCPU_HAS_EL2, \
|
||||
.regs = el2_regs, \
|
||||
.regs_n = ARRAY_SIZE(el2_regs), \
|
||||
}
|
||||
|
||||
static struct vcpu_reg_list vregs_config = {
|
||||
.sublists = {
|
||||
|
|
@ -768,6 +834,65 @@ static struct vcpu_reg_list pauth_pmu_config = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list el2_vregs_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
EL2_SUBLIST,
|
||||
VREGS_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list el2_vregs_pmu_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
EL2_SUBLIST,
|
||||
VREGS_SUBLIST,
|
||||
PMU_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list el2_sve_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
EL2_SUBLIST,
|
||||
SVE_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list el2_sve_pmu_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
EL2_SUBLIST,
|
||||
SVE_SUBLIST,
|
||||
PMU_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list el2_pauth_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
EL2_SUBLIST,
|
||||
VREGS_SUBLIST,
|
||||
PAUTH_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
static struct vcpu_reg_list el2_pauth_pmu_config = {
|
||||
.sublists = {
|
||||
BASE_SUBLIST,
|
||||
EL2_SUBLIST,
|
||||
VREGS_SUBLIST,
|
||||
PAUTH_SUBLIST,
|
||||
PMU_SUBLIST,
|
||||
{0},
|
||||
},
|
||||
};
|
||||
|
||||
struct vcpu_reg_list *vcpu_configs[] = {
|
||||
&vregs_config,
|
||||
&vregs_pmu_config,
|
||||
|
|
@ -775,5 +900,12 @@ struct vcpu_reg_list *vcpu_configs[] = {
|
|||
&sve_pmu_config,
|
||||
&pauth_config,
|
||||
&pauth_pmu_config,
|
||||
|
||||
&el2_vregs_config,
|
||||
&el2_vregs_pmu_config,
|
||||
&el2_sve_config,
|
||||
&el2_sve_pmu_config,
|
||||
&el2_pauth_config,
|
||||
&el2_pauth_pmu_config,
|
||||
};
|
||||
int vcpu_configs_n = ARRAY_SIZE(vcpu_configs);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include <asm/kvm.h>
|
||||
#include <asm/kvm_para.h>
|
||||
|
||||
#include <arm64/gic_v3.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
|
|
@ -18,8 +20,6 @@
|
|||
|
||||
#define REG_OFFSET(vcpu, offset) (((uint64_t)vcpu << 32) | offset)
|
||||
|
||||
#define GICR_TYPER 0x8
|
||||
|
||||
#define VGIC_DEV_IS_V2(_d) ((_d) == KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
#define VGIC_DEV_IS_V3(_d) ((_d) == KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
|
||||
|
|
@ -715,6 +715,220 @@ int test_kvm_device(uint32_t gic_dev_type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct sr_def {
|
||||
const char *name;
|
||||
u32 encoding;
|
||||
};
|
||||
|
||||
#define PACK_SR(r) \
|
||||
((sys_reg_Op0(r) << 14) | \
|
||||
(sys_reg_Op1(r) << 11) | \
|
||||
(sys_reg_CRn(r) << 7) | \
|
||||
(sys_reg_CRm(r) << 3) | \
|
||||
(sys_reg_Op2(r)))
|
||||
|
||||
#define SR(r) \
|
||||
{ \
|
||||
.name = #r, \
|
||||
.encoding = r, \
|
||||
}
|
||||
|
||||
static const struct sr_def sysregs_el1[] = {
|
||||
SR(SYS_ICC_PMR_EL1),
|
||||
SR(SYS_ICC_BPR0_EL1),
|
||||
SR(SYS_ICC_AP0R0_EL1),
|
||||
SR(SYS_ICC_AP0R1_EL1),
|
||||
SR(SYS_ICC_AP0R2_EL1),
|
||||
SR(SYS_ICC_AP0R3_EL1),
|
||||
SR(SYS_ICC_AP1R0_EL1),
|
||||
SR(SYS_ICC_AP1R1_EL1),
|
||||
SR(SYS_ICC_AP1R2_EL1),
|
||||
SR(SYS_ICC_AP1R3_EL1),
|
||||
SR(SYS_ICC_BPR1_EL1),
|
||||
SR(SYS_ICC_CTLR_EL1),
|
||||
SR(SYS_ICC_SRE_EL1),
|
||||
SR(SYS_ICC_IGRPEN0_EL1),
|
||||
SR(SYS_ICC_IGRPEN1_EL1),
|
||||
};
|
||||
|
||||
static const struct sr_def sysregs_el2[] = {
|
||||
SR(SYS_ICH_AP0R0_EL2),
|
||||
SR(SYS_ICH_AP0R1_EL2),
|
||||
SR(SYS_ICH_AP0R2_EL2),
|
||||
SR(SYS_ICH_AP0R3_EL2),
|
||||
SR(SYS_ICH_AP1R0_EL2),
|
||||
SR(SYS_ICH_AP1R1_EL2),
|
||||
SR(SYS_ICH_AP1R2_EL2),
|
||||
SR(SYS_ICH_AP1R3_EL2),
|
||||
SR(SYS_ICH_HCR_EL2),
|
||||
SR(SYS_ICC_SRE_EL2),
|
||||
SR(SYS_ICH_VTR_EL2),
|
||||
SR(SYS_ICH_VMCR_EL2),
|
||||
SR(SYS_ICH_LR0_EL2),
|
||||
SR(SYS_ICH_LR1_EL2),
|
||||
SR(SYS_ICH_LR2_EL2),
|
||||
SR(SYS_ICH_LR3_EL2),
|
||||
SR(SYS_ICH_LR4_EL2),
|
||||
SR(SYS_ICH_LR5_EL2),
|
||||
SR(SYS_ICH_LR6_EL2),
|
||||
SR(SYS_ICH_LR7_EL2),
|
||||
SR(SYS_ICH_LR8_EL2),
|
||||
SR(SYS_ICH_LR9_EL2),
|
||||
SR(SYS_ICH_LR10_EL2),
|
||||
SR(SYS_ICH_LR11_EL2),
|
||||
SR(SYS_ICH_LR12_EL2),
|
||||
SR(SYS_ICH_LR13_EL2),
|
||||
SR(SYS_ICH_LR14_EL2),
|
||||
SR(SYS_ICH_LR15_EL2),
|
||||
};
|
||||
|
||||
static void test_sysreg_array(int gic, const struct sr_def *sr, int nr,
|
||||
int (*check)(int, const struct sr_def *, const char *))
|
||||
{
|
||||
for (int i = 0; i < nr; i++) {
|
||||
u64 val;
|
||||
u64 attr;
|
||||
int ret;
|
||||
|
||||
/* Assume MPIDR_EL1.Aff*=0 */
|
||||
attr = PACK_SR(sr[i].encoding);
|
||||
|
||||
/*
|
||||
* The API is braindead. A register can be advertised as
|
||||
* available, and yet not be readable or writable.
|
||||
* ICC_APnR{1,2,3}_EL1 are examples of such non-sense, and
|
||||
* ICH_APnR{1,2,3}_EL2 do follow suit for consistency.
|
||||
*
|
||||
* On the bright side, no known HW is implementing more than
|
||||
* 5 bits of priority, so we're safe. Sort of...
|
||||
*/
|
||||
ret = __kvm_has_device_attr(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
attr);
|
||||
TEST_ASSERT(ret == 0, "%s unavailable", sr[i].name);
|
||||
|
||||
/* Check that we can write back what we read */
|
||||
ret = __kvm_device_attr_get(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
attr, &val);
|
||||
TEST_ASSERT(ret == 0 || !check(gic, &sr[i], "read"), "%s unreadable", sr[i].name);
|
||||
ret = __kvm_device_attr_set(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
attr, &val);
|
||||
TEST_ASSERT(ret == 0 || !check(gic, &sr[i], "write"), "%s unwritable", sr[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 get_ctlr_pribits(int gic)
|
||||
{
|
||||
int ret;
|
||||
u64 val;
|
||||
u8 pri;
|
||||
|
||||
ret = __kvm_device_attr_get(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
PACK_SR(SYS_ICC_CTLR_EL1), &val);
|
||||
TEST_ASSERT(ret == 0, "ICC_CTLR_EL1 unreadable");
|
||||
|
||||
pri = FIELD_GET(ICC_CTLR_EL1_PRI_BITS_MASK, val) + 1;
|
||||
TEST_ASSERT(pri >= 5 && pri <= 7, "Bad pribits %d", pri);
|
||||
|
||||
return pri;
|
||||
}
|
||||
|
||||
static int check_unaccessible_el1_regs(int gic, const struct sr_def *sr, const char *what)
|
||||
{
|
||||
switch (sr->encoding) {
|
||||
case SYS_ICC_AP0R1_EL1:
|
||||
case SYS_ICC_AP1R1_EL1:
|
||||
if (get_ctlr_pribits(gic) >= 6)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SYS_ICC_AP0R2_EL1:
|
||||
case SYS_ICC_AP0R3_EL1:
|
||||
case SYS_ICC_AP1R2_EL1:
|
||||
case SYS_ICC_AP1R3_EL1:
|
||||
if (get_ctlr_pribits(gic) == 7)
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_info("SKIP %s for %s\n", sr->name, what);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 get_vtr_pribits(int gic)
|
||||
{
|
||||
int ret;
|
||||
u64 val;
|
||||
u8 pri;
|
||||
|
||||
ret = __kvm_device_attr_get(gic, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
PACK_SR(SYS_ICH_VTR_EL2), &val);
|
||||
TEST_ASSERT(ret == 0, "ICH_VTR_EL2 unreadable");
|
||||
|
||||
pri = FIELD_GET(ICH_VTR_EL2_PRIbits, val) + 1;
|
||||
TEST_ASSERT(pri >= 5 && pri <= 7, "Bad pribits %d", pri);
|
||||
|
||||
return pri;
|
||||
}
|
||||
|
||||
static int check_unaccessible_el2_regs(int gic, const struct sr_def *sr, const char *what)
|
||||
{
|
||||
switch (sr->encoding) {
|
||||
case SYS_ICH_AP0R1_EL2:
|
||||
case SYS_ICH_AP1R1_EL2:
|
||||
if (get_vtr_pribits(gic) >= 6)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SYS_ICH_AP0R2_EL2:
|
||||
case SYS_ICH_AP0R3_EL2:
|
||||
case SYS_ICH_AP1R2_EL2:
|
||||
case SYS_ICH_AP1R3_EL2:
|
||||
if (get_vtr_pribits(gic) == 7)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_info("SKIP %s for %s\n", sr->name, what);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_v3_sysregs(void)
|
||||
{
|
||||
struct kvm_vcpu_init init = {};
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
u32 feat = 0;
|
||||
int gic;
|
||||
|
||||
if (kvm_check_cap(KVM_CAP_ARM_EL2))
|
||||
feat |= BIT(KVM_ARM_VCPU_HAS_EL2);
|
||||
|
||||
vm = vm_create(1);
|
||||
|
||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
init.features[0] |= feat;
|
||||
|
||||
vcpu = aarch64_vcpu_add(vm, 0, &init, NULL);
|
||||
TEST_ASSERT(vcpu, "Can't create a vcpu?");
|
||||
|
||||
gic = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
TEST_ASSERT(gic >= 0, "No GIC???");
|
||||
|
||||
kvm_device_attr_set(gic, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
||||
test_sysreg_array(gic, sysregs_el1, ARRAY_SIZE(sysregs_el1), check_unaccessible_el1_regs);
|
||||
if (feat)
|
||||
test_sysreg_array(gic, sysregs_el2, ARRAY_SIZE(sysregs_el2), check_unaccessible_el2_regs);
|
||||
else
|
||||
pr_info("SKIP EL2 registers, not available\n");
|
||||
|
||||
close(gic);
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
void run_tests(uint32_t gic_dev_type)
|
||||
{
|
||||
test_vcpus_then_vgic(gic_dev_type);
|
||||
|
|
@ -730,6 +944,7 @@ void run_tests(uint32_t gic_dev_type)
|
|||
test_v3_last_bit_single_rdist();
|
||||
test_v3_redist_ipa_range_check_at_vcpu_run();
|
||||
test_v3_its_region();
|
||||
test_v3_sysregs();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user