KVM: arm64: Use guest ID register values for the sake of emulation
[linux-2.6-block.git] / arch / arm64 / kvm / sys_regs.c
index 2ca2973abe66fa7c9ac7029b6935f226cf41d31b..b73f9ff0899b9291ad9bbf4ea37ab61d22e8ca21 100644 (file)
@@ -379,7 +379,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
                          struct sys_reg_params *p,
                          const struct sys_reg_desc *r)
 {
-       u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+       u64 val = IDREG(vcpu->kvm, SYS_ID_AA64MMFR1_EL1);
        u32 sr = reg_to_encoding(r);
 
        if (!(val & (0xfUL << ID_AA64MMFR1_EL1_LO_SHIFT))) {
@@ -1373,6 +1373,13 @@ static inline bool is_id_reg(u32 id)
                sys_reg_CRm(id) < 8);
 }
 
+static inline bool is_aa32_id_reg(u32 id)
+{
+       return (sys_reg_Op0(id) == 3 && sys_reg_Op1(id) == 0 &&
+               sys_reg_CRn(id) == 0 && sys_reg_CRm(id) >= 1 &&
+               sys_reg_CRm(id) <= 3);
+}
+
 static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
                                  const struct sys_reg_desc *r)
 {
@@ -2151,6 +2158,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        { SYS_DESC(SYS_CONTEXTIDR_EL1), access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 },
        { SYS_DESC(SYS_TPIDR_EL1), NULL, reset_unknown, TPIDR_EL1 },
 
+       { SYS_DESC(SYS_ACCDATA_EL1), undef_access },
+
        { SYS_DESC(SYS_SCXTNUM_EL1), undef_access },
 
        { SYS_DESC(SYS_CNTKCTL_EL1), NULL, reset_val, CNTKCTL_EL1, 0},
@@ -2365,8 +2374,13 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        EL2_REG(MDCR_EL2, access_rw, reset_val, 0),
        EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1),
        EL2_REG(HSTR_EL2, access_rw, reset_val, 0),
+       EL2_REG(HFGRTR_EL2, access_rw, reset_val, 0),
+       EL2_REG(HFGWTR_EL2, access_rw, reset_val, 0),
+       EL2_REG(HFGITR_EL2, access_rw, reset_val, 0),
        EL2_REG(HACR_EL2, access_rw, reset_val, 0),
 
+       EL2_REG(HCRX_EL2, access_rw, reset_val, 0),
+
        EL2_REG(TTBR0_EL2, access_rw, reset_val, 0),
        EL2_REG(TTBR1_EL2, access_rw, reset_val, 0),
        EL2_REG(TCR_EL2, access_rw, reset_val, TCR_EL2_RES1),
@@ -2374,6 +2388,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        EL2_REG(VTCR_EL2, access_rw, reset_val, 0),
 
        { SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 },
+       EL2_REG(HDFGRTR_EL2, access_rw, reset_val, 0),
+       EL2_REG(HDFGWTR_EL2, access_rw, reset_val, 0),
        EL2_REG(SPSR_EL2, access_rw, reset_val, 0),
        EL2_REG(ELR_EL2, access_rw, reset_val, 0),
        { SYS_DESC(SYS_SP_EL1), access_sp_el1},
@@ -2429,8 +2445,8 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
        if (p->is_write) {
                return ignore_write(vcpu, p);
        } else {
-               u64 dfr = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
-               u64 pfr = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+               u64 dfr = IDREG(vcpu->kvm, SYS_ID_AA64DFR0_EL1);
+               u64 pfr = IDREG(vcpu->kvm, SYS_ID_AA64PFR0_EL1);
                u32 el3 = !!cpuid_feature_extract_unsigned_field(pfr, ID_AA64PFR0_EL1_EL3_SHIFT);
 
                p->regval = ((((dfr >> ID_AA64DFR0_EL1_WRPs_SHIFT) & 0xf) << 28) |
@@ -3170,6 +3186,9 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu)
 
        trace_kvm_handle_sys_reg(esr);
 
+       if (__check_nv_sr_forward(vcpu))
+               return 1;
+
        params = esr_sys64_to_params(esr);
        params.regval = vcpu_get_reg(vcpu, Rt);
 
@@ -3560,6 +3579,65 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
        return write_demux_regids(uindices);
 }
 
+#define KVM_ARM_FEATURE_ID_RANGE_INDEX(r)                      \
+       KVM_ARM_FEATURE_ID_RANGE_IDX(sys_reg_Op0(r),            \
+               sys_reg_Op1(r),                                 \
+               sys_reg_CRn(r),                                 \
+               sys_reg_CRm(r),                                 \
+               sys_reg_Op2(r))
+
+static bool is_feature_id_reg(u32 encoding)
+{
+       return (sys_reg_Op0(encoding) == 3 &&
+               (sys_reg_Op1(encoding) < 2 || sys_reg_Op1(encoding) == 3) &&
+               sys_reg_CRn(encoding) == 0 &&
+               sys_reg_CRm(encoding) <= 7);
+}
+
+int kvm_vm_ioctl_get_reg_writable_masks(struct kvm *kvm, struct reg_mask_range *range)
+{
+       const void *zero_page = page_to_virt(ZERO_PAGE(0));
+       u64 __user *masks = (u64 __user *)range->addr;
+
+       /* Only feature id range is supported, reserved[13] must be zero. */
+       if (range->range ||
+           memcmp(range->reserved, zero_page, sizeof(range->reserved)))
+               return -EINVAL;
+
+       /* Wipe the whole thing first */
+       if (clear_user(masks, KVM_ARM_FEATURE_ID_RANGE_SIZE * sizeof(__u64)))
+               return -EFAULT;
+
+       for (int i = 0; i < ARRAY_SIZE(sys_reg_descs); i++) {
+               const struct sys_reg_desc *reg = &sys_reg_descs[i];
+               u32 encoding = reg_to_encoding(reg);
+               u64 val;
+
+               if (!is_feature_id_reg(encoding) || !reg->set_user)
+                       continue;
+
+               /*
+                * For ID registers, we return the writable mask. Other feature
+                * registers return a full 64bit mask. That's not necessary
+                * compliant with a given revision of the architecture, but the
+                * RES0/RES1 definitions allow us to do that.
+                */
+               if (is_id_reg(encoding)) {
+                       if (!reg->val ||
+                           (is_aa32_id_reg(encoding) && !kvm_supports_32bit_el0()))
+                               continue;
+                       val = reg->val;
+               } else {
+                       val = ~0UL;
+               }
+
+               if (put_user(val, (masks + KVM_ARM_FEATURE_ID_RANGE_INDEX(encoding))))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
 int __init kvm_sys_reg_table_init(void)
 {
        struct sys_reg_params params;
@@ -3587,5 +3665,8 @@ int __init kvm_sys_reg_table_init(void)
        if (!first_idreg)
                return -EINVAL;
 
+       if (kvm_get_mode() == KVM_MODE_NV)
+               return populate_nv_trap_config();
+
        return 0;
 }