Request the initialization of the PMUv3.  If using the PMUv3 with an in-kernel
 virtual GIC implementation, this must be done after initializing the in-kernel
 irqchip.
+
+
+2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
+Architectures: ARM,ARM64
+
+2.1. ATTRIBUTE: KVM_ARM_VCPU_TIMER_IRQ_VTIMER
+2.2. ATTRIBUTE: KVM_ARM_VCPU_TIMER_IRQ_PTIMER
+Parameters: in kvm_device_attr.addr the address for the timer interrupt is a
+            pointer to an int
+Returns: -EINVAL: Invalid timer interrupt number
+         -EBUSY:  One or more VCPUs has already run
+
+A value describing the architected timer interrupt number when connected to an
+in-kernel virtual GIC.  These must be a PPI (16 <= intid < 32).  Setting the
+attribute overrides the default values (see below).
+
+KVM_ARM_VCPU_TIMER_IRQ_VTIMER: The EL1 virtual timer intid (default: 27)
+KVM_ARM_VCPU_TIMER_IRQ_PTIMER: The EL1 physical timer intid (default: 30)
+
+Setting the same PPI for different timers will prevent the VCPUs from running.
+Setting the interrupt number on a VCPU configures all VCPUs created at that
+time to use the number provided for a given timer, overwriting any previously
+configured values on other VCPUs.  Userspace should configure the interrupt
+numbers on at least one VCPU after creating all VCPUs and before running any
+VCPUs.
 
 #define KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK 0x3ff
 #define VGIC_LEVEL_INFO_LINE_LEVEL     0
 
+/* Device Control API on vcpu fd */
+#define KVM_ARM_VCPU_PMU_V3_CTRL       0
+#define   KVM_ARM_VCPU_PMU_V3_IRQ      0
+#define   KVM_ARM_VCPU_PMU_V3_INIT     1
+#define KVM_ARM_VCPU_TIMER_CTRL                1
+#define   KVM_ARM_VCPU_TIMER_IRQ_VTIMER                0
+#define   KVM_ARM_VCPU_TIMER_IRQ_PTIMER                1
+
 #define   KVM_DEV_ARM_VGIC_CTRL_INIT           0
 #define   KVM_DEV_ARM_ITS_SAVE_TABLES          1
 #define   KVM_DEV_ARM_ITS_RESTORE_TABLES       2
 
        int ret;
 
        switch (attr->group) {
+       case KVM_ARM_VCPU_TIMER_CTRL:
+               ret = kvm_arm_timer_set_attr(vcpu, attr);
+               break;
        default:
                ret = -ENXIO;
                break;
        int ret;
 
        switch (attr->group) {
+       case KVM_ARM_VCPU_TIMER_CTRL:
+               ret = kvm_arm_timer_get_attr(vcpu, attr);
+               break;
        default:
                ret = -ENXIO;
                break;
        int ret;
 
        switch (attr->group) {
+       case KVM_ARM_VCPU_TIMER_CTRL:
+               ret = kvm_arm_timer_has_attr(vcpu, attr);
+               break;
        default:
                ret = -ENXIO;
                break;
 
 #define KVM_ARM_VCPU_PMU_V3_CTRL       0
 #define   KVM_ARM_VCPU_PMU_V3_IRQ      0
 #define   KVM_ARM_VCPU_PMU_V3_INIT     1
+#define KVM_ARM_VCPU_TIMER_CTRL                1
+#define   KVM_ARM_VCPU_TIMER_IRQ_VTIMER                0
+#define   KVM_ARM_VCPU_TIMER_IRQ_PTIMER                1
 
 /* KVM_IRQ_LINE irq field index values */
 #define KVM_ARM_IRQ_TYPE_SHIFT         24
 
        case KVM_ARM_VCPU_PMU_V3_CTRL:
                ret = kvm_arm_pmu_v3_set_attr(vcpu, attr);
                break;
+       case KVM_ARM_VCPU_TIMER_CTRL:
+               ret = kvm_arm_timer_set_attr(vcpu, attr);
+               break;
        default:
                ret = -ENXIO;
                break;
        case KVM_ARM_VCPU_PMU_V3_CTRL:
                ret = kvm_arm_pmu_v3_get_attr(vcpu, attr);
                break;
+       case KVM_ARM_VCPU_TIMER_CTRL:
+               ret = kvm_arm_timer_get_attr(vcpu, attr);
+               break;
        default:
                ret = -ENXIO;
                break;
        case KVM_ARM_VCPU_PMU_V3_CTRL:
                ret = kvm_arm_pmu_v3_has_attr(vcpu, attr);
                break;
+       case KVM_ARM_VCPU_TIMER_CTRL:
+               ret = kvm_arm_timer_has_attr(vcpu, attr);
+               break;
        default:
                ret = -ENXIO;
                break;
 
 u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);
 int kvm_arm_timer_set_reg(struct kvm_vcpu *, u64 regid, u64 value);
 
+int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
+int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
+int kvm_arm_timer_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr);
+
 bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx);
 void kvm_timer_schedule(struct kvm_vcpu *vcpu);
 void kvm_timer_unschedule(struct kvm_vcpu *vcpu);
 
 #include <linux/kvm_host.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/uaccess.h>
 
 #include <clocksource/arm_arch_timer.h>
 #include <asm/arch_timer.h>
        kvm_vgic_unmap_phys_irq(vcpu, vtimer->irq.irq);
 }
 
+static bool timer_irqs_are_valid(struct kvm *kvm)
+{
+       struct kvm_vcpu *vcpu;
+       int vtimer_irq, ptimer_irq;
+       int i;
+
+       vcpu = kvm_get_vcpu(kvm, 0);
+       vtimer_irq = vcpu_vtimer(vcpu)->irq.irq;
+       ptimer_irq = vcpu_ptimer(vcpu)->irq.irq;
+
+       if (vtimer_irq == ptimer_irq)
+               return false;
+
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               if (vcpu_vtimer(vcpu)->irq.irq != vtimer_irq ||
+                   vcpu_ptimer(vcpu)->irq.irq != ptimer_irq)
+                       return false;
+       }
+
+       return true;
+}
+
 int kvm_timer_enable(struct kvm_vcpu *vcpu)
 {
        struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
        if (!vgic_initialized(vcpu->kvm))
                return -ENODEV;
 
+       if (!timer_irqs_are_valid(vcpu->kvm)) {
+               kvm_debug("incorrectly configured timer irqs\n");
+               return -EINVAL;
+       }
+
        /*
         * Find the physical IRQ number corresponding to the host_vtimer_irq
         */
        val |= (CNTHCTL_EL1PCTEN << cnthctl_shift);
        write_sysreg(val, cnthctl_el2);
 }
+
+static void set_timer_irqs(struct kvm *kvm, int vtimer_irq, int ptimer_irq)
+{
+       struct kvm_vcpu *vcpu;
+       int i;
+
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               vcpu_vtimer(vcpu)->irq.irq = vtimer_irq;
+               vcpu_ptimer(vcpu)->irq.irq = ptimer_irq;
+       }
+}
+
+int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
+{
+       int __user *uaddr = (int __user *)(long)attr->addr;
+       struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
+       struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
+       int irq;
+
+       if (!irqchip_in_kernel(vcpu->kvm))
+               return -EINVAL;
+
+       if (get_user(irq, uaddr))
+               return -EFAULT;
+
+       if (!(irq_is_ppi(irq)))
+               return -EINVAL;
+
+       if (vcpu->arch.timer_cpu.enabled)
+               return -EBUSY;
+
+       switch (attr->attr) {
+       case KVM_ARM_VCPU_TIMER_IRQ_VTIMER:
+               set_timer_irqs(vcpu->kvm, irq, ptimer->irq.irq);
+               break;
+       case KVM_ARM_VCPU_TIMER_IRQ_PTIMER:
+               set_timer_irqs(vcpu->kvm, vtimer->irq.irq, irq);
+               break;
+       default:
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
+{
+       int __user *uaddr = (int __user *)(long)attr->addr;
+       struct arch_timer_context *timer;
+       int irq;
+
+       switch (attr->attr) {
+       case KVM_ARM_VCPU_TIMER_IRQ_VTIMER:
+               timer = vcpu_vtimer(vcpu);
+               break;
+       case KVM_ARM_VCPU_TIMER_IRQ_PTIMER:
+               timer = vcpu_ptimer(vcpu);
+               break;
+       default:
+               return -ENXIO;
+       }
+
+       irq = timer->irq.irq;
+       return put_user(irq, uaddr);
+}
+
+int kvm_arm_timer_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
+{
+       switch (attr->attr) {
+       case KVM_ARM_VCPU_TIMER_IRQ_VTIMER:
+       case KVM_ARM_VCPU_TIMER_IRQ_PTIMER:
+               return 0;
+       }
+
+       return -ENXIO;
+}