diff mbox

[Xen-devel,v2,19/41] arm : acpi Add GIC specific ACPI boot support

Message ID 1431893048-5214-20-git-send-email-parth.dixit@linaro.org
State New
Headers show

Commit Message

Parth Dixit May 17, 2015, 8:03 p.m. UTC
ACPI on Xen hypervisor uses MADT table for proper GIC initialization.
It needs to parse GIC related subtables, collect CPU interface and distributor
addresses and call driver initialization function (which is hardware
abstraction agnostic). In a similar way, FDT initialize GICv2.

Modify MADT table to clear GICH and GICV which are not needed
in Dom0.

NOTE: This commit allow to initialize GICv2 only.

Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Naresh Bhat <naresh.bhat@linaro.org>
Signed-off-by: Parth Dixit <parth.dixit@linaro.org>
---
 xen/arch/arm/gic-v2.c       | 132 +++++++++++++++++++++++++++++++++++++++++-
 xen/arch/arm/gic.c          |  18 +++++-
 xen/drivers/char/arm-uart.c | 136 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 284 insertions(+), 2 deletions(-)
 create mode 100644 xen/drivers/char/arm-uart.c

Comments

Parth Dixit July 5, 2015, 1:13 p.m. UTC | #1
+shannon

On 21 May 2015 at 17:59, Julien Grall <julien.grall@citrix.com> wrote:
> Hi Parth,
>
> On 17/05/15 21:03, Parth Dixit wrote:
>> ACPI on Xen hypervisor uses MADT table for proper GIC initialization.
>> It needs to parse GIC related subtables, collect CPU interface and distributor
>> addresses and call driver initialization function (which is hardware
>> abstraction agnostic). In a similar way, FDT initialize GICv2.
>>
>> Modify MADT table to clear GICH and GICV which are not needed
>> in Dom0.
>>
>> NOTE: This commit allow to initialize GICv2 only.
>>
>> Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
>> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
>> Signed-off-by: Naresh Bhat <naresh.bhat@linaro.org>
>> Signed-off-by: Parth Dixit <parth.dixit@linaro.org>
>> ---
>>  xen/arch/arm/gic-v2.c       | 132 +++++++++++++++++++++++++++++++++++++++++-
>>  xen/arch/arm/gic.c          |  18 +++++-
>>  xen/drivers/char/arm-uart.c | 136 ++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 284 insertions(+), 2 deletions(-)
>>  create mode 100644 xen/drivers/char/arm-uart.c
>>
>> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
>> index 7276951..fcdcd19 100644
>> --- a/xen/arch/arm/gic-v2.c
>> +++ b/xen/arch/arm/gic-v2.c
>> @@ -28,6 +28,8 @@
>>  #include <xen/list.h>
>>  #include <xen/device_tree.h>
>>  #include <xen/libfdt/libfdt.h>
>> +#include <xen/acpi.h>
>> +#include <acpi/actables.h>
>>  #include <asm/p2m.h>
>>  #include <asm/domain.h>
>>  #include <asm/platform.h>
>> @@ -35,6 +37,7 @@
>>
>>  #include <asm/io.h>
>>  #include <asm/gic.h>
>> +#include <asm/acpi.h>
>>
>>  /*
>>   * LR register definitions are GIC v2 specific.
>> @@ -692,9 +695,122 @@ static int __init dt_gicv2_init(void)
>>      return 0;
>>  }
>>
>> +#ifdef CONFIG_ACPI
>> +static int __init
>> +gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
>> +                        const unsigned long end)
>> +{
>> +        struct acpi_madt_generic_interrupt *processor;
>> +
>> +        processor = (struct acpi_madt_generic_interrupt *)header;
>> +
>> +        if (BAD_MADT_ENTRY(processor, end))
>
> if ( ... )
>
>> +                return -EINVAL;
>
> The indentation looks wrong.
>
>> +
>> +        /* Read from APIC table and fill up the GIC variables */
>> +
>> +        gicv2.cbase = processor->base_address;
>> +        gicv2.hbase = processor->gich_base_address;
>> +        gicv2.vbase = processor->gicv_base_address;
>
> This is per processor right? The value should be the same on each CPU as
> we don't support non-banked GIC.
>
> You would have to check that each value is the same on every CPU.
>
>> +        gicv2_info.maintenance_irq = processor->vgic_maintenance_interrupt;
>> +        if( processor->flags & ACPI_MADT_ENABLED )
>
> The check doesn't seem necessary,
>
>> +        {
>> +            if( processor->flags & ACPI_MADT_VGIC )
>> +                set_irq_type(gicv2_info.maintenance_irq, ACPI_IRQ_TYPE_EDGE_BOTH);
>> +            else
>> +                set_irq_type(gicv2_info.maintenance_irq, ACPI_IRQ_TYPE_LEVEL_MASK);
>
> set_irq_type for local IRQ is very expensive. This should be done only one.
>
>> +        }
>> +
>> +        processor->gich_base_address = 0;
>> +        processor->gicv_base_address = 0;
>> +        processor->vgic_maintenance_interrupt = 0;
>> +        clean_dcache_va_range(processor, sizeof(struct acpi_madt_generic_interrupt));
>
> Same remark as for the GTDT and MADT table. This doesn't belong to the
> GIC initialization but DOM0 building.
>
> Furthermore, the number of CPU for domain may be different as long as
> the processor->flags.
>
> Overall, I think this table should be recreated for DOM0.
>
>> +        return 0;
>> +}
>> +
>> +static int __init
>> +gic_acpi_parse_madt_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))
>
> if ( ... )
>
>> +                return -EINVAL;
>
> The indentation looks wrong
>
>> +
>> +        gicv2.dbase = dist->base_address;
>> +
>> +        return 0;
>> +}
>> +
>> +static int __init acpi_gicv2_init(void)
>> +{
>> +    acpi_status status;
>> +    struct acpi_table_header *table;
>> +    u8 checksum;
>> +    int res;
>
> Given the usage of this variable I would rename it to count.
>
>> +
>> +    status = acpi_get_table(ACPI_SIG_MADT, 0, &table);
>
> For a generic ACPI device code it would make sense to pass the ACPI
> table in parameter as you will likely have to retrieve the same table
> within the same class of device (see Serial too).
>
>> +
>> +    if ( ACPI_FAILURE(status) )
>> +    {
>
> Wrong indentation within the block.
>
>> +              const char *msg = acpi_format_exception(status);
>
> Missing blank line
>
>> +              printk("\nFailed to get MADT table, %s\n", msg);
>
> The first "\n" is not necessary.
>
>> +              return 1;
>
> Please return a meaningful error code.
>
>> +    }
>> +
>> +    /* Collect CPU base addresses */
>> +    res = acpi_parse_entries(ACPI_SIG_MADT,
>> +                                sizeof(struct acpi_table_madt),
>> +                                gic_acpi_parse_madt_cpu, table,
>> +                                ACPI_MADT_TYPE_GENERIC_INTERRUPT,
>> +                                0);
>
> Wrong indentation
>
>> +    if ( res < 0 )
>
> 0 means the no GICC which should be throwing an error too. I would
> replace the check by
>
> res <= 0
>
>> +    {
>
>> +        printk("Error during GICC entries parsing\n");
>> +        printk("\nFailed to initialize GIC IRQ controller\n");
>
> One single printk("No valid GICC entries exists\n") would have been enough.
>
>> +        return -EINVAL;
>> +    }
>> +
>> +    /*
>> +     * Find distributor base address. We expect one distributor entry since
>> +     * ACPI 5.0 spec neither support multi-GIC instances nor GIC cascade.
>> +     */
>> +    res = acpi_parse_entries(ACPI_SIG_MADT,
>> +                                sizeof(struct acpi_table_madt),
>> +                                gic_acpi_parse_madt_distributor, table,
>> +                                ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
>> +                                0);
>
> Wrong indentation
>
>> +
>> +    if ( res < 0 )
>
> 0 means no distributor which means the ACPI is not valid. I would
> replace the check by
>
> res <= 0
>
>
> You also need to check that we don't have multiple GICD entry.
>
>> +    {
>> +        printk("Error during GICD entries parsing\n");
>> +        printk("\nFailed to initialize GIC IRQ controller\n");
>
> One single printk is enough.
>
>> +        return -EINVAL;
>> +    }
>> +
>> +    checksum = acpi_tb_checksum(ACPI_CAST_PTR(u8, table), table->length);
>> +    table->checksum -= checksum;
>> +    clean_dcache_va_range(table, sizeof(struct acpi_table_header));
>
> This is coming out of nowhere without any explanation.
>
> I guess this is related to the change in the ACPI table for DOM0, right?
> If so, this should not be in the GIC initialization but in a specific
> DOM0 building code.
>
>> +    return 0;
>> +}
>> +#else
>> +static int __init acpi_gicv2_init(void)
>> +{
>> +/* Should never reach here */
>> +    return -EINVAL;
>> +}
>> +#endif
>> +
>>  static int gicv2_init(void)
>>  {
>> -     dt_gicv2_init();
>> +    if( acpi_disabled )
>> +        dt_gicv2_init();
>> +    else
>> +        acpi_gicv2_init();
>
> acpi_gicv2_init is returning an error code. You should not ignore it.
>
>>
>>      /* TODO: Add check on distributor, cpu size */
>>
>> @@ -790,7 +906,21 @@ DT_DEVICE_START(gicv2, "GICv2", DEVICE_GIC)
>>          .dt_match = gicv2_dt_match,
>>          .init = dt_gicv2_preinit,
>>  DT_DEVICE_END
>
> Missing blank line.
>
>> +#ifdef CONFIG_ACPI
>> +/* Set up the GIC */
>> +static int __init acpi_gicv2_preinit(const void *data)
>> +{
>> +    gicv2_info.hw_version = GIC_V2;
>> +    register_gic_ops(&gicv2_ops);
>> +
>> +    return 0;
>> +}
>>
>> +ACPI_DEVICE_START(agicv2, "GICv2", DEVICE_GIC)
>> +        .class_type = GIC_V2,
>> +        .init = acpi_gicv2_preinit,
>> +ACPI_DEVICE_END
>> +#endif
>>  /*
>>   * Local variables:
>>   * mode: C
>> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
>> index 701c306..e33bfd7 100644
>> --- a/xen/arch/arm/gic.c
>> +++ b/xen/arch/arm/gic.c
>> @@ -27,6 +27,7 @@
>>  #include <xen/softirq.h>
>>  #include <xen/list.h>
>>  #include <xen/device_tree.h>
>> +#include <xen/acpi.h>
>>  #include <asm/p2m.h>
>>  #include <asm/domain.h>
>>  #include <asm/platform.h>
>> @@ -34,6 +35,7 @@
>>  #include <asm/io.h>
>>  #include <asm/gic.h>
>>  #include <asm/vgic.h>
>> +#include <asm/acpi.h>
>>
>>  static void gic_restore_pending_irqs(struct vcpu *v);
>>
>> @@ -261,9 +263,23 @@ void __init dt_gic_preinit(void)
>>      dt_device_set_used_by(node, DOMID_XEN);
>>  }
>>
>> +static void __init acpi_gic_init(void)
>
> This function should be called acpi_gic_preinit as it's used during
> pre-initialization.
>
>> +{
>> +    int rc;
>> +
>> +    /* NOTE: Only one GIC is supported */
>> +    /* hardcode to gic v2 for now */
>> +    rc = acpi_device_init(DEVICE_GIC, NULL, GIC_V2);
>
> This won't work for multiple GIC later. You should not try to use a
> generic implementation just because it's there.
>
> If you don't find a way to deal with genericity, I prefer a call to
> acpi_gicv2_preinit directly.
>
>> +    if ( rc )
>> +        panic("Unable to find compatible GIC in the ACPI table");
>
> This is not true. You check the presence of the GIC ACPI table later.
>
>> +}
>> +
>>  void __init gic_preinit(void)
>>  {
>> -    dt_gic_preinit();
>> +    if( acpi_disabled )
>> +        dt_gic_preinit();
>> +    else
>> +        acpi_gic_init();
>>  }
>>
>>  /* Set up the GIC */
>> diff --git a/xen/drivers/char/arm-uart.c b/xen/drivers/char/arm-uart.c
>> new file mode 100644
>> index 0000000..2603508
>> --- /dev/null
>> +++ b/xen/drivers/char/arm-uart.c
>
> This file doesn't belong to this patch. I will review it when it will be
> placed in the right patch.
>
> Regards,
>
> --
> Julien Grall
diff mbox

Patch

diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
index 7276951..fcdcd19 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -28,6 +28,8 @@ 
 #include <xen/list.h>
 #include <xen/device_tree.h>
 #include <xen/libfdt/libfdt.h>
+#include <xen/acpi.h>
+#include <acpi/actables.h>
 #include <asm/p2m.h>
 #include <asm/domain.h>
 #include <asm/platform.h>
@@ -35,6 +37,7 @@ 
 
 #include <asm/io.h>
 #include <asm/gic.h>
+#include <asm/acpi.h>
 
 /*
  * LR register definitions are GIC v2 specific.
@@ -692,9 +695,122 @@  static int __init dt_gicv2_init(void)
     return 0;
 }
 
+#ifdef CONFIG_ACPI
+static int __init
+gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
+                        const unsigned long end)
+{
+        struct acpi_madt_generic_interrupt *processor;
+
+        processor = (struct acpi_madt_generic_interrupt *)header;
+
+        if (BAD_MADT_ENTRY(processor, end))
+                return -EINVAL;
+
+        /* Read from APIC table and fill up the GIC variables */
+
+        gicv2.cbase = processor->base_address;
+        gicv2.hbase = processor->gich_base_address;
+        gicv2.vbase = processor->gicv_base_address;
+        gicv2_info.maintenance_irq = processor->vgic_maintenance_interrupt;
+        if( processor->flags & ACPI_MADT_ENABLED )
+        {
+            if( processor->flags & ACPI_MADT_VGIC )
+                set_irq_type(gicv2_info.maintenance_irq, ACPI_IRQ_TYPE_EDGE_BOTH);
+            else
+                set_irq_type(gicv2_info.maintenance_irq, ACPI_IRQ_TYPE_LEVEL_MASK);
+        }
+
+        processor->gich_base_address = 0;
+        processor->gicv_base_address = 0;
+        processor->vgic_maintenance_interrupt = 0;
+        clean_dcache_va_range(processor, sizeof(struct acpi_madt_generic_interrupt));
+
+        return 0;
+}
+
+static int __init
+gic_acpi_parse_madt_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;
+
+        gicv2.dbase = dist->base_address;
+
+        return 0;
+}
+
+static int __init acpi_gicv2_init(void)
+{
+    acpi_status status;
+    struct acpi_table_header *table;
+    u8 checksum;
+    int res;
+
+    status = acpi_get_table(ACPI_SIG_MADT, 0, &table);
+
+    if ( ACPI_FAILURE(status) )
+    {
+              const char *msg = acpi_format_exception(status);
+              printk("\nFailed to get MADT table, %s\n", msg);
+              return 1;
+    }
+
+    /* Collect CPU base addresses */
+    res = acpi_parse_entries(ACPI_SIG_MADT,
+                                sizeof(struct acpi_table_madt),
+                                gic_acpi_parse_madt_cpu, table,
+                                ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+                                0);
+    if ( res < 0 )
+    {
+        printk("Error during GICC entries parsing\n");
+        printk("\nFailed to initialize GIC IRQ controller\n");
+        return -EINVAL;
+    }
+
+    /*
+     * Find distributor base address. We expect one distributor entry since
+     * ACPI 5.0 spec neither support multi-GIC instances nor GIC cascade.
+     */
+    res = acpi_parse_entries(ACPI_SIG_MADT,
+                                sizeof(struct acpi_table_madt),
+                                gic_acpi_parse_madt_distributor, table,
+                                ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+                                0);
+
+    if ( res < 0 )
+    {
+        printk("Error during GICD entries parsing\n");
+        printk("\nFailed to initialize GIC IRQ controller\n");
+        return -EINVAL;
+    }
+
+    checksum = acpi_tb_checksum(ACPI_CAST_PTR(u8, table), table->length);
+    table->checksum -= checksum;
+    clean_dcache_va_range(table, sizeof(struct acpi_table_header));
+
+    return 0;
+}
+#else
+static int __init acpi_gicv2_init(void)
+{
+/* Should never reach here */
+    return -EINVAL;
+}
+#endif
+
 static int gicv2_init(void)
 {
-     dt_gicv2_init();
+    if( acpi_disabled )
+        dt_gicv2_init();
+    else
+        acpi_gicv2_init();
 
     /* TODO: Add check on distributor, cpu size */
 
@@ -790,7 +906,21 @@  DT_DEVICE_START(gicv2, "GICv2", DEVICE_GIC)
         .dt_match = gicv2_dt_match,
         .init = dt_gicv2_preinit,
 DT_DEVICE_END
+#ifdef CONFIG_ACPI
+/* Set up the GIC */
+static int __init acpi_gicv2_preinit(const void *data)
+{
+    gicv2_info.hw_version = GIC_V2;
+    register_gic_ops(&gicv2_ops);
+
+    return 0;
+}
 
+ACPI_DEVICE_START(agicv2, "GICv2", DEVICE_GIC)
+        .class_type = GIC_V2,
+        .init = acpi_gicv2_preinit,
+ACPI_DEVICE_END
+#endif
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 701c306..e33bfd7 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -27,6 +27,7 @@ 
 #include <xen/softirq.h>
 #include <xen/list.h>
 #include <xen/device_tree.h>
+#include <xen/acpi.h>
 #include <asm/p2m.h>
 #include <asm/domain.h>
 #include <asm/platform.h>
@@ -34,6 +35,7 @@ 
 #include <asm/io.h>
 #include <asm/gic.h>
 #include <asm/vgic.h>
+#include <asm/acpi.h>
 
 static void gic_restore_pending_irqs(struct vcpu *v);
 
@@ -261,9 +263,23 @@  void __init dt_gic_preinit(void)
     dt_device_set_used_by(node, DOMID_XEN);
 }
 
+static void __init acpi_gic_init(void)
+{
+    int rc;
+
+    /* NOTE: Only one GIC is supported */
+    /* hardcode to gic v2 for now */
+    rc = acpi_device_init(DEVICE_GIC, NULL, GIC_V2);
+    if ( rc )
+        panic("Unable to find compatible GIC in the ACPI table");
+}
+
 void __init gic_preinit(void)
 {
-    dt_gic_preinit();
+    if( acpi_disabled )
+        dt_gic_preinit();
+    else
+        acpi_gic_init();
 }
 
 /* Set up the GIC */
diff --git a/xen/drivers/char/arm-uart.c b/xen/drivers/char/arm-uart.c
new file mode 100644
index 0000000..2603508
--- /dev/null
+++ b/xen/drivers/char/arm-uart.c
@@ -0,0 +1,136 @@ 
+/*
+ * xen/drivers/char/dt-uart.c
+ *
+ * Generic uart retrieved via the device tree
+ *
+ * Julien Grall <julien.grall@linaro.org>
+ * Copyright (c) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <asm/device.h>
+#include <asm/types.h>
+#include <xen/console.h>
+#include <xen/device_tree.h>
+#include <xen/serial.h>
+#include <xen/errno.h>
+#include <xen/acpi.h>
+
+/*
+ * Configure UART port with a string:
+ * path:options
+ *
+ * @path: full path used in the device tree for the UART. If the path
+ * doesn't start with '/', we assuming that it's an alias.
+ * @options: UART speficic options (see in each UART driver)
+ */
+static char __initdata opt_dtuart[256] = "";
+string_param("dtuart", opt_dtuart);
+
+static void __init dt_uart_init(void)
+{
+    struct dt_device_node *dev;
+    int ret;
+    const char *devpath = opt_dtuart;
+    char *options;
+
+    if ( !console_has("dtuart") )
+        return; /* Not for us */
+
+    if ( !strcmp(opt_dtuart, "") )
+    {
+        const struct dt_device_node *chosen = dt_find_node_by_path("/chosen");
+
+        if ( chosen )
+        {
+            const char *stdout;
+
+            ret = dt_property_read_string(chosen, "stdout-path", &stdout);
+            if ( ret >= 0 )
+            {
+                printk("Taking dtuart configuration from /chosen/stdout-path\n");
+                if ( strlcpy(opt_dtuart, stdout, sizeof(opt_dtuart))
+                     >= sizeof(opt_dtuart) )
+                    printk("WARNING: /chosen/stdout-path too long, truncated\n");
+            }
+            else if ( ret != -EINVAL /* Not present */ )
+                printk("Failed to read /chosen/stdout-path (%d)\n", ret);
+        }
+    }
+
+    if ( !strcmp(opt_dtuart, "") )
+    {
+        printk("No dtuart path configured\n");
+        return;
+    }
+
+    options = strchr(opt_dtuart, ':');
+    if ( options != NULL )
+        *(options++) = '\0';
+    else
+        options = "";
+
+    printk("Looking for dtuart at \"%s\", options \"%s\"\n", devpath, options);
+    if ( *devpath == '/' )
+        dev = dt_find_node_by_path(devpath);
+    else
+        dev = dt_find_node_by_alias(devpath);
+
+    if ( !dev )
+    {
+        printk("Unable to find device \"%s\"\n", devpath);
+        return;
+    }
+
+    ret = device_init(dev, DEVICE_SERIAL, options);
+
+    if ( ret )
+        printk("Unable to initialize dtuart: %d\n", ret);
+}
+
+static void __init acpi_uart_init(void)
+{
+#ifdef CONFIG_ACPI
+    struct acpi_table_spcr *spcr=NULL;
+    int ret;
+
+    acpi_get_table(ACPI_SIG_SPCR, 0,(struct acpi_table_header **)&spcr);
+
+    if ( spcr == NULL )
+        printk("Unable to get spcr table\n");
+    else
+    {
+        ret = acpi_device_init(DEVICE_SERIAL, NULL, spcr->interface_type);
+
+        if ( ret )
+            printk("Unable to initialize acpi uart: %d\n", ret);
+    }
+#endif
+}
+
+void __init uart_init(void)
+{
+    if ( acpi_disabled )
+        dt_uart_init();
+    else
+        acpi_uart_init();
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */