KVM: arm/arm64: vgic-new: vgic_init: implement kvm_vgic_hyp_init
Implements kvm_vgic_hyp_init and vgic_probe function. This uses the new firmware independent VGIC probing to support both ACPI and DT based systems (code from Marc Zyngier). The vgic_global struct is enriched with new fields populated by those functions. Signed-off-by: Eric Auger <eric.auger@linaro.org> Signed-off-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Tento commit je obsažen v:
123
virt/kvm/arm/vgic/vgic-init.c
Normální soubor
123
virt/kvm/arm/vgic/vgic-init.c
Normální soubor
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 ARM Ltd.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include "vgic.h"
|
||||
|
||||
/* GENERIC PROBE */
|
||||
|
||||
static void vgic_init_maintenance_interrupt(void *info)
|
||||
{
|
||||
enable_percpu_irq(kvm_vgic_global_state.maint_irq, 0);
|
||||
}
|
||||
|
||||
static int vgic_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *cpu)
|
||||
{
|
||||
switch (action) {
|
||||
case CPU_STARTING:
|
||||
case CPU_STARTING_FROZEN:
|
||||
vgic_init_maintenance_interrupt(NULL);
|
||||
break;
|
||||
case CPU_DYING:
|
||||
case CPU_DYING_FROZEN:
|
||||
disable_percpu_irq(kvm_vgic_global_state.maint_irq);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block vgic_cpu_nb = {
|
||||
.notifier_call = vgic_cpu_notify,
|
||||
};
|
||||
|
||||
static irqreturn_t vgic_maintenance_handler(int irq, void *data)
|
||||
{
|
||||
/*
|
||||
* We cannot rely on the vgic maintenance interrupt to be
|
||||
* delivered synchronously. This means we can only use it to
|
||||
* exit the VM, and we perform the handling of EOIed
|
||||
* interrupts on the exit path (see vgic_process_maintenance).
|
||||
*/
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_hyp_init: populates the kvm_vgic_global_state variable
|
||||
* according to the host GIC model. Accordingly calls either
|
||||
* vgic_v2/v3_probe which registers the KVM_DEVICE that can be
|
||||
* instantiated by a guest later on .
|
||||
*/
|
||||
int kvm_vgic_hyp_init(void)
|
||||
{
|
||||
const struct gic_kvm_info *gic_kvm_info;
|
||||
int ret;
|
||||
|
||||
gic_kvm_info = gic_get_kvm_info();
|
||||
if (!gic_kvm_info)
|
||||
return -ENODEV;
|
||||
|
||||
if (!gic_kvm_info->maint_irq) {
|
||||
kvm_err("No vgic maintenance irq\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
switch (gic_kvm_info->type) {
|
||||
case GIC_V2:
|
||||
ret = vgic_v2_probe(gic_kvm_info);
|
||||
break;
|
||||
case GIC_V3:
|
||||
ret = vgic_v3_probe(gic_kvm_info);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
};
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kvm_vgic_global_state.maint_irq = gic_kvm_info->maint_irq;
|
||||
ret = request_percpu_irq(kvm_vgic_global_state.maint_irq,
|
||||
vgic_maintenance_handler,
|
||||
"vgic", kvm_get_running_vcpus());
|
||||
if (ret) {
|
||||
kvm_err("Cannot register interrupt %d\n",
|
||||
kvm_vgic_global_state.maint_irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = __register_cpu_notifier(&vgic_cpu_nb);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register vgic CPU notifier\n");
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1);
|
||||
|
||||
kvm_info("vgic interrupt IRQ%d\n", kvm_vgic_global_state.maint_irq);
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
free_percpu_irq(kvm_vgic_global_state.maint_irq,
|
||||
kvm_get_running_vcpus());
|
||||
return ret;
|
||||
}
|
Odkázat v novém úkolu
Zablokovat Uživatele