Add the SPE system registers to the VCPU context. Omitted are
PMBIDR_EL1, which cannot be trapped, and PMSIR_EL1, which is a read-only
register. The registers that KVM traps are stored in the sys_regs array
on a write, and returned on a read; complete emulation and save/restore
for all registers on world switch will be added a future patches.

KVM exposes FEAT_SPEv1p1 to guests in the ID_AA64DFR0_EL1 register and
doesn't trap accesses to the profiling control registers. If the hardware
supports FEAT_SPEv1p2, the guest will be able to access the PMSNEVFR_EL1
register, which is UNDEFINED for FEAT_SPEv1p1. However, that
inconsistency is somewhat consistent with the architecture because
PMBIDR_EL1 behaves similarly: the register is UNDEFINED if SPE is missing,
but a VCPU without the SPE feature can still read the register because
there is no (easy) way for KVM to trap accesses to the register.

Signed-off-by: Alexandru Elisei <[email protected]>
---
 arch/arm64/include/asm/kvm_host.h | 12 +++++++
 arch/arm64/include/asm/kvm_spe.h  |  7 ++++
 arch/arm64/kvm/spe.c              | 10 ++++++
 arch/arm64/kvm/sys_regs.c         | 54 ++++++++++++++++++++++++-------
 4 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h 
b/arch/arm64/include/asm/kvm_host.h
index 7b957e439b3d..4c0d3d5ba285 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -237,6 +237,18 @@ enum vcpu_sysreg {
        TFSR_EL1,       /* Tag Fault Status Register (EL1) */
        TFSRE0_EL1,     /* Tag Fault Status Register (EL0) */
 
+       /* Statistical Profiling Extension Registers. */
+       PMSCR_EL1,      /* Statistical Profiling Control Register */
+       PMSICR_EL1,     /* Sampling Interval Counter Register */
+       PMSIRR_EL1,     /* Sampling Interval Reload Register */
+       PMSFCR_EL1,     /* Sampling Filter Control Register */
+       PMSEVFR_EL1,    /* Sampling Event Filter Register */
+       PMSLATFR_EL1,   /* Sampling Latency Filter Register */
+       PMBLIMITR_EL1,  /* Profiling Buffer Limit Address Register */
+       PMBPTR_EL1,     /* Profiling Buffer Write Pointer Register */
+       PMBSR_EL1,      /* Profiling Buffer Status/syndrome Register */
+       PMSCR_EL2,      /* Statistical Profiling Control Register, EL2 */
+
        /* 32bit specific registers. Keep them at the end of the range */
        DACR32_EL2,     /* Domain Access Control Register */
        IFSR32_EL2,     /* Instruction Fault Status Register */
diff --git a/arch/arm64/include/asm/kvm_spe.h b/arch/arm64/include/asm/kvm_spe.h
index 2217b821ab37..934eedb0de46 100644
--- a/arch/arm64/include/asm/kvm_spe.h
+++ b/arch/arm64/include/asm/kvm_spe.h
@@ -25,9 +25,13 @@ void kvm_spe_init_supported_cpus(void);
 void kvm_spe_vm_init(struct kvm *kvm);
 int kvm_spe_vcpu_first_run_init(struct kvm_vcpu *vcpu);
 
+void kvm_spe_write_sysreg(struct kvm_vcpu *vcpu, int reg, u64 val);
+u64 kvm_spe_read_sysreg(struct kvm_vcpu *vcpu, int reg);
+
 int kvm_spe_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
 int kvm_spe_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
 int kvm_spe_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
+
 #else
 #define kvm_supports_spe()     (false)
 
@@ -38,6 +42,9 @@ static inline void kvm_spe_init_supported_cpus(void) {}
 static inline void kvm_spe_vm_init(struct kvm *kvm) {}
 static inline int kvm_spe_vcpu_first_run_init(struct kvm_vcpu *vcpu) { return 
-ENOEXEC; }
 
+static inline void kvm_spe_write_sysreg(struct kvm_vcpu *vcpu, int reg, u64 
val) {}
+static inline u64 kvm_spe_read_sysreg(struct kvm_vcpu *vcpu, int reg) { return 
0; }
+
 static inline int kvm_spe_set_attr(struct kvm_vcpu *vcpu,
                                   struct kvm_device_attr *attr)
 {
diff --git a/arch/arm64/kvm/spe.c b/arch/arm64/kvm/spe.c
index 801ceb66a3d0..f760ccd8306a 100644
--- a/arch/arm64/kvm/spe.c
+++ b/arch/arm64/kvm/spe.c
@@ -57,6 +57,16 @@ int kvm_spe_vcpu_first_run_init(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+void kvm_spe_write_sysreg(struct kvm_vcpu *vcpu, int reg, u64 val)
+{
+       __vcpu_sys_reg(vcpu, reg) = val;
+}
+
+u64 kvm_spe_read_sysreg(struct kvm_vcpu *vcpu, int reg)
+{
+       return __vcpu_sys_reg(vcpu, reg);
+}
+
 static bool kvm_vcpu_supports_spe(struct kvm_vcpu *vcpu)
 {
        if (!kvm_supports_spe())
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ab7370b7a44b..843822be5695 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -594,6 +594,33 @@ 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 spe_visibility(const struct kvm_vcpu *vcpu,
+                                 const struct sys_reg_desc *r)
+{
+       if (kvm_vcpu_has_spe(vcpu))
+               return 0;
+
+       return REG_HIDDEN;
+}
+
+static bool access_spe_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
+                          const struct sys_reg_desc *r)
+{      int reg = r->reg;
+       u64 val = p->regval;
+
+       if (reg < PMBLIMITR_EL1) {
+               print_sys_reg_msg(p, "Unsupported guest SPE register access at: 
%lx [%08lx]\n",
+                                 *vcpu_pc(vcpu), *vcpu_cpsr(vcpu));
+       }
+
+       if (p->is_write)
+               kvm_spe_write_sysreg(vcpu, reg, val);
+       else
+               p->regval = kvm_spe_read_sysreg(vcpu, reg);
+
+       return true;
+}
+
 static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
                                   const struct sys_reg_desc *r)
 {
@@ -956,6 +983,10 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct 
sys_reg_params *p,
        { PMU_SYS_REG(SYS_PMEVTYPERn_EL0(n)),                           \
          .access = access_pmu_evtyper, .reg = (PMEVTYPER0_EL0 + n), }
 
+#define SPE_SYS_REG(r)                                                 \
+       SYS_DESC(r), .access = access_spe_reg, .reset = reset_val,      \
+       .val = 0, .visibility = spe_visibility
+
 static bool undef_access(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
                         const struct sys_reg_desc *r)
 {
@@ -1530,18 +1561,17 @@ 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_PMSCR_EL1), undef_access },
-       { SYS_DESC(SYS_PMSNEVFR_EL1), undef_access },
-       { SYS_DESC(SYS_PMSICR_EL1), undef_access },
-       { SYS_DESC(SYS_PMSIRR_EL1), undef_access },
-       { SYS_DESC(SYS_PMSFCR_EL1), undef_access },
-       { SYS_DESC(SYS_PMSEVFR_EL1), undef_access },
-       { SYS_DESC(SYS_PMSLATFR_EL1), undef_access },
-       { SYS_DESC(SYS_PMSIDR_EL1), undef_access },
-       { SYS_DESC(SYS_PMBLIMITR_EL1), undef_access },
-       { SYS_DESC(SYS_PMBPTR_EL1), undef_access },
-       { SYS_DESC(SYS_PMBSR_EL1), undef_access },
-       /* PMBIDR_EL1 is not trapped */
+       { SPE_SYS_REG(SYS_PMSCR_EL1), .reg = PMSCR_EL1 },
+       { SPE_SYS_REG(SYS_PMSICR_EL1), .reg = PMSICR_EL1 },
+       { SPE_SYS_REG(SYS_PMSIRR_EL1), .reg = PMSIRR_EL1 },
+       { SPE_SYS_REG(SYS_PMSFCR_EL1), .reg = PMSFCR_EL1 },
+       { SPE_SYS_REG(SYS_PMSEVFR_EL1), .reg = PMSEVFR_EL1 },
+       { SPE_SYS_REG(SYS_PMSLATFR_EL1), .reg = PMSLATFR_EL1 },
+       { SPE_SYS_REG(SYS_PMSIDR_EL1), .reset = NULL },
+       { SPE_SYS_REG(SYS_PMBLIMITR_EL1), .reg = PMBLIMITR_EL1 },
+       { SPE_SYS_REG(SYS_PMBPTR_EL1), .reg = PMBPTR_EL1 },
+       { SPE_SYS_REG(SYS_PMBSR_EL1), .reg = PMBSR_EL1 },
+       /* PMBIDR_EL1 and PMSCR_EL2 are not trapped */
 
        { PMU_SYS_REG(SYS_PMINTENSET_EL1),
          .access = access_pminten, .reg = PMINTENSET_EL1 },
-- 
2.33.0

_______________________________________________
kvmarm mailing list
[email protected]
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

Reply via email to