@@ -32,6 +32,8 @@ obj-y += $(KVM)/arm/vgic/vgic-mmio.o
obj-y += $(KVM)/arm/vgic/vgic-mmio-v2.o
obj-y += $(KVM)/arm/vgic/vgic-mmio-v3.o
obj-y += $(KVM)/arm/vgic/vgic-kvm-device.o
+obj-y += $(KVM)/arm/vgic/vgic-sys-reg-common.o
+obj-y += $(KVM)/arm/vgic/vgic-coproc-reg-v3.o
obj-y += $(KVM)/arm/vgic/vgic-its.o
obj-y += $(KVM)/irqchip.o
obj-y += $(KVM)/arm/arch_timer.o
new file mode 100644
@@ -0,0 +1,155 @@
+/*
+ * VGIC system registers handling functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <kvm/arm_vgic.h>
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_arm.h>
+
+#include "vgic.h"
+#include "coproc.h"
+
+#define ACCESS_COPROC_REG(REG) \
+static bool access_gic_##REG##_coproc_reg(struct kvm_vcpu *vcpu, \
+ struct coproc_params *p, \
+ const struct coproc_reg *r) \
+{ \
+ return access_gic_##REG##_reg(vcpu, p->is_write, &p->Rt1); \
+}
+
+ACCESS_COPROC_REG(ctlr)
+ACCESS_COPROC_REG(pmr)
+ACCESS_COPROC_REG(bpr0)
+ACCESS_COPROC_REG(bpr1)
+ACCESS_COPROC_REG(sre)
+ACCESS_COPROC_REG(grpen0)
+ACCESS_COPROC_REG(grpen1)
+
+#define ACCESS_COPROC_APNR_REG(REG) \
+static bool access_gic_##REG##_coproc_reg(struct kvm_vcpu *vcpu, \
+ struct coproc_params *p, \
+ const struct coproc_reg *r) \
+{ \
+ u8 idx = p->Op2 & 3; \
+ \
+ return access_gic_##REG##_reg(vcpu, p->is_write, idx, &p->Rt1); \
+}
+
+ACCESS_COPROC_APNR_REG(ap0r)
+ACCESS_COPROC_APNR_REG(ap1r)
+
+static const struct coproc_reg gic_v3_icc_coproc_reg_descs[] = {
+ /* ICC_PMR_EL1 */
+ { CRn(0), CRm(6), Op1(0), Op2(0), is32, access_gic_pmr_coproc_reg },
+ /* ICC_BPR0_EL1 */
+ { CRn(12), CRm(8), Op1(0), Op2(3), is32, access_gic_bpr0_coproc_reg },
+ /* ICC_AP0R0_EL1 */
+ { CRn(12), CRm(8), Op1(0), Op2(4), is32, access_gic_ap0r_coproc_reg },
+ /* ICC_AP0R1_EL1 */
+ { CRn(12), CRm(8), Op1(0), Op2(5), is32, access_gic_ap0r_coproc_reg },
+ /* ICC_AP0R2_EL1 */
+ { CRn(12), CRm(8), Op1(0), Op2(6), is32, access_gic_ap0r_coproc_reg },
+ /* ICC_AP0R3_EL1 */
+ { CRn(12), CRm(8), Op1(0), Op2(7), is32, access_gic_ap0r_coproc_reg },
+ /* ICC_AP1R0_EL1 */
+ { CRn(12), CRm(9), Op1(0), Op2(0), is32, access_gic_ap1r_coproc_reg },
+ /* ICC_AP1R1_EL1 */
+ { CRn(12), CRm(9), Op1(0), Op2(1), is32, access_gic_ap1r_coproc_reg },
+ /* ICC_AP1R2_EL1 */
+ { CRn(12), CRm(9), Op1(0), Op2(2), is32, access_gic_ap1r_coproc_reg },
+ /* ICC_AP1R3_EL1 */
+ { CRn(12), CRm(9), Op1(0), Op2(3), is32, access_gic_ap1r_coproc_reg },
+ /* ICC_BPR1_EL1 */
+ { CRn(12), CRm(12), Op1(0), Op2(3), is32, access_gic_bpr1_coproc_reg },
+ /* ICC_CTLR_EL1 */
+ { CRn(12), CRm(12), Op1(0), Op2(4), is32, access_gic_ctlr_coproc_reg },
+ /* ICC_SRE_EL1 */
+ { CRn(12), CRm(12), Op1(0), Op2(5), is32, access_gic_sre_coproc_reg },
+ /* ICC_IGRPEN0_EL1 */
+ { CRn(12), CRm(12), Op1(0), Op2(6), is32,
+ access_gic_grpen0_coproc_reg },
+ /* ICC_GRPEN1_EL1 */
+ { CRn(12), CRm(12), Op1(0), Op2(7), is32,
+ access_gic_grpen1_coproc_reg },
+};
+
+
+#define KVM_DEV_ARM_VGIC_COPROC_MASK 0x3fff
+
+/* As per Documentation/virtual/kvm/devices/arm-vgic-v3.txt,
+ * CPUREGs id is passed in AArch64 system register encoding format.
+ * Format to COPROC register format for AArch32 mode before using.
+ */
+static u64 vgic_to_cpreg(u64 id)
+{
+ u64 cpreg = 0;
+
+ id = id & KVM_DEV_ARM_VGIC_COPROC_MASK;
+ cpreg = ((id & KVM_REG_ARM_VGIC_SYSREG_OP2_MASK) >>
+ KVM_REG_ARM_VGIC_SYSREG_OP2_SHIFT) <<
+ KVM_REG_ARM_32_OPC2_SHIFT;
+ cpreg |= ((id & KVM_REG_ARM_VGIC_SYSREG_CRM_MASK) >>
+ KVM_REG_ARM_VGIC_SYSREG_CRM_SHIFT) << KVM_REG_ARM_CRM_SHIFT;
+ cpreg |= ((id & KVM_REG_ARM_VGIC_SYSREG_CRN_MASK) >>
+ KVM_REG_ARM_VGIC_SYSREG_CRN_SHIFT) <<
+ KVM_REG_ARM_32_CRN_SHIFT;
+ cpreg |= ((id & KVM_REG_ARM_VGIC_SYSREG_OP1_MASK) >>
+ KVM_REG_ARM_VGIC_SYSREG_OP1_SHIFT) << KVM_REG_ARM_OPC1_SHIFT;
+ id |= (KVM_REG_ARM_COPROC_MASK | KVM_REG_SIZE_U32);
+
+ return cpreg;
+}
+
+int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
+ u64 *reg)
+{
+ struct coproc_params params;
+ u64 cpreg = vgic_to_cpreg(id);
+
+ params.Rt1 = *reg;
+ params.is_write = is_write;
+ params.is_64bit = false;
+
+ if (find_coproc_reg_by_id(cpreg, ¶ms, gic_v3_icc_coproc_reg_descs,
+ ARRAY_SIZE(gic_v3_icc_coproc_reg_descs)))
+ return 0;
+
+ return -ENOENT;
+}
+
+int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id,
+ u64 *reg)
+{
+ struct coproc_params params;
+ const struct coproc_reg *r;
+ u64 cpreg = vgic_to_cpreg(id);
+
+ if (is_write)
+ params.Rt1 = *reg;
+ params.is_write = is_write;
+ params.is_64bit = false;
+
+ r = find_coproc_reg_by_id(cpreg, ¶ms, gic_v3_icc_coproc_reg_descs,
+ ARRAY_SIZE(gic_v3_icc_coproc_reg_descs));
+ if (!r)
+ return -ENXIO;
+
+ if (!r->access(vcpu, ¶ms, r))
+ return -EINVAL;
+
+ if (!is_write)
+ *reg = params.Rt1;
+
+ return 0;
+}