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