aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei Huang <wei@redhat.com>2015-05-28 01:34:32 -0400
committerGraeme Gregory <graeme.gregory@linaro.org>2016-01-22 12:05:20 +0000
commit27179b26033c041b792a9c7747b8ce3f98e9d29e (patch)
treee3da3657705652816d13958196e4d43d735a9dca
parent1a9bca52725ea931acb1aa3d8362441318d4a66c (diff)
downloadleg-kernel-27179b26033c041b792a9c7747b8ce3f98e9d29e.tar.gz
kvm: arm64: Detect GIC version for proper ACPI vGIC probing
There are two GICs (GICv2 and GICv3) supported by KVM. So it is necessary to find out GIC version before calling ACPI probing functions defined in vgic-v2.c and vgic-v3.c. This patch detects GIC version by checking gic_version field of GIC distributor, which was defined since ACPI 6.0. In case of ACPI 5.1, we use manual hardware discovery to find out GIC version. NOTE: This patch is based on a recent patch by Hanjun Guo. Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org> Signed-off-by: Wei Huang <wei@redhat.com> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
-rw-r--r--include/kvm/arm_vgic.h18
-rw-r--r--virt/kvm/arm/vgic-v2.c10
-rw-r--r--virt/kvm/arm/vgic-v3.c10
-rw-r--r--virt/kvm/arm/vgic.c100
4 files changed, 137 insertions, 1 deletions
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 8f5a40fcc8f8..1798b483dc08 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -24,6 +24,7 @@
#include <linux/irqreturn.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+#include <linux/acpi.h>
#include <kvm/iodev.h>
#define VGIC_NR_IRQS_LEGACY 256
@@ -354,10 +355,18 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
int vgic_v2_dt_probe(struct device_node *vgic_node,
const struct vgic_ops **ops,
const struct vgic_params **params);
+#ifdef CONFIG_ACPI
+int vgic_v2_acpi_probe(struct acpi_madt_generic_interrupt *,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
+#endif /* CONFIG_ACPI */
#ifdef CONFIG_KVM_ARM_VGIC_V3
int vgic_v3_dt_probe(struct device_node *vgic_node,
const struct vgic_ops **ops,
const struct vgic_params **params);
+int vgic_v3_acpi_probe(struct acpi_madt_generic_interrupt *,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
#else
static inline int vgic_v3_dt_probe(struct device_node *vgic_node,
const struct vgic_ops **ops,
@@ -365,6 +374,15 @@ static inline int vgic_v3_dt_probe(struct device_node *vgic_node,
{
return -ENODEV;
}
+
+#ifdef CONFIG_ACPI
+int vgic_v3_acpi_probe(struct acpi_madt_generic_interrupt *vgic_acpi,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_ACPI */
#endif
#endif
diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c
index a016087b1da3..9cebf257638a 100644
--- a/virt/kvm/arm/vgic-v2.c
+++ b/virt/kvm/arm/vgic-v2.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#include <linux/irqchip/arm-gic.h>
@@ -267,3 +268,12 @@ out:
of_node_put(vgic_node);
return ret;
}
+
+#ifdef CONFIG_ACPI
+int vgic_v2_acpi_probe(struct acpi_madt_generic_interrupt *vgic_acpi,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_ACPI */
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
index 667a94364e54..ce6f391bb3a6 100644
--- a/virt/kvm/arm/vgic-v3.c
+++ b/virt/kvm/arm/vgic-v3.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#include <linux/irqchip/arm-gic-v3.h>
@@ -296,3 +297,12 @@ out:
of_node_put(vgic_node);
return ret;
}
+
+#ifdef CONFIG_ACPI
+int vgic_v3_acpi_probe(struct acpi_madt_generic_interrupt *vgic_acpi,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_ACPI */
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index acbf9d7241f1..4841bc2967df 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -34,6 +34,7 @@
#include <trace/events/kvm.h>
#include <asm/kvm.h>
#include <kvm/iodev.h>
+#include <linux/irqchip/arm-gic-v3.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -2416,9 +2417,106 @@ static int kvm_vgic_dt_probe(void)
}
#ifdef CONFIG_ACPI
+u8 gic_version = ACPI_MADT_GIC_VERSION_NONE;
+phys_addr_t dist_phy_base;
+static struct acpi_madt_generic_interrupt *vgic_acpi;
+
+static void gic_get_acpi_header(struct acpi_subtable_header *header)
+{
+ vgic_acpi = (struct acpi_madt_generic_interrupt *)header;
+}
+
+static int gic_parse_distributor(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_distributor *dist;
+
+ dist = (struct acpi_madt_generic_distributor *)header;
+
+ if (BAD_MADT_ENTRY(dist, end))
+ return -EINVAL;
+
+ gic_version = dist->version;
+ dist_phy_base = dist->base_address;
+
+ return 0;
+}
+
+static int gic_match_redist(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ return 0;
+}
+
+static bool gic_redist_is_present(void)
+{
+ int count;
+
+ /* scan MADT table to find if we have redistributor entries */
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
+ gic_match_redist, 0);
+
+ return (count > 0) ? true : false;
+}
+
static int kvm_vgic_acpi_probe(void)
{
- return -EINVAL;
+ u32 reg;
+ int count;
+ void __iomem *dist_base;
+ int ret;
+
+ /* MADT table */
+ ret = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ (acpi_tbl_entry_handler)gic_get_acpi_header, 0);
+ if (!ret) {
+ pr_err("Failed to get MADT VGIC CPU entry\n");
+ return -ENODEV;
+ }
+
+ /* detect GIC version */
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ gic_parse_distributor, 0);
+ if (count <= 0) {
+ pr_err("No valid GIC distributor entry exists\n");
+ return -ENODEV;
+ }
+ if (gic_version >= ACPI_MADT_GIC_VERSION_RESERVED) {
+ pr_err("Invalid GIC version %d in MADT\n", gic_version);
+ return -EINVAL;
+ }
+
+ /* falls back to manual hardware discovery under ACPI 5.1 */
+ if (gic_version == ACPI_MADT_GIC_VERSION_NONE) {
+ if (gic_redist_is_present()) {
+ dist_base = ioremap(dist_phy_base, SZ_64K);
+ if (!dist_base)
+ return -ENOMEM;
+
+ reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
+ if (reg == GIC_PIDR2_ARCH_GICv3)
+ gic_version = ACPI_MADT_GIC_VERSION_V3;
+ else
+ gic_version = ACPI_MADT_GIC_VERSION_V4;
+
+ iounmap(dist_base);
+ } else {
+ gic_version = ACPI_MADT_GIC_VERSION_V2;
+ }
+ }
+
+ switch (gic_version) {
+ case ACPI_MADT_GIC_VERSION_V2:
+ ret = vgic_v2_acpi_probe(vgic_acpi, &vgic_ops, &vgic);
+ break;
+ case ACPI_MADT_GIC_VERSION_V3:
+ ret = vgic_v3_acpi_probe(vgic_acpi, &vgic_ops, &vgic);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+
+ return ret;
}
#endif /* CONFIG_ACPI */