[Xen-devel,v2] xen/arm: gicv2: Export GICv2m register frames to domain0 by device tree

Message ID 1461577174-26553-1-git-send-email-Wei.Chen@linaro.org
State New
Headers show

Commit Message

Wei Chen April 25, 2016, 9:39 a.m.
This patch adds v2m extension support in GIC-v2 driver. The GICv2 driver
detects the MSI frames from device tree and creates corresponding device
tree nodes in Domain0's DTB. It also provides one hw_ops callback to map
v2m MMIO regions to domain0 and route v2m SPIs to domain0.

With this GICv2m extension support, the domain0 kernel can do GICv2m
frame setup and initialization.

This patch is based on the GICv2m patch of Suravee Suthikulpanit:
[PATCH 2/2] xen/arm: gicv2: Adding support for GICv2m in Dom0
http://lists.xen.org/archives/html/xen-devel/2015-04/msg02613.html

Signed-off-by: Wei Chen <Wei.Chen@linaro.org>

---
v2: Address comments from Julien
1. Pull non-DT code in a separate function. 
2. Fix one variable type error.
3. Fix indentation issues.
4. Revise some code comments.

xen/arch/arm/domain_build.c |   5 +
 xen/arch/arm/gic-v2.c       | 287 ++++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic.c          |   9 ++
 xen/include/asm-arm/gic.h   |   3 +
 4 files changed, 304 insertions(+)

Comments

Julien Grall April 26, 2016, 10:49 a.m. | #1
Hello Wei,

On 25/04/2016 10:39, Wei Chen wrote:
> This patch adds v2m extension support in GIC-v2 driver. The GICv2 driver
> detects the MSI frames from device tree and creates corresponding device
> tree nodes in Domain0's DTB. It also provides one hw_ops callback to map

NIT: s/Domain0/dom0/

> v2m MMIO regions to domain0 and route v2m SPIs to domain0.

Ditto.

>
> With this GICv2m extension support, the domain0 kernel can do GICv2m

Ditto

> frame setup and initialization.
>
> This patch is based on the GICv2m patch of Suravee Suthikulpanit:
> [PATCH 2/2] xen/arm: gicv2: Adding support for GICv2m in Dom0
> http://lists.xen.org/archives/html/xen-devel/2015-04/msg02613.html
>
> Signed-off-by: Wei Chen <Wei.Chen@linaro.org>

[...]

> +static int gicv2_map_hwdown_extra_mappings(struct domain *d)
> +{
> +    const struct v2m_data *v2m_data;
> +
> +    /* For the moment, we'll assign all v2m frames to the hardware domain. */
> +    list_for_each_entry( v2m_data, &gicv2m_info, entry )
> +    {
> +        int ret;
> +        u32 spi;
> +
> +        printk("GICv2: Mapping v2m frame to d%d: addr=0x%lx size=0x%lx spi_base=%u num_spis=%u\n",
> +               d->domain_id, v2m_data->addr, v2m_data->size,
> +               v2m_data->spi_start, v2m_data->nr_spis);
> +
> +        ret = map_mmio_regions(d, paddr_to_pfn(v2m_data->addr),
> +                            DIV_ROUND_UP(v2m_data->size, PAGE_SIZE),
> +                            paddr_to_pfn(v2m_data->addr));
> +        if ( ret )
> +        {
> +            printk(XENLOG_ERR "GICv2: Map v2m frame to s%d failed.\n",

s/s%d/d%d/

> +                   d->domain_id);
> +            return ret;
> +        }
> +
> +        /*
> +         * Map all SPIs that are allocated to MSIs for the frame to the
> +         * domain.
> +         */
> +        for ( spi = v2m_data->spi_start;
> +              spi < (v2m_data->spi_start + v2m_data->nr_spis); spi++ )
> +        {
> +            /*
> +             * MSIs are always edge-triggered. Configure the associated SPIs
> +             * to be edge-rising.

How did you find that SPIs should be configured edge-rising?

> +             */
> +            ret = irq_set_spi_type(spi, IRQ_TYPE_EDGE_RISING);
> +            if ( ret )
> +            {
> +                printk(XENLOG_ERR
> +                       "GICv2: Failed to set v2m MSI SPI[%d] type.\n", spi);
> +                return ret;
> +            }
> +
> +            /* Route a SPI that is allocated to MSI to the domain. */
> +            ret = route_irq_to_guest(d, spi, spi, "v2m");
> +            if ( ret )
> +            {
> +                printk(XENLOG_ERR
> +                       "GICv2: Failed to route v2m MSI SPI[%d] to Dom%d.\n",
> +                        spi, d->domain_id);
> +                return ret;
> +            }
> +
> +            /* Reserve a SPI that is allocated to MSI for the domain. */
> +            if ( !vgic_reserve_virq(d, spi) )
> +            {
> +                printk(XENLOG_ERR
> +                       "GICv2: Failed to reserve v2m MSI SPI[%d] for Dom%d.\n",
> +                        spi, d->domain_id);
> +                return -EINVAL;
> +            }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +/*
> + * Set up gic v2m DT sub-node.
> + * Please refer to the binding document:
> + * https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
> + */
> +static int gicv2m_make_dt_node(const struct domain *d,
> +                            const struct dt_device_node *gic,
> +                            void *fdt)

The indentation is still wrong here.

The start of the second and third lines should be aligned to "const..." 
on the first line. I.e

foo(const
____const
____void

Where _ is the empty space.

[...]

> +static void gicv2_add_v2m_frame_to_list(paddr_t addr, paddr_t size,
> +                                        u32 spi_start, u32 nr_spis,
> +                                        const struct dt_device_node *v2m)
> +{
> +    struct v2m_data *v2m_data;
> +
> +    v2m_data = xzalloc_bytes(sizeof(struct v2m_data));
> +    if ( !v2m_data )
> +        panic("GICv2: Cannot allocate memory for v2m frame");
> +
> +    /* Initialize a new entry to record new frame infomation. */

s/infomation/information/

> +    INIT_LIST_HEAD(&v2m_data->entry);
> +    v2m_data->addr = addr;
> +    v2m_data->size = size;
> +    v2m_data->spi_start = spi_start;
> +    v2m_data->nr_spis = nr_spis;
> +    v2m_data->dt_node = v2m;
> +
> +    printk("GICv2m extension register frame:\n"
> +           "        gic_v2m_addr=%"PRIpaddr"\n"
> +           "        gic_v2m_size=%"PRIpaddr"\n"
> +           "        gic_v2m_spi_base=%u\n"
> +           "        gic_v2m_num_spis=%u\n",
> +           v2m_data->addr, v2m_data->size,
> +           v2m_data->spi_start, v2m_data->nr_spis);
> +
> +    list_add_tail(&v2m_data->entry, &gicv2m_info);
> +}
> +
> +static void gicv2_extension_dt_init(const struct dt_device_node *node)
> +{
> +    int res;
> +    u32 spi_start, nr_spis;
> +    paddr_t addr, size;
> +    const struct dt_device_node *v2m = NULL;
> +
> +    /*
> +     * Check whether this GIC implements the v2m extension. If so,
> +     * add v2m register frames to gicv2m_info.
> +     */
> +    dt_for_each_child_node(node, v2m)
> +    {
> +        if ( !dt_device_is_compatible(v2m, "arm,gic-v2m-frame") )
> +            continue;
> +
> +        /*
> +         * Check whether DT uses msi-base-spi and msi-num-spis properties to
> +         * override the hardware setting.
> +         */
> +        if ( !dt_property_read_u32(v2m, "arm,msi-base-spi", &spi_start) ||
> +             !dt_property_read_u32(v2m, "arm,msi-num-spis", &nr_spis) )
> +        {
> +            u32 msi_typer;
> +            void __iomem *base;
> +
> +            /*
> +            * DT does not override the hardware setting, so we have to read
> +            * base_spi and num_spis from hardware registers to reserve irqs.
> +            */

The indentation of the comments if wrong.

It should be:

/*
  * FOo
  * Bar
  */

> +            res = dt_device_get_address(v2m, 0, &addr, &size);
> +            if ( res )
> +                panic("GICv2: Cannot find a valid v2m frame address");
> +
> +            base = ioremap_nocache(addr, size);
> +            if ( !base )
> +                panic("GICv2: Cannot remap v2m register frame");
> +
> +            msi_typer = readl_relaxed(base + V2M_MSI_TYPER);
> +            spi_start = V2M_MSI_TYPER_BASE_SPI(msi_typer);
> +            nr_spis = V2M_MSI_TYPER_NUM_SPI(msi_typer);

The ACPI code will likely need to read those registers. So this could go 
in the new function you have added.

You could pass pointers to your function to know whether the values are 
overridden by the firmware.

> +
> +            iounmap(base);
> +        }
> +
> +        /* Add this v2m frame information to list. */
> +        gicv2_add_v2m_frame_to_list(addr, size, spi_start, nr_spis, v2m);
> +    }
> +}
> +

Regards,
Wei Chen April 27, 2016, 6:05 a.m. | #2
Hi Julien,

On 26 April 2016 at 18:49, Julien Grall <julien.grall@arm.com> wrote:
> Hello Wei,
>
> On 25/04/2016 10:39, Wei Chen wrote:
>>
>> This patch adds v2m extension support in GIC-v2 driver. The GICv2 driver
>> detects the MSI frames from device tree and creates corresponding device
>> tree nodes in Domain0's DTB. It also provides one hw_ops callback to map
>
>
> NIT: s/Domain0/dom0/
>
>> v2m MMIO regions to domain0 and route v2m SPIs to domain0.
>
>
> Ditto.
>
>>
>> With this GICv2m extension support, the domain0 kernel can do GICv2m
>
>
> Ditto
>
>> frame setup and initialization.
>>
>> This patch is based on the GICv2m patch of Suravee Suthikulpanit:
>> [PATCH 2/2] xen/arm: gicv2: Adding support for GICv2m in Dom0
>> http://lists.xen.org/archives/html/xen-devel/2015-04/msg02613.html
>>
>> Signed-off-by: Wei Chen <Wei.Chen@linaro.org>
>
>
> [...]
>
>> +static int gicv2_map_hwdown_extra_mappings(struct domain *d)
>> +{
>> +    const struct v2m_data *v2m_data;
>> +
>> +    /* For the moment, we'll assign all v2m frames to the hardware
>> domain. */
>> +    list_for_each_entry( v2m_data, &gicv2m_info, entry )
>> +    {
>> +        int ret;
>> +        u32 spi;
>> +
>> +        printk("GICv2: Mapping v2m frame to d%d: addr=0x%lx size=0x%lx
>> spi_base=%u num_spis=%u\n",
>> +               d->domain_id, v2m_data->addr, v2m_data->size,
>> +               v2m_data->spi_start, v2m_data->nr_spis);
>> +
>> +        ret = map_mmio_regions(d, paddr_to_pfn(v2m_data->addr),
>> +                            DIV_ROUND_UP(v2m_data->size, PAGE_SIZE),
>> +                            paddr_to_pfn(v2m_data->addr));
>> +        if ( ret )
>> +        {
>> +            printk(XENLOG_ERR "GICv2: Map v2m frame to s%d failed.\n",
>
>
> s/s%d/d%d/
>
>> +                   d->domain_id);
>> +            return ret;
>> +        }
>> +
>> +        /*
>> +         * Map all SPIs that are allocated to MSIs for the frame to the
>> +         * domain.
>> +         */
>> +        for ( spi = v2m_data->spi_start;
>> +              spi < (v2m_data->spi_start + v2m_data->nr_spis); spi++ )
>> +        {
>> +            /*
>> +             * MSIs are always edge-triggered. Configure the associated
>> SPIs
>> +             * to be edge-rising.
>
>
> How did you find that SPIs should be configured edge-rising?
Before route_irq_to_guest, the SPI must be configured. I found Linux
v2m driver set
the SPI type to edge-rising, so I set edge-rising as v2m SPI default
type here too.
>
>
>> +             */
>> +            ret = irq_set_spi_type(spi, IRQ_TYPE_EDGE_RISING);
>> +            if ( ret )
>> +            {
>> +                printk(XENLOG_ERR
>> +                       "GICv2: Failed to set v2m MSI SPI[%d] type.\n",
>> spi);
>> +                return ret;
>> +            }
>> +
>> +            /* Route a SPI that is allocated to MSI to the domain. */
>> +            ret = route_irq_to_guest(d, spi, spi, "v2m");
>> +            if ( ret )
>> +            {
>> +                printk(XENLOG_ERR
>> +                       "GICv2: Failed to route v2m MSI SPI[%d] to
>> Dom%d.\n",
>> +                        spi, d->domain_id);
>> +                return ret;
>> +            }
>> +
>> +            /* Reserve a SPI that is allocated to MSI for the domain. */
>> +            if ( !vgic_reserve_virq(d, spi) )
>> +            {
>> +                printk(XENLOG_ERR
>> +                       "GICv2: Failed to reserve v2m MSI SPI[%d] for
>> Dom%d.\n",
>> +                        spi, d->domain_id);
>> +                return -EINVAL;
>> +            }
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Set up gic v2m DT sub-node.
>> + * Please refer to the binding document:
>> + *
>> https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
>> + */
>> +static int gicv2m_make_dt_node(const struct domain *d,
>> +                            const struct dt_device_node *gic,
>> +                            void *fdt)
>
>
> The indentation is still wrong here.
>
> The start of the second and third lines should be aligned to "const..." on
> the first line. I.e
>
> foo(const
> ____const
> ____void
>
> Where _ is the empty space.
>
> [...]
>

Thanks for your detailed example!

>> +static void gicv2_add_v2m_frame_to_list(paddr_t addr, paddr_t size,
>> +                                        u32 spi_start, u32 nr_spis,
>> +                                        const struct dt_device_node *v2m)
>> +{
>> +    struct v2m_data *v2m_data;
>> +
>> +    v2m_data = xzalloc_bytes(sizeof(struct v2m_data));
>> +    if ( !v2m_data )
>> +        panic("GICv2: Cannot allocate memory for v2m frame");
>> +
>> +    /* Initialize a new entry to record new frame infomation. */
>
>
> s/infomation/information/
>
>
>> +    INIT_LIST_HEAD(&v2m_data->entry);
>> +    v2m_data->addr = addr;
>> +    v2m_data->size = size;
>> +    v2m_data->spi_start = spi_start;
>> +    v2m_data->nr_spis = nr_spis;
>> +    v2m_data->dt_node = v2m;
>> +
>> +    printk("GICv2m extension register frame:\n"
>> +           "        gic_v2m_addr=%"PRIpaddr"\n"
>> +           "        gic_v2m_size=%"PRIpaddr"\n"
>> +           "        gic_v2m_spi_base=%u\n"
>> +           "        gic_v2m_num_spis=%u\n",
>> +           v2m_data->addr, v2m_data->size,
>> +           v2m_data->spi_start, v2m_data->nr_spis);
>> +
>> +    list_add_tail(&v2m_data->entry, &gicv2m_info);
>> +}
>> +
>> +static void gicv2_extension_dt_init(const struct dt_device_node *node)
>> +{
>> +    int res;
>> +    u32 spi_start, nr_spis;
>> +    paddr_t addr, size;
>> +    const struct dt_device_node *v2m = NULL;
>> +
>> +    /*
>> +     * Check whether this GIC implements the v2m extension. If so,
>> +     * add v2m register frames to gicv2m_info.
>> +     */
>> +    dt_for_each_child_node(node, v2m)
>> +    {
>> +        if ( !dt_device_is_compatible(v2m, "arm,gic-v2m-frame") )
>> +            continue;
>> +
>> +        /*
>> +         * Check whether DT uses msi-base-spi and msi-num-spis properties
>> to
>> +         * override the hardware setting.
>> +         */
>> +        if ( !dt_property_read_u32(v2m, "arm,msi-base-spi", &spi_start)
>> ||
>> +             !dt_property_read_u32(v2m, "arm,msi-num-spis", &nr_spis) )
>> +        {
>> +            u32 msi_typer;
>> +            void __iomem *base;
>> +
>> +            /*
>> +            * DT does not override the hardware setting, so we have to
>> read
>> +            * base_spi and num_spis from hardware registers to reserve
>> irqs.
>> +            */
>
>
> The indentation of the comments if wrong.
>
> It should be:
>
> /*
>  * FOo
>  * Bar
>  */
>
>> +            res = dt_device_get_address(v2m, 0, &addr, &size);
>> +            if ( res )
>> +                panic("GICv2: Cannot find a valid v2m frame address");
>> +
>> +            base = ioremap_nocache(addr, size);
>> +            if ( !base )
>> +                panic("GICv2: Cannot remap v2m register frame");
>> +
>> +            msi_typer = readl_relaxed(base + V2M_MSI_TYPER);
>> +            spi_start = V2M_MSI_TYPER_BASE_SPI(msi_typer);
>> +            nr_spis = V2M_MSI_TYPER_NUM_SPI(msi_typer);
>
>
> The ACPI code will likely need to read those registers. So this could go in
> the new function you have added.
>
> You could pass pointers to your function to know whether the values are
> overridden by the firmware.
>
I will refine the new function.
>> +
>> +            iounmap(base);
>> +        }
>> +
>> +        /* Add this v2m frame information to list. */
>> +        gicv2_add_v2m_frame_to_list(addr, size, spi_start, nr_spis, v2m);
>> +    }
>> +}
>> +
>
>
> Regards,
>
> --
> Julien Grall
Julien Grall April 27, 2016, 9:40 a.m. | #3
On 27/04/2016 07:05, Wei Chen wrote:
> Hi Julien,

Hi Wei,

> On 26 April 2016 at 18:49, Julien Grall <julien.grall@arm.com> wrote:
>> On 25/04/2016 10:39, Wei Chen wrote:
>>> +                   d->domain_id);
>>> +            return ret;
>>> +        }
>>> +
>>> +        /*
>>> +         * Map all SPIs that are allocated to MSIs for the frame to the
>>> +         * domain.
>>> +         */
>>> +        for ( spi = v2m_data->spi_start;
>>> +              spi < (v2m_data->spi_start + v2m_data->nr_spis); spi++ )
>>> +        {
>>> +            /*
>>> +             * MSIs are always edge-triggered. Configure the associated
>>> SPIs
>>> +             * to be edge-rising.
>>
>>
>> How did you find that SPIs should be configured edge-rising?
> Before route_irq_to_guest, the SPI must be configured. I found Linux
> v2m driver set
> the SPI type to edge-rising, so I set edge-rising as v2m SPI default
> type here too.

Well, Linux did it for a good reason (i.e based on a spec). We are 
trying in Xen to document piece of code which may not be 
straight-forward to deduce.

In this case, the IRQ is configured edge-rising because this is the only 
edge type supported by SPIs. I would expand the second sentence of the 
comment to explain that.

Regards,
Wei Chen April 28, 2016, 2:08 a.m. | #4
Hi Julien,

On 27 April 2016 at 17:40, Julien Grall <julien.grall@arm.com> wrote:
>
>
> On 27/04/2016 07:05, Wei Chen wrote:
>>
>> Hi Julien,
>
>
> Hi Wei,
>
>> On 26 April 2016 at 18:49, Julien Grall <julien.grall@arm.com> wrote:
>>>
>>> On 25/04/2016 10:39, Wei Chen wrote:
>>>>
>>>> +                   d->domain_id);
>>>> +            return ret;
>>>> +        }
>>>> +
>>>> +        /*
>>>> +         * Map all SPIs that are allocated to MSIs for the frame to the
>>>> +         * domain.
>>>> +         */
>>>> +        for ( spi = v2m_data->spi_start;
>>>> +              spi < (v2m_data->spi_start + v2m_data->nr_spis); spi++ )
>>>> +        {
>>>> +            /*
>>>> +             * MSIs are always edge-triggered. Configure the associated
>>>> SPIs
>>>> +             * to be edge-rising.
>>>
>>>
>>>
>>> How did you find that SPIs should be configured edge-rising?
>>
>> Before route_irq_to_guest, the SPI must be configured. I found Linux
>> v2m driver set
>> the SPI type to edge-rising, so I set edge-rising as v2m SPI default
>> type here too.
>
>
> Well, Linux did it for a good reason (i.e based on a spec). We are trying in
> Xen to document piece of code which may not be straight-forward to deduce.
>
> In this case, the IRQ is configured edge-rising because this is the only
> edge type supported by SPIs. I would expand the second sentence of the
> comment to explain that.
>

Thanks for your explanation. I have addressed your other comments in
the v3 patch.
So I will add the reason of configuring SPIs to edge-rising to v4 patch.

> Regards,
>
> --
> Julien Grall

Patch

diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index 00dc07a..2e4c295 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -2177,6 +2177,11 @@  int construct_dom0(struct domain *d)
     if ( rc < 0 )
         return rc;
 
+    /* Map extra GIC MMIO, irqs and other hw stuffs to dom0. */
+    rc = gic_map_hwdom_extra_mappings(d);
+    if ( rc < 0 )
+        return rc;
+
     rc = platform_specific_mapping(d);
     if ( rc < 0 )
         return rc;
diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
index 450755f..9222544 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -21,6 +21,7 @@ 
 #include <xen/lib.h>
 #include <xen/init.h>
 #include <xen/mm.h>
+#include <xen/vmap.h>
 #include <xen/irq.h>
 #include <xen/iocap.h>
 #include <xen/sched.h>
@@ -66,6 +67,41 @@ 
 #define GICH_V2_VMCR_PRIORITY_MASK   0x1f
 #define GICH_V2_VMCR_PRIORITY_SHIFT  27
 
+/* GICv2m extension register definitions. */
+/*
+* MSI_TYPER:
+*     [31:26] Reserved
+*     [25:16] lowest SPI assigned to MSI
+*     [15:10] Reserved
+*     [9:0]   Number of SPIs assigned to MSI
+*/
+#define V2M_MSI_TYPER               0x008
+#define V2M_MSI_TYPER_BASE_SHIFT    16
+#define V2M_MSI_TYPER_BASE_MASK     0x3FF
+#define V2M_MSI_TYPER_NUM_MASK      0x3FF
+#define V2M_MSI_SETSPI_NS           0x040
+#define V2M_MIN_SPI                 32
+#define V2M_MAX_SPI                 1019
+#define V2M_MSI_IIDR                0xFCC
+
+#define V2M_MSI_TYPER_BASE_SPI(x)   \
+                (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK)
+
+#define V2M_MSI_TYPER_NUM_SPI(x)    ((x) & V2M_MSI_TYPER_NUM_MASK)
+
+struct v2m_data {
+    struct list_head entry;
+    /* Pointer to the DT node representing the v2m frame */
+    const struct dt_device_node *dt_node;
+    paddr_t addr; /* Register frame base */
+    paddr_t size; /* Register frame size */
+    u32 spi_start; /* The SPI number that MSIs start */
+    u32 nr_spis; /* The number of SPIs for MSIs */
+};
+
+/* v2m extension register frame information list */
+static LIST_HEAD(gicv2m_info);
+
 /* Global state */
 static struct {
     void __iomem * map_dbase; /* IO mapped Address of distributor registers */
@@ -551,6 +587,167 @@  static void gicv2_irq_set_affinity(struct irq_desc *desc, const cpumask_t *cpu_m
     spin_unlock(&gicv2.lock);
 }
 
+static int gicv2_map_hwdown_extra_mappings(struct domain *d)
+{
+    const struct v2m_data *v2m_data;
+
+    /* For the moment, we'll assign all v2m frames to the hardware domain. */
+    list_for_each_entry( v2m_data, &gicv2m_info, entry )
+    {
+        int ret;
+        u32 spi;
+
+        printk("GICv2: Mapping v2m frame to d%d: addr=0x%lx size=0x%lx spi_base=%u num_spis=%u\n",
+               d->domain_id, v2m_data->addr, v2m_data->size,
+               v2m_data->spi_start, v2m_data->nr_spis);
+
+        ret = map_mmio_regions(d, paddr_to_pfn(v2m_data->addr),
+                            DIV_ROUND_UP(v2m_data->size, PAGE_SIZE),
+                            paddr_to_pfn(v2m_data->addr));
+        if ( ret )
+        {
+            printk(XENLOG_ERR "GICv2: Map v2m frame to s%d failed.\n",
+                   d->domain_id);
+            return ret;
+        }
+
+        /*
+         * Map all SPIs that are allocated to MSIs for the frame to the
+         * domain.
+         */
+        for ( spi = v2m_data->spi_start;
+              spi < (v2m_data->spi_start + v2m_data->nr_spis); spi++ )
+        {
+            /*
+             * MSIs are always edge-triggered. Configure the associated SPIs
+             * to be edge-rising.
+             */
+            ret = irq_set_spi_type(spi, IRQ_TYPE_EDGE_RISING);
+            if ( ret )
+            {
+                printk(XENLOG_ERR
+                       "GICv2: Failed to set v2m MSI SPI[%d] type.\n", spi);
+                return ret;
+            }
+
+            /* Route a SPI that is allocated to MSI to the domain. */
+            ret = route_irq_to_guest(d, spi, spi, "v2m");
+            if ( ret )
+            {
+                printk(XENLOG_ERR
+                       "GICv2: Failed to route v2m MSI SPI[%d] to Dom%d.\n",
+                        spi, d->domain_id);
+                return ret;
+            }
+
+            /* Reserve a SPI that is allocated to MSI for the domain. */
+            if ( !vgic_reserve_virq(d, spi) )
+            {
+                printk(XENLOG_ERR
+                       "GICv2: Failed to reserve v2m MSI SPI[%d] for Dom%d.\n",
+                        spi, d->domain_id);
+                return -EINVAL;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Set up gic v2m DT sub-node.
+ * Please refer to the binding document:
+ * https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
+ */
+static int gicv2m_make_dt_node(const struct domain *d,
+                            const struct dt_device_node *gic,
+                            void *fdt)
+{
+    u32 len;
+    int res;
+    const void *prop = NULL;
+    const struct dt_device_node *v2m = NULL;
+    const struct v2m_data *v2m_data;
+
+    /* The sub-nodes require the ranges property */
+    prop = dt_get_property(gic, "ranges", &len);
+    if ( !prop )
+    {
+        printk(XENLOG_ERR "Can't find ranges property for the gic node\n");
+        return -FDT_ERR_XEN(ENOENT);
+    }
+
+    res = fdt_property(fdt, "ranges", prop, len);
+    if ( res )
+        return res;
+
+    list_for_each_entry( v2m_data, &gicv2m_info, entry )
+    {
+        v2m = v2m_data->dt_node;
+
+        printk("GICv2: Creating v2m DT node for d%d: addr=0x%lx size=0x%lx spi_base=%u num_spis=%u\n",
+               d->domain_id, v2m_data->addr, v2m_data->size,
+               v2m_data->spi_start, v2m_data->nr_spis);
+
+        res = fdt_begin_node(fdt, v2m->name);
+        if ( res )
+            return res;
+
+        res = fdt_property_string(fdt, "compatible", "arm,gic-v2m-frame");
+        if ( res )
+            return res;
+
+        res = fdt_property(fdt, "msi-controller", NULL, 0);
+        if ( res )
+            return res;
+
+        if ( v2m->phandle )
+        {
+            res = fdt_property_cell(fdt, "phandle", v2m->phandle);
+            if ( res )
+                return res;
+        }
+
+        /* Use the same reg regions as v2m node in host DTB. */
+        prop = dt_get_property(v2m, "reg", &len);
+        if ( !prop )
+        {
+            printk(XENLOG_ERR "GICv2: Can't find v2m reg property.\n");
+            res = -FDT_ERR_XEN(ENOENT);
+            return res;
+        }
+
+        res = fdt_property(fdt, "reg", prop, len);
+        if ( res )
+            return res;
+
+        /*
+         * The properties msi-base-spi and msi-num-spis are used to override
+         * the hardware settings. Therefore it is fine to always write them
+         * in the guest DT.
+         */
+        res = fdt_property_u32(fdt, "arm,msi-base-spi", v2m_data->spi_start);
+        if ( res )
+        {
+            printk(XENLOG_ERR
+                   "GICv2: Failed to create v2m msi-base-spi in Guest DT.\n");
+            return res;
+        }
+
+        res = fdt_property_u32(fdt, "arm,msi-num-spis", v2m_data->nr_spis);
+        if ( res )
+        {
+            printk(XENLOG_ERR
+                   "GICv2: Failed to create v2m msi-num-spis in Guest DT.\n");
+            return res;
+        }
+
+        fdt_end_node(fdt);
+    }
+
+    return res;
+}
+
 static int gicv2_make_hwdom_dt_node(const struct domain *d,
                                     const struct dt_device_node *gic,
                                     void *fdt)
@@ -587,6 +784,10 @@  static int gicv2_make_hwdom_dt_node(const struct domain *d,
     len *= 2;
 
     res = fdt_property(fdt, "reg", regs, len);
+    if ( res )
+        return res;
+
+    res = gicv2m_make_dt_node(d, gic, fdt);
 
     return res;
 }
@@ -632,6 +833,85 @@  static bool_t gicv2_is_aliased(paddr_t cbase, paddr_t csize)
     return ((val_low & 0xfff0fff) == 0x0202043B && val_low == val_high);
 }
 
+static void gicv2_add_v2m_frame_to_list(paddr_t addr, paddr_t size,
+                                        u32 spi_start, u32 nr_spis,
+                                        const struct dt_device_node *v2m)
+{
+    struct v2m_data *v2m_data;
+
+    v2m_data = xzalloc_bytes(sizeof(struct v2m_data));
+    if ( !v2m_data )
+        panic("GICv2: Cannot allocate memory for v2m frame");
+
+    /* Initialize a new entry to record new frame infomation. */
+    INIT_LIST_HEAD(&v2m_data->entry);
+    v2m_data->addr = addr;
+    v2m_data->size = size;
+    v2m_data->spi_start = spi_start;
+    v2m_data->nr_spis = nr_spis;
+    v2m_data->dt_node = v2m;
+
+    printk("GICv2m extension register frame:\n"
+           "        gic_v2m_addr=%"PRIpaddr"\n"
+           "        gic_v2m_size=%"PRIpaddr"\n"
+           "        gic_v2m_spi_base=%u\n"
+           "        gic_v2m_num_spis=%u\n",
+           v2m_data->addr, v2m_data->size,
+           v2m_data->spi_start, v2m_data->nr_spis);
+
+    list_add_tail(&v2m_data->entry, &gicv2m_info);
+}
+
+static void gicv2_extension_dt_init(const struct dt_device_node *node)
+{
+    int res;
+    u32 spi_start, nr_spis;
+    paddr_t addr, size;
+    const struct dt_device_node *v2m = NULL;
+
+    /*
+     * Check whether this GIC implements the v2m extension. If so,
+     * add v2m register frames to gicv2m_info.
+     */
+    dt_for_each_child_node(node, v2m)
+    {
+        if ( !dt_device_is_compatible(v2m, "arm,gic-v2m-frame") )
+            continue;
+
+        /*
+         * Check whether DT uses msi-base-spi and msi-num-spis properties to
+         * override the hardware setting.
+         */
+        if ( !dt_property_read_u32(v2m, "arm,msi-base-spi", &spi_start) ||
+             !dt_property_read_u32(v2m, "arm,msi-num-spis", &nr_spis) )
+        {
+            u32 msi_typer;
+            void __iomem *base;
+
+            /*
+            * DT does not override the hardware setting, so we have to read
+            * base_spi and num_spis from hardware registers to reserve irqs.
+            */
+            res = dt_device_get_address(v2m, 0, &addr, &size);
+            if ( res )
+                panic("GICv2: Cannot find a valid v2m frame address");
+
+            base = ioremap_nocache(addr, size);
+            if ( !base )
+                panic("GICv2: Cannot remap v2m register frame");
+
+            msi_typer = readl_relaxed(base + V2M_MSI_TYPER);
+            spi_start = V2M_MSI_TYPER_BASE_SPI(msi_typer);
+            nr_spis = V2M_MSI_TYPER_NUM_SPI(msi_typer);
+
+            iounmap(base);
+        }
+
+        /* Add this v2m frame information to list. */
+        gicv2_add_v2m_frame_to_list(addr, size, spi_start, nr_spis, v2m);
+    }
+}
+
 static paddr_t __initdata hbase, dbase, cbase, csize, vbase;
 
 static void __init gicv2_dt_init(void)
@@ -683,6 +963,12 @@  static void __init gicv2_dt_init(void)
     if ( csize != vsize )
         panic("GICv2: Sizes of GICC (%#"PRIpaddr") and GICV (%#"PRIpaddr") don't match\n",
                csize, vsize);
+
+    /*
+     * Check whether this GIC implements the v2m extension. If so,
+     * add v2m register frames to gicv2_extension_info.
+     */
+    gicv2_extension_dt_init(node);
 }
 
 static int gicv2_iomem_deny_access(const struct domain *d)
@@ -936,6 +1222,7 @@  const static struct gic_hw_operations gicv2_ops = {
     .read_apr            = gicv2_read_apr,
     .make_hwdom_dt_node  = gicv2_make_hwdom_dt_node,
     .make_hwdom_madt     = gicv2_make_hwdom_madt,
+    .map_hwdom_extra_mappings = gicv2_map_hwdown_extra_mappings,
     .iomem_deny_access   = gicv2_iomem_deny_access,
 };
 
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 2bfe4de..12bb0ab 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -230,6 +230,15 @@  int gic_irq_xlate(const u32 *intspec, unsigned int intsize,
     return 0;
 }
 
+/* Map extra GIC MMIO, irqs and other hw stuffs to the hardware domain. */
+int gic_map_hwdom_extra_mappings(struct domain *d)
+{
+    if ( gic_hw_ops->map_hwdom_extra_mappings )
+        return gic_hw_ops->map_hwdom_extra_mappings(d);
+
+    return 0;
+}
+
 static void __init gic_dt_preinit(void)
 {
     int rc;
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index cd97bb2..836f1ad 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -360,6 +360,8 @@  struct gic_hw_operations {
                               const struct dt_device_node *gic, void *fdt);
     /* Create MADT table for the hardware domain */
     int (*make_hwdom_madt)(const struct domain *d, u32 offset);
+    /* Map extra GIC MMIO, irqs and other hw stuffs to the hardware domain. */
+    int (*map_hwdom_extra_mappings)(struct domain *d);
     /* Deny access to GIC regions */
     int (*iomem_deny_access)(const struct domain *d);
 };
@@ -369,6 +371,7 @@  int gic_make_hwdom_dt_node(const struct domain *d,
                            const struct dt_device_node *gic,
                            void *fdt);
 int gic_make_hwdom_madt(const struct domain *d, u32 offset);
+int gic_map_hwdom_extra_mappings(struct domain *d);
 int gic_iomem_deny_access(const struct domain *d);
 
 #endif /* __ASSEMBLY__ */