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

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

 



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).

>> +       }
>> +
>> +       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