Re: [PATCH v2 08/11] ARM: KVM: VGIC initialisation code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Aug 20, 2012 at 9:11 AM, Marc Zyngier <marc.zyngier@xxxxxxx> wrote:
> On 18/08/12 20:26, Christoffer Dall wrote:
>> On Thu, Jul 5, 2012 at 11:28 AM, Marc Zyngier <marc.zyngier@xxxxxxx> wrote:
>>> Add the init code for the hypervisor, the virtual machine, and
>>> the virtual CPUs.
>>>
>>> An interrupt handler is also wired to allow the VGIC maintainance
>>> interrupts, used to deal with level triggered interrupts and LR
>>> underflows.
>>>
>>> Signed-off-by: Marc Zyngier <marc.zyngier@xxxxxxx>
>>> ---
>>>  arch/arm/include/asm/kvm_vgic.h |    3 +
>>>  arch/arm/kvm/arm.c              |    8 +-
>>>  arch/arm/kvm/vgic.c             |  192 ++++++++++++++++++++++++++++++++++++++-
>>>  3 files changed, 197 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
>>> index 48c27da..450e74f 100644
>>> --- a/arch/arm/include/asm/kvm_vgic.h
>>> +++ b/arch/arm/include/asm/kvm_vgic.h
>>> @@ -218,6 +218,9 @@ struct kvm_run;
>>>  struct kvm_exit_mmio;
>>>
>>>  #ifdef CONFIG_KVM_ARM_VGIC
>>> +int kvm_vgic_hyp_init(void);
>>> +int kvm_vgic_init(struct kvm *kvm);
>>> +void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
>>>  void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
>>>  void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
>>>  int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq);
>>> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
>>> index 7b27a4e..1e94ec1 100644
>>> --- a/arch/arm/kvm/arm.c
>>> +++ b/arch/arm/kvm/arm.c
>>> @@ -54,6 +54,8 @@ static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
>>>  static u8 kvm_next_vmid;
>>>  DEFINE_SPINLOCK(kvm_vmid_lock);
>>>
>>> +static bool vgic_present;
>>> +
>>>  static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
>>>  {
>>>         BUG_ON(preemptible());
>>> @@ -179,6 +181,8 @@ int kvm_dev_ioctl_check_extension(long ext)
>>>         switch (ext) {
>>>  #ifdef CONFIG_KVM_ARM_VGIC
>>>         case KVM_CAP_IRQCHIP:
>>> +               r = vgic_present;
>>> +               break;
>>>  #endif
>>>         case KVM_CAP_USER_MEMORY:
>>>         case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
>>> @@ -861,8 +865,8 @@ static int init_hyp_mode(void)
>>>          * Init HYP view of VGIC
>>>          */
>>>         err = kvm_vgic_hyp_init();
>>> -       if (err)
>>> -               goto out_free_mappings;
>>> +       if (!err)
>>> +               vgic_present = true;
>>>
>>>         /*
>>>          * Set the HVBAR to the virtual kernel address
>>> diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
>>> index 4d5d23a..f184e97 100644
>>> --- a/arch/arm/kvm/vgic.c
>>> +++ b/arch/arm/kvm/vgic.c
>>> @@ -20,7 +20,14 @@
>>>  #include <linux/kvm_host.h>
>>>  #include <linux/interrupt.h>
>>>  #include <linux/io.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/of_irq.h>
>>> +
>>>  #include <asm/kvm_emulate.h>
>>> +#include <asm/hardware/gic.h>
>>> +#include <asm/kvm_arm.h>
>>> +#include <asm/kvm_mmu.h>
>>>
>>>  /*
>>>   * How the whole thing works (courtesy of Christoffer Dall):
>>> @@ -58,6 +65,13 @@
>>>  /* Temporary hacks, need to be provided by userspace emulation */
>>>  #define VGIC_DIST_BASE         0x2c001000
>>>  #define VGIC_DIST_SIZE         0x1000
>>> +#define VGIC_CPU_BASE          0x2c002000
>>> +#define VGIC_CPU_SIZE          0x2000
>>> +
>>> +/* Virtual control interface base address */
>>> +static void __iomem *vgic_vctrl_base;
>>> +
>>> +static struct device_node *vgic_node;
>>>
>>>  #define ACCESS_READ_VALUE      (1 << 0)
>>>  #define ACCESS_READ_RAZ                (0 << 0)
>>> @@ -771,8 +785,6 @@ void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
>>>         spin_lock(&dist->lock);
>>>         __kvm_vgic_sync_to_cpu(vcpu);
>>>         spin_unlock(&dist->lock);
>>> -
>>> -       *__this_cpu_ptr(vgic_vcpus) = vcpu;
>>>  }
>>>
>>>  void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
>>> @@ -785,8 +797,6 @@ void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
>>>         spin_lock(&dist->lock);
>>>         __kvm_vgic_sync_from_cpu(vcpu);
>>>         spin_unlock(&dist->lock);
>>> -
>>> -       *__this_cpu_ptr(vgic_vcpus) = NULL;
>>>  }
>>>
>>>  int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
>>> @@ -851,3 +861,177 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *
>>>
>>>         return 0;
>>>  }
>>> +
>>> +static irqreturn_t vgic_maintainance_handler(int irq, void *data)
>>> +{
>>> +       struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)data;
>>> +       struct vgic_dist *dist;
>>> +       struct vgic_cpu *vgic_cpu;
>>> +
>>> +       if (WARN(!vcpu,
>>> +                "VGIC interrupt on CPU %d with no vcpu\n", smp_processor_id()))
>>> +               return IRQ_HANDLED;
>>> +
>>> +       vgic_cpu = &vcpu->arch.vgic_cpu;
>>> +       dist = &vcpu->kvm->arch.vgic;
>>> +       kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr);
>>> +
>>> +       if (vgic_cpu->vgic_misr & VGIC_MISR_EOI) {
>>> +               /*
>>> +                * Some level interrupts have been EOIed. Clear their
>>> +                * active bit.
>>> +                */
>>> +               int lr, irq;
>>> +
>>> +               spin_lock(&dist->lock);
>>> +               for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr,
>>> +                                vgic_cpu->nr_lr) {
>>> +                       irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
>>> +
>>> +                       vgic_bitmap_set_irq_val(&dist->irq_active,
>>> +                                               vcpu->vcpu_id, irq, 0);
>>> +                       vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
>>> +                       writel_relaxed(vgic_cpu->vgic_lr[lr],
>>> +                                      dist->vctrl_base + GICH_LR0 + (lr << 2));
>>> +               }
>>> +               spin_unlock(&dist->lock);
>>> +       }
>>> +
>>> +       if (vgic_cpu->vgic_misr & VGIC_MISR_U) {
>>> +               vgic_cpu->vgic_hcr &= ~VGIC_HCR_UIE;
>>> +               writel_relaxed(vgic_cpu->vgic_hcr, dist->vctrl_base + GICH_HCR);
>>
>> don't we want to check if something is pending from the distributor
>> here and if so, kick the vcpus executing?
>
> We look at the dist state in kvm_vgic_sync_from_cpu() already, no need
> to do this in the interrupt handler (doesn't cover all cases).
>

ok, I see. thanks.

>>> +       }
>>> +
>>> +       return IRQ_HANDLED;
>>> +}
>>> +
>>> +void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
>>> +{
>>> +       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
>>> +       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
>>> +       u32 reg;
>>> +       int i;
>>> +
>>> +       if (!irqchip_in_kernel(vcpu->kvm))
>>> +               return;
>>> +
>>> +       for (i = 0; i < VGIC_NR_IRQS; i++) {
>>> +               if (i < 16)
>>> +                       vgic_bitmap_set_irq_val(&dist->irq_enabled,
>>> +                                               vcpu->vcpu_id, i, 1);
>>> +               if (i < 32)
>>> +                       vgic_bitmap_set_irq_val(&dist->irq_cfg,
>>> +                                               vcpu->vcpu_id, i, 1);
>>> +
>>> +               vgic_cpu->vgic_irq_lr_map[i] = LR_EMPTY;
>>> +       }
>>> +
>>> +       BUG_ON(!vcpu->kvm->arch.vgic.vctrl_base);
>>> +       reg = readl_relaxed(vcpu->kvm->arch.vgic.vctrl_base + GICH_VTR);
>>> +       vgic_cpu->nr_lr = (reg & 0x1f) + 1;
>>> +
>>> +       reg = readl_relaxed(vcpu->kvm->arch.vgic.vctrl_base + GICH_VMCR);
>>> +       vgic_cpu->vgic_vmcr = reg | (0x1f << 27); /* Priority */
>>> +
>>> +       vgic_cpu->vgic_hcr |= VGIC_HCR_EN; /* Get the show on the road... */
>>> +}
>>> +
>>> +static void vgic_init_maintainance_interrupt(void *info)
>>> +{
>>> +       unsigned int *irqp = info;
>>> +
>>> +       enable_percpu_irq(*irqp, 0);
>>> +}
>>> +
>>> +int kvm_vgic_hyp_init(void)
>>> +{
>>> +       int ret;
>>> +       unsigned int irq;
>>> +       struct resource vctrl_res;
>>> +
>>> +       vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic");
>>> +       if (!vgic_node)
>>> +               return -ENODEV;
>>> +
>>> +       irq = irq_of_parse_and_map(vgic_node, 0);
>>> +       if (!irq)
>>> +               return -ENXIO;
>>> +
>>> +       ret = request_percpu_irq(irq, vgic_maintainance_handler,
>>> +                                "vgic", kvm_get_running_vcpus());
>>> +       if (ret) {
>>> +               kvm_err("Cannot register interrupt %d\n", irq);
>>> +               return ret;
>>> +       }
>>> +
>>> +       ret = of_address_to_resource(vgic_node, 2, &vctrl_res);
>>> +       if (ret) {
>>> +               kvm_err("Cannot obtain VCTRL resource\n");
>>> +               goto out_free_irq;
>>> +       }
>>> +
>>> +       vgic_vctrl_base = of_iomap(vgic_node, 2);
>>> +       if (!vgic_vctrl_base) {
>>> +               kvm_err("Cannot ioremap VCTRL\n");
>>> +               ret = -ENOMEM;
>>> +               goto out_free_irq;
>>> +       }
>>> +
>>> +       ret = create_hyp_io_mappings(vgic_vctrl_base,
>>> +                                    vgic_vctrl_base + resource_size(&vctrl_res),
>>> +                                    vctrl_res.start);
>>> +       if (ret) {
>>> +               kvm_err("Cannot map VCTRL into hyp\n");
>>> +               goto out_unmap;
>>> +       }
>>> +
>>> +       kvm_info("%s@%llx IRQ%d\n", vgic_node->name, vctrl_res.start, irq);
>>> +       on_each_cpu(vgic_init_maintainance_interrupt, &irq, 1);
>>> +
>>> +       return 0;
>>> +
>>> +out_unmap:
>>> +       iounmap(vgic_vctrl_base);
>>> +out_free_irq:
>>> +       free_percpu_irq(irq, kvm_get_running_vcpus());
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +int kvm_vgic_init(struct kvm *kvm)
>>> +{
>>> +       int ret, i;
>>> +       struct resource vcpu_res;
>>> +
>>> +       mutex_lock(&kvm->lock);
>>> +
>>> +       if (of_address_to_resource(vgic_node, 3, &vcpu_res)) {
>>> +               kvm_err("Cannot obtain VCPU resource\n");
>>> +               ret = -ENXIO;
>>> +               goto out;
>>> +       }
>>> +
>>> +       if (atomic_read(&kvm->online_vcpus) || kvm->arch.vgic.vctrl_base) {
>>> +               ret = -EEXIST;
>>> +               goto out;
>>> +       }
>>> +
>>> +       spin_lock_init(&kvm->arch.vgic.lock);
>>> +       kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
>>> +       kvm->arch.vgic.vgic_dist_base = VGIC_DIST_BASE;
>>> +       kvm->arch.vgic.vgic_dist_size = VGIC_DIST_SIZE;
>>> +
>>> +       ret = kvm_phys_addr_ioremap(kvm, VGIC_CPU_BASE,
>>> +                                   vcpu_res.start, VGIC_CPU_SIZE);
>>> +       if (ret) {
>>> +               kvm_err("Unable to remap VGIC CPU to VCPU\n");
>>> +               goto out;
>>> +       }
>>> +
>>> +       for (i = 32; i < VGIC_NR_IRQS; i += 4)
>>> +               vgic_set_target_reg(kvm, 0, i);
>>> +
>>> +out:
>>> +       mutex_unlock(&kvm->lock);
>>> +       return ret;
>>> +}
>>> --
>>> 1.7.10.3
>>>
>>>
>>
>> looks pretty good.
>>
>
>
> --
> Jazz is not dead. It just smells funny...
>
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm


[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux