|
|
|
@@ -43,6 +43,10 @@
|
|
|
|
|
* 64bit interface.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define reg_to_encoding(x) \
|
|
|
|
|
sys_reg((u32)(x)->Op0, (u32)(x)->Op1, \
|
|
|
|
|
(u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2)
|
|
|
|
|
|
|
|
|
|
static bool read_from_write_only(struct kvm_vcpu *vcpu,
|
|
|
|
|
struct sys_reg_params *params,
|
|
|
|
|
const struct sys_reg_desc *r)
|
|
|
|
@@ -273,8 +277,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
|
|
|
|
|
const struct sys_reg_desc *r)
|
|
|
|
|
{
|
|
|
|
|
u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
|
|
|
|
u32 sr = sys_reg((u32)r->Op0, (u32)r->Op1,
|
|
|
|
|
(u32)r->CRn, (u32)r->CRm, (u32)r->Op2);
|
|
|
|
|
u32 sr = reg_to_encoding(r);
|
|
|
|
|
|
|
|
|
|
if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
|
|
|
|
|
kvm_inject_undefined(vcpu);
|
|
|
|
@@ -590,6 +593,15 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|
|
|
|
vcpu_write_sys_reg(vcpu, (1ULL << 31) | mpidr, MPIDR_EL1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
|
|
|
|
|
const struct sys_reg_desc *r)
|
|
|
|
|
{
|
|
|
|
|
if (kvm_vcpu_has_pmu(vcpu))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return REG_HIDDEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|
|
|
|
{
|
|
|
|
|
u64 pmcr, val;
|
|
|
|
@@ -613,9 +625,8 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|
|
|
|
static bool check_pmu_access_disabled(struct kvm_vcpu *vcpu, u64 flags)
|
|
|
|
|
{
|
|
|
|
|
u64 reg = __vcpu_sys_reg(vcpu, PMUSERENR_EL0);
|
|
|
|
|
bool enabled = kvm_vcpu_has_pmu(vcpu);
|
|
|
|
|
bool enabled = (reg & flags) || vcpu_mode_priv(vcpu);
|
|
|
|
|
|
|
|
|
|
enabled &= (reg & flags) || vcpu_mode_priv(vcpu);
|
|
|
|
|
if (!enabled)
|
|
|
|
|
kvm_inject_undefined(vcpu);
|
|
|
|
|
|
|
|
|
@@ -900,11 +911,6 @@ static bool access_pmswinc(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|
|
|
|
static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|
|
|
|
const struct sys_reg_desc *r)
|
|
|
|
|
{
|
|
|
|
|
if (!kvm_vcpu_has_pmu(vcpu)) {
|
|
|
|
|
kvm_inject_undefined(vcpu);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p->is_write) {
|
|
|
|
|
if (!vcpu_mode_priv(vcpu)) {
|
|
|
|
|
kvm_inject_undefined(vcpu);
|
|
|
|
@@ -921,10 +927,6 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define reg_to_encoding(x) \
|
|
|
|
|
sys_reg((u32)(x)->Op0, (u32)(x)->Op1, \
|
|
|
|
|
(u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2)
|
|
|
|
|
|
|
|
|
|
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
|
|
|
|
|
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
|
|
|
|
|
{ SYS_DESC(SYS_DBGBVRn_EL1(n)), \
|
|
|
|
@@ -936,15 +938,18 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|
|
|
|
{ SYS_DESC(SYS_DBGWCRn_EL1(n)), \
|
|
|
|
|
trap_wcr, reset_wcr, 0, 0, get_wcr, set_wcr }
|
|
|
|
|
|
|
|
|
|
#define PMU_SYS_REG(r) \
|
|
|
|
|
SYS_DESC(r), .reset = reset_unknown, .visibility = pmu_visibility
|
|
|
|
|
|
|
|
|
|
/* Macro to expand the PMEVCNTRn_EL0 register */
|
|
|
|
|
#define PMU_PMEVCNTR_EL0(n) \
|
|
|
|
|
{ SYS_DESC(SYS_PMEVCNTRn_EL0(n)), \
|
|
|
|
|
access_pmu_evcntr, reset_unknown, (PMEVCNTR0_EL0 + n), }
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMEVCNTRn_EL0(n)), \
|
|
|
|
|
.access = access_pmu_evcntr, .reg = (PMEVCNTR0_EL0 + n), }
|
|
|
|
|
|
|
|
|
|
/* Macro to expand the PMEVTYPERn_EL0 register */
|
|
|
|
|
#define PMU_PMEVTYPER_EL0(n) \
|
|
|
|
|
{ SYS_DESC(SYS_PMEVTYPERn_EL0(n)), \
|
|
|
|
|
access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), }
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMEVTYPERn_EL0(n)), \
|
|
|
|
|
.access = access_pmu_evtyper, .reg = (PMEVTYPER0_EL0 + n), }
|
|
|
|
|
|
|
|
|
|
static bool undef_access(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|
|
|
|
const struct sys_reg_desc *r)
|
|
|
|
@@ -1020,8 +1025,7 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
|
|
|
|
|
static u64 read_id_reg(const struct kvm_vcpu *vcpu,
|
|
|
|
|
struct sys_reg_desc const *r, bool raz)
|
|
|
|
|
{
|
|
|
|
|
u32 id = sys_reg((u32)r->Op0, (u32)r->Op1,
|
|
|
|
|
(u32)r->CRn, (u32)r->CRm, (u32)r->Op2);
|
|
|
|
|
u32 id = reg_to_encoding(r);
|
|
|
|
|
u64 val = raz ? 0 : read_sanitised_ftr_reg(id);
|
|
|
|
|
|
|
|
|
|
if (id == SYS_ID_AA64PFR0_EL1) {
|
|
|
|
@@ -1062,8 +1066,7 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
|
|
|
|
|
static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
|
|
|
|
|
const struct sys_reg_desc *r)
|
|
|
|
|
{
|
|
|
|
|
u32 id = sys_reg((u32)r->Op0, (u32)r->Op1,
|
|
|
|
|
(u32)r->CRn, (u32)r->CRm, (u32)r->Op2);
|
|
|
|
|
u32 id = reg_to_encoding(r);
|
|
|
|
|
|
|
|
|
|
switch (id) {
|
|
|
|
|
case SYS_ID_AA64ZFR0_EL1:
|
|
|
|
@@ -1486,8 +1489,10 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|
|
|
|
{ SYS_DESC(SYS_FAR_EL1), access_vm_reg, reset_unknown, FAR_EL1 },
|
|
|
|
|
{ SYS_DESC(SYS_PAR_EL1), NULL, reset_unknown, PAR_EL1 },
|
|
|
|
|
|
|
|
|
|
{ SYS_DESC(SYS_PMINTENSET_EL1), access_pminten, reset_unknown, PMINTENSET_EL1 },
|
|
|
|
|
{ SYS_DESC(SYS_PMINTENCLR_EL1), access_pminten, reset_unknown, PMINTENSET_EL1 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMINTENSET_EL1),
|
|
|
|
|
.access = access_pminten, .reg = PMINTENSET_EL1 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMINTENCLR_EL1),
|
|
|
|
|
.access = access_pminten, .reg = PMINTENSET_EL1 },
|
|
|
|
|
|
|
|
|
|
{ SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },
|
|
|
|
|
{ SYS_DESC(SYS_AMAIR_EL1), access_vm_reg, reset_amair_el1, AMAIR_EL1 },
|
|
|
|
@@ -1526,23 +1531,36 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|
|
|
|
{ SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 },
|
|
|
|
|
{ SYS_DESC(SYS_CTR_EL0), access_ctr },
|
|
|
|
|
|
|
|
|
|
{ SYS_DESC(SYS_PMCR_EL0), access_pmcr, reset_pmcr, PMCR_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMCNTENSET_EL0), access_pmcnten, reset_unknown, PMCNTENSET_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMCNTENCLR_EL0), access_pmcnten, reset_unknown, PMCNTENSET_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMOVSCLR_EL0), access_pmovs, reset_unknown, PMOVSSET_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMSWINC_EL0), access_pmswinc, reset_unknown, PMSWINC_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMSELR_EL0), access_pmselr, reset_unknown, PMSELR_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMCEID0_EL0), access_pmceid },
|
|
|
|
|
{ SYS_DESC(SYS_PMCEID1_EL0), access_pmceid },
|
|
|
|
|
{ SYS_DESC(SYS_PMCCNTR_EL0), access_pmu_evcntr, reset_unknown, PMCCNTR_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMXEVTYPER_EL0), access_pmu_evtyper },
|
|
|
|
|
{ SYS_DESC(SYS_PMXEVCNTR_EL0), access_pmu_evcntr },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMCR_EL0), .access = access_pmcr,
|
|
|
|
|
.reset = reset_pmcr, .reg = PMCR_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMCNTENSET_EL0),
|
|
|
|
|
.access = access_pmcnten, .reg = PMCNTENSET_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMCNTENCLR_EL0),
|
|
|
|
|
.access = access_pmcnten, .reg = PMCNTENSET_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMOVSCLR_EL0),
|
|
|
|
|
.access = access_pmovs, .reg = PMOVSSET_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMSWINC_EL0),
|
|
|
|
|
.access = access_pmswinc, .reg = PMSWINC_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMSELR_EL0),
|
|
|
|
|
.access = access_pmselr, .reg = PMSELR_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMCEID0_EL0),
|
|
|
|
|
.access = access_pmceid, .reset = NULL },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMCEID1_EL0),
|
|
|
|
|
.access = access_pmceid, .reset = NULL },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMCCNTR_EL0),
|
|
|
|
|
.access = access_pmu_evcntr, .reg = PMCCNTR_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMXEVTYPER_EL0),
|
|
|
|
|
.access = access_pmu_evtyper, .reset = NULL },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMXEVCNTR_EL0),
|
|
|
|
|
.access = access_pmu_evcntr, .reset = NULL },
|
|
|
|
|
/*
|
|
|
|
|
* PMUSERENR_EL0 resets as unknown in 64bit mode while it resets as zero
|
|
|
|
|
* in 32bit mode. Here we choose to reset it as zero for consistency.
|
|
|
|
|
*/
|
|
|
|
|
{ SYS_DESC(SYS_PMUSERENR_EL0), access_pmuserenr, reset_val, PMUSERENR_EL0, 0 },
|
|
|
|
|
{ SYS_DESC(SYS_PMOVSSET_EL0), access_pmovs, reset_unknown, PMOVSSET_EL0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMUSERENR_EL0), .access = access_pmuserenr,
|
|
|
|
|
.reset = reset_val, .reg = PMUSERENR_EL0, .val = 0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMOVSSET_EL0),
|
|
|
|
|
.access = access_pmovs, .reg = PMOVSSET_EL0 },
|
|
|
|
|
|
|
|
|
|
{ SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 },
|
|
|
|
|
{ SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 },
|
|
|
|
@@ -1694,7 +1712,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
|
|
|
|
* PMCCFILTR_EL0 resets as unknown in 64bit mode while it resets as zero
|
|
|
|
|
* in 32bit mode. Here we choose to reset it as zero for consistency.
|
|
|
|
|
*/
|
|
|
|
|
{ SYS_DESC(SYS_PMCCFILTR_EL0), access_pmu_evtyper, reset_val, PMCCFILTR_EL0, 0 },
|
|
|
|
|
{ PMU_SYS_REG(SYS_PMCCFILTR_EL0), .access = access_pmu_evtyper,
|
|
|
|
|
.reset = reset_val, .reg = PMCCFILTR_EL0, .val = 0 },
|
|
|
|
|
|
|
|
|
|
{ SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 },
|
|
|
|
|
{ SYS_DESC(SYS_IFSR32_EL2), NULL, reset_unknown, IFSR32_EL2 },
|
|
|
|
|