All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation
@ 2016-12-22 18:24 Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 01/26] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
                   ` (27 more replies)
  0 siblings, 28 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Hi,

this is a reworked version of the Dom0 GICv3-ITS emulation series.
This is still not fully where I want it and has some loose bits and
pieces still, but since there are significant changes in the architecture
I wanted to have an opinion before going ahead and replacing every single
number with a named constant ;-) If that smells like a "send out before
the end of the year", you are spot on.

This series introduces ARM GICv3 ITS emulation, for now restricted to
Dom0 only. The ITS is an interrupt controller widget providing a
sophisticated way to deal with MSIs in a scalable manner.
For hardware which relies on the ITS to provide interrupts for its
peripherals this code is needed to get a machine booted into Dom0 at all.
ITS emulation for DomUs is only really useful with PCI passthrough,
which is not yet available for ARM. It is expected that this feature
will be co-developed with the ITS DomU code. However this code drop here
considered DomU emulation already, to keep later architectural changes
to a minimum.

Some generic design principles:

* The current GIC code statically allocates structures for each supported
IRQ (both for the host and the guest), which due to the potentially
millions of LPI interrupts is not feasible to copy for the ITS.
So we refrain from introducing the ITS as a first class Xen interrupt
controller, also we don't hold struct irq_desc's or struct pending_irq's
for each possible LPI.
Fortunately LPIs are only interesting to guests, so we get away with
storing only the virtual IRQ number and the guest VCPU for each allocated
host LPI, which can be stashed into one uint64_t. This data is stored in
a two-level table, which is both memory efficient and quick to access.
We hook into the existing IRQ handling and VGIC code to avoid accessing
the normal structures, providing alternative methods for getting the
needed information (priority, is enabled?) for LPIs.
For interrupts which are queued to or are actually in a guest we
allocate struct pending_irq's on demand. As it is expected that only a
very small number of interrupts is ever on a VCPU at the same time, this
seems like the best approach. For now allocated structs are re-used and
held in a linked list.

* On the guest side we (later will) have to deal with malicious guests
trying to hog Xen with mapping requests for a lot of LPIs, for instance.
As the ITS actually uses system memory for storing status information,
we use this memory (which the guest has to provide) to naturally limit
a guest. For those tables which are page sized (devices, collections (CPUs),
LPI properties) we map those pages into Xen, so we can easily access
them from the virtual GIC code.
Unfortunately the actual interrupt mapping tables are not necessarily
page aligned, also can be much smaller than a page, so mapping all of
them permanently is fiddly. As ITS commands in need to iterate those
tables are pretty rare after all, we for now map them on demand upon
emulating a virtual ITS command.

* An obvious approach to handling some guest ITS commands would be to
propagate them to the host, for instance to map devices and LPIs and
to enable or disable LPIs.
However this (later with DomU support) will create an attack vector, as
a malicious guest could try to fill the host command queue with
propagated commands.
So in contrast to the previous RFC post this version now completely avoids
this situation. For mapping devices and LPIs we rely on this being done
via a hypercall prior to the actual guest run. For enabling and disabling
LPIs we keep this bit on the virtual side and let LPIs always be enabled
on the host side, dealing with the consequences this approach creates.

This series is still a draft, with some known and many unknown issues.
I made ITS support a Kconfig option, also it is only supported on arm64.
This leads to some hideous constructs like an #ifdef'ed header file with
empty function stubs, but I guess we can clean this up later in the
upstreaming process.

There are numerous changes compared to the last post, mainly affecting
the now missing ITS command progagation. I also added locking to the
"usual suspects" data structures.
I picked some low hanging fruits from the review comments.
Things I haven't addresses well is the whole memory management, in terms
of marking pages r/o for a guest or allocating Xen memory from the proper
bucket. This will be addresses with the next post.

For now this code happens to boot Dom0 on an ARM fast model with ITS
support. I still haven't had the chance to get hold of a Xen supported
hardware platform with an ITS yet, so running on real hardware is a bit
terra incognita.

The code can also be found on the its/rfc-v2 branch here:
git://linux-arm.org/xen-ap.git
http://www.linux-arm.org/git?p=xen-ap.git;a=shortlog;h=refs/heads/its/rfc-v2

Cheers,
Andre

Andre Przywara (26):
  ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  ARM: GICv3: allocate LPI pending and property table
  ARM: GICv3 ITS: allocate device and collection table
  ARM: GICv3 ITS: map ITS command buffer
  ARM: GICv3 ITS: introduce ITS command handling
  ARM: GICv3 ITS: introduce device mapping
  ARM: GICv3 ITS: introduce host LPI array
  ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall
  ARM: GICv3: introduce separate pending_irq structs for LPIs
  ARM: GICv3: forward pending LPIs to guests
  ARM: GICv3: enable ITS and LPIs on the host
  ARM: vGICv3: handle virtual LPI pending and property tables
  ARM: vGICv3: Handle disabled LPIs
  ARM: vGICv3: introduce basic ITS emulation bits
  ARM: vITS: handle CLEAR command
  ARM: vITS: handle INT command
  ARM: vITS: handle MAPC command
  ARM: vITS: handle MAPD command
  ARM: vITS: handle MAPTI command
  ARM: vITS: handle MOVI command
  ARM: vITS: handle DISCARD command
  ARM: vITS: handle INV command
  ARM: vITS: handle INVALL command
  ARM: vITS: create and initialize virtual ITSes for Dom0
  ARM: vITS: create ITS subnodes for Dom0 DT
  ARM: vGIC: advertising LPI support

 xen/arch/arm/Kconfig              |  20 +
 xen/arch/arm/Makefile             |   2 +
 xen/arch/arm/efi/efi-boot.h       |   1 -
 xen/arch/arm/gic-its.c            | 934 ++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c             |  92 +++-
 xen/arch/arm/gic.c                |   9 +-
 xen/arch/arm/physdev.c            |  24 +
 xen/arch/arm/vgic-its.c           | 842 ++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-v3.c            | 274 +++++++++--
 xen/arch/arm/vgic.c               |  71 ++-
 xen/include/asm-arm/bitops.h      |   2 +
 xen/include/asm-arm/cache.h       |   4 +
 xen/include/asm-arm/domain.h      |  13 +-
 xen/include/asm-arm/gic-its.h     | 217 +++++++++
 xen/include/asm-arm/gic_v3_defs.h |  67 ++-
 xen/include/asm-arm/irq.h         |   8 +
 xen/include/asm-arm/vgic.h        |  15 +
 17 files changed, 2558 insertions(+), 37 deletions(-)
 create mode 100644 xen/arch/arm/gic-its.c
 create mode 100644 xen/arch/arm/vgic-its.c
 create mode 100644 xen/include/asm-arm/gic-its.h

-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 01/26] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table Andre Przywara
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Parse the DT GIC subnodes to find every ITS MSI controller the hardware
offers. Store that information in a list to both propagate all of them
later to Dom0, but also to be able to iterate over all ITSes.
This introduces an ITS Kconfig option.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/Kconfig          |  4 +++
 xen/arch/arm/Makefile         |  1 +
 xen/arch/arm/gic-its.c        | 68 +++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c         |  6 ++++
 xen/include/asm-arm/gic-its.h | 57 ++++++++++++++++++++++++++++++++++++
 5 files changed, 136 insertions(+)
 create mode 100644 xen/arch/arm/gic-its.c
 create mode 100644 xen/include/asm-arm/gic-its.h

diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index 2e023d1..bf64c61 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -45,6 +45,10 @@ config ACPI
 config HAS_GICV3
 	bool
 
+config HAS_ITS
+        bool "GICv3 ITS MSI controller support"
+        depends on HAS_GICV3
+
 endmenu
 
 menu "ARM errata workaround via the alternative framework"
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 59b3b53..80c2951 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -18,6 +18,7 @@ obj-$(EARLY_PRINTK) += early_printk.o
 obj-y += gic.o
 obj-y += gic-v2.o
 obj-$(CONFIG_HAS_GICV3) += gic-v3.o
+obj-$(CONFIG_HAS_ITS) += gic-its.o
 obj-y += guestcopy.o
 obj-y += hvm.o
 obj-y += io.o
diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
new file mode 100644
index 0000000..973f9f9
--- /dev/null
+++ b/xen/arch/arm/gic-its.c
@@ -0,0 +1,68 @@
+/*
+ * xen/arch/arm/gic-its.c
+ *
+ * ARM Generic Interrupt Controller ITS support
+ *
+ * Copyright (C) 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 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 <xen/config.h>
+#include <xen/lib.h>
+#include <xen/device_tree.h>
+#include <xen/libfdt/libfdt.h>
+#include <asm/gic.h>
+#include <asm/gic_v3_defs.h>
+#include <asm/gic-its.h>
+
+/* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
+void gicv3_its_dt_init(const struct dt_device_node *node)
+{
+    const struct dt_device_node *its = NULL;
+    struct host_its *its_data;
+
+    /*
+     * Check for ITS MSI subnodes. If any, add the ITS register
+     * frames to the ITS list.
+     */
+    dt_for_each_child_node(node, its)
+    {
+        paddr_t addr, size;
+
+        if ( !dt_device_is_compatible(its, "arm,gic-v3-its") )
+            continue;
+
+        if ( dt_device_get_address(its, 0, &addr, &size) )
+            panic("GICv3: Cannot find a valid ITS frame address");
+
+        its_data = xzalloc(struct host_its);
+        if ( !its_data )
+            panic("GICv3: Cannot allocate memory for ITS frame");
+
+        its_data->addr = addr;
+        its_data->size = size;
+        its_data->dt_node = its;
+
+        printk("GICv3: Found ITS @0x%lx\n", addr);
+
+        list_add_tail(&its_data->entry, &host_its_list);
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index b8be395..238da84 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -43,9 +43,12 @@
 #include <asm/device.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
+#include <asm/gic-its.h>
 #include <asm/cpufeature.h>
 #include <asm/acpi.h>
 
+LIST_HEAD(host_its_list);
+
 /* Global state */
 static struct {
     void __iomem *map_dbase;  /* Mapped address of distributor registers */
@@ -1229,6 +1232,9 @@ static void __init gicv3_dt_init(void)
 
     dt_device_get_address(node, 1 + gicv3.rdist_count + 2,
                           &vbase, &vsize);
+
+    /* Check for ITS child nodes and build the host ITS list accordingly. */
+    gicv3_its_dt_init(node);
 }
 
 static int gicv3_iomem_deny_access(const struct domain *d)
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
new file mode 100644
index 0000000..2f5c51c
--- /dev/null
+++ b/xen/include/asm-arm/gic-its.h
@@ -0,0 +1,57 @@
+/*
+ * ARM GICv3 ITS support
+ *
+ * Andre Przywara <andre.przywara@arm.com>
+ * Copyright (c) 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 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.
+ */
+
+#ifndef __ASM_ARM_ITS_H__
+#define __ASM_ARM_ITS_H__
+
+#ifndef __ASSEMBLY__
+#include <xen/device_tree.h>
+
+/* data structure for each hardware ITS */
+struct host_its {
+    struct list_head entry;
+    const struct dt_device_node *dt_node;
+    paddr_t addr;
+    paddr_t size;
+};
+
+extern struct list_head host_its_list;
+
+#ifdef CONFIG_HAS_ITS
+
+/* Parse the host DT and pick up all host ITSes. */
+void gicv3_its_dt_init(const struct dt_device_node *node);
+
+#else
+
+static inline void gicv3_its_dt_init(const struct dt_device_node *node)
+{
+}
+
+#endif /* CONFIG_HAS_ITS */
+
+#endif /* __ASSEMBLY__ */
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 01/26] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-04 21:09   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
                   ` (25 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The ARM GICv3 ITS provides a new kind of interrupt called LPIs.
The pending bits and the configuration data (priority, enable bits) for
those LPIs are stored in tables in normal memory, which software has to
provide to the hardware.
Allocate the required memory, initialize it and hand it over to each
ITS. The maximum number of LPIs to be used can be adjusted with the
command line option "max_lpi_bits", which defaults to a compile time
constant exposed in Kconfig.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/Kconfig              | 10 +++++
 xen/arch/arm/efi/efi-boot.h       |  1 -
 xen/arch/arm/gic-its.c            | 94 +++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c             | 43 ++++++++++++++++++
 xen/include/asm-arm/bitops.h      |  1 +
 xen/include/asm-arm/cache.h       |  4 ++
 xen/include/asm-arm/gic-its.h     | 22 ++++++++-
 xen/include/asm-arm/gic_v3_defs.h | 48 +++++++++++++++++++-
 8 files changed, 220 insertions(+), 3 deletions(-)

diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index bf64c61..a7d941c 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -49,6 +49,16 @@ config HAS_ITS
         bool "GICv3 ITS MSI controller support"
         depends on HAS_GICV3
 
+config MAX_HOST_LPI_BITS
+        depends on HAS_ITS
+        int "Maximum bits for GICv3 host LPIs (14-32)"
+        range 14 32
+        default "20"
+        help
+          Specifies the maximum number of LPIs (bits) Xen should take care of.
+          This can be overriden on the command line with the max_lpi_bits
+          parameter.
+
 endmenu
 
 menu "ARM errata workaround via the alternative framework"
diff --git a/xen/arch/arm/efi/efi-boot.h b/xen/arch/arm/efi/efi-boot.h
index 045d6ce..dc64aec 100644
--- a/xen/arch/arm/efi/efi-boot.h
+++ b/xen/arch/arm/efi/efi-boot.h
@@ -10,7 +10,6 @@
 #include "efi-dom0.h"
 
 void noreturn efi_xen_start(void *fdt_ptr, uint32_t fdt_size);
-void __flush_dcache_area(const void *vaddr, unsigned long size);
 
 #define DEVICE_TREE_GUID \
 {0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}}
diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 973f9f9..6c3a35d 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -20,10 +20,104 @@
 #include <xen/lib.h>
 #include <xen/device_tree.h>
 #include <xen/libfdt/libfdt.h>
+#include <xen/sizes.h>
+#include <asm/p2m.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic-its.h>
 
+/* Global state */
+static struct {
+    uint8_t *lpi_property;
+    unsigned int host_lpi_bits;
+} lpi_data;
+
+/* Pending table for each redistributor */
+static DEFINE_PER_CPU(void *, pending_table);
+
+#define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
+
+uint64_t gicv3_lpi_allocate_pendtable(void)
+{
+    uint64_t reg, attr;
+    void *pendtable;
+
+    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
+    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
+    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
+
+    if ( !this_cpu(pending_table) )
+    {
+        /*
+         * The pending table holds one bit per LPI, so we need (2 << 3) bits
+         * less bytes. The pending table covers even bits for interrupt IDs
+         * below 8192, so we allocate the full range.
+         * The ITS imposes a 64KB alignment requirement.
+         */
+        pendtable = _xmalloc(BIT_ULL(lpi_data.host_lpi_bits) / 8, SZ_64K);
+        if ( !pendtable )
+            return 0;
+
+        memset(pendtable, 0, BIT_ULL(lpi_data.host_lpi_bits) / 8);
+        __flush_dcache_area(pendtable, BIT_ULL(lpi_data.host_lpi_bits) / 8);
+
+        this_cpu(pending_table) = pendtable;
+    }
+    else
+    {
+        pendtable = this_cpu(pending_table);
+    }
+
+    reg  = attr | GICR_PENDBASER_PTZ;
+    reg |= virt_to_maddr(pendtable) & GENMASK(51, 16);
+
+    return reg;
+}
+
+uint64_t gicv3_lpi_get_proptable()
+{
+    uint64_t attr, reg;
+
+    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
+    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
+    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
+
+    /*
+     * The property table is shared across all redistributors, so allocate
+     * this only once, but return the same value on subsequent calls.
+     */
+    if ( !lpi_data.lpi_property )
+    {
+        /* The property table holds one byte per LPI. */
+        void *table = alloc_xenheap_pages(lpi_data.host_lpi_bits - PAGE_SHIFT,
+                                          0);
+
+        if ( !table )
+            return 0;
+
+        memset(table, GIC_PRI_IRQ | LPI_PROP_RES1, MAX_PHYS_LPIS);
+        __flush_dcache_area(table, MAX_PHYS_LPIS);
+        lpi_data.lpi_property = table;
+    }
+
+    reg  = attr | ((lpi_data.host_lpi_bits - 1) << 0);
+    reg |= virt_to_maddr(lpi_data.lpi_property) & GENMASK(51, 12);
+
+    return reg;
+}
+
+static unsigned int max_lpi_bits = CONFIG_MAX_HOST_LPI_BITS;
+integer_param("max_lpi_bits", max_lpi_bits);
+
+int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
+{
+    lpi_data.host_lpi_bits = min(hw_lpi_bits, max_lpi_bits);
+
+    printk("GICv3: using at most %lld LPIs on the host.\n", MAX_PHYS_LPIS);
+
+    return 0;
+}
+
 /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
 void gicv3_its_dt_init(const struct dt_device_node *node)
 {
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index 238da84..1c6869d 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -546,6 +546,9 @@ static void __init gicv3_dist_init(void)
     type = readl_relaxed(GICD + GICD_TYPER);
     nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1);
 
+    if ( type & GICD_TYPE_LPIS )
+        gicv3_lpi_init_host_lpis(((type >> GICD_TYPE_ID_BITS_SHIFT) & 0x1f) + 1);
+
     printk("GICv3: %d lines, (IID %8.8x).\n",
            nr_lines, readl_relaxed(GICD + GICD_IIDR));
 
@@ -616,6 +619,32 @@ static int gicv3_enable_redist(void)
     return 0;
 }
 
+static int gicv3_rdist_init_lpis(void __iomem * rdist_base)
+{
+    uint32_t reg;
+    uint64_t table_reg;
+
+    if ( list_empty(&host_its_list) )
+        return -ENODEV;
+
+    /* Make sure LPIs are disabled before setting up the BASERs. */
+    reg = readl_relaxed(rdist_base + GICR_CTLR);
+    if ( reg & GICR_CTLR_ENABLE_LPIS )
+        return -EBUSY;
+
+    table_reg = gicv3_lpi_allocate_pendtable();
+    if ( !table_reg )
+        return -ENOMEM;
+    writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
+
+    table_reg = gicv3_lpi_get_proptable();
+    if ( !table_reg )
+        return -ENOMEM;
+    writeq_relaxed(table_reg, rdist_base + GICR_PROPBASER);
+
+    return 0;
+}
+
 static int __init gicv3_populate_rdist(void)
 {
     int i;
@@ -658,6 +687,20 @@ static int __init gicv3_populate_rdist(void)
             if ( (typer >> 32) == aff )
             {
                 this_cpu(rbase) = ptr;
+
+                if ( typer & GICR_TYPER_PLPIS )
+                {
+                    int ret;
+
+                    ret = gicv3_rdist_init_lpis(ptr);
+                    if ( ret && ret != -ENODEV )
+                    {
+                        printk("GICv3: CPU%d: Cannot initialize LPIs: %d\n",
+                               smp_processor_id(), ret);
+                        break;
+                    }
+                }
+
                 printk("GICv3: CPU%d: Found redistributor in region %d @%p\n",
                         smp_processor_id(), i, ptr);
                 return 0;
diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
index bda8898..c9adf79 100644
--- a/xen/include/asm-arm/bitops.h
+++ b/xen/include/asm-arm/bitops.h
@@ -25,6 +25,7 @@
 #define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_WORD))
 #define BIT_WORD(nr)            ((nr) / BITS_PER_WORD)
 #define BITS_PER_BYTE           8
+#define BIT_ULL(nr)             (1ULL << (nr))
 
 #define ADDR (*(volatile int *) addr)
 #define CONST_ADDR (*(const volatile int *) addr)
diff --git a/xen/include/asm-arm/cache.h b/xen/include/asm-arm/cache.h
index 2de6564..af96eee 100644
--- a/xen/include/asm-arm/cache.h
+++ b/xen/include/asm-arm/cache.h
@@ -7,6 +7,10 @@
 #define L1_CACHE_SHIFT  (CONFIG_ARM_L1_CACHE_SHIFT)
 #define L1_CACHE_BYTES  (1 << L1_CACHE_SHIFT)
 
+#ifndef __ASSEMBLY__
+void __flush_dcache_area(const void *vaddr, unsigned long size);
+#endif
+
 #define __read_mostly __section(".data.read_mostly")
 
 #endif
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 2f5c51c..a66b6be 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -36,12 +36,32 @@ extern struct list_head host_its_list;
 /* Parse the host DT and pick up all host ITSes. */
 void gicv3_its_dt_init(const struct dt_device_node *node);
 
+/* Allocate and initialize tables for each host redistributor.
+ * Returns the respective {PROP,PEND}BASER register value.
+ */
+uint64_t gicv3_lpi_get_proptable(void);
+uint64_t gicv3_lpi_allocate_pendtable(void);
+
+/* Initialize the host structures for LPIs. */
+int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
+
 #else
 
 static inline void gicv3_its_dt_init(const struct dt_device_node *node)
 {
 }
-
+static inline uint64_t gicv3_lpi_get_proptable(void)
+{
+    return 0;
+}
+static inline uint64_t gicv3_lpi_allocate_pendtable(void)
+{
+    return 0;
+}
+static inline int gicv3_lpi_init_host_lpis(unsigned int nr_lpis)
+{
+    return 0;
+}
 #endif /* CONFIG_HAS_ITS */
 
 #endif /* __ASSEMBLY__ */
diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
index 6bd25a5..da5fb77 100644
--- a/xen/include/asm-arm/gic_v3_defs.h
+++ b/xen/include/asm-arm/gic_v3_defs.h
@@ -44,7 +44,8 @@
 #define GICC_SRE_EL2_ENEL1           (1UL << 3)
 
 /* Additional bits in GICD_TYPER defined by GICv3 */
-#define GICD_TYPE_ID_BITS_SHIFT 19
+#define GICD_TYPE_ID_BITS_SHIFT      19
+#define GICD_TYPE_LPIS               (1U << 17)
 
 #define GICD_CTLR_RWP                (1UL << 31)
 #define GICD_CTLR_ARE_NS             (1U << 4)
@@ -95,12 +96,57 @@
 #define GICR_IGRPMODR0               (0x0D00)
 #define GICR_NSACR                   (0x0E00)
 
+#define GICR_CTLR_ENABLE_LPIS        (1U << 0)
 #define GICR_TYPER_PLPIS             (1U << 0)
 #define GICR_TYPER_VLPIS             (1U << 1)
 #define GICR_TYPER_LAST              (1U << 4)
 
+#define GIC_BASER_CACHE_nCnB         0ULL
+#define GIC_BASER_CACHE_SameAsInner  0ULL
+#define GIC_BASER_CACHE_nC           1ULL
+#define GIC_BASER_CACHE_RaWt         2ULL
+#define GIC_BASER_CACHE_RaWb         3ULL
+#define GIC_BASER_CACHE_WaWt         4ULL
+#define GIC_BASER_CACHE_WaWb         5ULL
+#define GIC_BASER_CACHE_RaWaWt       6ULL
+#define GIC_BASER_CACHE_RaWaWb       7ULL
+#define GIC_BASER_CACHE_MASK         7ULL
+#define GIC_BASER_NonShareable       0ULL
+#define GIC_BASER_InnerShareable     1ULL
+#define GIC_BASER_OuterShareable     2ULL
+
+#define GICR_PROPBASER_SHAREABILITY_SHIFT               10
+#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT         7
+#define GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT         56
+#define GICR_PROPBASER_SHAREABILITY_MASK                     \
+        (3UL << GICR_PROPBASER_SHAREABILITY_SHIFT)
+#define GICR_PROPBASER_INNER_CACHEABILITY_MASK               \
+        (7UL << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT)
+#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK               \
+        (7UL << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT)
+#define PROPBASER_RES0_MASK                                  \
+        (GENMASK(63, 59) | GENMASK(55, 52) | GENMASK(6, 5))
+
+#define GICR_PENDBASER_SHAREABILITY_SHIFT               10
+#define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT         7
+#define GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT         56
+#define GICR_PENDBASER_SHAREABILITY_MASK                     \
+	(3UL << GICR_PENDBASER_SHAREABILITY_SHIFT)
+#define GICR_PENDBASER_INNER_CACHEABILITY_MASK               \
+	(7UL << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT)
+#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK               \
+        (7UL << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT)
+#define GICR_PENDBASER_PTZ                              BIT(62)
+#define PENDBASER_RES0_MASK                                  \
+        (BIT(63) | GENMASK(61, 59) | GENMASK(55, 52) |       \
+         GENMASK(15, 12) | GENMASK(6, 0))
+
 #define DEFAULT_PMR_VALUE            0xff
 
+#define LPI_PROP_DEFAULT_PRIO        0xa0
+#define LPI_PROP_RES1                (1 << 1)
+#define LPI_PROP_ENABLED             (1 << 0)
+
 #define GICH_VMCR_EOI                (1 << 9)
 #define GICH_VMCR_VENG1              (1 << 1)
 
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 01/26] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-04 21:47   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 04/26] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
                   ` (24 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
an EventID (the MSI payload or interrupt ID) to a pair of LPI number
and collection ID, which points to the target CPU.
This mapping is stored in the device and collection tables, which software
has to provide for the ITS to use.
Allocate the required memory and hand it the ITS.
The maximum number of devices is limited to a compile-time constant
exposed in Kconfig.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/Kconfig          |   6 +++
 xen/arch/arm/gic-its.c        | 114 ++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c         |   5 ++
 xen/include/asm-arm/bitops.h  |   1 +
 xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
 5 files changed, 176 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index a7d941c..a369305 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
           This can be overriden on the command line with the max_lpi_bits
           parameter.
 
+config MAX_ITS_PCI_BUSSES
+        depends on HAS_ITS
+        int "Number of PCI busses the ITS supports (4)"
+        range 1 1024
+        default "4"
+
 endmenu
 
 menu "ARM errata workaround via the alternative framework"
diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 6c3a35d..f1540b2 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -22,6 +22,7 @@
 #include <xen/libfdt/libfdt.h>
 #include <xen/sizes.h>
 #include <asm/p2m.h>
+#include <asm/io.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic-its.h>
@@ -37,6 +38,119 @@ static DEFINE_PER_CPU(void *, pending_table);
 
 #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
 
+#define BASER_ATTR_MASK                                           \
+        ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
+         (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
+         (0x7UL << GITS_BASER_INNER_CACHEABILITY_SHIFT))
+#define BASER_RO_MASK   (GENMASK(58, 56) | GENMASK(52, 48))
+
+static uint64_t encode_phys_addr(paddr_t addr, int page_bits)
+{
+    uint64_t ret;
+
+    if ( page_bits < 16)
+        return (uint64_t)addr & GENMASK(47, page_bits);
+
+    ret = addr & GENMASK(47, 16);
+    return ret | (addr & GENMASK(51, 48)) >> (48 - 12);
+}
+
+static int its_map_baser(void __iomem *basereg, uint64_t regc, int nr_items)
+{
+    uint64_t attr;
+    int entry_size = ((regc >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1;
+    int pagesz;
+    int order;
+    void *buffer = NULL;
+
+    attr  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
+    attr |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
+    attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
+
+    /*
+     * Loop over the page sizes (4K, 16K, 64K) to find out what the host
+     * supports.
+     */
+    for ( pagesz = 0; pagesz < 3; pagesz++ )
+    {
+        uint64_t reg;
+        int nr_bytes;
+
+        nr_bytes = ROUNDUP(nr_items * entry_size, BIT(pagesz * 2 + 12));
+        order = get_order_from_bytes(nr_bytes);
+
+        if ( !buffer )
+            buffer = alloc_xenheap_pages(order, 0);
+        if ( !buffer )
+            return -ENOMEM;
+
+        reg  = attr;
+        reg |= (pagesz << GITS_BASER_PAGE_SIZE_SHIFT);
+        reg |= nr_bytes >> (pagesz * 2 + 12);
+        reg |= regc & BASER_RO_MASK;
+        reg |= GITS_BASER_VALID;
+        reg |= encode_phys_addr(virt_to_maddr(buffer), pagesz * 2 + 12);
+
+        writeq_relaxed(reg, basereg);
+        regc = readl_relaxed(basereg);
+
+        /* The host didn't like our attributes, just use what it returned. */
+        if ( (regc & BASER_ATTR_MASK) != attr )
+            attr = regc & BASER_ATTR_MASK;
+
+        /* If the host accepted our page size, we are done. */
+        if ( (reg & (3UL << GITS_BASER_PAGE_SIZE_SHIFT)) == pagesz )
+            return 0;
+
+        /* Check whether our buffer is aligned to the next page size already. */
+        if ( !(virt_to_maddr(buffer) & (BIT(pagesz * 2 + 12 + 2) - 1)) )
+        {
+            free_xenheap_pages(buffer, order);
+            buffer = NULL;
+        }
+    }
+
+    if ( buffer )
+        free_xenheap_pages(buffer, order);
+
+    return -EINVAL;
+}
+
+int gicv3_its_init(struct host_its *hw_its)
+{
+    uint64_t reg;
+    int i;
+
+    hw_its->its_base = ioremap_nocache(hw_its->addr, hw_its->size);
+    if ( !hw_its->its_base )
+        return -ENOMEM;
+
+    for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
+    {
+        void __iomem *basereg = hw_its->its_base + GITS_BASER0 + i * 8;
+        int type;
+
+        reg = readq_relaxed(basereg);
+        type = (reg & GITS_BASER_TYPE_MASK) >> GITS_BASER_TYPE_SHIFT;
+        switch ( type )
+        {
+        case GITS_BASER_TYPE_NONE:
+            continue;
+        case GITS_BASER_TYPE_DEVICE:
+            /* TODO: find some better way of limiting the number of devices */
+            its_map_baser(basereg, reg, CONFIG_MAX_ITS_PCI_BUSSES * 256);
+            break;
+        case GITS_BASER_TYPE_COLLECTION:
+            its_map_baser(basereg, reg, NR_CPUS);
+            break;
+        default:
+            continue;
+        }
+    }
+
+    return 0;
+}
+
 uint64_t gicv3_lpi_allocate_pendtable(void)
 {
     uint64_t reg, attr;
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index 1c6869d..8ca7da2 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -29,6 +29,7 @@
 #include <xen/irq.h>
 #include <xen/iocap.h>
 #include <xen/sched.h>
+#include <xen/err.h>
 #include <xen/errno.h>
 #include <xen/delay.h>
 #include <xen/device_tree.h>
@@ -1564,6 +1565,7 @@ static int __init gicv3_init(void)
 {
     int res, i;
     uint32_t reg;
+    struct host_its *hw_its;
 
     if ( !cpu_has_gicv3 )
     {
@@ -1619,6 +1621,9 @@ static int __init gicv3_init(void)
     res = gicv3_cpu_init();
     gicv3_hyp_init();
 
+    list_for_each_entry(hw_its, &host_its_list, entry)
+        gicv3_its_init(hw_its);
+
     spin_unlock(&gicv3.lock);
 
     return res;
diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
index c9adf79..4ccb444 100644
--- a/xen/include/asm-arm/bitops.h
+++ b/xen/include/asm-arm/bitops.h
@@ -24,6 +24,7 @@
 #define BIT(nr)                 (1UL << (nr))
 #define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_WORD))
 #define BIT_WORD(nr)            ((nr) / BITS_PER_WORD)
+#define BIT_ULL(nr)             (1ULL << (nr))
 #define BITS_PER_BYTE           8
 #define BIT_ULL(nr)             (1ULL << (nr))
 
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index a66b6be..26b0584 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -18,6 +18,49 @@
 #ifndef __ASM_ARM_ITS_H__
 #define __ASM_ARM_ITS_H__
 
+#define LPI_OFFSET                      8192
+
+#define GITS_CTLR                       0x000
+#define GITS_IIDR                       0x004
+#define GITS_TYPER                      0x008
+#define GITS_CBASER                     0x080
+#define GITS_CWRITER                    0x088
+#define GITS_CREADR                     0x090
+#define GITS_BASER_NR_REGS              8
+#define GITS_BASER0                     0x100
+#define GITS_BASER1                     0x108
+#define GITS_BASER2                     0x110
+#define GITS_BASER3                     0x118
+#define GITS_BASER4                     0x120
+#define GITS_BASER5                     0x128
+#define GITS_BASER6                     0x130
+#define GITS_BASER7                     0x138
+
+/* Register bits */
+#define GITS_CTLR_ENABLE                BIT_ULL(0)
+#define GITS_IIDR_VALUE                 0x34c
+
+#define GITS_BASER_VALID                BIT_ULL(63)
+#define GITS_BASER_INDIRECT             BIT_ULL(62)
+#define GITS_BASER_INNER_CACHEABILITY_SHIFT        59
+#define GITS_BASER_TYPE_SHIFT           56
+#define GITS_BASER_TYPE_MASK            (7ULL << GITS_BASER_TYPE_SHIFT)
+#define GITS_BASER_OUTER_CACHEABILITY_SHIFT        53
+#define GITS_BASER_TYPE_NONE            0UL
+#define GITS_BASER_TYPE_DEVICE          1UL
+#define GITS_BASER_TYPE_VCPU            2UL
+#define GITS_BASER_TYPE_CPU             3UL
+#define GITS_BASER_TYPE_COLLECTION      4UL
+#define GITS_BASER_TYPE_RESERVED5       5UL
+#define GITS_BASER_TYPE_RESERVED6       6UL
+#define GITS_BASER_TYPE_RESERVED7       7UL
+#define GITS_BASER_ENTRY_SIZE_SHIFT     48
+#define GITS_BASER_SHAREABILITY_SHIFT   10
+#define GITS_BASER_PAGE_SIZE_SHIFT      8
+#define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
+                                        (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
+                                        GITS_BASER_INDIRECT)
+
 #ifndef __ASSEMBLY__
 #include <xen/device_tree.h>
 
@@ -27,6 +70,7 @@ struct host_its {
     const struct dt_device_node *dt_node;
     paddr_t addr;
     paddr_t size;
+    void __iomem *its_base;
 };
 
 extern struct list_head host_its_list;
@@ -42,8 +86,9 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
 uint64_t gicv3_lpi_get_proptable(void);
 uint64_t gicv3_lpi_allocate_pendtable(void);
 
-/* Initialize the host structures for LPIs. */
+/* Initialize the host structures for LPIs and the host ITSes. */
 int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
+int gicv3_its_init(struct host_its *hw_its);
 
 #else
 
@@ -62,6 +107,10 @@ static inline int gicv3_lpi_init_host_lpis(unsigned int nr_lpis)
 {
     return 0;
 }
+static inline int gicv3_its_init(struct host_its *hw_its)
+{
+    return 0;
+}
 #endif /* CONFIG_HAS_ITS */
 
 #endif /* __ASSEMBLY__ */
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 04/26] ARM: GICv3 ITS: map ITS command buffer
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (2 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-04 21:53   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 05/26] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
                   ` (23 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Instead of directly manipulating the tables in memory, an ITS driver
sends commands via a ring buffer to the ITS h/w to create or alter the
LPI mappings.
Allocate memory for that buffer and tell the ITS about it to be able
to send ITS commands.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c        | 27 +++++++++++++++++++++++++++
 xen/include/asm-arm/gic-its.h |  3 +++
 2 files changed, 30 insertions(+)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index f1540b2..2fb3bcb 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -18,6 +18,7 @@
 
 #include <xen/config.h>
 #include <xen/lib.h>
+#include <xen/err.h>
 #include <xen/device_tree.h>
 #include <xen/libfdt/libfdt.h>
 #include <xen/sizes.h>
@@ -38,6 +39,8 @@ static DEFINE_PER_CPU(void *, pending_table);
 
 #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
 
+#define ITS_CMD_QUEUE_SZ                                SZ_64K
+
 #define BASER_ATTR_MASK                                           \
         ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
          (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
@@ -55,6 +58,26 @@ static uint64_t encode_phys_addr(paddr_t addr, int page_bits)
     return ret | (addr & GENMASK(51, 48)) >> (48 - 12);
 }
 
+static void *its_map_cbaser(void __iomem *cbasereg)
+{
+    uint64_t attr, reg;
+    void *buffer;
+
+    attr  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
+    attr |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
+    attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
+
+    buffer = _xzalloc(ITS_CMD_QUEUE_SZ, PAGE_SIZE);
+    if ( !buffer )
+        return NULL;
+
+    reg = attr | BIT_ULL(63) | (virt_to_maddr(buffer) & GENMASK(51, 12));
+    reg |= ((ITS_CMD_QUEUE_SZ / PAGE_SIZE) - 1) & GITS_CBASER_SIZE_MASK;
+    writeq_relaxed(reg, cbasereg);
+
+    return buffer;
+}
+
 static int its_map_baser(void __iomem *basereg, uint64_t regc, int nr_items)
 {
     uint64_t attr;
@@ -148,6 +171,10 @@ int gicv3_its_init(struct host_its *hw_its)
         }
     }
 
+    hw_its->cmd_buf = its_map_cbaser(hw_its->its_base + GITS_CBASER);
+    if ( !hw_its->cmd_buf )
+        return -ENOMEM;
+
     return 0;
 }
 
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 26b0584..e0fded8 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -61,6 +61,8 @@
                                         (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
                                         GITS_BASER_INDIRECT)
 
+#define GITS_CBASER_SIZE_MASK           0xff
+
 #ifndef __ASSEMBLY__
 #include <xen/device_tree.h>
 
@@ -71,6 +73,7 @@ struct host_its {
     paddr_t addr;
     paddr_t size;
     void __iomem *its_base;
+    void *cmd_buf;
 };
 
 extern struct list_head host_its_list;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 05/26] ARM: GICv3 ITS: introduce ITS command handling
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (3 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 04/26] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-04 22:08   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping Andre Przywara
                   ` (22 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

To be able to easily send commands to the ITS, create the respective
wrapper functions, which take care of the ring buffer.
The first two commands we implement provide methods to map a collection
to a redistributor (aka host core) and to flush the command queue (SYNC).
Start using these commands for mapping one collection to each host CPU.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c        | 100 ++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c         |  15 +++++++
 xen/include/asm-arm/gic-its.h |  33 ++++++++++++++
 3 files changed, 148 insertions(+)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 2fb3bcb..d0f5fd1 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -34,6 +34,10 @@ static struct {
     unsigned int host_lpi_bits;
 } lpi_data;
 
+/* Physical redistributor address */
+static DEFINE_PER_CPU(uint64_t, rdist_addr);
+/* Redistributor ID */
+static DEFINE_PER_CPU(uint64_t, rdist_id);
 /* Pending table for each redistributor */
 static DEFINE_PER_CPU(void *, pending_table);
 
@@ -41,6 +45,85 @@ static DEFINE_PER_CPU(void *, pending_table);
 
 #define ITS_CMD_QUEUE_SZ                                SZ_64K
 
+static int its_send_command(struct host_its *hw_its, void *its_cmd)
+{
+    uint64_t readp, writep;
+
+    spin_lock(&hw_its->cmd_lock);
+
+    readp = readq_relaxed(hw_its->its_base + GITS_CREADR) & GENMASK(19, 5);
+    writep = readq_relaxed(hw_its->its_base + GITS_CWRITER) & GENMASK(19, 5);
+
+    if ( ((writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ) == readp )
+    {
+        spin_unlock(&hw_its->cmd_lock);
+        return -EBUSY;
+    }
+
+    memcpy(hw_its->cmd_buf + writep, its_cmd, ITS_CMD_SIZE);
+    __flush_dcache_area(hw_its->cmd_buf + writep, ITS_CMD_SIZE);
+    writep = (writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ;
+
+    writeq_relaxed(writep & GENMASK(19, 5), hw_its->its_base + GITS_CWRITER);
+
+    spin_unlock(&hw_its->cmd_lock);
+
+    return 0;
+}
+
+static uint64_t encode_rdbase(struct host_its *hw_its, int cpu, uint64_t reg)
+{
+    reg &= ~GENMASK(51, 16);
+
+    if ( hw_its->pta )
+        reg |= per_cpu(rdist_addr, cpu) & GENMASK(51, 16);
+    else
+        reg |= per_cpu(rdist_id, cpu) << 16;
+
+    return reg;
+}
+
+static int its_send_cmd_sync(struct host_its *its, int cpu)
+{
+    uint64_t cmd[4];
+
+    cmd[0] = GITS_CMD_SYNC;
+    cmd[1] = 0x00;
+    cmd[2] = encode_rdbase(its, cpu, 0x0);
+    cmd[3] = 0x00;
+
+    return its_send_command(its, cmd);
+}
+
+static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
+{
+    uint64_t cmd[4];
+
+    cmd[0] = GITS_CMD_MAPC;
+    cmd[1] = 0x00;
+    cmd[2] = encode_rdbase(its, cpu, (collection_id & GENMASK(15, 0)));
+    cmd[2] |= BIT_ULL(63);
+    cmd[3] = 0x00;
+
+    return its_send_command(its, cmd);
+}
+
+/* Set up the (1:1) collection mapping for the given host CPU. */
+void gicv3_its_setup_collection(int cpu)
+{
+    struct host_its *its;
+
+    list_for_each_entry(its, &host_its_list, entry)
+    {
+        /* Only send commands to ITS that have been initialized already. */
+        if ( !its->cmd_buf )
+            continue;
+
+        its_send_cmd_mapc(its, cpu, cpu);
+        its_send_cmd_sync(its, cpu);
+    }
+}
+
 #define BASER_ATTR_MASK                                           \
         ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
          (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
@@ -148,6 +231,13 @@ int gicv3_its_init(struct host_its *hw_its)
     if ( !hw_its->its_base )
         return -ENOMEM;
 
+    /* Make sure the ITS is disabled before programming the BASE registers. */
+    reg = readl_relaxed(hw_its->its_base + GITS_CTLR);
+    writel_relaxed(reg & ~GITS_CTLR_ENABLE, hw_its->its_base + GITS_CTLR);
+
+    reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
+    hw_its->pta = !!(reg & GITS_TYPER_PTA);
+
     for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
     {
         void __iomem *basereg = hw_its->its_base + GITS_BASER0 + i * 8;
@@ -175,9 +265,18 @@ int gicv3_its_init(struct host_its *hw_its)
     if ( !hw_its->cmd_buf )
         return -ENOMEM;
 
+    its_send_cmd_mapc(hw_its, smp_processor_id(), smp_processor_id());
+    its_send_cmd_sync(hw_its, smp_processor_id());
+
     return 0;
 }
 
+void gicv3_set_redist_addr(paddr_t address, int redist_id)
+{
+    this_cpu(rdist_addr) = address;
+    this_cpu(rdist_id) = redist_id;
+}
+
 uint64_t gicv3_lpi_allocate_pendtable(void)
 {
     uint64_t reg, attr;
@@ -286,6 +385,7 @@ void gicv3_its_dt_init(const struct dt_device_node *node)
         its_data->addr = addr;
         its_data->size = size;
         its_data->dt_node = its;
+        spin_lock_init(&its_data->cmd_lock);
 
         printk("GICv3: Found ITS @0x%lx\n", addr);
 
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index 8ca7da2..d2461cb 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -643,6 +643,8 @@ static int gicv3_rdist_init_lpis(void __iomem * rdist_base)
         return -ENOMEM;
     writeq_relaxed(table_reg, rdist_base + GICR_PROPBASER);
 
+    gicv3_its_setup_collection(smp_processor_id());
+
     return 0;
 }
 
@@ -691,8 +693,21 @@ static int __init gicv3_populate_rdist(void)
 
                 if ( typer & GICR_TYPER_PLPIS )
                 {
+                    paddr_t rdist_addr;
                     int ret;
 
+                    rdist_addr = gicv3.rdist_regions[i].base;
+                    rdist_addr += ptr - gicv3.rdist_regions[i].map_base;
+
+                    /* The ITS refers to redistributors either by their physical
+                     * address or by their ID. Determine those two values and
+                     * let the ITS code store them in per host CPU variables to
+                     * later be able to address those redistributors.
+                     */
+                    gicv3_set_redist_addr(rdist_addr,
+                                          (typer >> GITS_TYPER_IDBITS_SHIFT) &
+                                          GENMASK(15, 0));
+
                     ret = gicv3_rdist_init_lpis(ptr);
                     if ( ret && ret != -ENODEV )
                     {
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index e0fded8..68e5f63 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -38,6 +38,8 @@
 
 /* Register bits */
 #define GITS_CTLR_ENABLE                BIT_ULL(0)
+#define GITS_TYPER_PTA                  BIT_ULL(19)
+#define GITS_TYPER_IDBITS_SHIFT         8
 #define GITS_IIDR_VALUE                 0x34c
 
 #define GITS_BASER_VALID                BIT_ULL(63)
@@ -63,6 +65,22 @@
 
 #define GITS_CBASER_SIZE_MASK           0xff
 
+/* ITS command definitions */
+#define ITS_CMD_SIZE                    32
+
+#define GITS_CMD_MOVI                   0x01
+#define GITS_CMD_INT                    0x03
+#define GITS_CMD_CLEAR                  0x04
+#define GITS_CMD_SYNC                   0x05
+#define GITS_CMD_MAPD                   0x08
+#define GITS_CMD_MAPC                   0x09
+#define GITS_CMD_MAPTI                  0x0a
+#define GITS_CMD_MAPI                   0x0b
+#define GITS_CMD_INV                    0x0c
+#define GITS_CMD_INVALL                 0x0d
+#define GITS_CMD_MOVALL                 0x0e
+#define GITS_CMD_DISCARD                0x0f
+
 #ifndef __ASSEMBLY__
 #include <xen/device_tree.h>
 
@@ -73,7 +91,9 @@ struct host_its {
     paddr_t addr;
     paddr_t size;
     void __iomem *its_base;
+    spinlock_t cmd_lock;
     void *cmd_buf;
+    bool pta;
 };
 
 extern struct list_head host_its_list;
@@ -93,6 +113,12 @@ uint64_t gicv3_lpi_allocate_pendtable(void);
 int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
 int gicv3_its_init(struct host_its *hw_its);
 
+/* Set the physical address and ID for each redistributor as read from DT. */
+void gicv3_set_redist_addr(paddr_t address, int redist_id);
+
+/* Map a collection for this host CPU to each host ITS. */
+void gicv3_its_setup_collection(int cpu);
+
 #else
 
 static inline void gicv3_its_dt_init(const struct dt_device_node *node)
@@ -114,6 +140,13 @@ static inline int gicv3_its_init(struct host_its *hw_its)
 {
     return 0;
 }
+static inline void gicv3_set_redist_addr(paddr_t address, int redist_id)
+{
+}
+static inline void gicv3_its_setup_collection(int cpu)
+{
+}
+
 #endif /* CONFIG_HAS_ITS */
 
 #endif /* __ASSEMBLY__ */
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (4 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 05/26] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05  0:13   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
                   ` (21 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The ITS uses device IDs to map LPIs to a device. Dom0 will later use
those IDs, which we directly pass on to the host.
For this we have to map each device that Dom0 may request to a host
ITS device with the same identifier.
Allocate the respective memory and enter each device into a list to
later be able to iterate over it or to easily teardown guests.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c        | 118 ++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic.c           |   3 ++
 xen/include/asm-arm/domain.h  |   2 +
 xen/include/asm-arm/gic-its.h |  22 ++++++++
 4 files changed, 145 insertions(+)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index d0f5fd1..e157c6b 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -21,6 +21,7 @@
 #include <xen/err.h>
 #include <xen/device_tree.h>
 #include <xen/libfdt/libfdt.h>
+#include <xen/sched.h>
 #include <xen/sizes.h>
 #include <asm/p2m.h>
 #include <asm/io.h>
@@ -108,6 +109,21 @@ static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
     return its_send_command(its, cmd);
 }
 
+static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
+                             int size, uint64_t itt_addr, bool valid)
+{
+    uint64_t cmd[4];
+
+    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
+    cmd[1] = size & GENMASK(4, 0);
+    cmd[2] = itt_addr & GENMASK(51, 8);
+    if ( valid )
+        cmd[2] |= BIT_ULL(63);
+    cmd[3] = 0x00;
+
+    return its_send_command(its, cmd);
+}
+
 /* Set up the (1:1) collection mapping for the given host CPU. */
 void gicv3_its_setup_collection(int cpu)
 {
@@ -237,6 +253,7 @@ int gicv3_its_init(struct host_its *hw_its)
 
     reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
     hw_its->pta = !!(reg & GITS_TYPER_PTA);
+    hw_its->itte_size = ((reg >> 4) & 0xf) + 1;
 
     for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
     {
@@ -358,6 +375,107 @@ int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
     return 0;
 }
 
+static void remove_mapped_guest_device(struct its_devices *dev)
+{
+    if ( dev->hw_its )
+        its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
+
+    xfree(dev->itt_addr);
+    xfree(dev);
+}
+
+int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
+                         int bits, bool valid)
+{
+    void *itt_addr = NULL;
+    struct its_devices *dev, *temp;
+    struct host_its *hw_its;
+    int ret;
+
+    /* check for already existing mappings */
+    spin_lock(&d->arch.vgic.its_devices_lock);
+    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
+    {
+        if ( dev->guest_devid != guest_devid )
+            continue;
+
+        if ( !valid )
+            list_del(&dev->entry);
+
+        spin_unlock(&d->arch.vgic.its_devices_lock);
+
+        if ( valid )
+            return -EBUSY;
+
+        remove_mapped_guest_device(dev);
+
+        return 0;
+    }
+    spin_unlock(&d->arch.vgic.its_devices_lock);
+
+    if ( !valid )
+        return -ENOENT;
+
+    /* TODO: Work out the correct hardware ITS to use here.
+     * Maybe a per-platform function: devid -> ITS?
+     * Or parsing the DT to find the msi_parent?
+     * Or get Dom0 to give us this information?
+     * For now just use the first ITS.
+     */
+    hw_its = list_first_entry(&host_its_list, struct host_its, entry);
+
+    itt_addr = _xmalloc(BIT(bits) * hw_its->itte_size, 256);
+    if ( !itt_addr )
+        return -ENOMEM;
+
+    dev = xmalloc(struct its_devices);
+    if ( !dev )
+    {
+        xfree(itt_addr);
+        return -ENOMEM;
+    }
+
+    ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
+                            virt_to_maddr(itt_addr), true);
+    if (ret) {
+        xfree(itt_addr);
+        xfree(dev);
+        return ret;
+    }
+
+    dev->itt_addr = itt_addr;
+    dev->hw_its = hw_its;
+    dev->guest_devid = guest_devid;
+    dev->host_devid = host_devid;
+    dev->eventids = BIT(bits);
+
+    spin_lock(&d->arch.vgic.its_devices_lock);
+    list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
+    spin_unlock(&d->arch.vgic.its_devices_lock);
+
+    return 0;
+}
+
+/* Removing any connections a domain had to any ITS in the system. */
+int its_remove_domain(struct domain *d)
+{
+    struct its_devices *dev, *temp;
+
+retry:
+
+    spin_lock(&d->arch.vgic.its_devices_lock);
+    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
+    {
+        list_del(&dev->entry);
+        spin_unlock(&d->arch.vgic.its_devices_lock);
+
+        remove_mapped_guest_device(dev);
+        goto retry;
+    }
+
+    return 0;
+}
+
 /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
 void gicv3_its_dt_init(const struct dt_device_node *node)
 {
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index 364d5f0..de77aaa 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -156,6 +156,9 @@ int domain_vgic_init(struct domain *d, unsigned int nr_spis)
     for ( i = 0; i < NR_GIC_SGI; i++ )
         set_bit(i, d->arch.vgic.allocated_irqs);
 
+    spin_lock_init(&d->arch.vgic.its_devices_lock);
+    INIT_LIST_HEAD(&d->arch.vgic.its_devices);
+
     return 0;
 }
 
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 2d6fbb1..8ccc32a 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -109,6 +109,8 @@ struct arch_domain
         } *rdist_regions;
         int nr_regions;                     /* Number of rdist regions */
         uint32_t rdist_stride;              /* Re-Distributor stride */
+        struct list_head its_devices;       /* devices mapped to an ITS */
+        spinlock_t its_devices_lock;        /* protects the its_devices list */
 #endif
     } vgic;
 
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 68e5f63..525a29d 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -93,9 +93,19 @@ struct host_its {
     void __iomem *its_base;
     spinlock_t cmd_lock;
     void *cmd_buf;
+    int itte_size;
     bool pta;
 };
 
+struct its_devices {
+    struct list_head entry;
+    struct host_its *hw_its;
+    void *itt_addr;
+    uint32_t guest_devid;
+    uint32_t host_devid;
+    uint32_t eventids;
+};
+
 extern struct list_head host_its_list;
 
 #ifdef CONFIG_HAS_ITS
@@ -119,6 +129,13 @@ void gicv3_set_redist_addr(paddr_t address, int redist_id);
 /* Map a collection for this host CPU to each host ITS. */
 void gicv3_its_setup_collection(int cpu);
 
+/* Map a device on the host by allocating an ITT on the host (ITS).
+ * "bits" specifies how many events (interrupts) this device will need.
+ * Setting "valid" to false deallocates the device.
+ */
+int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
+                         int bits, bool valid);
+
 #else
 
 static inline void gicv3_its_dt_init(const struct dt_device_node *node)
@@ -146,6 +163,11 @@ static inline void gicv3_set_redist_addr(paddr_t address, int redist_id)
 static inline void gicv3_its_setup_collection(int cpu)
 {
 }
+static inline int gicv3_its_map_device(struct domain *d, int host_devid,
+                         int guest_devid, int bits, bool valid)
+{
+    return -ENODEV;
+}
 
 #endif /* CONFIG_HAS_ITS */
 
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (5 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05 18:56   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 08/26] ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall Andre Przywara
                   ` (20 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The number of LPIs on a host can be potentially huge (millions),
although in practise will be mostly reasonable. So prematurely allocating
an array of struct irq_desc's for each LPI is not an option.
However Xen itself does not care about LPIs, as every LPI will be injected
into a guest (Dom0 for now).
Create a dense data structure (8 Bytes) for each LPI which holds just
enough information to determine the virtual IRQ number and the VCPU into
which the LPI needs to be injected.
Also to not artificially limit the number of LPIs, we create a 2-level
table for holding those structures.
This patch introduces functions to initialize these tables and to
create, lookup and destroy entries for a given LPI.
We allocate and access LPI information in a way that does not require
a lock.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c        | 233 +++++++++++++++++++++++++++++++++++++++++-
 xen/include/asm-arm/gic-its.h |   1 +
 2 files changed, 233 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index e157c6b..e7ddd90 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -18,21 +18,36 @@
 
 #include <xen/config.h>
 #include <xen/lib.h>
+#include <xen/sched.h>
 #include <xen/err.h>
 #include <xen/device_tree.h>
 #include <xen/libfdt/libfdt.h>
 #include <xen/sched.h>
 #include <xen/sizes.h>
 #include <asm/p2m.h>
+#include <asm/domain.h>
 #include <asm/io.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic-its.h>
 
+/* LPIs on the host always go to a guest, so no struct irq_desc for them. */
+union host_lpi {
+    uint64_t data;
+    struct {
+        uint64_t virt_lpi:32;
+        uint64_t dom_id:16;
+        uint64_t vcpu_id:16;
+    };
+};
+
 /* Global state */
 static struct {
     uint8_t *lpi_property;
+    union host_lpi **host_lpis;
     unsigned int host_lpi_bits;
+    /* Protects allocation and deallocation of host LPIs, but not the access */
+    spinlock_t host_lpis_lock;
 } lpi_data;
 
 /* Physical redistributor address */
@@ -43,6 +58,19 @@ static DEFINE_PER_CPU(uint64_t, rdist_id);
 static DEFINE_PER_CPU(void *, pending_table);
 
 #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
+#define HOST_LPIS_PER_PAGE      (PAGE_SIZE / sizeof(union host_lpi))
+
+static union host_lpi *gic_get_host_lpi(uint32_t plpi)
+{
+    if ( plpi < 8192 || plpi >= MAX_PHYS_LPIS + 8192 )
+        return NULL;
+
+    plpi -= 8192;
+    if ( !lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE] )
+        return NULL;
+
+    return &lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE][plpi % HOST_LPIS_PER_PAGE];
+}
 
 #define ITS_CMD_QUEUE_SZ                                SZ_64K
 
@@ -96,6 +124,20 @@ static int its_send_cmd_sync(struct host_its *its, int cpu)
     return its_send_command(its, cmd);
 }
 
+static int its_send_cmd_mapti(struct host_its *its,
+                              uint32_t deviceid, uint32_t eventid,
+                              uint32_t pintid, uint16_t icid)
+{
+    uint64_t cmd[4];
+
+    cmd[0] = GITS_CMD_MAPTI | ((uint64_t)deviceid << 32);
+    cmd[1] = eventid | ((uint64_t)pintid << 32);
+    cmd[2] = icid;
+    cmd[3] = 0x00;
+
+    return its_send_command(its, cmd);
+}
+
 static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
 {
     uint64_t cmd[4];
@@ -124,6 +166,19 @@ static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
     return its_send_command(its, cmd);
 }
 
+static int its_send_cmd_inv(struct host_its *its,
+                            uint32_t deviceid, uint32_t eventid)
+{
+    uint64_t cmd[4];
+
+    cmd[0] = GITS_CMD_INV | ((uint64_t)deviceid << 32);
+    cmd[1] = eventid;
+    cmd[2] = 0x00;
+    cmd[3] = 0x00;
+
+    return its_send_command(its, cmd);
+}
+
 /* Set up the (1:1) collection mapping for the given host CPU. */
 void gicv3_its_setup_collection(int cpu)
 {
@@ -366,21 +421,181 @@ uint64_t gicv3_lpi_get_proptable()
 static unsigned int max_lpi_bits = CONFIG_MAX_HOST_LPI_BITS;
 integer_param("max_lpi_bits", max_lpi_bits);
 
+/* Allocate the 2nd level array for host LPIs. This one holds pointers
+ * to the page with the actual "union host_lpi" entries. Our LPI limit
+ * avoids excessive memory usage.
+ */
 int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
 {
+    int nr_lpi_ptrs;
+
     lpi_data.host_lpi_bits = min(hw_lpi_bits, max_lpi_bits);
 
+    spin_lock_init(&lpi_data.host_lpis_lock);
+
+    nr_lpi_ptrs = MAX_PHYS_LPIS / (PAGE_SIZE / sizeof(union host_lpi));
+    lpi_data.host_lpis = xzalloc_array(union host_lpi *, nr_lpi_ptrs);
+    if ( !lpi_data.host_lpis )
+        return -ENOMEM;
+
     printk("GICv3: using at most %lld LPIs on the host.\n", MAX_PHYS_LPIS);
 
     return 0;
 }
 
+#define INVALID_DOMID ((uint16_t)~0)
+#define LPI_BLOCK       32
+
+/* Must be called with host_lpis_lock held. */
+static int find_unused_host_lpi(int start, uint32_t *index)
+{
+    int chunk;
+    uint32_t i = *index;
+
+    for ( chunk = start; chunk < MAX_PHYS_LPIS / HOST_LPIS_PER_PAGE; chunk++ )
+    {
+        /* If we hit an unallocated chunk, use entry 0 in that one. */
+        if ( !lpi_data.host_lpis[chunk] )
+        {
+            *index = 0;
+            return chunk;
+        }
+
+        /* Find an unallocated entry in this chunk. */
+        for ( ; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
+        {
+            if ( lpi_data.host_lpis[chunk][i].dom_id == INVALID_DOMID )
+            {
+                *index = i;
+                return chunk;
+            }
+        }
+        i = 0;
+    }
+
+    return -1;
+}
+
+/*
+ * Allocate a block of 32 LPIs on the given host ITS for device "devid",
+ * starting with "eventid". Put them into the respective ITT by issuing a
+ * MAPTI command for each of them.
+ */
+static int gicv3_allocate_host_lpi_block(struct host_its *its, struct domain *d,
+                                         uint32_t host_devid, uint32_t eventid)
+{
+    static uint32_t next_lpi = 0;
+    uint32_t lpi, lpi_idx = next_lpi % HOST_LPIS_PER_PAGE;
+    int chunk;
+    int i;
+
+    spin_lock(&lpi_data.host_lpis_lock);
+    chunk = find_unused_host_lpi(next_lpi / HOST_LPIS_PER_PAGE, &lpi_idx);
+
+    if ( chunk == - 1 )          /* rescan for a hole from the beginning */
+    {
+        lpi_idx = 0;
+        chunk = find_unused_host_lpi(0, &lpi_idx);
+        if ( chunk == -1 )
+        {
+            spin_unlock(&lpi_data.host_lpis_lock);
+            return -ENOSPC;
+        }
+    }
+
+    /* If we hit an unallocated chunk, we initialize it and use entry 0. */
+    if ( !lpi_data.host_lpis[chunk] )
+    {
+        union host_lpi *new_chunk;
+
+        new_chunk = alloc_xenheap_pages(0, 0);
+        if ( !new_chunk )
+        {
+            spin_unlock(&lpi_data.host_lpis_lock);
+            return -ENOMEM;
+        }
+
+        for ( i = 0; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
+            new_chunk[i].dom_id = INVALID_DOMID;
+
+        lpi_data.host_lpis[chunk] = new_chunk;
+        lpi_idx = 0;
+    }
+
+    lpi = chunk * HOST_LPIS_PER_PAGE + lpi_idx;
+
+    for ( i = 0; i < LPI_BLOCK; i++ )
+    {
+        union host_lpi hlpi;
+
+        /*
+         * Mark this host LPI as belonging to the domain, but don't assign
+         * any virtual LPI or a VCPU yet.
+         */
+        hlpi.virt_lpi = 0;
+        hlpi.dom_id = d->domain_id;
+        hlpi.vcpu_id = ~0;
+        lpi_data.host_lpis[chunk][lpi_idx + i].data = hlpi.data;
+
+        /*
+         * Enable this host LPI, so we don't have to do this during the
+         * guest's runtime.
+         */
+        lpi_data.lpi_property[lpi + i] |= LPI_PROP_ENABLED;
+    }
+
+    /* We have allocated and initialized the host LPI entries, so it's safe
+     * to drop the lock now. Access to the structures can be done concurrently
+     * as it involves only an atomic uint64_t access.
+     */
+    spin_unlock(&lpi_data.host_lpis_lock);
+
+    __flush_dcache_area(&lpi_data.lpi_property[lpi], LPI_BLOCK);
+
+    /* Tell the redistributor about the changed enabled bits and map the LPIs */
+    for ( i = 0; i < LPI_BLOCK; i++ )
+    {
+        its_send_cmd_mapti(its, host_devid, eventid + i, lpi + i + 8192, 0);
+        its_send_cmd_inv(its, host_devid, eventid + i);
+    }
+
+    its_send_cmd_sync(its, 0);
+
+    next_lpi = lpi + LPI_BLOCK;
+    return lpi + 8192;
+}
+
+static int gicv3_free_host_lpi_block(struct host_its *its, uint32_t lpi)
+{
+    union host_lpi *hlpi, empty_lpi = { .dom_id = INVALID_DOMID };
+    int i;
+
+    hlpi = gic_get_host_lpi(lpi);
+    if ( !hlpi )
+        return -ENOENT;
+
+    spin_lock(&lpi_data.host_lpis_lock);
+
+    for ( i = 0; i < LPI_BLOCK; i++ )
+        hlpi[i].data = empty_lpi.data;
+
+    spin_unlock(&lpi_data.host_lpis_lock);
+
+    return 0;
+}
+
 static void remove_mapped_guest_device(struct its_devices *dev)
 {
+    int i;
+
     if ( dev->hw_its )
         its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
 
+    for ( i = 0; i < dev->eventids / 32; i++ )
+        gicv3_free_host_lpi_block(dev->hw_its, dev->host_lpis[i]);
+
     xfree(dev->itt_addr);
+    xfree(dev->host_lpis);
     xfree(dev);
 }
 
@@ -390,7 +605,7 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
     void *itt_addr = NULL;
     struct its_devices *dev, *temp;
     struct host_its *hw_its;
-    int ret;
+    int ret, i;
 
     /* check for already existing mappings */
     spin_lock(&d->arch.vgic.its_devices_lock);
@@ -435,9 +650,18 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
         return -ENOMEM;
     }
 
+    dev->host_lpis = xzalloc_array(uint32_t, BIT(bits) / 32);
+    if ( !dev->host_lpis )
+    {
+        xfree(dev);
+        xfree(itt_addr);
+        return -ENOMEM;
+    }
+
     ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
                             virt_to_maddr(itt_addr), true);
     if (ret) {
+        xfree(dev->host_lpis);
         xfree(itt_addr);
         xfree(dev);
         return ret;
@@ -453,6 +677,13 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
     list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
     spin_unlock(&d->arch.vgic.its_devices_lock);
 
+    /* Map all host LPIs within this device already. We can't afford to queue
+     * any host ITS commands later on during the guest's runtime.
+     */
+    for ( i = 0; i < BIT(bits) / 32; i++ )
+        dev->host_lpis[i] = gicv3_allocate_host_lpi_block(hw_its, d, host_devid,
+                                                          i * 32);
+
     return 0;
 }
 
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 525a29d..d1ebc19 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -104,6 +104,7 @@ struct its_devices {
     uint32_t guest_devid;
     uint32_t host_devid;
     uint32_t eventids;
+    uint32_t *host_lpis;
 };
 
 extern struct list_head host_its_list;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 08/26] ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (6 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05 21:23   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
                   ` (19 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

To get MSIs from devices forwarded to a CPU, we need to name the device
and its MSIs by mapping them to an ITS.
Since this involves queueing commands to the ITS command queue, we can't
really afford to do this during the guest's runtime, as this would open
up a denial-of-service attack vector.
So we require every device with MSI interrupts to be mapped explicitly by
Dom0. For Dom0 itself we can just use the existing PCI physdev_op
hypercalls, which the existing Linux kernel issues already.
So upon receipt of this hypercall we map the device to the hardware ITS
and prepare it to be later mapped by the virtual ITS by using the very
same device ID (for Dom0 only).
Also we ask for mapping 32 LPIs to cover 32 MSIs that the device may
use.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/physdev.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/xen/arch/arm/physdev.c b/xen/arch/arm/physdev.c
index 27bbbda..d9e6be3 100644
--- a/xen/arch/arm/physdev.c
+++ b/xen/arch/arm/physdev.c
@@ -9,11 +9,35 @@
 #include <xen/lib.h>
 #include <xen/errno.h>
 #include <xen/sched.h>
+#include <xen/guest_access.h>
+#include <asm/gic-its.h>
 #include <asm/hypercall.h>
 
 
 int do_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
 {
+    struct physdev_manage_pci manage;
+    struct domain *dom0;
+    u32 devid;
+    int ret;
+
+    switch (cmd)
+    {
+        case PHYSDEVOP_manage_pci_add:
+        case PHYSDEVOP_manage_pci_remove:
+            if ( copy_from_guest(&manage, arg, 1) != 0 )
+                return -EFAULT;
+
+            dom0 = hardware_domain;
+            devid = manage.bus << 8 | manage.devfn;
+            /* Allocate an ITS device table with space for 32 MSIs */
+            ret = gicv3_its_map_device(dom0, devid, devid, 5,
+                                       cmd == PHYSDEVOP_manage_pci_add);
+
+            put_domain(dom0);
+            return ret;
+    }
+
     gdprintk(XENLOG_DEBUG, "PHYSDEVOP cmd=%d: not implemented\n", cmd);
     return -ENOSYS;
 }
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (7 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 08/26] ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05 21:36   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests Andre Przywara
                   ` (18 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

For the same reason that allocating a struct irq_desc for each
possible LPI is not an option, having a struct pending_irq for each LPI
is also not feasible. However we actually only need those when an
interrupt is on a vCPU (or is about to be injected).
Maintain a list of those structs that we can use for the lifecycle of
a guest LPI. We allocate new entries if necessary, however reuse
pre-owned entries whenever possible.
I added some locking around this list here, however my gut feeling is
that we don't need one because this a per-VCPU structure anyway.
If someone could confirm this, I'd be grateful.
Teach the existing VGIC functions to find the right pointer when being
given a virtual LPI number.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic.c           |  3 +++
 xen/arch/arm/vgic-v3.c       | 11 ++++++++
 xen/arch/arm/vgic.c          | 64 +++++++++++++++++++++++++++++++++++++++++---
 xen/include/asm-arm/domain.h |  2 ++
 xen/include/asm-arm/vgic.h   | 10 +++++++
 5 files changed, 87 insertions(+), 3 deletions(-)

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index a5348f2..6f25501 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -509,6 +509,9 @@ static void gic_update_one_lr(struct vcpu *v, int i)
                 struct vcpu *v_target = vgic_get_target_vcpu(v, irq);
                 irq_set_affinity(p->desc, cpumask_of(v_target->processor));
             }
+            /* If this was an LPI, mark this struct as available again. */
+            if ( p->irq >= 8192 )
+                p->irq = 0;
         }
     }
 }
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index d61479d..0ffde74 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -331,6 +331,14 @@ read_unknown:
     return 1;
 }
 
+int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi)
+{
+    if ( vlpi >= d->arch.vgic.nr_lpis )
+        return GIC_PRI_IRQ;
+
+    return d->arch.vgic.proptable[vlpi - 8192] & 0xfc;
+}
+
 static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
                                           uint32_t gicr_reg,
                                           register_t r)
@@ -1426,6 +1434,9 @@ static int vgic_v3_vcpu_init(struct vcpu *v)
     if ( v->vcpu_id == last_cpu || (v->vcpu_id == (d->max_vcpus - 1)) )
         v->arch.vgic.flags |= VGIC_V3_RDIST_LAST;
 
+    spin_lock_init(&v->arch.vgic.pending_lpi_list_lock);
+    INIT_LIST_HEAD(&v->arch.vgic.pending_lpi_list);
+
     return 0;
 }
 
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index de77aaa..f15eb3e 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -31,6 +31,8 @@
 #include <asm/mmio.h>
 #include <asm/gic.h>
 #include <asm/vgic.h>
+#include <asm/gic_v3_defs.h>
+#include <asm/gic-its.h>
 
 static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v, int rank)
 {
@@ -61,7 +63,7 @@ struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq)
     return vgic_get_rank(v, rank);
 }
 
-static void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq)
+void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq)
 {
     INIT_LIST_HEAD(&p->inflight);
     INIT_LIST_HEAD(&p->lr_queue);
@@ -247,10 +249,14 @@ struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq)
 
 static int vgic_get_virq_priority(struct vcpu *v, unsigned int virq)
 {
-    struct vgic_irq_rank *rank = vgic_rank_irq(v, virq);
+    struct vgic_irq_rank *rank;
     unsigned long flags;
     int priority;
 
+    if ( virq >= 8192 )
+        return vgic_lpi_get_priority(v->domain, virq);
+
+    rank = vgic_rank_irq(v, virq);
     vgic_lock_rank(v, rank, flags);
     priority = rank->priority[virq & INTERRUPT_RANK_MASK];
     vgic_unlock_rank(v, rank, flags);
@@ -449,13 +455,63 @@ bool vgic_to_sgi(struct vcpu *v, register_t sgir, enum gic_sgi_mode irqmode,
     return true;
 }
 
+/*
+ * Holding struct pending_irq's for each possible virtual LPI in each domain
+ * requires too much Xen memory, also a malicious guest could potentially
+ * spam Xen with LPI map requests. We cannot cover those with (guest allocated)
+ * ITS memory, so we use a dynamic scheme of allocating struct pending_irq's
+ * on demand.
+ */
+struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int lpi,
+                                   bool allocate)
+{
+    struct lpi_pending_irq *lpi_irq, *empty = NULL;
+
+    spin_lock(&v->arch.vgic.pending_lpi_list_lock);
+    list_for_each_entry(lpi_irq, &v->arch.vgic.pending_lpi_list, entry)
+    {
+        if ( lpi_irq->pirq.irq == lpi )
+        {
+            spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
+            return &lpi_irq->pirq;
+        }
+
+        if ( lpi_irq->pirq.irq == 0 && !empty )
+            empty = lpi_irq;
+    }
+
+    if ( !allocate )
+    {
+        spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
+        return NULL;
+    }
+
+    if ( !empty )
+    {
+        empty = xzalloc(struct lpi_pending_irq);
+        vgic_init_pending_irq(&empty->pirq, lpi);
+        list_add_tail(&empty->entry, &v->arch.vgic.pending_lpi_list);
+    } else
+    {
+        empty->pirq.status = 0;
+        empty->pirq.irq = lpi;
+    }
+
+    spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
+
+    return &empty->pirq;
+}
+
 struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq)
 {
     struct pending_irq *n;
+
     /* Pending irqs allocation strategy: the first vgic.nr_spis irqs
      * are used for SPIs; the rests are used for per cpu irqs */
     if ( irq < 32 )
         n = &v->arch.vgic.pending_irqs[irq];
+    else if ( irq >= 8192 )
+        n = lpi_to_pending(v, irq, true);
     else
         n = &v->domain->arch.vgic.pending_irqs[irq - 32];
     return n;
@@ -483,7 +539,7 @@ void vgic_clear_pending_irqs(struct vcpu *v)
 void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
 {
     uint8_t priority;
-    struct pending_irq *iter, *n = irq_to_pending(v, virq);
+    struct pending_irq *iter, *n;
     unsigned long flags;
     bool running;
 
@@ -491,6 +547,8 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
 
     spin_lock_irqsave(&v->arch.vgic.lock, flags);
 
+    n = irq_to_pending(v, virq);
+
     /* vcpu offline */
     if ( test_bit(_VPF_down, &v->pause_flags) )
     {
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 8ccc32a..02ae2cd 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -256,6 +256,8 @@ struct arch_vcpu
         paddr_t rdist_base;
 #define VGIC_V3_RDIST_LAST  (1 << 0)        /* last vCPU of the rdist */
         uint8_t flags;
+        struct list_head pending_lpi_list;
+        spinlock_t pending_lpi_list_lock;   /* protects the pending_lpi_list */
     } vgic;
 
     /* Timer registers  */
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 672f649..a503321 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -83,6 +83,12 @@ struct pending_irq
     struct list_head lr_queue;
 };
 
+struct lpi_pending_irq
+{
+    struct list_head entry;
+    struct pending_irq pirq;
+};
+
 #define NR_INTERRUPT_PER_RANK   32
 #define INTERRUPT_RANK_MASK (NR_INTERRUPT_PER_RANK - 1)
 
@@ -296,13 +302,17 @@ extern struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq);
 extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq);
 extern void vgic_vcpu_inject_spi(struct domain *d, unsigned int virq);
 extern void vgic_clear_pending_irqs(struct vcpu *v);
+extern void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq);
 extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq);
 extern struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq);
+extern struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int irq,
+                                          bool allocate);
 extern struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
 extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq);
 extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
 extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
 extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
+extern int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi);
 extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
 int vgic_v2_init(struct domain *d, int *mmio_count);
 int vgic_v3_init(struct domain *d, int *mmio_count);
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (8 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05 22:10   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 11/26] ARM: GICv3: enable ITS and LPIs on the host Andre Przywara
                   ` (17 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Upon receiving an LPI, we need to find the right VCPU and virtual IRQ
number to get this IRQ injected.
Iterate our two-level LPI table to find this information quickly when
the host takes an LPI. Call the existing injection function to let the
GIC emulation deal with this interrupt.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c    | 35 +++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic.c        |  6 ++++--
 xen/include/asm-arm/irq.h |  8 ++++++++
 3 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index e7ddd90..0d4ca1b 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -72,6 +72,41 @@ static union host_lpi *gic_get_host_lpi(uint32_t plpi)
     return &lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE][plpi % HOST_LPIS_PER_PAGE];
 }
 
+/* Handle incoming LPIs, which are a bit special, because they are potentially
+ * numerous and also only get injected into guests. Treat them specially here,
+ * by just looking up their target vCPU and virtual LPI number and hand it
+ * over to the injection function.
+ */
+void do_LPI(unsigned int lpi)
+{
+    struct domain *d;
+    union host_lpi *hlpip, hlpi;
+    struct vcpu *vcpu;
+
+    WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
+
+    hlpip = gic_get_host_lpi(lpi);
+    if ( !hlpip )
+        return;
+
+    hlpi.data = hlpip->data;
+
+    /* We may have mapped more host LPIs than the guest actually asked for. */
+    if ( !hlpi.virt_lpi )
+        return;
+
+    d = get_domain_by_id(hlpi.dom_id);
+    if ( !d )
+        return;
+
+    if ( hlpi.vcpu_id >= d->max_vcpus )
+        return;
+
+    vcpu = d->vcpu[hlpi.vcpu_id];
+
+    vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
+}
+
 #define ITS_CMD_QUEUE_SZ                                SZ_64K
 
 static int its_send_command(struct host_its *hw_its, void *its_cmd)
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 6f25501..7d428dc 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -700,8 +700,10 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
             local_irq_enable();
             do_IRQ(regs, irq, is_fiq);
             local_irq_disable();
-        }
-        else if (unlikely(irq < 16))
+        } else if ( irq >= 8192 )
+        {
+            do_LPI(irq);
+        } else if ( unlikely(irq < 16) )
         {
             do_sgi(regs, irq);
         }
diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
index 8f7a167..ee47de8 100644
--- a/xen/include/asm-arm/irq.h
+++ b/xen/include/asm-arm/irq.h
@@ -34,6 +34,14 @@ struct irq_desc *__irq_to_desc(int irq);
 
 void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq);
 
+#ifdef CONFIG_HAS_ITS
+void do_LPI(unsigned int irq);
+#else
+static inline void do_LPI(unsigned int irq)
+{
+}
+#endif
+
 #define domain_pirq_to_irq(d, pirq) (pirq)
 
 bool_t is_assignable_irq(unsigned int irq);
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 11/26] ARM: GICv3: enable ITS and LPIs on the host
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (9 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 12/26] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Now that the host part of the ITS code is in place, we can enable the
ITS and also LPIs on each redistributor to get the show rolling.
At this point there would be no LPIs mapped, as guests don't know about
the ITS yet.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c |  4 ++++
 xen/arch/arm/gic-v3.c  | 19 +++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 0d4ca1b..602e19a 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -375,6 +375,10 @@ int gicv3_its_init(struct host_its *hw_its)
     its_send_cmd_mapc(hw_its, smp_processor_id(), smp_processor_id());
     its_send_cmd_sync(hw_its, smp_processor_id());
 
+    /* Now enable interrupt translation on that ITS. */
+    reg = readl_relaxed(hw_its->its_base + GITS_CTLR);
+    writel_relaxed(reg | GITS_CTLR_ENABLE, hw_its->its_base + GITS_CTLR);
+
     return 0;
 }
 
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index d2461cb..5bfdf24 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -648,6 +648,21 @@ static int gicv3_rdist_init_lpis(void __iomem * rdist_base)
     return 0;
 }
 
+/* Enable LPIs on this redistributor (only useful when the host has an ITS. */
+static bool gicv3_enable_lpis(void)
+{
+    uint32_t val;
+
+    val = readl_relaxed(GICD_RDIST_BASE + GICR_TYPER);
+    if ( !(val & GICR_TYPER_PLPIS) )
+        return false;
+
+    val = readl_relaxed(GICD_RDIST_BASE + GICR_CTLR);
+    writel_relaxed(val | GICR_CTLR_ENABLE_LPIS, GICD_RDIST_BASE + GICR_CTLR);
+
+    return true;
+}
+
 static int __init gicv3_populate_rdist(void)
 {
     int i;
@@ -755,6 +770,10 @@ static int gicv3_cpu_init(void)
     if ( gicv3_enable_redist() )
         return -ENODEV;
 
+    /* If the host has any ITSes, enable LPIs now. */
+    if ( !list_empty(&host_its_list) )
+        gicv3_enable_lpis();
+
     /* Set priority on PPI and SGI interrupts */
     priority = (GIC_PRI_IPI << 24 | GIC_PRI_IPI << 16 | GIC_PRI_IPI << 8 |
                 GIC_PRI_IPI);
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 12/26] ARM: vGICv3: handle virtual LPI pending and property tables
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (10 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 11/26] ARM: GICv3: enable ITS and LPIs on the host Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05 22:17   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIs Andre Przywara
                   ` (15 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Allow a guest to provide the address and size for the memory regions
it has reserved for the GICv3 pending and property tables.
We sanitise the various fields of the respective redistributor
registers and map those pages into Xen's address space to have easy
access.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-v3.c       | 202 ++++++++++++++++++++++++++++++++++++++++---
 xen/arch/arm/vgic.c          |   4 +
 xen/include/asm-arm/domain.h |   8 +-
 xen/include/asm-arm/vgic.h   |   4 +
 4 files changed, 203 insertions(+), 15 deletions(-)

diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 0ffde74..b981d4e 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -20,12 +20,14 @@
 
 #include <xen/bitops.h>
 #include <xen/config.h>
+#include <xen/domain_page.h>
 #include <xen/lib.h>
 #include <xen/init.h>
 #include <xen/softirq.h>
 #include <xen/irq.h>
 #include <xen/sched.h>
 #include <xen/sizes.h>
+#include <xen/vmap.h>
 #include <asm/current.h>
 #include <asm/mmio.h>
 #include <asm/gic_v3_defs.h>
@@ -229,12 +231,14 @@ static int __vgic_v3_rdistr_rd_mmio_read(struct vcpu *v, mmio_info_t *info,
         goto read_reserved;
 
     case VREG64(GICR_PROPBASER):
-        /* LPI's not implemented */
-        goto read_as_zero_64;
+        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
+        *r = vgic_reg64_extract(v->domain->arch.vgic.rdist_propbase, info);
+        return 1;
 
     case VREG64(GICR_PENDBASER):
-        /* LPI's not implemented */
-        goto read_as_zero_64;
+        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
+        *r = vgic_reg64_extract(v->arch.vgic.rdist_pendbase, info);
+        return 1;
 
     case 0x0080:
         goto read_reserved;
@@ -302,11 +306,6 @@ bad_width:
     domain_crash_synchronous();
     return 0;
 
-read_as_zero_64:
-    if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
-    *r = 0;
-    return 1;
-
 read_as_zero_32:
     if ( dabt.size != DABT_WORD ) goto bad_width;
     *r = 0;
@@ -331,6 +330,143 @@ read_unknown:
     return 1;
 }
 
+static uint64_t vgic_sanitise_field(uint64_t reg, uint64_t field_mask,
+                                    int field_shift,
+                                    uint64_t (*sanitise_fn)(uint64_t))
+{
+    uint64_t field = (reg & field_mask) >> field_shift;
+
+    field = sanitise_fn(field) << field_shift;
+    return (reg & ~field_mask) | field;
+}
+
+/* We want to avoid outer shareable. */
+static uint64_t vgic_sanitise_shareability(uint64_t field)
+{
+    switch (field) {
+    case GIC_BASER_OuterShareable:
+        return GIC_BASER_InnerShareable;
+    default:
+        return field;
+    }
+}
+
+/* Avoid any inner non-cacheable mapping. */
+static uint64_t vgic_sanitise_inner_cacheability(uint64_t field)
+{
+    switch (field) {
+    case GIC_BASER_CACHE_nCnB:
+    case GIC_BASER_CACHE_nC:
+        return GIC_BASER_CACHE_RaWb;
+    default:
+        return field;
+    }
+}
+
+/* Non-cacheable or same-as-inner are OK. */
+static uint64_t vgic_sanitise_outer_cacheability(uint64_t field)
+{
+    switch (field) {
+    case GIC_BASER_CACHE_SameAsInner:
+    case GIC_BASER_CACHE_nC:
+        return field;
+    default:
+        return GIC_BASER_CACHE_nC;
+    }
+}
+
+static uint64_t sanitize_propbaser(uint64_t reg)
+{
+    reg = vgic_sanitise_field(reg, GICR_PROPBASER_SHAREABILITY_MASK,
+                              GICR_PROPBASER_SHAREABILITY_SHIFT,
+                              vgic_sanitise_shareability);
+    reg = vgic_sanitise_field(reg, GICR_PROPBASER_INNER_CACHEABILITY_MASK,
+                              GICR_PROPBASER_INNER_CACHEABILITY_SHIFT,
+                              vgic_sanitise_inner_cacheability);
+    reg = vgic_sanitise_field(reg, GICR_PROPBASER_OUTER_CACHEABILITY_MASK,
+                              GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT,
+                              vgic_sanitise_outer_cacheability);
+
+    reg &= ~PROPBASER_RES0_MASK;
+    reg &= ~GENMASK(51, 48);
+    return reg;
+}
+
+static uint64_t sanitize_pendbaser(uint64_t reg)
+{
+    reg = vgic_sanitise_field(reg, GICR_PENDBASER_SHAREABILITY_MASK,
+                              GICR_PENDBASER_SHAREABILITY_SHIFT,
+                              vgic_sanitise_shareability);
+    reg = vgic_sanitise_field(reg, GICR_PENDBASER_INNER_CACHEABILITY_MASK,
+                              GICR_PENDBASER_INNER_CACHEABILITY_SHIFT,
+                              vgic_sanitise_inner_cacheability);
+    reg = vgic_sanitise_field(reg, GICR_PENDBASER_OUTER_CACHEABILITY_MASK,
+                              GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT,
+                              vgic_sanitise_outer_cacheability);
+
+    reg &= ~PENDBASER_RES0_MASK;
+    reg &= ~GENMASK(51, 48);
+    return reg;
+}
+
+/*
+ * Allow mapping some parts of guest memory into Xen's VA space to have easy
+ * access to it. This is to allow ITS configuration data to be held in
+ * guest memory and avoid using Xen memory for that.
+ */
+void *map_guest_pages(struct domain *d, paddr_t guest_addr, int nr_pages)
+{
+    mfn_t onepage;
+    mfn_t *pages;
+    int i;
+    void *ptr;
+
+    /* TODO: free previous mapping, change prototype? use get-put-put? */
+
+    guest_addr &= PAGE_MASK;
+
+    if ( nr_pages == 1 )
+    {
+        pages = &onepage;
+    } else
+    {
+        pages = xmalloc_array(mfn_t, nr_pages);
+        if ( !pages )
+            return NULL;
+    }
+
+    for ( i = 0; i < nr_pages; i++ )
+    {
+        get_page_from_gfn(d, (guest_addr >> PAGE_SHIFT) + i, NULL, P2M_ALLOC);
+        pages[i] = _mfn((guest_addr + i * PAGE_SIZE) >> PAGE_SHIFT);
+    }
+
+    ptr = vmap(pages, nr_pages);
+
+    if ( nr_pages > 1 )
+        xfree(pages);
+
+    return ptr;
+}
+
+void unmap_guest_pages(void *va, int nr_pages)
+{
+    paddr_t pa;
+    unsigned long i;
+
+    if ( !va )
+        return;
+
+    va = (void *)((uintptr_t)va & PAGE_MASK);
+    pa = virt_to_maddr(va);
+
+    vunmap(va);
+    for ( i = 0; i < nr_pages; i++ )
+        put_page(mfn_to_page((pa >> PAGE_SHIFT) + i));
+
+    return;
+}
+
 int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi)
 {
     if ( vlpi >= d->arch.vgic.nr_lpis )
@@ -339,11 +475,20 @@ int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi)
     return d->arch.vgic.proptable[vlpi - 8192] & 0xfc;
 }
 
+bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi)
+{
+    if ( vlpi >= d->arch.vgic.nr_lpis )
+        return false;
+
+    return d->arch.vgic.proptable[vlpi - 8192] & LPI_PROP_ENABLED;
+}
+
 static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
                                           uint32_t gicr_reg,
                                           register_t r)
 {
     struct hsr_dabt dabt = info->dabt;
+    uint64_t reg;
 
     switch ( gicr_reg )
     {
@@ -384,13 +529,42 @@ static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
     case 0x0050:
         goto write_reserved;
 
-    case VREG64(GICR_PROPBASER):
-        /* LPI is not implemented */
-        goto write_ignore_64;
+    case VREG64(GICR_PROPBASER): {
+        int nr_pages;
+
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        if ( v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
+            return 1;
 
+        reg = v->domain->arch.vgic.rdist_propbase;
+        if ( v->domain->arch.vgic.proptable )
+        {
+            nr_pages = BIT((reg & 0x1f) + 1) - 8192;
+            nr_pages = DIV_ROUND_UP(nr_pages, PAGE_SIZE);
+            unmap_guest_pages(v->domain->arch.vgic.proptable, nr_pages);
+        }
+        vgic_reg64_update(&reg, r, info);
+        reg = sanitize_propbaser(reg);
+        v->domain->arch.vgic.rdist_propbase = reg;
+
+        v->domain->arch.vgic.nr_lpis = BIT((reg & 0x1f) + 1) - 8192;
+        nr_pages = DIV_ROUND_UP(v->domain->arch.vgic.nr_lpis, PAGE_SIZE);
+        v->domain->arch.vgic.proptable = map_guest_pages(v->domain,
+                                                         reg & GENMASK(47, 12),
+                                                         nr_pages);
+        return 1;
+    }
     case VREG64(GICR_PENDBASER):
-        /* LPI is not implemented */
-        goto write_ignore_64;
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+	reg = v->arch.vgic.rdist_pendbase;
+	vgic_reg64_update(&reg, r, info);
+	reg = sanitize_pendbaser(reg);
+	v->arch.vgic.rdist_pendbase = reg;
+
+        unmap_guest_pages(v->arch.vgic.pendtable, 16);
+	v->arch.vgic.pendtable = map_guest_pages(v->domain,
+                                                 reg & GENMASK(47, 12), 16);
+	return 1;
 
     case 0x0080:
         goto write_reserved;
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index f15eb3e..2a33a0e 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -497,6 +497,10 @@ struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int lpi,
         empty->pirq.irq = lpi;
     }
 
+    /* Update the enabled status */
+    if ( vgic_lpi_is_enabled(v->domain, lpi) )
+        set_bit(GIC_IRQ_GUEST_ENABLED, &empty->pirq.status);
+
     spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
 
     return &empty->pirq;
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 02ae2cd..543d058 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -109,6 +109,9 @@ struct arch_domain
         } *rdist_regions;
         int nr_regions;                     /* Number of rdist regions */
         uint32_t rdist_stride;              /* Re-Distributor stride */
+        int nr_lpis;
+        uint64_t rdist_propbase;
+        uint8_t *proptable;
         struct list_head its_devices;       /* devices mapped to an ITS */
         spinlock_t its_devices_lock;        /* protects the its_devices list */
 #endif
@@ -254,7 +257,10 @@ struct arch_vcpu
 
         /* GICv3: redistributor base and flags for this vCPU */
         paddr_t rdist_base;
-#define VGIC_V3_RDIST_LAST  (1 << 0)        /* last vCPU of the rdist */
+#define VGIC_V3_RDIST_LAST      (1 << 0)        /* last vCPU of the rdist */
+#define VGIC_V3_LPIS_ENABLED    (1 << 1)
+        uint64_t rdist_pendbase;
+        unsigned long *pendtable;
         uint8_t flags;
         struct list_head pending_lpi_list;
         spinlock_t pending_lpi_list_lock;   /* protects the pending_lpi_list */
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index a503321..1c157d3 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -285,6 +285,9 @@ VGIC_REG_HELPERS(32, 0x3);
 
 #undef VGIC_REG_HELPERS
 
+void *map_guest_pages(struct domain *d, paddr_t guest_addr, int nr_pages);
+void unmap_guest_pages(void *va, int nr_pages);
+
 enum gic_sgi_mode;
 
 /*
@@ -312,6 +315,7 @@ extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq);
 extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
 extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
 extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
+extern bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi);
 extern int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi);
 extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
 int vgic_v2_init(struct domain *d, int *mmio_count);
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIs
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (11 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 12/26] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05 22:28   ` [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIso Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 14/26] ARM: vGICv3: introduce basic ITS emulation bits Andre Przywara
                   ` (14 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

If a guest disables an LPI, we do not forward this to the associated
host LPI to avoid queueing commands to the host ITS command queue.
So it may happen that an LPI fires nevertheless on the host. In this
case we can bail out early, but have to save the pending state on the
virtual side.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c     |  8 ++++++++
 xen/arch/arm/vgic-v3.c     | 12 ++++++++++++
 xen/include/asm-arm/vgic.h |  1 +
 3 files changed, 21 insertions(+)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 602e19a..54e604a 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -104,6 +104,14 @@ void do_LPI(unsigned int lpi)
 
     vcpu = d->vcpu[hlpi.vcpu_id];
 
+    /*
+     * We keep all host LPIs enabled, so check if it's disabled on the guest
+     * side and just record this LPI in the virtual pending table in this case.
+     * The guest picks it up once it gets enabled again.
+     */
+    if ( !vgic_can_inject_lpi(vcpu, hlpi.virt_lpi) )
+        return;
+
     vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
 }
 
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index b981d4e..206e00b 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -483,6 +483,18 @@ bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi)
     return d->arch.vgic.proptable[vlpi - 8192] & LPI_PROP_ENABLED;
 }
 
+bool vgic_can_inject_lpi(struct vcpu *vcpu, uint32_t vlpi)
+{
+    if ( vlpi >= vcpu->domain->arch.vgic.nr_lpis )
+        return false;
+
+    if ( vgic_lpi_is_enabled(vcpu->domain, vlpi) )
+        return true;
+
+    set_bit(vlpi - 8192, vcpu->arch.vgic.pendtable);
+    return false;
+}
+
 static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
                                           uint32_t gicr_reg,
                                           register_t r)
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 1c157d3..73291dd 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -317,6 +317,7 @@ extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
 extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
 extern bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi);
 extern int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi);
+extern bool vgic_can_inject_lpi(struct vcpu *v, uint32_t vlpi);
 extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
 int vgic_v2_init(struct domain *d, int *mmio_count);
 int vgic_v3_init(struct domain *d, int *mmio_count);
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 14/26] ARM: vGICv3: introduce basic ITS emulation bits
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (12 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIs Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-02-15  0:02   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 15/26] ARM: vITS: handle CLEAR command Andre Przywara
                   ` (13 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Create a new file to hold the emulation code for the ITS widget.
For now we emulate the memory mapped ITS registers and provide a stub
to introduce the ITS command handling framework (but without actually
emulating any commands at this time).

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/Makefile             |   1 +
 xen/arch/arm/vgic-its.c           | 377 ++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-v3.c            |   9 -
 xen/include/asm-arm/gic_v3_defs.h |  19 ++
 4 files changed, 397 insertions(+), 9 deletions(-)
 create mode 100644 xen/arch/arm/vgic-its.c

diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 80c2951..346454d 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -45,6 +45,7 @@ obj-y += traps.o
 obj-y += vgic.o
 obj-y += vgic-v2.o
 obj-$(CONFIG_HAS_GICV3) += vgic-v3.o
+obj-$(CONFIG_HAS_ITS) += vgic-its.o
 obj-y += vm_event.o
 obj-y += vtimer.o
 obj-y += vpsci.o
diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
new file mode 100644
index 0000000..e5e9ae4
--- /dev/null
+++ b/xen/arch/arm/vgic-its.c
@@ -0,0 +1,377 @@
+/*
+ * xen/arch/arm/vgic-its.c
+ *
+ * ARM Interrupt Translation Service (ITS) emulation
+ *
+ * Andre Przywara <andre.przywara@arm.com>
+ * Copyright (c) 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 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 <xen/bitops.h>
+#include <xen/config.h>
+#include <xen/domain_page.h>
+#include <xen/lib.h>
+#include <xen/init.h>
+#include <xen/softirq.h>
+#include <xen/irq.h>
+#include <xen/sched.h>
+#include <xen/sizes.h>
+#include <asm/current.h>
+#include <asm/mmio.h>
+#include <asm/gic_v3_defs.h>
+#include <asm/gic-its.h>
+#include <asm/vgic.h>
+#include <asm/vgic-emul.h>
+
+/* Data structure to describe a virtual ITS */
+struct virt_its {
+    struct domain *d;
+    struct host_its *hw_its;
+    spinlock_t vcmd_lock;       /* protects the virtual command buffer */
+    uint64_t cbaser;
+    uint64_t *cmdbuf;
+    int cwriter;
+    int creadr;
+    spinlock_t its_lock;        /* protects the collection and device tables */
+    uint64_t baser0, baser1;
+    uint16_t *coll_table;
+    int max_collections;
+    uint64_t *dev_table;
+    int max_devices;
+    bool enabled;
+};
+
+/* An Interrupt Translation Table Entry: this is indexed by a
+ * DeviceID/EventID pair and is located in guest memory.
+ */
+struct vits_itte
+{
+    uint32_t vlpi;
+    uint16_t collection;
+};
+
+/**************************************
+ * Functions that handle ITS commands *
+ **************************************/
+
+static uint64_t its_cmd_mask_field(uint64_t *its_cmd,
+                                   int word, int shift, int size)
+{
+    return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT(size) - 1);
+}
+
+#define its_cmd_get_command(cmd)        its_cmd_mask_field(cmd, 0,  0,  8)
+#define its_cmd_get_deviceid(cmd)       its_cmd_mask_field(cmd, 0, 32, 32)
+#define its_cmd_get_size(cmd)           its_cmd_mask_field(cmd, 1,  0,  5)
+#define its_cmd_get_id(cmd)             its_cmd_mask_field(cmd, 1,  0, 32)
+#define its_cmd_get_physical_id(cmd)    its_cmd_mask_field(cmd, 1, 32, 32)
+#define its_cmd_get_collection(cmd)     its_cmd_mask_field(cmd, 2,  0, 16)
+#define its_cmd_get_target_addr(cmd)    its_cmd_mask_field(cmd, 2, 16, 32)
+#define its_cmd_get_validbit(cmd)       its_cmd_mask_field(cmd, 2, 63,  1)
+
+#define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
+
+static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
+                                uint32_t writer)
+{
+    uint64_t *cmdptr;
+
+    if ( !its->cmdbuf )
+        return -1;
+
+    if ( writer >= ITS_CMD_BUFFER_SIZE(its->cbaser) )
+        return -1;
+
+    spin_lock(&its->vcmd_lock);
+
+    while ( its->creadr != writer )
+    {
+        cmdptr = its->cmdbuf + (its->creadr / sizeof(*its->cmdbuf));
+        switch (its_cmd_get_command(cmdptr))
+        {
+        case GITS_CMD_SYNC:
+            /* We handle ITS commands synchronously, so we ignore SYNC. */
+	    break;
+        default:
+            gdprintk(XENLOG_G_WARNING, "ITS: unhandled ITS command %ld\n",
+                   its_cmd_get_command(cmdptr));
+            break;
+        }
+
+        its->creadr += ITS_CMD_SIZE;
+        if ( its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser) )
+            its->creadr = 0;
+    }
+    its->cwriter = writer;
+
+    spin_unlock(&its->vcmd_lock);
+
+    return 0;
+}
+
+/*****************************
+ * ITS registers read access *
+ *****************************/
+
+/* The physical address is encoded slightly differently depending on
+ * the used page size: the highest four bits are stored in the lowest
+ * four bits of the field for 64K pages.
+ */
+static paddr_t get_baser_phys_addr(uint64_t reg)
+{
+    if ( reg & BIT(9) )
+        return (reg & GENMASK(47, 16)) | ((reg & GENMASK(15, 12)) << 36);
+    else
+        return reg & GENMASK(47, 12);
+}
+
+static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
+                                 register_t *r, void *priv)
+{
+    struct virt_its *its = priv;
+
+    switch ( info->gpa & 0xffff )
+    {
+    case VREG32(GITS_CTLR):
+        if ( info->dabt.size != DABT_WORD ) goto bad_width;
+        *r = vgic_reg32_extract(its->enabled | BIT(31), info);
+	break;
+    case VREG32(GITS_IIDR):
+        if ( info->dabt.size != DABT_WORD ) goto bad_width;
+        *r = vgic_reg32_extract(GITS_IIDR_VALUE, info);
+        break;
+    case VREG64(GITS_TYPER):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        *r = vgic_reg64_extract(0x1eff1, info);
+        break;
+    case VREG64(GITS_CBASER):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        *r = vgic_reg64_extract(its->cbaser, info);
+        break;
+    case VREG64(GITS_CWRITER):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        *r = vgic_reg64_extract(its->cwriter, info);
+        break;
+    case VREG64(GITS_CREADR):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        *r = vgic_reg64_extract(its->creadr, info);
+        break;
+    case VREG64(GITS_BASER0):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        *r = vgic_reg64_extract(its->baser0, info);
+        break;
+    case VREG64(GITS_BASER1):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        *r = vgic_reg64_extract(its->baser1, info);
+        break;
+    case VRANGE64(GITS_BASER2, GITS_BASER7):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        *r = vgic_reg64_extract(0, info);
+        break;
+    case VREG32(GICD_PIDR2):
+        if ( info->dabt.size != DABT_WORD ) goto bad_width;
+        *r = vgic_reg32_extract(GICV3_GICD_PIDR2, info);
+        break;
+    }
+
+    return 1;
+
+bad_width:
+    domain_crash_synchronous();
+
+    return 0;
+}
+
+/******************************
+ * ITS registers write access *
+ ******************************/
+
+static int its_baser_table_size(uint64_t baser)
+{
+    int page_size = 0;
+
+    switch ( (baser >> 8) & 3 )
+    {
+    case 0: page_size = SZ_4K; break;
+    case 1: page_size = SZ_16K; break;
+    case 2:
+    case 3: page_size = SZ_64K; break;
+    }
+
+    return page_size * ((baser & GENMASK(7, 0)) + 1);
+}
+
+static int its_baser_nr_entries(uint64_t baser)
+{
+    int entry_size = ((baser & GENMASK(52, 48)) >> 48) + 1;
+
+    return its_baser_table_size(baser) / entry_size;
+}
+
+static int vgic_v3_its_mmio_write(struct vcpu *v, mmio_info_t *info,
+                                  register_t r, void *priv)
+{
+    struct domain *d = v->domain;
+    struct virt_its *its = priv;
+    uint64_t reg;
+    uint32_t ctlr;
+
+    switch ( info->gpa & 0xffff )
+    {
+    case VREG32(GITS_CTLR):
+        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
+        if ( info->dabt.size != DABT_WORD ) goto bad_width;
+	vgic_reg32_update(&ctlr, r, info);
+	its->enabled = ctlr & GITS_CTLR_ENABLE;
+	/* TODO: trigger something ... */
+        return 1;
+    case VREG32(GITS_IIDR):
+        goto write_ignore_32;
+    case VREG32(GITS_TYPER):
+        goto write_ignore_32;
+    case VREG64(GITS_CBASER):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+
+        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
+        if ( its->enabled )
+            return 1;
+
+        reg = its->cbaser;
+        vgic_reg64_update(&reg, r, info);
+        /* TODO: sanitise! */
+        its->cbaser = reg;
+
+        if ( reg & BIT(63) )
+        {
+            its->cmdbuf = map_guest_pages(d, reg & GENMASK(51, 12), 1);
+        }
+        else
+        {
+            unmap_guest_pages(its->cmdbuf, 1);
+            its->cmdbuf = NULL;
+        }
+
+	return 1;
+    case VREG64(GITS_CWRITER):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+        reg = its->cwriter;
+        vgic_reg64_update(&reg, r, info);
+        vgic_its_handle_cmds(d, its, reg);
+        return 1;
+    case VREG64(GITS_CREADR):
+        goto write_ignore_64;
+    case VREG64(GITS_BASER0):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+
+        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
+        if ( its->enabled )
+            return 1;
+
+        reg = its->baser0;
+        vgic_reg64_update(&reg, r, info);
+
+        reg &= ~GITS_BASER_RO_MASK;
+        reg |= (sizeof(uint64_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
+        reg |= GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT;
+        /* TODO: sanitise! */
+        /* TODO: locking(?) */
+
+        if ( reg & GITS_BASER_VALID )
+        {
+            its->dev_table = map_guest_pages(d,
+                                             get_baser_phys_addr(reg),
+                                             its_baser_table_size(reg) >> PAGE_SHIFT);
+            its->max_devices = its_baser_nr_entries(reg);
+            memset(its->dev_table, 0, its->max_devices * sizeof(uint64_t));
+        }
+        else
+        {
+            unmap_guest_pages(its->dev_table,
+                              its_baser_table_size(reg) >> PAGE_SHIFT);
+            its->max_devices = 0;
+        }
+
+        its->baser0 = reg;
+        return 1;
+    case VREG64(GITS_BASER1):
+        if ( info->dabt.size < DABT_WORD ) goto bad_width;
+
+        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
+        if ( its->enabled )
+            return 1;
+
+        reg = its->baser1;
+        vgic_reg64_update(&reg, r, info);
+        reg &= ~GITS_BASER_RO_MASK;
+        reg |= (sizeof(uint16_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
+        reg |= GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT;
+        /* TODO: sanitise! */
+
+        /* TODO: sort out locking */
+        /* TODO: repeated calls: free old mapping */
+        if ( reg & GITS_BASER_VALID )
+        {
+            its->coll_table = map_guest_pages(d, get_baser_phys_addr(reg),
+                                              its_baser_table_size(reg) >> PAGE_SHIFT);
+            its->max_collections = its_baser_nr_entries(reg);
+            memset(its->coll_table, 0xff,
+                   its->max_collections * sizeof(uint16_t));
+        }
+        else
+        {
+            unmap_guest_pages(its->coll_table,
+                              its_baser_table_size(reg) >> PAGE_SHIFT);
+            its->max_collections = 0;
+        }
+        its->baser1 = reg;
+        return 1;
+    case VRANGE64(GITS_BASER2, GITS_BASER7):
+        goto write_ignore_64;
+    default:
+        gdprintk(XENLOG_G_WARNING, "ITS: unhandled ITS register 0x%lx\n",
+                 info->gpa & 0xffff);
+        return 0;
+    }
+
+    return 1;
+
+write_ignore_64:
+    if ( ! vgic_reg64_check_access(info->dabt) ) goto bad_width;
+    return 1;
+
+write_ignore_32:
+    if ( info->dabt.size != DABT_WORD ) goto bad_width;
+    return 1;
+
+bad_width:
+    printk(XENLOG_G_ERR "%pv vGICR: bad read width %d r%d offset %#08lx\n",
+           v, info->dabt.size, info->dabt.reg, info->gpa & 0xffff);
+
+    domain_crash_synchronous();
+
+    return 0;
+}
+
+static const struct mmio_handler_ops vgic_its_mmio_handler = {
+    .read  = vgic_v3_its_mmio_read,
+    .write = vgic_v3_its_mmio_write,
+};
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 206e00b..a9b04ab 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -159,15 +159,6 @@ static void vgic_store_irouter(struct domain *d, struct vgic_irq_rank *rank,
     rank->vcpu[offset] = new_vcpu->vcpu_id;
 }
 
-static inline bool vgic_reg64_check_access(struct hsr_dabt dabt)
-{
-    /*
-     * 64 bits registers can be accessible using 32-bit and 64-bit unless
-     * stated otherwise (See 8.1.3 ARM IHI 0069A).
-     */
-    return ( dabt.size == DABT_DOUBLE_WORD || dabt.size == DABT_WORD );
-}
-
 static int __vgic_v3_rdistr_rd_mmio_read(struct vcpu *v, mmio_info_t *info,
                                          uint32_t gicr_reg,
                                          register_t *r)
diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
index da5fb77..6a91f5b 100644
--- a/xen/include/asm-arm/gic_v3_defs.h
+++ b/xen/include/asm-arm/gic_v3_defs.h
@@ -147,6 +147,16 @@
 #define LPI_PROP_RES1                (1 << 1)
 #define LPI_PROP_ENABLED             (1 << 0)
 
+/*
+ * PIDR2: Only bits[7:4] are not implementation defined. We are
+ * emulating a GICv3 ([7:4] = 0x3).
+ *
+ * We don't emulate a specific registers scheme so implement the others
+ * bits as RES0 as recommended by the spec (see 8.1.13 in ARM IHI 0069A).
+ */
+#define GICV3_GICD_PIDR2  0x30
+#define GICV3_GICR_PIDR2  GICV3_GICD_PIDR2
+
 #define GICH_VMCR_EOI                (1 << 9)
 #define GICH_VMCR_VENG1              (1 << 1)
 
@@ -190,6 +200,15 @@ struct rdist_region {
     bool single_rdist;
 };
 
+/*
+ * 64 bits registers can be accessible using 32-bit and 64-bit unless
+ * stated otherwise (See 8.1.3 ARM IHI 0069A).
+ */
+static inline bool vgic_reg64_check_access(struct hsr_dabt dabt)
+{
+    return ( dabt.size == DABT_DOUBLE_WORD || dabt.size == DABT_WORD );
+}
+
 #endif /* __ASM_ARM_GIC_V3_DEFS_H__ */
 
 /*
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 15/26] ARM: vITS: handle CLEAR command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (13 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 14/26] ARM: vGICv3: introduce basic ITS emulation bits Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 16/26] ARM: vITS: handle INT command Andre Przywara
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

This introduces the ITS command handler for the CLEAR command, which
clears the pending state of an LPI.
This removes a not-yet injected, but already queued IRQ from a VCPU.

In addition this patch introduces the lookup function which translates
a given DeviceID/EventID pair into a pointer to our vITTE structure.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-its.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index e5e9ae4..e2e94c4 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -60,6 +60,73 @@ struct vits_itte
     uint16_t collection;
 };
 
+#define UNMAPPED_COLLECTION      ((uint16_t)~0)
+
+/* Must be called with the ITS lock held. */
+static struct vcpu *get_vcpu_from_collection(struct virt_its *its, int collid)
+{
+    uint16_t vcpu_id;
+
+    if ( collid >= its->max_collections )
+        return NULL;
+
+    vcpu_id = its->coll_table[collid];
+    if ( vcpu_id == UNMAPPED_COLLECTION || vcpu_id >= its->d->max_vcpus )
+        return NULL;
+
+    return its->d->vcpu[vcpu_id];
+}
+
+#define DEV_TABLE_ITT_ADDR(x) ((x) & GENMASK(51, 8))
+#define DEV_TABLE_ITT_SIZE(x) (BIT(((x) & GENMASK(7, 0)) + 1))
+#define DEV_TABLE_ENTRY(addr, bits)                     \
+        (((addr) & GENMASK(51, 8)) | (((bits) - 1) & GENMASK(7, 0)))
+
+static paddr_t get_itte_address(struct virt_its *its,
+                                uint32_t devid, uint32_t evid)
+{
+    paddr_t addr;
+
+    if ( devid >= its->max_devices )
+        return ~0;
+
+    if ( evid >= DEV_TABLE_ITT_SIZE(its->dev_table[devid]) )
+        return ~0;
+
+    addr = DEV_TABLE_ITT_ADDR(its->dev_table[devid]);
+
+    return addr + evid * sizeof(struct vits_itte);
+}
+
+/* Looks up a given deviceID/eventID pair on an ITS and returns a pointer to
+ * the corresponding ITTE. This maps the respective guest page into Xen.
+ * Once finished with handling the ITTE, call put_devid_evid() to unmap
+ * the page again.
+ * Must be called with the ITS lock held.
+ */
+static struct vits_itte *get_devid_evid(struct virt_its *its,
+                                        uint32_t devid, uint32_t evid)
+{
+    paddr_t addr = get_itte_address(its, devid, evid);
+    struct vits_itte *itte;
+
+    if ( addr == ~0 )
+        return NULL;
+
+    /* TODO: check locking for map_guest_pages() */
+    itte = map_guest_pages(its->d, addr & PAGE_MASK, 1);
+    if ( !itte )
+        return NULL;
+
+    return itte + (addr & ~PAGE_MASK) / sizeof(struct vits_itte);
+}
+
+/* Must be called with the ITS lock held. */
+static void put_devid_evid(struct virt_its *its, struct vits_itte *itte)
+{
+    unmap_guest_pages(itte, 1);
+}
+
 /**************************************
  * Functions that handle ITS commands *
  **************************************/
@@ -79,6 +146,53 @@ static uint64_t its_cmd_mask_field(uint64_t *its_cmd,
 #define its_cmd_get_target_addr(cmd)    its_cmd_mask_field(cmd, 2, 16, 32)
 #define its_cmd_get_validbit(cmd)       its_cmd_mask_field(cmd, 2, 63,  1)
 
+static int its_handle_clear(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    uint32_t eventid = its_cmd_get_id(cmdptr);
+    struct pending_irq *pirq;
+    struct vits_itte *itte;
+    struct vcpu *vcpu;
+    uint32_t vlpi;
+
+    spin_lock(&its->its_lock);
+
+    itte = get_devid_evid(its, devid, eventid);
+    if ( !itte )
+    {
+        spin_unlock(&its->its_lock);
+        return -1;
+    }
+
+    vcpu = get_vcpu_from_collection(its, itte->collection);
+    if ( !vcpu )
+    {
+        spin_unlock(&its->its_lock);
+        return -1;
+    }
+
+    vlpi = itte->vlpi;
+
+    put_devid_evid(its, itte);
+    spin_unlock(&its->its_lock);
+
+    /* Remove a pending, but not yet injected guest IRQ. */
+    pirq = lpi_to_pending(vcpu, vlpi, false);
+    if ( pirq )
+    {
+        clear_bit(GIC_IRQ_GUEST_QUEUED, &pirq->status);
+        gic_remove_from_queues(vcpu, vlpi);
+
+        /* Mark this pending IRQ struct as availabe again. */
+        if ( !test_bit(GIC_IRQ_GUEST_VISIBLE, &pirq->status) )
+            pirq->irq = 0;
+    }
+
+    clear_bit(vlpi - 8192, vcpu->arch.vgic.pendtable);
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -99,6 +213,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         cmdptr = its->cmdbuf + (its->creadr / sizeof(*its->cmdbuf));
         switch (its_cmd_get_command(cmdptr))
         {
+        case GITS_CMD_CLEAR:
+            its_handle_clear(its, cmdptr);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
 	    break;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 16/26] ARM: vITS: handle INT command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (14 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 15/26] ARM: vITS: handle CLEAR command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 17/26] ARM: vITS: handle MAPC command Andre Przywara
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The INT command sets a given LPI identified by a DeviceID/EventID pair
as pending and thus triggers it to be injected.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-its.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index e2e94c4..612f55b 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -193,6 +193,41 @@ static int its_handle_clear(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+static int its_handle_int(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    uint32_t eventid = its_cmd_get_id(cmdptr);
+    struct vits_itte *itte;
+    struct vcpu *vcpu;
+    int ret = -1;
+    uint32_t vlpi;
+
+    spin_lock(&its->its_lock);
+
+    itte = get_devid_evid(its, devid, eventid);
+    if ( !itte )
+        goto out_unlock;
+
+    vcpu = its->d->vcpu[itte->collection];
+    vlpi = itte->vlpi;
+
+    ret = 0;
+
+    put_devid_evid(its, itte);
+
+out_unlock:
+    spin_unlock(&its->its_lock);
+
+    if ( !ret) {
+        if (vcpu->domain->arch.vgic.proptable[vlpi - 8192] & LPI_PROP_ENABLED)
+            vgic_vcpu_inject_irq(vcpu, vlpi);
+        else
+            set_bit(vlpi - 8192, vcpu->arch.vgic.pendtable);
+    }
+
+    return ret;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -216,6 +251,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_CLEAR:
             its_handle_clear(its, cmdptr);
             break;
+        case GITS_CMD_INT:
+            its_handle_int(its, cmdptr);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
 	    break;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 17/26] ARM: vITS: handle MAPC command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (15 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 16/26] ARM: vITS: handle INT command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 18/26] ARM: vITS: handle MAPD command Andre Przywara
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The MAPC command associates a given collection ID with a given
redistributor, thus mapping collections to VCPUs.
We just store the vcpu_id in the collection table for that.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-its.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index 612f55b..b425776 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -228,6 +228,33 @@ out_unlock:
     return ret;
 }
 
+static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t collid = its_cmd_get_collection(cmdptr);
+    uint64_t rdbase = its_cmd_mask_field(cmdptr, 2, 16, 44);
+    int ret = -1;
+
+    if ( collid >= its->max_collections )
+        return ret;
+
+    if ( rdbase >= its->d->max_vcpus )
+        return ret;
+
+    spin_lock(&its->its_lock);
+    if ( its->coll_table )
+    {
+        if ( its_cmd_get_validbit(cmdptr) )
+            its->coll_table[collid] = rdbase;
+        else
+            its->coll_table[collid] = UNMAPPED_COLLECTION;
+
+        ret = 0;
+    }
+    spin_unlock(&its->its_lock);
+
+    return ret;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -254,6 +281,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_INT:
             its_handle_int(its, cmdptr);
             break;
+        case GITS_CMD_MAPC:
+            its_handle_mapc(its, cmdptr);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
 	    break;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 18/26] ARM: vITS: handle MAPD command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (16 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 17/26] ARM: vITS: handle MAPC command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 19/26] ARM: vITS: handle MAPTI command Andre Przywara
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The MAPD command maps a device by associating a memory region for
storing ITTEs with a certain device ID.
We just store the given guest physical address in the device table.
We don't map the device tables permanently, as their alignment
requirement is only 256 Bytes, thus making mapping of several tables
complicated. We map the device tables on demand when we need them later.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-its.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index b425776..918b504 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -255,6 +255,27 @@ static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
     return ret;
 }
 
+static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    int size = its_cmd_get_size(cmdptr);
+    bool valid = its_cmd_get_validbit(cmdptr);
+    paddr_t itt_addr = its_cmd_mask_field(cmdptr, 2, 0, 52) & GENMASK(51, 8);
+
+    if ( !its->dev_table )
+        return -1;
+
+    spin_lock(&its->its_lock);
+    if ( valid )
+        its->dev_table[devid] = DEV_TABLE_ENTRY(itt_addr, size + 1);
+    else
+        its->dev_table[devid] = 0;
+
+    spin_unlock(&its->its_lock);
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -284,6 +305,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_MAPC:
             its_handle_mapc(its, cmdptr);
             break;
+        case GITS_CMD_MAPD:
+            its_handle_mapd(its, cmdptr);
+	    break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
 	    break;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 19/26] ARM: vITS: handle MAPTI command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (17 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 18/26] ARM: vITS: handle MAPD command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 20/26] ARM: vITS: handle MOVI command Andre Przywara
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The MAPTI commands associates a DeviceID/EventID pair with a LPI/CPU
pair and actually instantiates LPI interrupts.
We connect the already allocated host LPI to this virtual LPI, so that
any triggering IRQ on the host can be quickly forwarded to
a guest.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c        | 49 +++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-its.c       | 44 ++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic-its.h |  4 ++++
 3 files changed, 97 insertions(+)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 54e604a..0acbd83 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -754,6 +754,55 @@ retry:
     return 0;
 }
 
+static union host_lpi *find_guest_lpi(struct domain *d,
+                                      uint32_t devid, uint32_t eventid)
+{
+    struct its_devices *dev;
+    uint32_t lpi;
+
+    list_for_each_entry( dev, &d->arch.vgic.its_devices, entry )
+    {
+        if (dev->guest_devid != devid )
+            continue;
+
+        if ( eventid >= dev->eventids )
+            return NULL;
+
+        lpi = dev->host_lpis[eventid / 32] + (eventid % 32);
+        if (lpi < 8192)
+            return NULL;
+
+        lpi -= 8192;
+
+        return &lpi_data.host_lpis[lpi / HOST_LPIS_PER_PAGE][lpi % HOST_LPIS_PER_PAGE];
+    }
+
+    return NULL;
+}
+
+/* Connects the event ID for an already assigned device to the given VCPU/vLPI
+ * pair. The corresponding physical LPI is already mapped on the host side
+ * (when assigning the physical device to the guest), so we just connect the
+ * target VCPU/vLPI pair to that interrupt to inject it properly if it fires.
+ */
+int gicv3_assign_guest_event(struct domain *d, uint32_t devid, uint32_t eventid,
+                             struct vcpu *v, uint32_t virt_lpi)
+{
+    union host_lpi hlpi, *hlpip;
+
+    hlpip = find_guest_lpi(d, devid, eventid);
+    if ( !hlpip )
+        return -1;
+
+    hlpi.virt_lpi = virt_lpi;
+    hlpi.dom_id = d->domain_id;
+    hlpi.vcpu_id = v->vcpu_id;
+
+    hlpip->data = hlpi.data;
+
+    return 0;
+}
+
 /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
 void gicv3_its_dt_init(const struct dt_device_node *node)
 {
diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index 918b504..f9f438a 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -276,6 +276,46 @@ static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+static int its_handle_mapti(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    uint32_t eventid = its_cmd_get_id(cmdptr);
+    uint32_t intid = its_cmd_get_physical_id(cmdptr);
+    int collid = its_cmd_get_collection(cmdptr);
+    struct vits_itte *itte;
+    struct vcpu *vcpu;
+    int ret = -1;
+
+    if ( its_cmd_get_command(cmdptr) == GITS_CMD_MAPI )
+        intid = eventid;
+
+    if ( collid >= its->max_collections )
+        return -1;
+
+    spin_lock(&its->its_lock);
+    vcpu = get_vcpu_from_collection(its, collid);
+    if ( !vcpu )
+        goto out_unlock;
+
+    itte = get_devid_evid(its, devid, eventid);
+    if ( !itte )
+        goto out_unlock;
+
+    itte->vlpi = intid;
+    itte->collection = collid;
+
+    gicv3_assign_guest_event(its->d, devid, eventid, vcpu, intid);
+
+    ret = 0;
+
+    put_devid_evid(its, itte);
+
+out_unlock:
+    spin_unlock(&its->its_lock);
+    
+    return ret;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -308,6 +348,10 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_MAPD:
             its_handle_mapd(its, cmdptr);
 	    break;
+        case GITS_CMD_MAPI:
+        case GITS_CMD_MAPTI:
+            its_handle_mapti(its, cmdptr);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
 	    break;
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index d1ebc19..9843674 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -137,6 +137,10 @@ void gicv3_its_setup_collection(int cpu);
 int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
                          int bits, bool valid);
 
+int gicv3_assign_guest_event(struct domain *d,
+                             uint32_t devid, uint32_t eventid,
+                             struct vcpu *v, uint32_t virt_lpi);
+
 #else
 
 static inline void gicv3_its_dt_init(const struct dt_device_node *node)
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 20/26] ARM: vITS: handle MOVI command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (18 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 19/26] ARM: vITS: handle MAPTI command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 21/26] ARM: vITS: handle DISCARD command Andre Przywara
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The MOVI command moves the interrupt affinity from one redistributor
(read: VCPU) to another.
For now migration of "live" LPIs is not yet implemented, but we store
the changed affinity in the host LPI structure and in our virtual ITTE.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c        | 14 ++++++++++++++
 xen/arch/arm/vgic-its.c       | 41 +++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic-its.h |  2 ++
 3 files changed, 57 insertions(+)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 0acbd83..1da28b9 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -799,6 +799,20 @@ int gicv3_assign_guest_event(struct domain *d, uint32_t devid, uint32_t eventid,
     hlpi.vcpu_id = v->vcpu_id;
 
     hlpip->data = hlpi.data;
+    return 0;
+}
+
+/* Changes the target VCPU for a given host LPI assigned to a domain. */
+int gicv3_lpi_change_vcpu(struct domain *d,
+                          uint32_t devid, uint32_t eventid, int vcpu_id)
+{
+    union host_lpi *hlpip;
+
+    hlpip = find_guest_lpi(d, devid, eventid);
+    if ( !hlpip )
+        return -1;
+
+    hlpip->vcpu_id = vcpu_id;
 
     return 0;
 }
diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index f9f438a..7c80c17 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -316,6 +316,41 @@ out_unlock:
     return ret;
 }
 
+static int its_handle_movi(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    uint32_t eventid = its_cmd_get_id(cmdptr);
+    int collid = its_cmd_get_collection(cmdptr);
+    struct vits_itte *itte;
+    struct vcpu *vcpu;
+
+    if ( collid >= its->max_collections )
+        return -1;
+
+    spin_lock(&its->its_lock);
+
+    vcpu = get_vcpu_from_collection(its, collid);
+    if ( !vcpu )
+        goto out_unlock;
+
+    itte = get_devid_evid(its, devid, eventid);
+    if ( !itte )
+        goto out_unlock;
+
+    itte->collection = collid;
+
+    /* TODO: lookup currently-in-guest virtual IRQs and migrate them */
+
+    put_devid_evid(its, itte);
+
+out_unlock:
+    spin_unlock(&its->its_lock);
+
+    gicv3_lpi_change_vcpu(its->d, devid, eventid, vcpu->vcpu_id);
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -352,6 +387,12 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_MAPTI:
             its_handle_mapti(its, cmdptr);
             break;
+        case GITS_CMD_MOVALL:
+            gdprintk(XENLOG_G_INFO, "ITS: ignoring MOVALL command\n");
+            break;
+        case GITS_CMD_MOVI:
+            its_handle_movi(its, cmdptr);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
 	    break;
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 9843674..c5355f9 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -140,6 +140,8 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
 int gicv3_assign_guest_event(struct domain *d,
                              uint32_t devid, uint32_t eventid,
                              struct vcpu *v, uint32_t virt_lpi);
+int gicv3_lpi_change_vcpu(struct domain *d,
+                          uint32_t devid, uint32_t eventid, int vcpu_id);
 
 #else
 
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 21/26] ARM: vITS: handle DISCARD command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (19 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 20/26] ARM: vITS: handle MOVI command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 22/26] ARM: vITS: handle INV command Andre Przywara
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The DISCARD command drops the connection between a DeviceID/EventID
and an LPI/collection pair.
We mark the respective structure entries as not allocated and make
sure that any queued IRQs are removed.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-its.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index 7c80c17..52c660a 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -351,6 +351,44 @@ out_unlock:
     return 0;
 }
 
+static int its_handle_discard(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    uint32_t eventid = its_cmd_get_id(cmdptr);
+    struct pending_irq *pirq;
+    struct vits_itte *itte;
+    struct vcpu *vcpu;
+
+    spin_lock(&its->its_lock);
+    itte = get_devid_evid(its, devid, eventid);
+    if ( !itte )
+        goto out_unlock;
+
+    vcpu = get_vcpu_from_collection(its, itte->collection);
+
+    pirq = lpi_to_pending(vcpu, itte->vlpi, false);
+    if ( pirq )
+    {
+        clear_bit(GIC_IRQ_GUEST_QUEUED, &pirq->status);
+        gic_remove_from_queues(vcpu, itte->vlpi);
+
+        /* Mark this pending IRQ struct as availabe again. */
+        if ( !test_bit(GIC_IRQ_GUEST_VISIBLE, &pirq->status) )
+            pirq->irq = 0;
+    }
+
+    itte->vlpi = 0;             /* Mark this ITTE as unused. */
+    itte->collection = ~0;
+    gicv3_assign_guest_event(its->d, devid, eventid, NULL, 0);
+
+    put_devid_evid(its, itte);
+
+out_unlock:
+    spin_unlock(&its->its_lock);
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
@@ -374,6 +412,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_CLEAR:
             its_handle_clear(its, cmdptr);
             break;
+        case GITS_CMD_DISCARD:
+            its_handle_discard(its, cmdptr);
+            break;
         case GITS_CMD_INT:
             its_handle_int(its, cmdptr);
             break;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 22/26] ARM: vITS: handle INV command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (20 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 21/26] ARM: vITS: handle DISCARD command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 23/26] ARM: vITS: handle INVALL command Andre Przywara
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The INV command instructs the ITS to update the configuration data for
a given LPI by re-reading its entry from the property table.
We don't need to care so much about the priority value, but enabling
or disabling an LPI has some effect: We remove or push virtual LPIs
to their VCPUs, also check the virtual pending bit if an LPI gets enabled.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c  |  2 +-
 xen/arch/arm/vgic-its.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 1da28b9..7dbb9e6 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -248,7 +248,7 @@ static uint64_t encode_phys_addr(paddr_t addr, int page_bits)
 {
     uint64_t ret;
 
-    if ( page_bits < 16)
+    if ( page_bits < 16 )
         return (uint64_t)addr & GENMASK(47, page_bits);
 
     ret = addr & GENMASK(47, 16);
diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index 52c660a..bcabb04 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -228,6 +228,75 @@ out_unlock:
     return ret;
 }
 
+/* For a given virtual LPI read the enabled bit from the virtual property
+ * table and update the virtual IRQ's state.
+ * This takes care of removing or pushing of virtual LPIs to their VCPUs.
+ */
+static void update_lpi_enabled_status(struct virt_its* its,
+                                      struct vcpu *vcpu, uint32_t vlpi)
+{
+    struct pending_irq *pirq = lpi_to_pending(vcpu, vlpi, false);
+    uint8_t property = its->d->arch.vgic.proptable[vlpi - 8192];
+
+    if ( property & LPI_PROP_ENABLED )
+    {
+        if ( pirq )
+        {
+            unsigned long flags;
+
+            set_bit(GIC_IRQ_GUEST_ENABLED, &pirq->status);
+            spin_lock_irqsave(&vcpu->arch.vgic.lock, flags);
+            if ( !list_empty(&pirq->inflight) &&
+                 !test_bit(GIC_IRQ_GUEST_VISIBLE, &pirq->status) )
+                gic_raise_guest_irq(vcpu, vlpi, property & 0xfc);
+            spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);
+        }
+
+        /* Check whether the LPI has fired while the guest had it disabled. */
+        if (test_and_clear_bit(vlpi - 8192, vcpu->arch.vgic.pendtable))
+            vgic_vcpu_inject_irq(vcpu, vlpi);
+    }
+    else
+    {
+        if ( pirq )
+        {
+            clear_bit(GIC_IRQ_GUEST_ENABLED, &pirq->status);
+            gic_remove_from_queues(vcpu, vlpi);
+        }
+    }
+}
+
+static int its_handle_inv(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    uint32_t eventid = its_cmd_get_id(cmdptr);
+    struct vits_itte *itte;
+    struct vcpu *vcpu;
+    uint32_t vlpi;
+    int ret = -1;
+
+    spin_lock(&its->its_lock);
+
+    itte = get_devid_evid(its, devid, eventid);
+    if ( !itte )
+        goto out_unlock;
+
+    vcpu = its->d->vcpu[itte->collection];
+    vlpi = itte->vlpi;
+
+    ret = 0;
+
+    put_devid_evid(its, itte);
+
+out_unlock:
+    spin_unlock(&its->its_lock);
+
+    if ( !ret )
+        update_lpi_enabled_status(its, vcpu, vlpi);
+
+    return ret;
+}
+
 static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
 {
     uint32_t collid = its_cmd_get_collection(cmdptr);
@@ -418,6 +487,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_INT:
             its_handle_int(its, cmdptr);
             break;
+        case GITS_CMD_INV:
+            its_handle_inv(its, cmdptr);
+	    break;
         case GITS_CMD_MAPC:
             its_handle_mapc(its, cmdptr);
             break;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 23/26] ARM: vITS: handle INVALL command
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (21 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 22/26] ARM: vITS: handle INV command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-05 22:50   ` Stefano Stabellini
  2016-12-22 18:24 ` [RFC PATCH v2 24/26] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
                   ` (4 subsequent siblings)
  27 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

The INVALL command instructs an ITS to invalidate the configuration
data for all LPIs associated with a given redistributor (read: VCPU).
This is nasty to emulate exactly with our architecture, so we just scan
the pending table and inject _every_ LPI found there that got enabled.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-its.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index bcabb04..c130d98 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -297,6 +297,39 @@ out_unlock:
     return ret;
 }
 
+/* INVALL updates the per-LPI configuration status for every LPI mapped to
+ * a particular redistributor. Since our property table is referenced when
+ * needed, we don't need to sync anything, really. But we have to take care
+ * of LPIs getting enabled if there is an interrupt pending.
+ * To catch every LPI without iterating through the device table we just
+ * look for set bits in our virtual pending table and check the status of
+ * the enabled bit in the respective property table entry.
+ * This actually covers every (pending) LPI from every redistributor,
+ * but update_lpi_enabled_status() is a NOP for LPIs not being mapped
+ * to the redistributor/VCPU we are interested in.
+ */
+static int its_handle_invall(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t collid = its_cmd_get_collection(cmdptr);
+    struct vcpu *vcpu;
+    int vlpi = 0;
+
+    spin_lock(&its->its_lock);
+    vcpu = get_vcpu_from_collection(its, collid);
+    spin_unlock(&its->its_lock);
+
+    do {
+        vlpi = find_next_bit(vcpu->arch.vgic.pendtable,
+                             its->d->arch.vgic.nr_lpis, vlpi);
+        if (vlpi >= its->d->arch.vgic.nr_lpis)
+            break;
+
+        update_lpi_enabled_status(its, vcpu, vlpi);
+    } while (1);
+
+    return 0;
+}
+
 static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
 {
     uint32_t collid = its_cmd_get_collection(cmdptr);
@@ -490,6 +523,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
         case GITS_CMD_INV:
             its_handle_inv(its, cmdptr);
 	    break;
+        case GITS_CMD_INVALL:
+            its_handle_invall(its, cmdptr);
+	    break;
         case GITS_CMD_MAPC:
             its_handle_mapc(its, cmdptr);
             break;
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 24/26] ARM: vITS: create and initialize virtual ITSes for Dom0
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (22 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 23/26] ARM: vITS: handle INVALL command Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 25/26] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

For each hardware ITS create and initialize a virtual ITS for Dom0.
We use the same memory mapped address to keep the doorbell working.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-its.c       | 22 ++++++++++++++++++++++
 xen/arch/arm/vgic-v3.c        | 12 ++++++++++++
 xen/include/asm-arm/domain.h  |  1 +
 xen/include/asm-arm/gic-its.h | 13 +++++++++++++
 4 files changed, 48 insertions(+)

diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
index c130d98..2eec468 100644
--- a/xen/arch/arm/vgic-its.c
+++ b/xen/arch/arm/vgic-its.c
@@ -810,6 +810,28 @@ static const struct mmio_handler_ops vgic_its_mmio_handler = {
     .write = vgic_v3_its_mmio_write,
 };
 
+int vgic_v3_its_init_virtual(struct domain *d, struct host_its *hw_its,
+                             paddr_t guest_addr)
+{
+    struct virt_its *its;
+
+    its = xzalloc(struct virt_its);
+    if ( ! its )
+        return -ENOMEM;
+
+    its->d = d;
+    its->hw_its = hw_its;
+    its->baser0 = 0x7917000000000400;
+    its->baser1 = 0x3c01000000000400;
+    its->cbaser = 0x380e000000000400;
+    spin_lock_init(&its->vcmd_lock);
+    spin_lock_init(&its->its_lock);
+
+    register_mmio_handler(d, &vgic_its_mmio_handler, guest_addr, SZ_64K, its);
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index a9b04ab..1c682d1 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -31,6 +31,7 @@
 #include <asm/current.h>
 #include <asm/mmio.h>
 #include <asm/gic_v3_defs.h>
+#include <asm/gic-its.h>
 #include <asm/vgic.h>
 #include <asm/vgic-emul.h>
 #include <asm/vreg.h>
@@ -1644,6 +1645,7 @@ static int vgic_v3_domain_init(struct domain *d)
      */
     if ( is_hardware_domain(d) )
     {
+        struct host_its *hw_its;
         unsigned int first_cpu = 0;
 
         d->arch.vgic.dbase = vgic_v3_hw.dbase;
@@ -1669,6 +1671,16 @@ static int vgic_v3_domain_init(struct domain *d)
 
             first_cpu += size / d->arch.vgic.rdist_stride;
         }
+        d->arch.vgic.nr_regions = vgic_v3_hw.nr_rdist_regions;
+
+        list_for_each_entry(hw_its, &host_its_list, entry)
+        {
+            /* Emulate the control registers frame (lower 64K). */
+            vgic_v3_its_init_virtual(d, hw_its, hw_its->addr);
+
+            d->arch.vgic.has_its = true;
+        }
+
     }
     else
     {
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 543d058..6890b3b 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -114,6 +114,7 @@ struct arch_domain
         uint8_t *proptable;
         struct list_head its_devices;       /* devices mapped to an ITS */
         spinlock_t its_devices_lock;        /* protects the its_devices list */
+        bool has_its;
 #endif
     } vgic;
 
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index c5355f9..9956afb 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -130,6 +130,13 @@ void gicv3_set_redist_addr(paddr_t address, int redist_id);
 /* Map a collection for this host CPU to each host ITS. */
 void gicv3_its_setup_collection(int cpu);
 
+/* Create and register a virtual ITS at the given guest address.
+ * If a host ITS is specified, a hardware domain can reach out to that host
+ * ITS to deal with devices and LPI mappings and can enable/disable LPIs.
+ */
+int vgic_v3_its_init_virtual(struct domain *d, struct host_its *hw_its,
+                             paddr_t guest_addr);
+
 /* Map a device on the host by allocating an ITT on the host (ITS).
  * "bits" specifies how many events (interrupts) this device will need.
  * Setting "valid" to false deallocates the device.
@@ -175,6 +182,12 @@ static inline int gicv3_its_map_device(struct domain *d, int host_devid,
 {
     return -ENODEV;
 }
+static inline int vgic_v3_its_init_virtual(struct domain *d,
+                                           struct host_its *hw_its,
+                                           paddr_t guest_addr)
+{
+    return 0;
+}
 
 #endif /* CONFIG_HAS_ITS */
 
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 25/26] ARM: vITS: create ITS subnodes for Dom0 DT
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (23 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 24/26] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2016-12-22 18:24 ` [RFC PATCH v2 26/26] ARM: vGIC: advertising LPI support Andre Przywara
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Dom0 expects all ITSes in the system to be propagated to be able to
use MSIs.
Create Dom0 DT nodes for each hardware ITS, keeping the register frame
address the same, as the doorbell address that the Dom0 drivers program
into the BARs has to match the hardware.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-its.c        | 72 +++++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c         |  4 ++-
 xen/include/asm-arm/gic-its.h | 13 ++++++++
 3 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
index 7dbb9e6..d5d4d3b 100644
--- a/xen/arch/arm/gic-its.c
+++ b/xen/arch/arm/gic-its.c
@@ -817,6 +817,78 @@ int gicv3_lpi_change_vcpu(struct domain *d,
     return 0;
 }
 
+/* Create the respective guest DT nodes for a list of host ITSes.
+ * This copies the reg property, so the guest sees the ITS at the same address
+ * as the host.
+ */
+int gicv3_its_make_dt_nodes(struct list_head *its_list,
+                            const struct domain *d,
+                            const struct dt_device_node *gic,
+                            void *fdt)
+{
+    uint32_t len;
+    int res;
+    const void *prop = NULL;
+    const struct dt_device_node *its = NULL;
+    const struct host_its *its_data;
+
+    if ( list_empty(its_list) )
+        return 0;
+
+    /* 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(its_data, its_list, entry)
+    {
+        its = its_data->dt_node;
+
+        res = fdt_begin_node(fdt, its->name);
+        if ( res )
+            return res;
+
+        res = fdt_property_string(fdt, "compatible", "arm,gic-v3-its");
+        if ( res )
+            return res;
+
+        res = fdt_property(fdt, "msi-controller", NULL, 0);
+        if ( res )
+            return res;
+
+        if ( its->phandle )
+        {
+            res = fdt_property_cell(fdt, "phandle", its->phandle);
+            if ( res )
+                return res;
+        }
+
+        /* Use the same reg regions as the ITS node in host DTB. */
+        prop = dt_get_property(its, "reg", &len);
+        if ( !prop )
+        {
+            printk(XENLOG_ERR "GICv3: Can't find ITS reg property.\n");
+            res = -FDT_ERR_XEN(ENOENT);
+            return res;
+        }
+
+        res = fdt_property(fdt, "reg", prop, len);
+        if ( res )
+            return res;
+
+        fdt_end_node(fdt);
+    }
+
+    return res;
+}
+
 /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
 void gicv3_its_dt_init(const struct dt_device_node *node)
 {
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index 5bfdf24..b680a29 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -1190,8 +1190,10 @@ static int gicv3_make_hwdom_dt_node(const struct domain *d,
 
     res = fdt_property(fdt, "reg", new_cells, len);
     xfree(new_cells);
+    if ( res )
+        return res;
 
-    return res;
+    return gicv3_its_make_dt_nodes(&host_its_list, d, gic, fdt);
 }
 
 static const hw_irq_controller gicv3_host_irq_type = {
diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
index 9956afb..bd3af50 100644
--- a/xen/include/asm-arm/gic-its.h
+++ b/xen/include/asm-arm/gic-its.h
@@ -137,6 +137,12 @@ void gicv3_its_setup_collection(int cpu);
 int vgic_v3_its_init_virtual(struct domain *d, struct host_its *hw_its,
                              paddr_t guest_addr);
 
+/* Given a list of ITSes, create the appropriate DT nodes for a domain. */
+int gicv3_its_make_dt_nodes(struct list_head *its_list,
+                            const struct domain *d,
+                            const struct dt_device_node *gic,
+                            void *fdt);
+
 /* Map a device on the host by allocating an ITT on the host (ITS).
  * "bits" specifies how many events (interrupts) this device will need.
  * Setting "valid" to false deallocates the device.
@@ -188,6 +194,13 @@ static inline int vgic_v3_its_init_virtual(struct domain *d,
 {
     return 0;
 }
+static inline int gicv3_its_make_dt_nodes(struct list_head *its_list,
+                                       const struct domain *d,
+                                       const struct dt_device_node *gic,
+                                       void *fdt)
+{
+    return 0;
+}
 
 #endif /* CONFIG_HAS_ITS */
 
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [RFC PATCH v2 26/26] ARM: vGIC: advertising LPI support
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (24 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 25/26] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
@ 2016-12-22 18:24 ` Andre Przywara
  2017-01-18  8:13 ` [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Vijay Kilari
  2017-01-19 12:26 ` Vijay Kilari
  27 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2016-12-22 18:24 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

To let a guest know about the availability of virtual LPIs, set the
respective bits in the virtual GIC registers and let a guest control
the LPI enable bit.
Only report the LPI capability if the host has initialized at least
one ITS.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-v3.c | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 1c682d1..7e02da6 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -169,8 +169,10 @@ static int __vgic_v3_rdistr_rd_mmio_read(struct vcpu *v, mmio_info_t *info,
     switch ( gicr_reg )
     {
     case VREG32(GICR_CTLR):
-        /* We have not implemented LPI's, read zero */
-        goto read_as_zero_32;
+        if ( dabt.size != DABT_WORD ) goto bad_width;
+        *r = vgic_reg32_extract(!!(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED),
+                                info);
+        return 1;
 
     case VREG32(GICR_IIDR):
         if ( dabt.size != DABT_WORD ) goto bad_width;
@@ -182,16 +184,19 @@ static int __vgic_v3_rdistr_rd_mmio_read(struct vcpu *v, mmio_info_t *info,
         uint64_t typer, aff;
 
         if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
-        /* TBD: Update processor id in [23:8] when ITS support is added */
         aff = (MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 3) << 56 |
                MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 2) << 48 |
                MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 1) << 40 |
                MPIDR_AFFINITY_LEVEL(v->arch.vmpidr, 0) << 32);
         typer = aff;
+        typer |= (v->vcpu_id & 0xffff) << 8;
 
         if ( v->arch.vgic.flags & VGIC_V3_RDIST_LAST )
             typer |= GICR_TYPER_LAST;
 
+        if ( v->domain->arch.vgic.has_its )
+            typer |= GICR_TYPER_PLPIS;
+
         *r = vgic_reg64_extract(typer, info);
 
         return 1;
@@ -497,8 +502,16 @@ static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
     switch ( gicr_reg )
     {
     case VREG32(GICR_CTLR):
-        /* LPI's not implemented */
-        goto write_ignore_32;
+        if ( dabt.size != DABT_WORD ) goto bad_width;
+        if ( !v->domain->arch.vgic.has_its )
+            return 1;
+
+        if ( r & 1 )
+            v->arch.vgic.flags |= VGIC_V3_LPIS_ENABLED;
+        else
+            v->arch.vgic.flags &= !VGIC_V3_LPIS_ENABLED;
+
+        return 1;
 
     case VREG32(GICR_IIDR):
         /* RO */
@@ -1109,6 +1122,11 @@ static int vgic_v3_distr_mmio_read(struct vcpu *v, mmio_info_t *info,
         typer = ((ncpus - 1) << GICD_TYPE_CPUS_SHIFT |
                  DIV_ROUND_UP(v->domain->arch.vgic.nr_spis, 32));
 
+        if ( v->domain->arch.vgic.has_its )
+        {
+            typer |= GICD_TYPE_LPIS;
+            irq_bits = 16;
+        }
         typer |= (irq_bits - 1) << GICD_TYPE_ID_BITS_SHIFT;
 
         *r = vgic_reg32_extract(typer, info);
-- 
2.9.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table
  2016-12-22 18:24 ` [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table Andre Przywara
@ 2017-01-04 21:09   ` Stefano Stabellini
  2017-01-05 17:13     ` Andre Przywara
  0 siblings, 1 reply; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-04 21:09 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> The ARM GICv3 ITS provides a new kind of interrupt called LPIs.
> The pending bits and the configuration data (priority, enable bits) for
> those LPIs are stored in tables in normal memory, which software has to
> provide to the hardware.
> Allocate the required memory, initialize it and hand it over to each
> ITS. The maximum number of LPIs to be used can be adjusted with the
> command line option "max_lpi_bits", which defaults to a compile time
> constant exposed in Kconfig.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/Kconfig              | 10 +++++
>  xen/arch/arm/efi/efi-boot.h       |  1 -
>  xen/arch/arm/gic-its.c            | 94 +++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c             | 43 ++++++++++++++++++
>  xen/include/asm-arm/bitops.h      |  1 +
>  xen/include/asm-arm/cache.h       |  4 ++
>  xen/include/asm-arm/gic-its.h     | 22 ++++++++-
>  xen/include/asm-arm/gic_v3_defs.h | 48 +++++++++++++++++++-
>  8 files changed, 220 insertions(+), 3 deletions(-)
> 
> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
> index bf64c61..a7d941c 100644
> --- a/xen/arch/arm/Kconfig
> +++ b/xen/arch/arm/Kconfig
> @@ -49,6 +49,16 @@ config HAS_ITS
>          bool "GICv3 ITS MSI controller support"
>          depends on HAS_GICV3
>  
> +config MAX_HOST_LPI_BITS
            ^ MAX_PHYS_LPI_BITS


> +        depends on HAS_ITS
> +        int "Maximum bits for GICv3 host LPIs (14-32)"
> +        range 14 32
> +        default "20"
> +        help
> +          Specifies the maximum number of LPIs (bits) Xen should take care of.
> +          This can be overriden on the command line with the max_lpi_bits
> +          parameter.
> +
>  endmenu
>  
>  menu "ARM errata workaround via the alternative framework"
> diff --git a/xen/arch/arm/efi/efi-boot.h b/xen/arch/arm/efi/efi-boot.h
> index 045d6ce..dc64aec 100644
> --- a/xen/arch/arm/efi/efi-boot.h
> +++ b/xen/arch/arm/efi/efi-boot.h
> @@ -10,7 +10,6 @@
>  #include "efi-dom0.h"
>  
>  void noreturn efi_xen_start(void *fdt_ptr, uint32_t fdt_size);
> -void __flush_dcache_area(const void *vaddr, unsigned long size);
>  
>  #define DEVICE_TREE_GUID \
>  {0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}}
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index 973f9f9..6c3a35d 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -20,10 +20,104 @@
>  #include <xen/lib.h>
>  #include <xen/device_tree.h>
>  #include <xen/libfdt/libfdt.h>
> +#include <xen/sizes.h>
> +#include <asm/p2m.h>
>  #include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
>  #include <asm/gic-its.h>
>  
> +/* Global state */
> +static struct {
> +    uint8_t *lpi_property;
> +    unsigned int host_lpi_bits;

I think it is best to rename host_lpi_bits to phys_lpi_bits, or, even
better, phys_nr_lpi because it represents the number of LPIs.


> +} lpi_data;
> +
> +/* Pending table for each redistributor */
> +static DEFINE_PER_CPU(void *, pending_table);
> +
> +#define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
> +
> +uint64_t gicv3_lpi_allocate_pendtable(void)
> +{
> +    uint64_t reg, attr;
> +    void *pendtable;
> +
> +    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
> +    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
> +    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
> +
> +    if ( !this_cpu(pending_table) )
> +    {
> +        /*
> +         * The pending table holds one bit per LPI, so we need (2 << 3) bits
> +         * less bytes. The pending table covers even bits for interrupt IDs
> +         * below 8192, so we allocate the full range.
> +         * The ITS imposes a 64KB alignment requirement.
> +         */
> +        pendtable = _xmalloc(BIT_ULL(lpi_data.host_lpi_bits) / 8, SZ_64K);

If we need (2 << 3) less bytes, why are we dividing by 8?
Isn't this just a straightforward bits to bytes conversion?
I think the comment is probably wrong or outdated.


> +        if ( !pendtable )
> +            return 0;
> +
> +        memset(pendtable, 0, BIT_ULL(lpi_data.host_lpi_bits) / 8);
> +        __flush_dcache_area(pendtable, BIT_ULL(lpi_data.host_lpi_bits) / 8);
> +
> +        this_cpu(pending_table) = pendtable;
> +    }
> +    else
> +    {
> +        pendtable = this_cpu(pending_table);

Isn't this an error condition?


> +    }
> +
> +    reg  = attr | GICR_PENDBASER_PTZ;
> +    reg |= virt_to_maddr(pendtable) & GENMASK(51, 16);
> +
> +    return reg;
> +}
> +
> +uint64_t gicv3_lpi_get_proptable()
> +{
> +    uint64_t attr, reg;
> +
> +    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
> +    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
> +    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
> +
> +    /*
> +     * The property table is shared across all redistributors, so allocate
> +     * this only once, but return the same value on subsequent calls.
> +     */
> +    if ( !lpi_data.lpi_property )
> +    {
> +        /* The property table holds one byte per LPI. */
> +        void *table = alloc_xenheap_pages(lpi_data.host_lpi_bits - PAGE_SHIFT,
> +                                          0);
> +
> +        if ( !table )
> +            return 0;
> +
> +        memset(table, GIC_PRI_IRQ | LPI_PROP_RES1, MAX_PHYS_LPIS);
> +        __flush_dcache_area(table, MAX_PHYS_LPIS);
> +        lpi_data.lpi_property = table;
> +    }
> +
> +    reg  = attr | ((lpi_data.host_lpi_bits - 1) << 0);
> +    reg |= virt_to_maddr(lpi_data.lpi_property) & GENMASK(51, 12);
> +
> +    return reg;
> +}
> +
> +static unsigned int max_lpi_bits = CONFIG_MAX_HOST_LPI_BITS;
> +integer_param("max_lpi_bits", max_lpi_bits);
> +
> +int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
                  ^ gicv3_lpi_init_phys_lpis


> +{
> +    lpi_data.host_lpi_bits = min(hw_lpi_bits, max_lpi_bits);
> +
> +    printk("GICv3: using at most %lld LPIs on the host.\n", MAX_PHYS_LPIS);
> +
> +    return 0;
> +}
> +
>  /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
>  void gicv3_its_dt_init(const struct dt_device_node *node)
>  {
> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> index 238da84..1c6869d 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -546,6 +546,9 @@ static void __init gicv3_dist_init(void)
>      type = readl_relaxed(GICD + GICD_TYPER);
>      nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1);
>  
> +    if ( type & GICD_TYPE_LPIS )
> +        gicv3_lpi_init_host_lpis(((type >> GICD_TYPE_ID_BITS_SHIFT) & 0x1f) + 1);

Still need to define a mask instead of using 0x1f


> +
>      printk("GICv3: %d lines, (IID %8.8x).\n",
>             nr_lines, readl_relaxed(GICD + GICD_IIDR));
>  
> @@ -616,6 +619,32 @@ static int gicv3_enable_redist(void)
>      return 0;
>  }
>  
> +static int gicv3_rdist_init_lpis(void __iomem * rdist_base)
> +{
> +    uint32_t reg;
> +    uint64_t table_reg;
> +
> +    if ( list_empty(&host_its_list) )
> +        return -ENODEV;
> +
> +    /* Make sure LPIs are disabled before setting up the BASERs. */
> +    reg = readl_relaxed(rdist_base + GICR_CTLR);
> +    if ( reg & GICR_CTLR_ENABLE_LPIS )
> +        return -EBUSY;
> +
> +    table_reg = gicv3_lpi_allocate_pendtable();
> +    if ( !table_reg )
> +        return -ENOMEM;
> +    writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
> +
> +    table_reg = gicv3_lpi_get_proptable();
> +    if ( !table_reg )
> +        return -ENOMEM;
> +    writeq_relaxed(table_reg, rdist_base + GICR_PROPBASER);
> +
> +    return 0;
> +}
> +
>  static int __init gicv3_populate_rdist(void)
>  {
>      int i;
> @@ -658,6 +687,20 @@ static int __init gicv3_populate_rdist(void)
>              if ( (typer >> 32) == aff )
>              {
>                  this_cpu(rbase) = ptr;
> +
> +                if ( typer & GICR_TYPER_PLPIS )
> +                {
> +                    int ret;
> +
> +                    ret = gicv3_rdist_init_lpis(ptr);
> +                    if ( ret && ret != -ENODEV )
> +                    {
> +                        printk("GICv3: CPU%d: Cannot initialize LPIs: %d\n",
> +                               smp_processor_id(), ret);
> +                        break;
> +                    }
> +                }
> +
>                  printk("GICv3: CPU%d: Found redistributor in region %d @%p\n",
>                          smp_processor_id(), i, ptr);
>                  return 0;
> diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
> index bda8898..c9adf79 100644
> --- a/xen/include/asm-arm/bitops.h
> +++ b/xen/include/asm-arm/bitops.h
> @@ -25,6 +25,7 @@
>  #define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_WORD))
>  #define BIT_WORD(nr)            ((nr) / BITS_PER_WORD)
>  #define BITS_PER_BYTE           8
> +#define BIT_ULL(nr)             (1ULL << (nr))
>  
>  #define ADDR (*(volatile int *) addr)
>  #define CONST_ADDR (*(const volatile int *) addr)
> diff --git a/xen/include/asm-arm/cache.h b/xen/include/asm-arm/cache.h
> index 2de6564..af96eee 100644
> --- a/xen/include/asm-arm/cache.h
> +++ b/xen/include/asm-arm/cache.h
> @@ -7,6 +7,10 @@
>  #define L1_CACHE_SHIFT  (CONFIG_ARM_L1_CACHE_SHIFT)
>  #define L1_CACHE_BYTES  (1 << L1_CACHE_SHIFT)
>  
> +#ifndef __ASSEMBLY__
> +void __flush_dcache_area(const void *vaddr, unsigned long size);
> +#endif

I think it might be best to add it to xen/include/asm-arm/arm64/page.h


>  #define __read_mostly __section(".data.read_mostly")
>  
>  #endif
> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> index 2f5c51c..a66b6be 100644
> --- a/xen/include/asm-arm/gic-its.h
> +++ b/xen/include/asm-arm/gic-its.h
> @@ -36,12 +36,32 @@ extern struct list_head host_its_list;
>  /* Parse the host DT and pick up all host ITSes. */
>  void gicv3_its_dt_init(const struct dt_device_node *node);
>  
> +/* Allocate and initialize tables for each host redistributor.
> + * Returns the respective {PROP,PEND}BASER register value.
> + */
> +uint64_t gicv3_lpi_get_proptable(void);
> +uint64_t gicv3_lpi_allocate_pendtable(void);
> +
> +/* Initialize the host structures for LPIs. */
> +int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
> +
>  #else
>  
>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
>  {
>  }
> -
> +static inline uint64_t gicv3_lpi_get_proptable(void)
> +{
> +    return 0;
> +}
> +static inline uint64_t gicv3_lpi_allocate_pendtable(void)
> +{
> +    return 0;
> +}
> +static inline int gicv3_lpi_init_host_lpis(unsigned int nr_lpis)
> +{
> +    return 0;
> +}
>  #endif /* CONFIG_HAS_ITS */
>  
>  #endif /* __ASSEMBLY__ */
> diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
> index 6bd25a5..da5fb77 100644
> --- a/xen/include/asm-arm/gic_v3_defs.h
> +++ b/xen/include/asm-arm/gic_v3_defs.h
> @@ -44,7 +44,8 @@
>  #define GICC_SRE_EL2_ENEL1           (1UL << 3)
>  
>  /* Additional bits in GICD_TYPER defined by GICv3 */
> -#define GICD_TYPE_ID_BITS_SHIFT 19
> +#define GICD_TYPE_ID_BITS_SHIFT      19
> +#define GICD_TYPE_LPIS               (1U << 17)
>  
>  #define GICD_CTLR_RWP                (1UL << 31)
>  #define GICD_CTLR_ARE_NS             (1U << 4)
> @@ -95,12 +96,57 @@
>  #define GICR_IGRPMODR0               (0x0D00)
>  #define GICR_NSACR                   (0x0E00)
>  
> +#define GICR_CTLR_ENABLE_LPIS        (1U << 0)
>  #define GICR_TYPER_PLPIS             (1U << 0)
>  #define GICR_TYPER_VLPIS             (1U << 1)
>  #define GICR_TYPER_LAST              (1U << 4)
>  
> +#define GIC_BASER_CACHE_nCnB         0ULL
> +#define GIC_BASER_CACHE_SameAsInner  0ULL
> +#define GIC_BASER_CACHE_nC           1ULL
> +#define GIC_BASER_CACHE_RaWt         2ULL
> +#define GIC_BASER_CACHE_RaWb         3ULL
> +#define GIC_BASER_CACHE_WaWt         4ULL
> +#define GIC_BASER_CACHE_WaWb         5ULL
> +#define GIC_BASER_CACHE_RaWaWt       6ULL
> +#define GIC_BASER_CACHE_RaWaWb       7ULL
> +#define GIC_BASER_CACHE_MASK         7ULL
> +#define GIC_BASER_NonShareable       0ULL
> +#define GIC_BASER_InnerShareable     1ULL
> +#define GIC_BASER_OuterShareable     2ULL
> +
> +#define GICR_PROPBASER_SHAREABILITY_SHIFT               10
> +#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT         7
> +#define GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT         56
> +#define GICR_PROPBASER_SHAREABILITY_MASK                     \
> +        (3UL << GICR_PROPBASER_SHAREABILITY_SHIFT)
> +#define GICR_PROPBASER_INNER_CACHEABILITY_MASK               \
> +        (7UL << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT)
> +#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK               \
> +        (7UL << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT)
> +#define PROPBASER_RES0_MASK                                  \
> +        (GENMASK(63, 59) | GENMASK(55, 52) | GENMASK(6, 5))
> +
> +#define GICR_PENDBASER_SHAREABILITY_SHIFT               10
> +#define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT         7
> +#define GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT         56
> +#define GICR_PENDBASER_SHAREABILITY_MASK                     \
> +	(3UL << GICR_PENDBASER_SHAREABILITY_SHIFT)
> +#define GICR_PENDBASER_INNER_CACHEABILITY_MASK               \
> +	(7UL << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT)
> +#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK               \
> +        (7UL << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT)
> +#define GICR_PENDBASER_PTZ                              BIT(62)
> +#define PENDBASER_RES0_MASK                                  \
> +        (BIT(63) | GENMASK(61, 59) | GENMASK(55, 52) |       \
> +         GENMASK(15, 12) | GENMASK(6, 0))
> +
>  #define DEFAULT_PMR_VALUE            0xff
>  
> +#define LPI_PROP_DEFAULT_PRIO        0xa0
> +#define LPI_PROP_RES1                (1 << 1)
> +#define LPI_PROP_ENABLED             (1 << 0)
> +
>  #define GICH_VMCR_EOI                (1 << 9)
>  #define GICH_VMCR_VENG1              (1 << 1)
>  
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2016-12-22 18:24 ` [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
@ 2017-01-04 21:47   ` Stefano Stabellini
  2017-01-05 17:56     ` Andre Przywara
  2017-01-20 11:12     ` Julien Grall
  0 siblings, 2 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-04 21:47 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
> an EventID (the MSI payload or interrupt ID) to a pair of LPI number
> and collection ID, which points to the target CPU.
> This mapping is stored in the device and collection tables, which software
> has to provide for the ITS to use.
> Allocate the required memory and hand it the ITS.
> The maximum number of devices is limited to a compile-time constant
> exposed in Kconfig.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/Kconfig          |   6 +++
>  xen/arch/arm/gic-its.c        | 114 ++++++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c         |   5 ++
>  xen/include/asm-arm/bitops.h  |   1 +
>  xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
>  5 files changed, 176 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
> index a7d941c..a369305 100644
> --- a/xen/arch/arm/Kconfig
> +++ b/xen/arch/arm/Kconfig
> @@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
>            This can be overriden on the command line with the max_lpi_bits
>            parameter.
>  
> +config MAX_ITS_PCI_BUSSES
> +        depends on HAS_ITS
> +        int "Number of PCI busses the ITS supports (4)"
> +        range 1 1024
> +        default "4"

Given that any kind of devices can be behind an ITS, including non-PCI
devices, I think it is best to call this MAX_PHYS_ITS_DEVICES.


>  endmenu
>  
>  menu "ARM errata workaround via the alternative framework"
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index 6c3a35d..f1540b2 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -22,6 +22,7 @@
>  #include <xen/libfdt/libfdt.h>
>  #include <xen/sizes.h>
>  #include <asm/p2m.h>
> +#include <asm/io.h>
>  #include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
>  #include <asm/gic-its.h>
> @@ -37,6 +38,119 @@ static DEFINE_PER_CPU(void *, pending_table);
>  
>  #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
>  
> +#define BASER_ATTR_MASK                                           \
> +        ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
> +         (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
> +         (0x7UL << GITS_BASER_INNER_CACHEABILITY_SHIFT))
> +#define BASER_RO_MASK   (GENMASK(58, 56) | GENMASK(52, 48))
> +
> +static uint64_t encode_phys_addr(paddr_t addr, int page_bits)
> +{
> +    uint64_t ret;
> +
> +    if ( page_bits < 16)
> +        return (uint64_t)addr & GENMASK(47, page_bits);
> +
> +    ret = addr & GENMASK(47, 16);
> +    return ret | (addr & GENMASK(51, 48)) >> (48 - 12);
> +}
> +
> +static int its_map_baser(void __iomem *basereg, uint64_t regc, int nr_items)
> +{
> +    uint64_t attr;
> +    int entry_size = ((regc >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1;
> +    int pagesz;
> +    int order;
> +    void *buffer = NULL;
> +
> +    attr  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
> +    attr |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
> +    attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
> +
> +    /*
> +     * Loop over the page sizes (4K, 16K, 64K) to find out what the host
> +     * supports.
> +     */
> +    for ( pagesz = 0; pagesz < 3; pagesz++ )
> +    {
> +        uint64_t reg;
> +        int nr_bytes;
> +
> +        nr_bytes = ROUNDUP(nr_items * entry_size, BIT(pagesz * 2 + 12));

12/PAGE_SHIFT


> +        order = get_order_from_bytes(nr_bytes);
> +
> +        if ( !buffer )
> +            buffer = alloc_xenheap_pages(order, 0);
> +        if ( !buffer )
> +            return -ENOMEM;
> +
> +        reg  = attr;
> +        reg |= (pagesz << GITS_BASER_PAGE_SIZE_SHIFT);
> +        reg |= nr_bytes >> (pagesz * 2 + 12);
> +        reg |= regc & BASER_RO_MASK;
> +        reg |= GITS_BASER_VALID;
> +        reg |= encode_phys_addr(virt_to_maddr(buffer), pagesz * 2 + 12);
> +
> +        writeq_relaxed(reg, basereg);
> +        regc = readl_relaxed(basereg);
> +
> +        /* The host didn't like our attributes, just use what it returned. */
> +        if ( (regc & BASER_ATTR_MASK) != attr )
> +            attr = regc & BASER_ATTR_MASK;
> +
> +        /* If the host accepted our page size, we are done. */
> +        if ( (reg & (3UL << GITS_BASER_PAGE_SIZE_SHIFT)) == pagesz )
> +            return 0;
> +
> +        /* Check whether our buffer is aligned to the next page size already. */
> +        if ( !(virt_to_maddr(buffer) & (BIT(pagesz * 2 + 12 + 2) - 1)) )
> +        {
> +            free_xenheap_pages(buffer, order);
> +            buffer = NULL;
> +        }

Regardless of alignment, should we always free buffer here, given that
we need to allocate the right size for the next attempt?


> +    }
> +
> +    if ( buffer )
> +        free_xenheap_pages(buffer, order);
> +
> +    return -EINVAL;
> +}
> +
> +int gicv3_its_init(struct host_its *hw_its)
> +{
> +    uint64_t reg;
> +    int i;
> + 
> +    hw_its->its_base = ioremap_nocache(hw_its->addr, hw_its->size);
> +    if ( !hw_its->its_base )
> +        return -ENOMEM;
> +
> +    for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
> +    {
> +        void __iomem *basereg = hw_its->its_base + GITS_BASER0 + i * 8;
> +        int type;
> +
> +        reg = readq_relaxed(basereg);
> +        type = (reg & GITS_BASER_TYPE_MASK) >> GITS_BASER_TYPE_SHIFT;
> +        switch ( type )
> +        {
> +        case GITS_BASER_TYPE_NONE:
> +            continue;
> +        case GITS_BASER_TYPE_DEVICE:
> +            /* TODO: find some better way of limiting the number of devices */
> +            its_map_baser(basereg, reg, CONFIG_MAX_ITS_PCI_BUSSES * 256);
> +            break;
> +        case GITS_BASER_TYPE_COLLECTION:
> +            its_map_baser(basereg, reg, NR_CPUS);
> +            break;
> +        default:
> +            continue;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
>  uint64_t gicv3_lpi_allocate_pendtable(void)
>  {
>      uint64_t reg, attr;
> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> index 1c6869d..8ca7da2 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -29,6 +29,7 @@
>  #include <xen/irq.h>
>  #include <xen/iocap.h>
>  #include <xen/sched.h>
> +#include <xen/err.h>
>  #include <xen/errno.h>
>  #include <xen/delay.h>
>  #include <xen/device_tree.h>
> @@ -1564,6 +1565,7 @@ static int __init gicv3_init(void)
>  {
>      int res, i;
>      uint32_t reg;
> +    struct host_its *hw_its;
>  
>      if ( !cpu_has_gicv3 )
>      {
> @@ -1619,6 +1621,9 @@ static int __init gicv3_init(void)
>      res = gicv3_cpu_init();
>      gicv3_hyp_init();
>  
> +    list_for_each_entry(hw_its, &host_its_list, entry)
> +        gicv3_its_init(hw_its);
> +
>      spin_unlock(&gicv3.lock);
>  
>      return res;
> diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
> index c9adf79..4ccb444 100644
> --- a/xen/include/asm-arm/bitops.h
> +++ b/xen/include/asm-arm/bitops.h
> @@ -24,6 +24,7 @@
>  #define BIT(nr)                 (1UL << (nr))
>  #define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_WORD))
>  #define BIT_WORD(nr)            ((nr) / BITS_PER_WORD)
> +#define BIT_ULL(nr)             (1ULL << (nr))
>  #define BITS_PER_BYTE           8
>  #define BIT_ULL(nr)             (1ULL << (nr))
>  
> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> index a66b6be..26b0584 100644
> --- a/xen/include/asm-arm/gic-its.h
> +++ b/xen/include/asm-arm/gic-its.h
> @@ -18,6 +18,49 @@
>  #ifndef __ASM_ARM_ITS_H__
>  #define __ASM_ARM_ITS_H__
>  
> +#define LPI_OFFSET                      8192
> +
> +#define GITS_CTLR                       0x000
> +#define GITS_IIDR                       0x004
> +#define GITS_TYPER                      0x008
> +#define GITS_CBASER                     0x080
> +#define GITS_CWRITER                    0x088
> +#define GITS_CREADR                     0x090
> +#define GITS_BASER_NR_REGS              8
> +#define GITS_BASER0                     0x100
> +#define GITS_BASER1                     0x108
> +#define GITS_BASER2                     0x110
> +#define GITS_BASER3                     0x118
> +#define GITS_BASER4                     0x120
> +#define GITS_BASER5                     0x128
> +#define GITS_BASER6                     0x130
> +#define GITS_BASER7                     0x138
> +
> +/* Register bits */
> +#define GITS_CTLR_ENABLE                BIT_ULL(0)
> +#define GITS_IIDR_VALUE                 0x34c
> +
> +#define GITS_BASER_VALID                BIT_ULL(63)
> +#define GITS_BASER_INDIRECT             BIT_ULL(62)
> +#define GITS_BASER_INNER_CACHEABILITY_SHIFT        59
> +#define GITS_BASER_TYPE_SHIFT           56
> +#define GITS_BASER_TYPE_MASK            (7ULL << GITS_BASER_TYPE_SHIFT)
> +#define GITS_BASER_OUTER_CACHEABILITY_SHIFT        53
> +#define GITS_BASER_TYPE_NONE            0UL
> +#define GITS_BASER_TYPE_DEVICE          1UL
> +#define GITS_BASER_TYPE_VCPU            2UL
> +#define GITS_BASER_TYPE_CPU             3UL
> +#define GITS_BASER_TYPE_COLLECTION      4UL
> +#define GITS_BASER_TYPE_RESERVED5       5UL
> +#define GITS_BASER_TYPE_RESERVED6       6UL
> +#define GITS_BASER_TYPE_RESERVED7       7UL
> +#define GITS_BASER_ENTRY_SIZE_SHIFT     48
> +#define GITS_BASER_SHAREABILITY_SHIFT   10
> +#define GITS_BASER_PAGE_SIZE_SHIFT      8
> +#define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
> +                                        (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
> +                                        GITS_BASER_INDIRECT)
> +
>  #ifndef __ASSEMBLY__
>  #include <xen/device_tree.h>
>  
> @@ -27,6 +70,7 @@ struct host_its {
>      const struct dt_device_node *dt_node;
>      paddr_t addr;
>      paddr_t size;
> +    void __iomem *its_base;
>  };
>  
>  extern struct list_head host_its_list;
> @@ -42,8 +86,9 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
>  uint64_t gicv3_lpi_get_proptable(void);
>  uint64_t gicv3_lpi_allocate_pendtable(void);
>  
> -/* Initialize the host structures for LPIs. */
> +/* Initialize the host structures for LPIs and the host ITSes. */
>  int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
> +int gicv3_its_init(struct host_its *hw_its);
>  
>  #else
>  
> @@ -62,6 +107,10 @@ static inline int gicv3_lpi_init_host_lpis(unsigned int nr_lpis)
>  {
>      return 0;
>  }
> +static inline int gicv3_its_init(struct host_its *hw_its)
> +{
> +    return 0;
> +}
>  #endif /* CONFIG_HAS_ITS */
>  
>  #endif /* __ASSEMBLY__ */
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 04/26] ARM: GICv3 ITS: map ITS command buffer
  2016-12-22 18:24 ` [RFC PATCH v2 04/26] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
@ 2017-01-04 21:53   ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-04 21:53 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> Instead of directly manipulating the tables in memory, an ITS driver
> sends commands via a ring buffer to the ITS h/w to create or alter the
> LPI mappings.
> Allocate memory for that buffer and tell the ITS about it to be able
> to send ITS commands.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Please address Julien's comments too, but this looks OK from my POV


>  xen/arch/arm/gic-its.c        | 27 +++++++++++++++++++++++++++
>  xen/include/asm-arm/gic-its.h |  3 +++
>  2 files changed, 30 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index f1540b2..2fb3bcb 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -18,6 +18,7 @@
>  
>  #include <xen/config.h>
>  #include <xen/lib.h>
> +#include <xen/err.h>
>  #include <xen/device_tree.h>
>  #include <xen/libfdt/libfdt.h>
>  #include <xen/sizes.h>
> @@ -38,6 +39,8 @@ static DEFINE_PER_CPU(void *, pending_table);
>  
>  #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
>  
> +#define ITS_CMD_QUEUE_SZ                                SZ_64K
> +
>  #define BASER_ATTR_MASK                                           \
>          ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
>           (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
> @@ -55,6 +58,26 @@ static uint64_t encode_phys_addr(paddr_t addr, int page_bits)
>      return ret | (addr & GENMASK(51, 48)) >> (48 - 12);
>  }
>  
> +static void *its_map_cbaser(void __iomem *cbasereg)
> +{
> +    uint64_t attr, reg;
> +    void *buffer;
> +
> +    attr  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
> +    attr |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
> +    attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
> +
> +    buffer = _xzalloc(ITS_CMD_QUEUE_SZ, PAGE_SIZE);
> +    if ( !buffer )
> +        return NULL;
> +
> +    reg = attr | BIT_ULL(63) | (virt_to_maddr(buffer) & GENMASK(51, 12));
> +    reg |= ((ITS_CMD_QUEUE_SZ / PAGE_SIZE) - 1) & GITS_CBASER_SIZE_MASK;
> +    writeq_relaxed(reg, cbasereg);
> +
> +    return buffer;
> +}
> +
>  static int its_map_baser(void __iomem *basereg, uint64_t regc, int nr_items)
>  {
>      uint64_t attr;
> @@ -148,6 +171,10 @@ int gicv3_its_init(struct host_its *hw_its)
>          }
>      }
>  
> +    hw_its->cmd_buf = its_map_cbaser(hw_its->its_base + GITS_CBASER);
> +    if ( !hw_its->cmd_buf )
> +        return -ENOMEM;
> +
>      return 0;
>  }
>  
> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> index 26b0584..e0fded8 100644
> --- a/xen/include/asm-arm/gic-its.h
> +++ b/xen/include/asm-arm/gic-its.h
> @@ -61,6 +61,8 @@
>                                          (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
>                                          GITS_BASER_INDIRECT)
>  
> +#define GITS_CBASER_SIZE_MASK           0xff
> +
>  #ifndef __ASSEMBLY__
>  #include <xen/device_tree.h>
>  
> @@ -71,6 +73,7 @@ struct host_its {
>      paddr_t addr;
>      paddr_t size;
>      void __iomem *its_base;
> +    void *cmd_buf;
>  };
>  
>  extern struct list_head host_its_list;
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 05/26] ARM: GICv3 ITS: introduce ITS command handling
  2016-12-22 18:24 ` [RFC PATCH v2 05/26] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
@ 2017-01-04 22:08   ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-04 22:08 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> To be able to easily send commands to the ITS, create the respective
> wrapper functions, which take care of the ring buffer.
> The first two commands we implement provide methods to map a collection
> to a redistributor (aka host core) and to flush the command queue (SYNC).
> Start using these commands for mapping one collection to each host CPU.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Hi Andre,

please address Julien's comments too


> ---
>  xen/arch/arm/gic-its.c        | 100 ++++++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c         |  15 +++++++
>  xen/include/asm-arm/gic-its.h |  33 ++++++++++++++
>  3 files changed, 148 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index 2fb3bcb..d0f5fd1 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -34,6 +34,10 @@ static struct {
>      unsigned int host_lpi_bits;
>  } lpi_data;
>  
> +/* Physical redistributor address */
> +static DEFINE_PER_CPU(uint64_t, rdist_addr);
> +/* Redistributor ID */
> +static DEFINE_PER_CPU(uint64_t, rdist_id);
>  /* Pending table for each redistributor */
>  static DEFINE_PER_CPU(void *, pending_table);
>  
> @@ -41,6 +45,85 @@ static DEFINE_PER_CPU(void *, pending_table);
>  
>  #define ITS_CMD_QUEUE_SZ                                SZ_64K
>  
> +static int its_send_command(struct host_its *hw_its, void *its_cmd)
> +{
> +    uint64_t readp, writep;
> +
> +    spin_lock(&hw_its->cmd_lock);
> +
> +    readp = readq_relaxed(hw_its->its_base + GITS_CREADR) & GENMASK(19, 5);
> +    writep = readq_relaxed(hw_its->its_base + GITS_CWRITER) & GENMASK(19, 5);
> +
> +    if ( ((writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ) == readp )
> +    {
> +        spin_unlock(&hw_its->cmd_lock);
> +        return -EBUSY;
> +    }
> +
> +    memcpy(hw_its->cmd_buf + writep, its_cmd, ITS_CMD_SIZE);
> +    __flush_dcache_area(hw_its->cmd_buf + writep, ITS_CMD_SIZE);
> +    writep = (writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ;
> +
> +    writeq_relaxed(writep & GENMASK(19, 5), hw_its->its_base + GITS_CWRITER);
> +
> +    spin_unlock(&hw_its->cmd_lock);
> +
> +    return 0;
> +}
> +
> +static uint64_t encode_rdbase(struct host_its *hw_its, int cpu, uint64_t reg)
> +{
> +    reg &= ~GENMASK(51, 16);
> +
> +    if ( hw_its->pta )
> +        reg |= per_cpu(rdist_addr, cpu) & GENMASK(51, 16);
> +    else
> +        reg |= per_cpu(rdist_id, cpu) << 16;
> +
> +    return reg;
> +}
> +
> +static int its_send_cmd_sync(struct host_its *its, int cpu)
> +{
> +    uint64_t cmd[4];
> +
> +    cmd[0] = GITS_CMD_SYNC;
> +    cmd[1] = 0x00;
> +    cmd[2] = encode_rdbase(its, cpu, 0x0);
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
> +static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
> +{
> +    uint64_t cmd[4];
> +
> +    cmd[0] = GITS_CMD_MAPC;
> +    cmd[1] = 0x00;
> +    cmd[2] = encode_rdbase(its, cpu, (collection_id & GENMASK(15, 0)));
> +    cmd[2] |= BIT_ULL(63);
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
> +/* Set up the (1:1) collection mapping for the given host CPU. */
> +void gicv3_its_setup_collection(int cpu)
> +{
> +    struct host_its *its;
> +
> +    list_for_each_entry(its, &host_its_list, entry)
> +    {
> +        /* Only send commands to ITS that have been initialized already. */
> +        if ( !its->cmd_buf )
> +            continue;

Please add a comment to say that this function gets called on cpu0 when
ITS is not initialized, this is why the check is necessary.


> +        its_send_cmd_mapc(its, cpu, cpu);
> +        its_send_cmd_sync(its, cpu);

We need to check for returned errors


> +    }
> +}
> +
>  #define BASER_ATTR_MASK                                           \
>          ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
>           (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
> @@ -148,6 +231,13 @@ int gicv3_its_init(struct host_its *hw_its)
>      if ( !hw_its->its_base )
>          return -ENOMEM;
>  
> +    /* Make sure the ITS is disabled before programming the BASE registers. */
> +    reg = readl_relaxed(hw_its->its_base + GITS_CTLR);
> +    writel_relaxed(reg & ~GITS_CTLR_ENABLE, hw_its->its_base + GITS_CTLR);
> +
> +    reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
> +    hw_its->pta = !!(reg & GITS_TYPER_PTA);
> +
>      for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
>      {
>          void __iomem *basereg = hw_its->its_base + GITS_BASER0 + i * 8;
> @@ -175,9 +265,18 @@ int gicv3_its_init(struct host_its *hw_its)
>      if ( !hw_its->cmd_buf )
>          return -ENOMEM;
>  
> +    its_send_cmd_mapc(hw_its, smp_processor_id(), smp_processor_id());
> +    its_send_cmd_sync(hw_its, smp_processor_id());

We need to check for returned errors.

Please add a comment to say that we are collection mapping cpu0. Also,
it could be done by calling gicv3_its_setup_collection.


>      return 0;
>  }
>  
> +void gicv3_set_redist_addr(paddr_t address, int redist_id)
> +{
> +    this_cpu(rdist_addr) = address;
> +    this_cpu(rdist_id) = redist_id;
> +}
> +
>  uint64_t gicv3_lpi_allocate_pendtable(void)
>  {
>      uint64_t reg, attr;
> @@ -286,6 +385,7 @@ void gicv3_its_dt_init(const struct dt_device_node *node)
>          its_data->addr = addr;
>          its_data->size = size;
>          its_data->dt_node = its;
> +        spin_lock_init(&its_data->cmd_lock);
>  
>          printk("GICv3: Found ITS @0x%lx\n", addr);
>  
> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> index 8ca7da2..d2461cb 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -643,6 +643,8 @@ static int gicv3_rdist_init_lpis(void __iomem * rdist_base)
>          return -ENOMEM;
>      writeq_relaxed(table_reg, rdist_base + GICR_PROPBASER);
>  
> +    gicv3_its_setup_collection(smp_processor_id());
> +
>      return 0;
>  }
>  
> @@ -691,8 +693,21 @@ static int __init gicv3_populate_rdist(void)
>  
>                  if ( typer & GICR_TYPER_PLPIS )
>                  {
> +                    paddr_t rdist_addr;
>                      int ret;
>  
> +                    rdist_addr = gicv3.rdist_regions[i].base;
> +                    rdist_addr += ptr - gicv3.rdist_regions[i].map_base;
> +
> +                    /* The ITS refers to redistributors either by their physical
> +                     * address or by their ID. Determine those two values and
> +                     * let the ITS code store them in per host CPU variables to
> +                     * later be able to address those redistributors.
> +                     */
> +                    gicv3_set_redist_addr(rdist_addr,
> +                                          (typer >> GITS_TYPER_IDBITS_SHIFT) &
> +                                          GENMASK(15, 0));
> +
>                      ret = gicv3_rdist_init_lpis(ptr);
>                      if ( ret && ret != -ENODEV )
>                      {
> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> index e0fded8..68e5f63 100644
> --- a/xen/include/asm-arm/gic-its.h
> +++ b/xen/include/asm-arm/gic-its.h
> @@ -38,6 +38,8 @@
>  
>  /* Register bits */
>  #define GITS_CTLR_ENABLE                BIT_ULL(0)
> +#define GITS_TYPER_PTA                  BIT_ULL(19)
> +#define GITS_TYPER_IDBITS_SHIFT         8
>  #define GITS_IIDR_VALUE                 0x34c
>  
>  #define GITS_BASER_VALID                BIT_ULL(63)
> @@ -63,6 +65,22 @@
>  
>  #define GITS_CBASER_SIZE_MASK           0xff
>  
> +/* ITS command definitions */
> +#define ITS_CMD_SIZE                    32
> +
> +#define GITS_CMD_MOVI                   0x01
> +#define GITS_CMD_INT                    0x03
> +#define GITS_CMD_CLEAR                  0x04
> +#define GITS_CMD_SYNC                   0x05
> +#define GITS_CMD_MAPD                   0x08
> +#define GITS_CMD_MAPC                   0x09
> +#define GITS_CMD_MAPTI                  0x0a
> +#define GITS_CMD_MAPI                   0x0b
> +#define GITS_CMD_INV                    0x0c
> +#define GITS_CMD_INVALL                 0x0d
> +#define GITS_CMD_MOVALL                 0x0e
> +#define GITS_CMD_DISCARD                0x0f
> +
>  #ifndef __ASSEMBLY__
>  #include <xen/device_tree.h>
>  
> @@ -73,7 +91,9 @@ struct host_its {
>      paddr_t addr;
>      paddr_t size;
>      void __iomem *its_base;
> +    spinlock_t cmd_lock;
>      void *cmd_buf;
> +    bool pta;
>  };
>  
>  extern struct list_head host_its_list;
> @@ -93,6 +113,12 @@ uint64_t gicv3_lpi_allocate_pendtable(void);
>  int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
>  int gicv3_its_init(struct host_its *hw_its);
>  
> +/* Set the physical address and ID for each redistributor as read from DT. */
> +void gicv3_set_redist_addr(paddr_t address, int redist_id);
> +
> +/* Map a collection for this host CPU to each host ITS. */
> +void gicv3_its_setup_collection(int cpu);
> +
>  #else
>  
>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
> @@ -114,6 +140,13 @@ static inline int gicv3_its_init(struct host_its *hw_its)
>  {
>      return 0;
>  }
> +static inline void gicv3_set_redist_addr(paddr_t address, int redist_id)
> +{
> +}
> +static inline void gicv3_its_setup_collection(int cpu)
> +{
> +}
> +
>  #endif /* CONFIG_HAS_ITS */
>  
>  #endif /* __ASSEMBLY__ */
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping
  2016-12-22 18:24 ` [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping Andre Przywara
@ 2017-01-05  0:13   ` Stefano Stabellini
  2017-01-13 12:31     ` Andre Przywara
  2017-01-13 12:31     ` Andre Przywara
  0 siblings, 2 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05  0:13 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> The ITS uses device IDs to map LPIs to a device. Dom0 will later use
> those IDs, which we directly pass on to the host.
> For this we have to map each device that Dom0 may request to a host
> ITS device with the same identifier.
> Allocate the respective memory and enter each device into a list to
> later be able to iterate over it or to easily teardown guests.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-its.c        | 118 ++++++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic.c           |   3 ++
>  xen/include/asm-arm/domain.h  |   2 +
>  xen/include/asm-arm/gic-its.h |  22 ++++++++
>  4 files changed, 145 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index d0f5fd1..e157c6b 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -21,6 +21,7 @@
>  #include <xen/err.h>
>  #include <xen/device_tree.h>
>  #include <xen/libfdt/libfdt.h>
> +#include <xen/sched.h>
>  #include <xen/sizes.h>
>  #include <asm/p2m.h>
>  #include <asm/io.h>
> @@ -108,6 +109,21 @@ static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
>      return its_send_command(its, cmd);
>  }
>  
> +static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
> +                             int size, uint64_t itt_addr, bool valid)
> +{
> +    uint64_t cmd[4];
> +
> +    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
> +    cmd[1] = size & GENMASK(4, 0);
> +    cmd[2] = itt_addr & GENMASK(51, 8);
> +    if ( valid )
> +        cmd[2] |= BIT_ULL(63);
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
>  /* Set up the (1:1) collection mapping for the given host CPU. */
>  void gicv3_its_setup_collection(int cpu)
>  {
> @@ -237,6 +253,7 @@ int gicv3_its_init(struct host_its *hw_its)
>  
>      reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
>      hw_its->pta = !!(reg & GITS_TYPER_PTA);
> +    hw_its->itte_size = ((reg >> 4) & 0xf) + 1;

Please #define all numbers


>      for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
>      {
> @@ -358,6 +375,107 @@ int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
>      return 0;
>  }
>  
> +static void remove_mapped_guest_device(struct its_devices *dev)
> +{
> +    if ( dev->hw_its )
> +        its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
> +
> +    xfree(dev->itt_addr);
> +    xfree(dev);
> +}
> +
> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
> +                         int bits, bool valid)
> +{
> +    void *itt_addr = NULL;
> +    struct its_devices *dev, *temp;
> +    struct host_its *hw_its;
> +    int ret;
> +
> +    /* check for already existing mappings */
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
> +    {
> +        if ( dev->guest_devid != guest_devid )
> +            continue;
> +
> +        if ( !valid )
> +            list_del(&dev->entry);
> +
> +        spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +        if ( valid )
> +            return -EBUSY;
> +
> +        remove_mapped_guest_device(dev);
> +
> +        return 0;
> +    }
> +    spin_unlock(&d->arch.vgic.its_devices_lock);

Compared to the previous version, now the list is per-domain, which is
better for DomUs, but not for Dom0. In the case of Dom0 the number of
iterations will be the same.

I suggest we move to a different data structure, such as an hashtable or
an rbtree. If devids were guaranteed to be dense, then we could store
struct its_devices pointers in an array and direct access them, but I
don't think they are?

If devids are sparse, using a per-domain structure could be a good idea,
but we still need to prevent a device from being assigned to two domains
simultaneously. Is there a check to avoid that?


> +    if ( !valid )
> +        return -ENOENT;
> +
> +    /* TODO: Work out the correct hardware ITS to use here.
> +     * Maybe a per-platform function: devid -> ITS?
> +     * Or parsing the DT to find the msi_parent?
> +     * Or get Dom0 to give us this information?
> +     * For now just use the first ITS.
> +     */
> +    hw_its = list_first_entry(&host_its_list, struct host_its, entry);
> +
> +    itt_addr = _xmalloc(BIT(bits) * hw_its->itte_size, 256);
> +    if ( !itt_addr )
> +        return -ENOMEM;
> +
> +    dev = xmalloc(struct its_devices);
> +    if ( !dev )
> +    {
> +        xfree(itt_addr);
> +        return -ENOMEM;
> +    }
> +
> +    ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
> +                            virt_to_maddr(itt_addr), true);
> +    if (ret) {
> +        xfree(itt_addr);
> +        xfree(dev);
> +        return ret;
> +    }
> +
> +    dev->itt_addr = itt_addr;
> +    dev->hw_its = hw_its;
> +    dev->guest_devid = guest_devid;
> +    dev->host_devid = host_devid;
> +    dev->eventids = BIT(bits);
> +
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +    return 0;
> +}
> +
> +/* Removing any connections a domain had to any ITS in the system. */
> +int its_remove_domain(struct domain *d)
> +{
> +    struct its_devices *dev, *temp;
> +
> +retry:
> +
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
> +    {
> +        list_del(&dev->entry);
> +        spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +        remove_mapped_guest_device(dev);
> +        goto retry;
> +    }
> +
> +    return 0;
> +}
> +
>  /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
>  void gicv3_its_dt_init(const struct dt_device_node *node)
>  {
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index 364d5f0..de77aaa 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -156,6 +156,9 @@ int domain_vgic_init(struct domain *d, unsigned int nr_spis)
>      for ( i = 0; i < NR_GIC_SGI; i++ )
>          set_bit(i, d->arch.vgic.allocated_irqs);
>  
> +    spin_lock_init(&d->arch.vgic.its_devices_lock);
> +    INIT_LIST_HEAD(&d->arch.vgic.its_devices);
> +
>      return 0;
>  }
>  
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 2d6fbb1..8ccc32a 100644
> --- a/xen/include/asm-arm/domain.h
> +++ b/xen/include/asm-arm/domain.h
> @@ -109,6 +109,8 @@ struct arch_domain
>          } *rdist_regions;
>          int nr_regions;                     /* Number of rdist regions */
>          uint32_t rdist_stride;              /* Re-Distributor stride */
> +        struct list_head its_devices;       /* devices mapped to an ITS */
> +        spinlock_t its_devices_lock;        /* protects the its_devices list */
>  #endif
>      } vgic;
>  
> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> index 68e5f63..525a29d 100644
> --- a/xen/include/asm-arm/gic-its.h
> +++ b/xen/include/asm-arm/gic-its.h
> @@ -93,9 +93,19 @@ struct host_its {
>      void __iomem *its_base;
>      spinlock_t cmd_lock;
>      void *cmd_buf;
> +    int itte_size;
>      bool pta;
>  };
>  
> +struct its_devices {
> +    struct list_head entry;
> +    struct host_its *hw_its;
> +    void *itt_addr;
> +    uint32_t guest_devid;
> +    uint32_t host_devid;
> +    uint32_t eventids;
> +};
> +
>  extern struct list_head host_its_list;
>  
>  #ifdef CONFIG_HAS_ITS
> @@ -119,6 +129,13 @@ void gicv3_set_redist_addr(paddr_t address, int redist_id);
>  /* Map a collection for this host CPU to each host ITS. */
>  void gicv3_its_setup_collection(int cpu);
>  
> +/* Map a device on the host by allocating an ITT on the host (ITS).
> + * "bits" specifies how many events (interrupts) this device will need.
> + * Setting "valid" to false deallocates the device.
> + */
> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
> +                         int bits, bool valid);
> +
>  #else
>  
>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
> @@ -146,6 +163,11 @@ static inline void gicv3_set_redist_addr(paddr_t address, int redist_id)
>  static inline void gicv3_its_setup_collection(int cpu)
>  {
>  }
> +static inline int gicv3_its_map_device(struct domain *d, int host_devid,
> +                         int guest_devid, int bits, bool valid)
> +{
> +    return -ENODEV;
> +}
>  
>  #endif /* CONFIG_HAS_ITS */
>  
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table
  2017-01-04 21:09   ` Stefano Stabellini
@ 2017-01-05 17:13     ` Andre Przywara
  2017-01-05 17:52       ` Stefano Stabellini
  0 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-05 17:13 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi,

On 04/01/17 21:09, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
>> The ARM GICv3 ITS provides a new kind of interrupt called LPIs.
>> The pending bits and the configuration data (priority, enable bits) for
>> those LPIs are stored in tables in normal memory, which software has to
>> provide to the hardware.
>> Allocate the required memory, initialize it and hand it over to each
>> ITS. The maximum number of LPIs to be used can be adjusted with the
>> command line option "max_lpi_bits", which defaults to a compile time
>> constant exposed in Kconfig.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/Kconfig              | 10 +++++
>>  xen/arch/arm/efi/efi-boot.h       |  1 -
>>  xen/arch/arm/gic-its.c            | 94 +++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic-v3.c             | 43 ++++++++++++++++++
>>  xen/include/asm-arm/bitops.h      |  1 +
>>  xen/include/asm-arm/cache.h       |  4 ++
>>  xen/include/asm-arm/gic-its.h     | 22 ++++++++-
>>  xen/include/asm-arm/gic_v3_defs.h | 48 +++++++++++++++++++-
>>  8 files changed, 220 insertions(+), 3 deletions(-)
>>
>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>> index bf64c61..a7d941c 100644
>> --- a/xen/arch/arm/Kconfig
>> +++ b/xen/arch/arm/Kconfig
>> @@ -49,6 +49,16 @@ config HAS_ITS
>>          bool "GICv3 ITS MSI controller support"
>>          depends on HAS_GICV3
>>  
>> +config MAX_HOST_LPI_BITS
>             ^ MAX_PHYS_LPI_BITS

Right, I missed that symbol.

>> +        depends on HAS_ITS
>> +        int "Maximum bits for GICv3 host LPIs (14-32)"
>> +        range 14 32
>> +        default "20"
>> +        help
>> +          Specifies the maximum number of LPIs (bits) Xen should take care of.
>> +          This can be overriden on the command line with the max_lpi_bits
>> +          parameter.
>> +
>>  endmenu
>>  
>>  menu "ARM errata workaround via the alternative framework"
>> diff --git a/xen/arch/arm/efi/efi-boot.h b/xen/arch/arm/efi/efi-boot.h
>> index 045d6ce..dc64aec 100644
>> --- a/xen/arch/arm/efi/efi-boot.h
>> +++ b/xen/arch/arm/efi/efi-boot.h
>> @@ -10,7 +10,6 @@
>>  #include "efi-dom0.h"
>>  
>>  void noreturn efi_xen_start(void *fdt_ptr, uint32_t fdt_size);
>> -void __flush_dcache_area(const void *vaddr, unsigned long size);
>>  
>>  #define DEVICE_TREE_GUID \
>>  {0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}}
>> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
>> index 973f9f9..6c3a35d 100644
>> --- a/xen/arch/arm/gic-its.c
>> +++ b/xen/arch/arm/gic-its.c
>> @@ -20,10 +20,104 @@
>>  #include <xen/lib.h>
>>  #include <xen/device_tree.h>
>>  #include <xen/libfdt/libfdt.h>
>> +#include <xen/sizes.h>
>> +#include <asm/p2m.h>
>>  #include <asm/gic.h>
>>  #include <asm/gic_v3_defs.h>
>>  #include <asm/gic-its.h>
>>  
>> +/* Global state */
>> +static struct {
>> +    uint8_t *lpi_property;
>> +    unsigned int host_lpi_bits;
> 
> I think it is best to rename host_lpi_bits to phys_lpi_bits, or, even
> better, phys_nr_lpi because it represents the number of LPIs.

But we need the number of bits in at least one function (for populating
the PROPBASER register), also the number of bits is the original input
value and sets the limit on possible values - as it needs to be a power
of two.
So shall I use nr_phys_lpi_bits instead?

>> +} lpi_data;
>> +
>> +/* Pending table for each redistributor */
>> +static DEFINE_PER_CPU(void *, pending_table);
>> +
>> +#define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
>> +
>> +uint64_t gicv3_lpi_allocate_pendtable(void)
>> +{
>> +    uint64_t reg, attr;
>> +    void *pendtable;
>> +
>> +    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
>> +    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
>> +    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
>> +
>> +    if ( !this_cpu(pending_table) )
>> +    {
>> +        /*
>> +         * The pending table holds one bit per LPI, so we need (2 << 3) bits
>> +         * less bytes. The pending table covers even bits for interrupt IDs
>> +         * below 8192, so we allocate the full range.
>> +         * The ITS imposes a 64KB alignment requirement.
>> +         */
>> +        pendtable = _xmalloc(BIT_ULL(lpi_data.host_lpi_bits) / 8, SZ_64K);
> 
> If we need (2 << 3) less bytes, why are we dividing by 8?

Because ... I am wrong here ;-)

> Isn't this just a straightforward bits to bytes conversion?

Yes.

> I think the comment is probably wrong or outdated.

Yeah, I tried to make this clearer after your previous comments (where
this simple bits-to-bytes apparently didn't come through) and got
somehow carried away ;-)

>> +        if ( !pendtable )
>> +            return 0;
>> +
>> +        memset(pendtable, 0, BIT_ULL(lpi_data.host_lpi_bits) / 8);
>> +        __flush_dcache_area(pendtable, BIT_ULL(lpi_data.host_lpi_bits) / 8);
>> +
>> +        this_cpu(pending_table) = pendtable;
>> +    }
>> +    else
>> +    {
>> +        pendtable = this_cpu(pending_table);
> 
> Isn't this an error condition?

This is internally only called once when each redistributor is
enumerated. So this maybe a BUG_ON(), but on the other hand I don't see
a reason why this would be fatal. If we try to "allocate" a second time,
we just get the existing version.
But I can happily replace this with a BUG_ON(), if you prefer. Seems to
be more matching the Xen policy, I guess.

>> +    }
>> +
>> +    reg  = attr | GICR_PENDBASER_PTZ;
>> +    reg |= virt_to_maddr(pendtable) & GENMASK(51, 16);
>> +
>> +    return reg;
>> +}
>> +
>> +uint64_t gicv3_lpi_get_proptable()
>> +{
>> +    uint64_t attr, reg;
>> +
>> +    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
>> +    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
>> +    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
>> +
>> +    /*
>> +     * The property table is shared across all redistributors, so allocate
>> +     * this only once, but return the same value on subsequent calls.
>> +     */
>> +    if ( !lpi_data.lpi_property )
>> +    {
>> +        /* The property table holds one byte per LPI. */
>> +        void *table = alloc_xenheap_pages(lpi_data.host_lpi_bits - PAGE_SHIFT,
>> +                                          0);
>> +
>> +        if ( !table )
>> +            return 0;
>> +
>> +        memset(table, GIC_PRI_IRQ | LPI_PROP_RES1, MAX_PHYS_LPIS);
>> +        __flush_dcache_area(table, MAX_PHYS_LPIS);
>> +        lpi_data.lpi_property = table;
>> +    }
>> +
>> +    reg  = attr | ((lpi_data.host_lpi_bits - 1) << 0);
>> +    reg |= virt_to_maddr(lpi_data.lpi_property) & GENMASK(51, 12);
>> +
>> +    return reg;
>> +}
>> +
>> +static unsigned int max_lpi_bits = CONFIG_MAX_HOST_LPI_BITS;
>> +integer_param("max_lpi_bits", max_lpi_bits);
>> +
>> +int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
>                   ^ gicv3_lpi_init_phys_lpis

OK.

>> +{
>> +    lpi_data.host_lpi_bits = min(hw_lpi_bits, max_lpi_bits);
>> +
>> +    printk("GICv3: using at most %lld LPIs on the host.\n", MAX_PHYS_LPIS);
>> +
>> +    return 0;
>> +}
>> +
>>  /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
>>  void gicv3_its_dt_init(const struct dt_device_node *node)
>>  {
>> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
>> index 238da84..1c6869d 100644
>> --- a/xen/arch/arm/gic-v3.c
>> +++ b/xen/arch/arm/gic-v3.c
>> @@ -546,6 +546,9 @@ static void __init gicv3_dist_init(void)
>>      type = readl_relaxed(GICD + GICD_TYPER);
>>      nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1);
>>  
>> +    if ( type & GICD_TYPE_LPIS )
>> +        gicv3_lpi_init_host_lpis(((type >> GICD_TYPE_ID_BITS_SHIFT) & 0x1f) + 1);
> 
> Still need to define a mask instead of using 0x1f

I guess I prefer a macro for the whole statement here. I regularly get
annoyed by those descriptive names getting too long and forcing a rather
simple statement on two or more lines ...

>> +
>>      printk("GICv3: %d lines, (IID %8.8x).\n",
>>             nr_lines, readl_relaxed(GICD + GICD_IIDR));
>>  
>> @@ -616,6 +619,32 @@ static int gicv3_enable_redist(void)
>>      return 0;
>>  }
>>  
>> +static int gicv3_rdist_init_lpis(void __iomem * rdist_base)
>> +{
>> +    uint32_t reg;
>> +    uint64_t table_reg;
>> +
>> +    if ( list_empty(&host_its_list) )
>> +        return -ENODEV;
>> +
>> +    /* Make sure LPIs are disabled before setting up the BASERs. */
>> +    reg = readl_relaxed(rdist_base + GICR_CTLR);
>> +    if ( reg & GICR_CTLR_ENABLE_LPIS )
>> +        return -EBUSY;
>> +
>> +    table_reg = gicv3_lpi_allocate_pendtable();
>> +    if ( !table_reg )
>> +        return -ENOMEM;
>> +    writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
>> +
>> +    table_reg = gicv3_lpi_get_proptable();
>> +    if ( !table_reg )
>> +        return -ENOMEM;
>> +    writeq_relaxed(table_reg, rdist_base + GICR_PROPBASER);
>> +
>> +    return 0;
>> +}
>> +
>>  static int __init gicv3_populate_rdist(void)
>>  {
>>      int i;
>> @@ -658,6 +687,20 @@ static int __init gicv3_populate_rdist(void)
>>              if ( (typer >> 32) == aff )
>>              {
>>                  this_cpu(rbase) = ptr;
>> +
>> +                if ( typer & GICR_TYPER_PLPIS )
>> +                {
>> +                    int ret;
>> +
>> +                    ret = gicv3_rdist_init_lpis(ptr);
>> +                    if ( ret && ret != -ENODEV )
>> +                    {
>> +                        printk("GICv3: CPU%d: Cannot initialize LPIs: %d\n",
>> +                               smp_processor_id(), ret);
>> +                        break;
>> +                    }
>> +                }
>> +
>>                  printk("GICv3: CPU%d: Found redistributor in region %d @%p\n",
>>                          smp_processor_id(), i, ptr);
>>                  return 0;
>> diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
>> index bda8898..c9adf79 100644
>> --- a/xen/include/asm-arm/bitops.h
>> +++ b/xen/include/asm-arm/bitops.h
>> @@ -25,6 +25,7 @@
>>  #define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_WORD))
>>  #define BIT_WORD(nr)            ((nr) / BITS_PER_WORD)
>>  #define BITS_PER_BYTE           8
>> +#define BIT_ULL(nr)             (1ULL << (nr))
>>  
>>  #define ADDR (*(volatile int *) addr)
>>  #define CONST_ADDR (*(const volatile int *) addr)
>> diff --git a/xen/include/asm-arm/cache.h b/xen/include/asm-arm/cache.h
>> index 2de6564..af96eee 100644
>> --- a/xen/include/asm-arm/cache.h
>> +++ b/xen/include/asm-arm/cache.h
>> @@ -7,6 +7,10 @@
>>  #define L1_CACHE_SHIFT  (CONFIG_ARM_L1_CACHE_SHIFT)
>>  #define L1_CACHE_BYTES  (1 << L1_CACHE_SHIFT)
>>  
>> +#ifndef __ASSEMBLY__
>> +void __flush_dcache_area(const void *vaddr, unsigned long size);
>> +#endif
> 
> I think it might be best to add it to xen/include/asm-arm/arm64/page.h

Apart from the fact that it's not needed atm on ARM, is there any other
reason to confine it to ARM64?

Cheers,
Andre.

>>  #define __read_mostly __section(".data.read_mostly")
>>  
>>  #endif
>> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
>> index 2f5c51c..a66b6be 100644
>> --- a/xen/include/asm-arm/gic-its.h
>> +++ b/xen/include/asm-arm/gic-its.h
>> @@ -36,12 +36,32 @@ extern struct list_head host_its_list;
>>  /* Parse the host DT and pick up all host ITSes. */
>>  void gicv3_its_dt_init(const struct dt_device_node *node);
>>  
>> +/* Allocate and initialize tables for each host redistributor.
>> + * Returns the respective {PROP,PEND}BASER register value.
>> + */
>> +uint64_t gicv3_lpi_get_proptable(void);
>> +uint64_t gicv3_lpi_allocate_pendtable(void);
>> +
>> +/* Initialize the host structures for LPIs. */
>> +int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
>> +
>>  #else
>>  
>>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
>>  {
>>  }
>> -
>> +static inline uint64_t gicv3_lpi_get_proptable(void)
>> +{
>> +    return 0;
>> +}
>> +static inline uint64_t gicv3_lpi_allocate_pendtable(void)
>> +{
>> +    return 0;
>> +}
>> +static inline int gicv3_lpi_init_host_lpis(unsigned int nr_lpis)
>> +{
>> +    return 0;
>> +}
>>  #endif /* CONFIG_HAS_ITS */
>>  
>>  #endif /* __ASSEMBLY__ */
>> diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
>> index 6bd25a5..da5fb77 100644
>> --- a/xen/include/asm-arm/gic_v3_defs.h
>> +++ b/xen/include/asm-arm/gic_v3_defs.h
>> @@ -44,7 +44,8 @@
>>  #define GICC_SRE_EL2_ENEL1           (1UL << 3)
>>  
>>  /* Additional bits in GICD_TYPER defined by GICv3 */
>> -#define GICD_TYPE_ID_BITS_SHIFT 19
>> +#define GICD_TYPE_ID_BITS_SHIFT      19
>> +#define GICD_TYPE_LPIS               (1U << 17)
>>  
>>  #define GICD_CTLR_RWP                (1UL << 31)
>>  #define GICD_CTLR_ARE_NS             (1U << 4)
>> @@ -95,12 +96,57 @@
>>  #define GICR_IGRPMODR0               (0x0D00)
>>  #define GICR_NSACR                   (0x0E00)
>>  
>> +#define GICR_CTLR_ENABLE_LPIS        (1U << 0)
>>  #define GICR_TYPER_PLPIS             (1U << 0)
>>  #define GICR_TYPER_VLPIS             (1U << 1)
>>  #define GICR_TYPER_LAST              (1U << 4)
>>  
>> +#define GIC_BASER_CACHE_nCnB         0ULL
>> +#define GIC_BASER_CACHE_SameAsInner  0ULL
>> +#define GIC_BASER_CACHE_nC           1ULL
>> +#define GIC_BASER_CACHE_RaWt         2ULL
>> +#define GIC_BASER_CACHE_RaWb         3ULL
>> +#define GIC_BASER_CACHE_WaWt         4ULL
>> +#define GIC_BASER_CACHE_WaWb         5ULL
>> +#define GIC_BASER_CACHE_RaWaWt       6ULL
>> +#define GIC_BASER_CACHE_RaWaWb       7ULL
>> +#define GIC_BASER_CACHE_MASK         7ULL
>> +#define GIC_BASER_NonShareable       0ULL
>> +#define GIC_BASER_InnerShareable     1ULL
>> +#define GIC_BASER_OuterShareable     2ULL
>> +
>> +#define GICR_PROPBASER_SHAREABILITY_SHIFT               10
>> +#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT         7
>> +#define GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT         56
>> +#define GICR_PROPBASER_SHAREABILITY_MASK                     \
>> +        (3UL << GICR_PROPBASER_SHAREABILITY_SHIFT)
>> +#define GICR_PROPBASER_INNER_CACHEABILITY_MASK               \
>> +        (7UL << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT)
>> +#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK               \
>> +        (7UL << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT)
>> +#define PROPBASER_RES0_MASK                                  \
>> +        (GENMASK(63, 59) | GENMASK(55, 52) | GENMASK(6, 5))
>> +
>> +#define GICR_PENDBASER_SHAREABILITY_SHIFT               10
>> +#define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT         7
>> +#define GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT         56
>> +#define GICR_PENDBASER_SHAREABILITY_MASK                     \
>> +	(3UL << GICR_PENDBASER_SHAREABILITY_SHIFT)
>> +#define GICR_PENDBASER_INNER_CACHEABILITY_MASK               \
>> +	(7UL << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT)
>> +#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK               \
>> +        (7UL << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT)
>> +#define GICR_PENDBASER_PTZ                              BIT(62)
>> +#define PENDBASER_RES0_MASK                                  \
>> +        (BIT(63) | GENMASK(61, 59) | GENMASK(55, 52) |       \
>> +         GENMASK(15, 12) | GENMASK(6, 0))
>> +
>>  #define DEFAULT_PMR_VALUE            0xff
>>  
>> +#define LPI_PROP_DEFAULT_PRIO        0xa0
>> +#define LPI_PROP_RES1                (1 << 1)
>> +#define LPI_PROP_ENABLED             (1 << 0)
>> +
>>  #define GICH_VMCR_EOI                (1 << 9)
>>  #define GICH_VMCR_VENG1              (1 << 1)
>>  
>> -- 
>> 2.9.0
>>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table
  2017-01-05 17:13     ` Andre Przywara
@ 2017-01-05 17:52       ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 17:52 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 5 Jan 2017, Andre Przywara wrote:
> Hi,
> 
> On 04/01/17 21:09, Stefano Stabellini wrote:
> > On Thu, 22 Dec 2016, Andre Przywara wrote:
> >> The ARM GICv3 ITS provides a new kind of interrupt called LPIs.
> >> The pending bits and the configuration data (priority, enable bits) for
> >> those LPIs are stored in tables in normal memory, which software has to
> >> provide to the hardware.
> >> Allocate the required memory, initialize it and hand it over to each
> >> ITS. The maximum number of LPIs to be used can be adjusted with the
> >> command line option "max_lpi_bits", which defaults to a compile time
> >> constant exposed in Kconfig.
> >>
> >> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> >> ---
> >>  xen/arch/arm/Kconfig              | 10 +++++
> >>  xen/arch/arm/efi/efi-boot.h       |  1 -
> >>  xen/arch/arm/gic-its.c            | 94 +++++++++++++++++++++++++++++++++++++++
> >>  xen/arch/arm/gic-v3.c             | 43 ++++++++++++++++++
> >>  xen/include/asm-arm/bitops.h      |  1 +
> >>  xen/include/asm-arm/cache.h       |  4 ++
> >>  xen/include/asm-arm/gic-its.h     | 22 ++++++++-
> >>  xen/include/asm-arm/gic_v3_defs.h | 48 +++++++++++++++++++-
> >>  8 files changed, 220 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
> >> index bf64c61..a7d941c 100644
> >> --- a/xen/arch/arm/Kconfig
> >> +++ b/xen/arch/arm/Kconfig
> >> @@ -49,6 +49,16 @@ config HAS_ITS
> >>          bool "GICv3 ITS MSI controller support"
> >>          depends on HAS_GICV3
> >>  
> >> +config MAX_HOST_LPI_BITS
> >             ^ MAX_PHYS_LPI_BITS
> 
> Right, I missed that symbol.
> 
> >> +        depends on HAS_ITS
> >> +        int "Maximum bits for GICv3 host LPIs (14-32)"
> >> +        range 14 32
> >> +        default "20"
> >> +        help
> >> +          Specifies the maximum number of LPIs (bits) Xen should take care of.
> >> +          This can be overriden on the command line with the max_lpi_bits
> >> +          parameter.
> >> +
> >>  endmenu
> >>  
> >>  menu "ARM errata workaround via the alternative framework"
> >> diff --git a/xen/arch/arm/efi/efi-boot.h b/xen/arch/arm/efi/efi-boot.h
> >> index 045d6ce..dc64aec 100644
> >> --- a/xen/arch/arm/efi/efi-boot.h
> >> +++ b/xen/arch/arm/efi/efi-boot.h
> >> @@ -10,7 +10,6 @@
> >>  #include "efi-dom0.h"
> >>  
> >>  void noreturn efi_xen_start(void *fdt_ptr, uint32_t fdt_size);
> >> -void __flush_dcache_area(const void *vaddr, unsigned long size);
> >>  
> >>  #define DEVICE_TREE_GUID \
> >>  {0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}}
> >> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> >> index 973f9f9..6c3a35d 100644
> >> --- a/xen/arch/arm/gic-its.c
> >> +++ b/xen/arch/arm/gic-its.c
> >> @@ -20,10 +20,104 @@
> >>  #include <xen/lib.h>
> >>  #include <xen/device_tree.h>
> >>  #include <xen/libfdt/libfdt.h>
> >> +#include <xen/sizes.h>
> >> +#include <asm/p2m.h>
> >>  #include <asm/gic.h>
> >>  #include <asm/gic_v3_defs.h>
> >>  #include <asm/gic-its.h>
> >>  
> >> +/* Global state */
> >> +static struct {
> >> +    uint8_t *lpi_property;
> >> +    unsigned int host_lpi_bits;
> > 
> > I think it is best to rename host_lpi_bits to phys_lpi_bits, or, even
> > better, phys_nr_lpi because it represents the number of LPIs.
> 
> But we need the number of bits in at least one function (for populating
> the PROPBASER register), also the number of bits is the original input
> value and sets the limit on possible values - as it needs to be a power
> of two.
> So shall I use nr_phys_lpi_bits instead?

Let me explain why I made that comment. When I read "host_lpi_bits", it
is not immediately clear that it is strictly related to the number of
physical lpis. If you prefer to store the number of bits, instead of the
number of plpis (althought it is trivial to calculate one from the
other), then add a comment here.


> >> +} lpi_data;
> >> +
> >> +/* Pending table for each redistributor */
> >> +static DEFINE_PER_CPU(void *, pending_table);
> >> +
> >> +#define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
> >> +
> >> +uint64_t gicv3_lpi_allocate_pendtable(void)
> >> +{
> >> +    uint64_t reg, attr;
> >> +    void *pendtable;
> >> +
> >> +    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
> >> +    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
> >> +    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
> >> +
> >> +    if ( !this_cpu(pending_table) )
> >> +    {
> >> +        /*
> >> +         * The pending table holds one bit per LPI, so we need (2 << 3) bits
> >> +         * less bytes. The pending table covers even bits for interrupt IDs
> >> +         * below 8192, so we allocate the full range.
> >> +         * The ITS imposes a 64KB alignment requirement.
> >> +         */
> >> +        pendtable = _xmalloc(BIT_ULL(lpi_data.host_lpi_bits) / 8, SZ_64K);
> > 
> > If we need (2 << 3) less bytes, why are we dividing by 8?
> 
> Because ... I am wrong here ;-)
> 
> > Isn't this just a straightforward bits to bytes conversion?
> 
> Yes.
> 
> > I think the comment is probably wrong or outdated.
> 
> Yeah, I tried to make this clearer after your previous comments (where
> this simple bits-to-bytes apparently didn't come through) and got
> somehow carried away ;-)
> 
> >> +        if ( !pendtable )
> >> +            return 0;
> >> +
> >> +        memset(pendtable, 0, BIT_ULL(lpi_data.host_lpi_bits) / 8);
> >> +        __flush_dcache_area(pendtable, BIT_ULL(lpi_data.host_lpi_bits) / 8);
> >> +
> >> +        this_cpu(pending_table) = pendtable;
> >> +    }
> >> +    else
> >> +    {
> >> +        pendtable = this_cpu(pending_table);
> > 
> > Isn't this an error condition?
> 
> This is internally only called once when each redistributor is
> enumerated. So this maybe a BUG_ON(), but on the other hand I don't see
> a reason why this would be fatal. If we try to "allocate" a second time,
> we just get the existing version.
> But I can happily replace this with a BUG_ON(), if you prefer. Seems to
> be more matching the Xen policy, I guess.

BUG_ON is fine.


> >> +    }
> >> +
> >> +    reg  = attr | GICR_PENDBASER_PTZ;
> >> +    reg |= virt_to_maddr(pendtable) & GENMASK(51, 16);
> >> +
> >> +    return reg;
> >> +}
> >> +
> >> +uint64_t gicv3_lpi_get_proptable()
> >> +{
> >> +    uint64_t attr, reg;
> >> +
> >> +    attr  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
> >> +    attr |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
> >> +    attr |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
> >> +
> >> +    /*
> >> +     * The property table is shared across all redistributors, so allocate
> >> +     * this only once, but return the same value on subsequent calls.
> >> +     */
> >> +    if ( !lpi_data.lpi_property )
> >> +    {
> >> +        /* The property table holds one byte per LPI. */
> >> +        void *table = alloc_xenheap_pages(lpi_data.host_lpi_bits - PAGE_SHIFT,
> >> +                                          0);
> >> +
> >> +        if ( !table )
> >> +            return 0;
> >> +
> >> +        memset(table, GIC_PRI_IRQ | LPI_PROP_RES1, MAX_PHYS_LPIS);
> >> +        __flush_dcache_area(table, MAX_PHYS_LPIS);
> >> +        lpi_data.lpi_property = table;
> >> +    }
> >> +
> >> +    reg  = attr | ((lpi_data.host_lpi_bits - 1) << 0);
> >> +    reg |= virt_to_maddr(lpi_data.lpi_property) & GENMASK(51, 12);
> >> +
> >> +    return reg;
> >> +}
> >> +
> >> +static unsigned int max_lpi_bits = CONFIG_MAX_HOST_LPI_BITS;
> >> +integer_param("max_lpi_bits", max_lpi_bits);
> >> +
> >> +int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
> >                   ^ gicv3_lpi_init_phys_lpis
> 
> OK.
> 
> >> +{
> >> +    lpi_data.host_lpi_bits = min(hw_lpi_bits, max_lpi_bits);
> >> +
> >> +    printk("GICv3: using at most %lld LPIs on the host.\n", MAX_PHYS_LPIS);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >>  /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
> >>  void gicv3_its_dt_init(const struct dt_device_node *node)
> >>  {
> >> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> >> index 238da84..1c6869d 100644
> >> --- a/xen/arch/arm/gic-v3.c
> >> +++ b/xen/arch/arm/gic-v3.c
> >> @@ -546,6 +546,9 @@ static void __init gicv3_dist_init(void)
> >>      type = readl_relaxed(GICD + GICD_TYPER);
> >>      nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1);
> >>  
> >> +    if ( type & GICD_TYPE_LPIS )
> >> +        gicv3_lpi_init_host_lpis(((type >> GICD_TYPE_ID_BITS_SHIFT) & 0x1f) + 1);
> > 
> > Still need to define a mask instead of using 0x1f
> 
> I guess I prefer a macro for the whole statement here. I regularly get
> annoyed by those descriptive names getting too long and forcing a rather
> simple statement on two or more lines ...

That's fine by me. The idea is that I shouldn't have to dig up the spec
every time I want to back to understand what this code is doing.


> >> +
> >>      printk("GICv3: %d lines, (IID %8.8x).\n",
> >>             nr_lines, readl_relaxed(GICD + GICD_IIDR));
> >>  
> >> @@ -616,6 +619,32 @@ static int gicv3_enable_redist(void)
> >>      return 0;
> >>  }
> >>  
> >> +static int gicv3_rdist_init_lpis(void __iomem * rdist_base)
> >> +{
> >> +    uint32_t reg;
> >> +    uint64_t table_reg;
> >> +
> >> +    if ( list_empty(&host_its_list) )
> >> +        return -ENODEV;
> >> +
> >> +    /* Make sure LPIs are disabled before setting up the BASERs. */
> >> +    reg = readl_relaxed(rdist_base + GICR_CTLR);
> >> +    if ( reg & GICR_CTLR_ENABLE_LPIS )
> >> +        return -EBUSY;
> >> +
> >> +    table_reg = gicv3_lpi_allocate_pendtable();
> >> +    if ( !table_reg )
> >> +        return -ENOMEM;
> >> +    writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
> >> +
> >> +    table_reg = gicv3_lpi_get_proptable();
> >> +    if ( !table_reg )
> >> +        return -ENOMEM;
> >> +    writeq_relaxed(table_reg, rdist_base + GICR_PROPBASER);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >>  static int __init gicv3_populate_rdist(void)
> >>  {
> >>      int i;
> >> @@ -658,6 +687,20 @@ static int __init gicv3_populate_rdist(void)
> >>              if ( (typer >> 32) == aff )
> >>              {
> >>                  this_cpu(rbase) = ptr;
> >> +
> >> +                if ( typer & GICR_TYPER_PLPIS )
> >> +                {
> >> +                    int ret;
> >> +
> >> +                    ret = gicv3_rdist_init_lpis(ptr);
> >> +                    if ( ret && ret != -ENODEV )
> >> +                    {
> >> +                        printk("GICv3: CPU%d: Cannot initialize LPIs: %d\n",
> >> +                               smp_processor_id(), ret);
> >> +                        break;
> >> +                    }
> >> +                }
> >> +
> >>                  printk("GICv3: CPU%d: Found redistributor in region %d @%p\n",
> >>                          smp_processor_id(), i, ptr);
> >>                  return 0;
> >> diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
> >> index bda8898..c9adf79 100644
> >> --- a/xen/include/asm-arm/bitops.h
> >> +++ b/xen/include/asm-arm/bitops.h
> >> @@ -25,6 +25,7 @@
> >>  #define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_WORD))
> >>  #define BIT_WORD(nr)            ((nr) / BITS_PER_WORD)
> >>  #define BITS_PER_BYTE           8
> >> +#define BIT_ULL(nr)             (1ULL << (nr))
> >>  
> >>  #define ADDR (*(volatile int *) addr)
> >>  #define CONST_ADDR (*(const volatile int *) addr)
> >> diff --git a/xen/include/asm-arm/cache.h b/xen/include/asm-arm/cache.h
> >> index 2de6564..af96eee 100644
> >> --- a/xen/include/asm-arm/cache.h
> >> +++ b/xen/include/asm-arm/cache.h
> >> @@ -7,6 +7,10 @@
> >>  #define L1_CACHE_SHIFT  (CONFIG_ARM_L1_CACHE_SHIFT)
> >>  #define L1_CACHE_BYTES  (1 << L1_CACHE_SHIFT)
> >>  
> >> +#ifndef __ASSEMBLY__
> >> +void __flush_dcache_area(const void *vaddr, unsigned long size);
> >> +#endif
> > 
> > I think it might be best to add it to xen/include/asm-arm/arm64/page.h
> 
> Apart from the fact that it's not needed atm on ARM, is there any other
> reason to confine it to ARM64?

I am OK with exporting it on ARM too.


> Cheers,
> Andre.
> 
> >>  #define __read_mostly __section(".data.read_mostly")
> >>  
> >>  #endif
> >> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> >> index 2f5c51c..a66b6be 100644
> >> --- a/xen/include/asm-arm/gic-its.h
> >> +++ b/xen/include/asm-arm/gic-its.h
> >> @@ -36,12 +36,32 @@ extern struct list_head host_its_list;
> >>  /* Parse the host DT and pick up all host ITSes. */
> >>  void gicv3_its_dt_init(const struct dt_device_node *node);
> >>  
> >> +/* Allocate and initialize tables for each host redistributor.
> >> + * Returns the respective {PROP,PEND}BASER register value.
> >> + */
> >> +uint64_t gicv3_lpi_get_proptable(void);
> >> +uint64_t gicv3_lpi_allocate_pendtable(void);
> >> +
> >> +/* Initialize the host structures for LPIs. */
> >> +int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
> >> +
> >>  #else
> >>  
> >>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
> >>  {
> >>  }
> >> -
> >> +static inline uint64_t gicv3_lpi_get_proptable(void)
> >> +{
> >> +    return 0;
> >> +}
> >> +static inline uint64_t gicv3_lpi_allocate_pendtable(void)
> >> +{
> >> +    return 0;
> >> +}
> >> +static inline int gicv3_lpi_init_host_lpis(unsigned int nr_lpis)
> >> +{
> >> +    return 0;
> >> +}
> >>  #endif /* CONFIG_HAS_ITS */
> >>  
> >>  #endif /* __ASSEMBLY__ */
> >> diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
> >> index 6bd25a5..da5fb77 100644
> >> --- a/xen/include/asm-arm/gic_v3_defs.h
> >> +++ b/xen/include/asm-arm/gic_v3_defs.h
> >> @@ -44,7 +44,8 @@
> >>  #define GICC_SRE_EL2_ENEL1           (1UL << 3)
> >>  
> >>  /* Additional bits in GICD_TYPER defined by GICv3 */
> >> -#define GICD_TYPE_ID_BITS_SHIFT 19
> >> +#define GICD_TYPE_ID_BITS_SHIFT      19
> >> +#define GICD_TYPE_LPIS               (1U << 17)
> >>  
> >>  #define GICD_CTLR_RWP                (1UL << 31)
> >>  #define GICD_CTLR_ARE_NS             (1U << 4)
> >> @@ -95,12 +96,57 @@
> >>  #define GICR_IGRPMODR0               (0x0D00)
> >>  #define GICR_NSACR                   (0x0E00)
> >>  
> >> +#define GICR_CTLR_ENABLE_LPIS        (1U << 0)
> >>  #define GICR_TYPER_PLPIS             (1U << 0)
> >>  #define GICR_TYPER_VLPIS             (1U << 1)
> >>  #define GICR_TYPER_LAST              (1U << 4)
> >>  
> >> +#define GIC_BASER_CACHE_nCnB         0ULL
> >> +#define GIC_BASER_CACHE_SameAsInner  0ULL
> >> +#define GIC_BASER_CACHE_nC           1ULL
> >> +#define GIC_BASER_CACHE_RaWt         2ULL
> >> +#define GIC_BASER_CACHE_RaWb         3ULL
> >> +#define GIC_BASER_CACHE_WaWt         4ULL
> >> +#define GIC_BASER_CACHE_WaWb         5ULL
> >> +#define GIC_BASER_CACHE_RaWaWt       6ULL
> >> +#define GIC_BASER_CACHE_RaWaWb       7ULL
> >> +#define GIC_BASER_CACHE_MASK         7ULL
> >> +#define GIC_BASER_NonShareable       0ULL
> >> +#define GIC_BASER_InnerShareable     1ULL
> >> +#define GIC_BASER_OuterShareable     2ULL
> >> +
> >> +#define GICR_PROPBASER_SHAREABILITY_SHIFT               10
> >> +#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT         7
> >> +#define GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT         56
> >> +#define GICR_PROPBASER_SHAREABILITY_MASK                     \
> >> +        (3UL << GICR_PROPBASER_SHAREABILITY_SHIFT)
> >> +#define GICR_PROPBASER_INNER_CACHEABILITY_MASK               \
> >> +        (7UL << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT)
> >> +#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK               \
> >> +        (7UL << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT)
> >> +#define PROPBASER_RES0_MASK                                  \
> >> +        (GENMASK(63, 59) | GENMASK(55, 52) | GENMASK(6, 5))
> >> +
> >> +#define GICR_PENDBASER_SHAREABILITY_SHIFT               10
> >> +#define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT         7
> >> +#define GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT         56
> >> +#define GICR_PENDBASER_SHAREABILITY_MASK                     \
> >> +	(3UL << GICR_PENDBASER_SHAREABILITY_SHIFT)
> >> +#define GICR_PENDBASER_INNER_CACHEABILITY_MASK               \
> >> +	(7UL << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT)
> >> +#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK               \
> >> +        (7UL << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT)
> >> +#define GICR_PENDBASER_PTZ                              BIT(62)
> >> +#define PENDBASER_RES0_MASK                                  \
> >> +        (BIT(63) | GENMASK(61, 59) | GENMASK(55, 52) |       \
> >> +         GENMASK(15, 12) | GENMASK(6, 0))
> >> +
> >>  #define DEFAULT_PMR_VALUE            0xff
> >>  
> >> +#define LPI_PROP_DEFAULT_PRIO        0xa0
> >> +#define LPI_PROP_RES1                (1 << 1)
> >> +#define LPI_PROP_ENABLED             (1 << 0)
> >> +
> >>  #define GICH_VMCR_EOI                (1 << 9)
> >>  #define GICH_VMCR_VENG1              (1 << 1)
> >>  
> >> -- 
> >> 2.9.0
> >>
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2017-01-04 21:47   ` Stefano Stabellini
@ 2017-01-05 17:56     ` Andre Przywara
  2017-01-20 11:12     ` Julien Grall
  1 sibling, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2017-01-05 17:56 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi Stefano,

On 04/01/17 21:47, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
>> Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
>> an EventID (the MSI payload or interrupt ID) to a pair of LPI number
>> and collection ID, which points to the target CPU.
>> This mapping is stored in the device and collection tables, which software
>> has to provide for the ITS to use.
>> Allocate the required memory and hand it the ITS.
>> The maximum number of devices is limited to a compile-time constant
>> exposed in Kconfig.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/Kconfig          |   6 +++
>>  xen/arch/arm/gic-its.c        | 114 ++++++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic-v3.c         |   5 ++
>>  xen/include/asm-arm/bitops.h  |   1 +
>>  xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
>>  5 files changed, 176 insertions(+), 1 deletion(-)
>>
>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>> index a7d941c..a369305 100644
>> --- a/xen/arch/arm/Kconfig
>> +++ b/xen/arch/arm/Kconfig
>> @@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
>>            This can be overriden on the command line with the max_lpi_bits
>>            parameter.
>>  
>> +config MAX_ITS_PCI_BUSSES
>> +        depends on HAS_ITS
>> +        int "Number of PCI busses the ITS supports (4)"
>> +        range 1 1024
>> +        default "4"
> 
> Given that any kind of devices can be behind an ITS, including non-PCI
> devices, I think it is best to call this MAX_PHYS_ITS_DEVICES.

Good point.

>>  endmenu
>>  
>>  menu "ARM errata workaround via the alternative framework"
>> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
>> index 6c3a35d..f1540b2 100644
>> --- a/xen/arch/arm/gic-its.c
>> +++ b/xen/arch/arm/gic-its.c
>> @@ -22,6 +22,7 @@
>>  #include <xen/libfdt/libfdt.h>
>>  #include <xen/sizes.h>
>>  #include <asm/p2m.h>
>> +#include <asm/io.h>
>>  #include <asm/gic.h>
>>  #include <asm/gic_v3_defs.h>
>>  #include <asm/gic-its.h>
>> @@ -37,6 +38,119 @@ static DEFINE_PER_CPU(void *, pending_table);
>>  
>>  #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
>>  
>> +#define BASER_ATTR_MASK                                           \
>> +        ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
>> +         (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
>> +         (0x7UL << GITS_BASER_INNER_CACHEABILITY_SHIFT))
>> +#define BASER_RO_MASK   (GENMASK(58, 56) | GENMASK(52, 48))
>> +
>> +static uint64_t encode_phys_addr(paddr_t addr, int page_bits)
>> +{
>> +    uint64_t ret;
>> +
>> +    if ( page_bits < 16)
>> +        return (uint64_t)addr & GENMASK(47, page_bits);
>> +
>> +    ret = addr & GENMASK(47, 16);
>> +    return ret | (addr & GENMASK(51, 48)) >> (48 - 12);
>> +}
>> +
>> +static int its_map_baser(void __iomem *basereg, uint64_t regc, int nr_items)
>> +{
>> +    uint64_t attr;
>> +    int entry_size = ((regc >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1;
>> +    int pagesz;
>> +    int order;
>> +    void *buffer = NULL;
>> +
>> +    attr  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
>> +    attr |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
>> +    attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
>> +
>> +    /*
>> +     * Loop over the page sizes (4K, 16K, 64K) to find out what the host
>> +     * supports.
>> +     */
>> +    for ( pagesz = 0; pagesz < 3; pagesz++ )
>> +    {
>> +        uint64_t reg;
>> +        int nr_bytes;
>> +
>> +        nr_bytes = ROUNDUP(nr_items * entry_size, BIT(pagesz * 2 + 12));
> 
> 12/PAGE_SHIFT

Will use a macro to convert the loop counter into the number of bits.

>> +        order = get_order_from_bytes(nr_bytes);
>> +
>> +        if ( !buffer )
>> +            buffer = alloc_xenheap_pages(order, 0);
>> +        if ( !buffer )
>> +            return -ENOMEM;
>> +
>> +        reg  = attr;
>> +        reg |= (pagesz << GITS_BASER_PAGE_SIZE_SHIFT);
>> +        reg |= nr_bytes >> (pagesz * 2 + 12);
>> +        reg |= regc & BASER_RO_MASK;
>> +        reg |= GITS_BASER_VALID;
>> +        reg |= encode_phys_addr(virt_to_maddr(buffer), pagesz * 2 + 12);
>> +
>> +        writeq_relaxed(reg, basereg);
>> +        regc = readl_relaxed(basereg);
>> +
>> +        /* The host didn't like our attributes, just use what it returned. */
>> +        if ( (regc & BASER_ATTR_MASK) != attr )
>> +            attr = regc & BASER_ATTR_MASK;
>> +
>> +        /* If the host accepted our page size, we are done. */
>> +        if ( (reg & (3UL << GITS_BASER_PAGE_SIZE_SHIFT)) == pagesz )
>> +            return 0;
>> +
>> +        /* Check whether our buffer is aligned to the next page size already. */
>> +        if ( !(virt_to_maddr(buffer) & (BIT(pagesz * 2 + 12 + 2) - 1)) )
>> +        {
>> +            free_xenheap_pages(buffer, order);
>> +            buffer = NULL;
>> +        }
> 
> Regardless of alignment, should we always free buffer here, given that
> we need to allocate the right size for the next attempt?

This was an attempt to (a premature) optimization to avoid repetitive
allocations in case the first allocation was already 64K aligned.
Looking at it again I see that the order depends on the page size as
well, so this is broken. I will unconditionally free the buffer here.

Cheers,
Andre.

>> +    }
>> +
>> +    if ( buffer )
>> +        free_xenheap_pages(buffer, order);
>> +
>> +    return -EINVAL;
>> +}
>> +
>> +int gicv3_its_init(struct host_its *hw_its)
>> +{
>> +    uint64_t reg;
>> +    int i;
>> + 
>> +    hw_its->its_base = ioremap_nocache(hw_its->addr, hw_its->size);
>> +    if ( !hw_its->its_base )
>> +        return -ENOMEM;
>> +
>> +    for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
>> +    {
>> +        void __iomem *basereg = hw_its->its_base + GITS_BASER0 + i * 8;
>> +        int type;
>> +
>> +        reg = readq_relaxed(basereg);
>> +        type = (reg & GITS_BASER_TYPE_MASK) >> GITS_BASER_TYPE_SHIFT;
>> +        switch ( type )
>> +        {
>> +        case GITS_BASER_TYPE_NONE:
>> +            continue;
>> +        case GITS_BASER_TYPE_DEVICE:
>> +            /* TODO: find some better way of limiting the number of devices */
>> +            its_map_baser(basereg, reg, CONFIG_MAX_ITS_PCI_BUSSES * 256);
>> +            break;
>> +        case GITS_BASER_TYPE_COLLECTION:
>> +            its_map_baser(basereg, reg, NR_CPUS);
>> +            break;
>> +        default:
>> +            continue;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>  uint64_t gicv3_lpi_allocate_pendtable(void)
>>  {
>>      uint64_t reg, attr;
>> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
>> index 1c6869d..8ca7da2 100644
>> --- a/xen/arch/arm/gic-v3.c
>> +++ b/xen/arch/arm/gic-v3.c
>> @@ -29,6 +29,7 @@
>>  #include <xen/irq.h>
>>  #include <xen/iocap.h>
>>  #include <xen/sched.h>
>> +#include <xen/err.h>
>>  #include <xen/errno.h>
>>  #include <xen/delay.h>
>>  #include <xen/device_tree.h>
>> @@ -1564,6 +1565,7 @@ static int __init gicv3_init(void)
>>  {
>>      int res, i;
>>      uint32_t reg;
>> +    struct host_its *hw_its;
>>  
>>      if ( !cpu_has_gicv3 )
>>      {
>> @@ -1619,6 +1621,9 @@ static int __init gicv3_init(void)
>>      res = gicv3_cpu_init();
>>      gicv3_hyp_init();
>>  
>> +    list_for_each_entry(hw_its, &host_its_list, entry)
>> +        gicv3_its_init(hw_its);
>> +
>>      spin_unlock(&gicv3.lock);
>>  
>>      return res;
>> diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
>> index c9adf79..4ccb444 100644
>> --- a/xen/include/asm-arm/bitops.h
>> +++ b/xen/include/asm-arm/bitops.h
>> @@ -24,6 +24,7 @@
>>  #define BIT(nr)                 (1UL << (nr))
>>  #define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_WORD))
>>  #define BIT_WORD(nr)            ((nr) / BITS_PER_WORD)
>> +#define BIT_ULL(nr)             (1ULL << (nr))
>>  #define BITS_PER_BYTE           8
>>  #define BIT_ULL(nr)             (1ULL << (nr))
>>  
>> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
>> index a66b6be..26b0584 100644
>> --- a/xen/include/asm-arm/gic-its.h
>> +++ b/xen/include/asm-arm/gic-its.h
>> @@ -18,6 +18,49 @@
>>  #ifndef __ASM_ARM_ITS_H__
>>  #define __ASM_ARM_ITS_H__
>>  
>> +#define LPI_OFFSET                      8192
>> +
>> +#define GITS_CTLR                       0x000
>> +#define GITS_IIDR                       0x004
>> +#define GITS_TYPER                      0x008
>> +#define GITS_CBASER                     0x080
>> +#define GITS_CWRITER                    0x088
>> +#define GITS_CREADR                     0x090
>> +#define GITS_BASER_NR_REGS              8
>> +#define GITS_BASER0                     0x100
>> +#define GITS_BASER1                     0x108
>> +#define GITS_BASER2                     0x110
>> +#define GITS_BASER3                     0x118
>> +#define GITS_BASER4                     0x120
>> +#define GITS_BASER5                     0x128
>> +#define GITS_BASER6                     0x130
>> +#define GITS_BASER7                     0x138
>> +
>> +/* Register bits */
>> +#define GITS_CTLR_ENABLE                BIT_ULL(0)
>> +#define GITS_IIDR_VALUE                 0x34c
>> +
>> +#define GITS_BASER_VALID                BIT_ULL(63)
>> +#define GITS_BASER_INDIRECT             BIT_ULL(62)
>> +#define GITS_BASER_INNER_CACHEABILITY_SHIFT        59
>> +#define GITS_BASER_TYPE_SHIFT           56
>> +#define GITS_BASER_TYPE_MASK            (7ULL << GITS_BASER_TYPE_SHIFT)
>> +#define GITS_BASER_OUTER_CACHEABILITY_SHIFT        53
>> +#define GITS_BASER_TYPE_NONE            0UL
>> +#define GITS_BASER_TYPE_DEVICE          1UL
>> +#define GITS_BASER_TYPE_VCPU            2UL
>> +#define GITS_BASER_TYPE_CPU             3UL
>> +#define GITS_BASER_TYPE_COLLECTION      4UL
>> +#define GITS_BASER_TYPE_RESERVED5       5UL
>> +#define GITS_BASER_TYPE_RESERVED6       6UL
>> +#define GITS_BASER_TYPE_RESERVED7       7UL
>> +#define GITS_BASER_ENTRY_SIZE_SHIFT     48
>> +#define GITS_BASER_SHAREABILITY_SHIFT   10
>> +#define GITS_BASER_PAGE_SIZE_SHIFT      8
>> +#define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
>> +                                        (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
>> +                                        GITS_BASER_INDIRECT)
>> +
>>  #ifndef __ASSEMBLY__
>>  #include <xen/device_tree.h>
>>  
>> @@ -27,6 +70,7 @@ struct host_its {
>>      const struct dt_device_node *dt_node;
>>      paddr_t addr;
>>      paddr_t size;
>> +    void __iomem *its_base;
>>  };
>>  
>>  extern struct list_head host_its_list;
>> @@ -42,8 +86,9 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
>>  uint64_t gicv3_lpi_get_proptable(void);
>>  uint64_t gicv3_lpi_allocate_pendtable(void);
>>  
>> -/* Initialize the host structures for LPIs. */
>> +/* Initialize the host structures for LPIs and the host ITSes. */
>>  int gicv3_lpi_init_host_lpis(unsigned int nr_lpis);
>> +int gicv3_its_init(struct host_its *hw_its);
>>  
>>  #else
>>  
>> @@ -62,6 +107,10 @@ static inline int gicv3_lpi_init_host_lpis(unsigned int nr_lpis)
>>  {
>>      return 0;
>>  }
>> +static inline int gicv3_its_init(struct host_its *hw_its)
>> +{
>> +    return 0;
>> +}
>>  #endif /* CONFIG_HAS_ITS */
>>  
>>  #endif /* __ASSEMBLY__ */
>> -- 
>> 2.9.0
>>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array
  2016-12-22 18:24 ` [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
@ 2017-01-05 18:56   ` Stefano Stabellini
  2017-01-06 17:59     ` Andre Przywara
  0 siblings, 1 reply; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 18:56 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
7> The number of LPIs on a host can be potentially huge (millions),
> although in practise will be mostly reasonable. So prematurely allocating
> an array of struct irq_desc's for each LPI is not an option.
> However Xen itself does not care about LPIs, as every LPI will be injected
> into a guest (Dom0 for now).
> Create a dense data structure (8 Bytes) for each LPI which holds just
> enough information to determine the virtual IRQ number and the VCPU into
> which the LPI needs to be injected.
> Also to not artificially limit the number of LPIs, we create a 2-level
> table for holding those structures.
> This patch introduces functions to initialize these tables and to
> create, lookup and destroy entries for a given LPI.
> We allocate and access LPI information in a way that does not require
> a lock.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-its.c        | 233 +++++++++++++++++++++++++++++++++++++++++-
>  xen/include/asm-arm/gic-its.h |   1 +
>  2 files changed, 233 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index e157c6b..e7ddd90 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -18,21 +18,36 @@
>  
>  #include <xen/config.h>
>  #include <xen/lib.h>
> +#include <xen/sched.h>
>  #include <xen/err.h>
>  #include <xen/device_tree.h>
>  #include <xen/libfdt/libfdt.h>
>  #include <xen/sched.h>
>  #include <xen/sizes.h>
>  #include <asm/p2m.h>
> +#include <asm/domain.h>
>  #include <asm/io.h>
>  #include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
>  #include <asm/gic-its.h>
>  
> +/* LPIs on the host always go to a guest, so no struct irq_desc for them. */
> +union host_lpi {
> +    uint64_t data;
> +    struct {
> +        uint64_t virt_lpi:32;
> +        uint64_t dom_id:16;
> +        uint64_t vcpu_id:16;
> +    };
> +};

Just go with a regular struct

    struct host_lpi {
        uint32_t virt_lpi;
        uint16_t dom_id;
        uint16_t vcpu_id;
    };

The aarch64 C ABI guarantees the alignments of the fields.


>  /* Global state */
>  static struct {
>      uint8_t *lpi_property;
> +    union host_lpi **host_lpis;
>      unsigned int host_lpi_bits;
> +    /* Protects allocation and deallocation of host LPIs, but not the access */
> +    spinlock_t host_lpis_lock;
>  } lpi_data;
>  
>  /* Physical redistributor address */
> @@ -43,6 +58,19 @@ static DEFINE_PER_CPU(uint64_t, rdist_id);
>  static DEFINE_PER_CPU(void *, pending_table);
>  
>  #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
> +#define HOST_LPIS_PER_PAGE      (PAGE_SIZE / sizeof(union host_lpi))
> +
> +static union host_lpi *gic_get_host_lpi(uint32_t plpi)
> +{
> +    if ( plpi < 8192 || plpi >= MAX_PHYS_LPIS + 8192 )
> +        return NULL;
> +
> +    plpi -= 8192;
> +    if ( !lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE] )
> +        return NULL;
> +
> +    return &lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE][plpi % HOST_LPIS_PER_PAGE];
> +}
>  
>  #define ITS_CMD_QUEUE_SZ                                SZ_64K
>  
> @@ -96,6 +124,20 @@ static int its_send_cmd_sync(struct host_its *its, int cpu)
>      return its_send_command(its, cmd);
>  }
>  
> +static int its_send_cmd_mapti(struct host_its *its,
> +                              uint32_t deviceid, uint32_t eventid,
> +                              uint32_t pintid, uint16_t icid)
> +{
> +    uint64_t cmd[4];
> +
> +    cmd[0] = GITS_CMD_MAPTI | ((uint64_t)deviceid << 32);
> +    cmd[1] = eventid | ((uint64_t)pintid << 32);
> +    cmd[2] = icid;
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
>  static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
>  {
>      uint64_t cmd[4];
> @@ -124,6 +166,19 @@ static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
>      return its_send_command(its, cmd);
>  }
>  
> +static int its_send_cmd_inv(struct host_its *its,
> +                            uint32_t deviceid, uint32_t eventid)
> +{
> +    uint64_t cmd[4];
> +
> +    cmd[0] = GITS_CMD_INV | ((uint64_t)deviceid << 32);
> +    cmd[1] = eventid;
> +    cmd[2] = 0x00;
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
>  /* Set up the (1:1) collection mapping for the given host CPU. */
>  void gicv3_its_setup_collection(int cpu)
>  {
> @@ -366,21 +421,181 @@ uint64_t gicv3_lpi_get_proptable()
>  static unsigned int max_lpi_bits = CONFIG_MAX_HOST_LPI_BITS;
>  integer_param("max_lpi_bits", max_lpi_bits);
>  
> +/* Allocate the 2nd level array for host LPIs. This one holds pointers
> + * to the page with the actual "union host_lpi" entries. Our LPI limit
> + * avoids excessive memory usage.
> + */
>  int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
>  {
> +    int nr_lpi_ptrs;
> +
>      lpi_data.host_lpi_bits = min(hw_lpi_bits, max_lpi_bits);
>  
> +    spin_lock_init(&lpi_data.host_lpis_lock);
> +
> +    nr_lpi_ptrs = MAX_PHYS_LPIS / (PAGE_SIZE / sizeof(union host_lpi));
> +    lpi_data.host_lpis = xzalloc_array(union host_lpi *, nr_lpi_ptrs);
> +    if ( !lpi_data.host_lpis )
> +        return -ENOMEM;
> +
>      printk("GICv3: using at most %lld LPIs on the host.\n", MAX_PHYS_LPIS);
>  
>      return 0;
>  }
>  
> +#define INVALID_DOMID ((uint16_t)~0)
> +#define LPI_BLOCK       32

Why 32? What is the reason behind it? It should be explained in the
commit message and in the code.

Please add some numbers of memory consumption and average number of
iterations required to setup lpis on an example server system. It is
important to understand if the current algorithm and data structs are
sufficient for our needs.

With the new host_lpis data struct below, the memory consumption is

    ROUNDUP(nr_plpis * 8, 4096) +
    (ROUNDUP(nr_plpis * 8, 4096) / 4096) * 8 +
    (nr_events / 32) * 4

Which, assuming nr_events = nr_plpis = 1024, means 8192 + 16 + 128, right?


> +/* Must be called with host_lpis_lock held. */
> +static int find_unused_host_lpi(int start, uint32_t *index)
> +{
> +    int chunk;
> +    uint32_t i = *index;
> +
> +    for ( chunk = start; chunk < MAX_PHYS_LPIS / HOST_LPIS_PER_PAGE; chunk++ )
> +    {
> +        /* If we hit an unallocated chunk, use entry 0 in that one. */
> +        if ( !lpi_data.host_lpis[chunk] )
> +        {
> +            *index = 0;
> +            return chunk;
> +        }
> +
> +        /* Find an unallocated entry in this chunk. */
> +        for ( ; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
> +        {
> +            if ( lpi_data.host_lpis[chunk][i].dom_id == INVALID_DOMID )
> +            {
> +                *index = i;
> +                return chunk;
> +            }

This assumes that plpis are always allocated 32 at a time, by not
checking within a block.


> +        }
> +        i = 0;
> +    }
> +
> +    return -1;
> +}
> +
> +/*
> + * Allocate a block of 32 LPIs on the given host ITS for device "devid",
> + * starting with "eventid". Put them into the respective ITT by issuing a
> + * MAPTI command for each of them.
> + */
> +static int gicv3_allocate_host_lpi_block(struct host_its *its, struct domain *d,
> +                                         uint32_t host_devid, uint32_t eventid)
> +{
> +    static uint32_t next_lpi = 0;
> +    uint32_t lpi, lpi_idx = next_lpi % HOST_LPIS_PER_PAGE;
> +    int chunk;
> +    int i;
> +
> +    spin_lock(&lpi_data.host_lpis_lock);
> +    chunk = find_unused_host_lpi(next_lpi / HOST_LPIS_PER_PAGE, &lpi_idx);
> +
> +    if ( chunk == - 1 )          /* rescan for a hole from the beginning */
> +    {
> +        lpi_idx = 0;
> +        chunk = find_unused_host_lpi(0, &lpi_idx);
> +        if ( chunk == -1 )
> +        {
> +            spin_unlock(&lpi_data.host_lpis_lock);
> +            return -ENOSPC;
> +        }
> +    }
> +
> +    /* If we hit an unallocated chunk, we initialize it and use entry 0. */
> +    if ( !lpi_data.host_lpis[chunk] )
> +    {
> +        union host_lpi *new_chunk;
> +
> +        new_chunk = alloc_xenheap_pages(0, 0);
> +        if ( !new_chunk )
> +        {
> +            spin_unlock(&lpi_data.host_lpis_lock);
> +            return -ENOMEM;
> +        }
> +
> +        for ( i = 0; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
> +            new_chunk[i].dom_id = INVALID_DOMID;

Don't we need to properly initialize each entry in the block? I would
use memset for initialization.


> +        lpi_data.host_lpis[chunk] = new_chunk;
> +        lpi_idx = 0;
> +    }
> +
> +    lpi = chunk * HOST_LPIS_PER_PAGE + lpi_idx;
> +
> +    for ( i = 0; i < LPI_BLOCK; i++ )
> +    {
> +        union host_lpi hlpi;
> +
> +        /*
> +         * Mark this host LPI as belonging to the domain, but don't assign
> +         * any virtual LPI or a VCPU yet.
> +         */
> +        hlpi.virt_lpi = 0;

 #define 0 to invalid vlpi


> +        hlpi.dom_id = d->domain_id;
> +        hlpi.vcpu_id = ~0;

 #define ~0 to invalid vcpu


> +        lpi_data.host_lpis[chunk][lpi_idx + i].data = hlpi.data;
> +
> +        /*
> +         * Enable this host LPI, so we don't have to do this during the
> +         * guest's runtime.
> +         */
> +        lpi_data.lpi_property[lpi + i] |= LPI_PROP_ENABLED;
> +    }
> +
> +    /* We have allocated and initialized the host LPI entries, so it's safe
> +     * to drop the lock now. Access to the structures can be done concurrently
> +     * as it involves only an atomic uint64_t access.
> +     */
> +    spin_unlock(&lpi_data.host_lpis_lock);
> +
> +    __flush_dcache_area(&lpi_data.lpi_property[lpi], LPI_BLOCK);
> +
> +    /* Tell the redistributor about the changed enabled bits and map the LPIs */
> +    for ( i = 0; i < LPI_BLOCK; i++ )
> +    {
> +        its_send_cmd_mapti(its, host_devid, eventid + i, lpi + i + 8192, 0);
> +        its_send_cmd_inv(its, host_devid, eventid + i);
> +    }
> +
> +    its_send_cmd_sync(its, 0);
> +
> +    next_lpi = lpi + LPI_BLOCK;
> +    return lpi + 8192;
> +}
> +
> +static int gicv3_free_host_lpi_block(struct host_its *its, uint32_t lpi)
> +{
> +    union host_lpi *hlpi, empty_lpi = { .dom_id = INVALID_DOMID };
> +    int i;
> +
> +    hlpi = gic_get_host_lpi(lpi);
> +    if ( !hlpi )
> +        return -ENOENT;
> +
> +    spin_lock(&lpi_data.host_lpis_lock);
> +
> +    for ( i = 0; i < LPI_BLOCK; i++ )
> +        hlpi[i].data = empty_lpi.data;
> +
> +    spin_unlock(&lpi_data.host_lpis_lock);
> +
> +    return 0;
> +}
> +
>  static void remove_mapped_guest_device(struct its_devices *dev)
>  {
> +    int i;
> +
>      if ( dev->hw_its )
>          its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
>  
> +    for ( i = 0; i < dev->eventids / 32; i++ )
> +        gicv3_free_host_lpi_block(dev->hw_its, dev->host_lpis[i]);
> +
>      xfree(dev->itt_addr);
> +    xfree(dev->host_lpis);
>      xfree(dev);
>  }
>  
> @@ -390,7 +605,7 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>      void *itt_addr = NULL;
>      struct its_devices *dev, *temp;
>      struct host_its *hw_its;
> -    int ret;
> +    int ret, i;
>  
>      /* check for already existing mappings */
>      spin_lock(&d->arch.vgic.its_devices_lock);
> @@ -435,9 +650,18 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>          return -ENOMEM;
>      }
>  
> +    dev->host_lpis = xzalloc_array(uint32_t, BIT(bits) / 32);
> +    if ( !dev->host_lpis )
> +    {
> +        xfree(dev);
> +        xfree(itt_addr);
> +        return -ENOMEM;
> +    }
> +
>      ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
>                              virt_to_maddr(itt_addr), true);
>      if (ret) {
> +        xfree(dev->host_lpis);
>          xfree(itt_addr);
>          xfree(dev);
>          return ret;
> @@ -453,6 +677,13 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>      list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
>      spin_unlock(&d->arch.vgic.its_devices_lock);
>  
> +    /* Map all host LPIs within this device already. We can't afford to queue
> +     * any host ITS commands later on during the guest's runtime.
> +     */
> +    for ( i = 0; i < BIT(bits) / 32; i++ )
> +        dev->host_lpis[i] = gicv3_allocate_host_lpi_block(hw_its, d, host_devid,
> +                                                          i * 32);

Shouldn't we check for errors returned by gicv3_allocate_host_lpi_block?
If BIT(bits) < 32, it is possible to allocate more plpis than necessary.
It is also possible to end up calling mapti with an inexistent eventid
for host_devid. Could that be a problem?


>      return 0;
>  }
>  
> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> index 525a29d..d1ebc19 100644
> --- a/xen/include/asm-arm/gic-its.h
> +++ b/xen/include/asm-arm/gic-its.h
> @@ -104,6 +104,7 @@ struct its_devices {
>      uint32_t guest_devid;
>      uint32_t host_devid;
>      uint32_t eventids;
> +    uint32_t *host_lpis;
>  };
>  
>  extern struct list_head host_its_list;
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 08/26] ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall
  2016-12-22 18:24 ` [RFC PATCH v2 08/26] ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall Andre Przywara
@ 2017-01-05 21:23   ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 21:23 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> To get MSIs from devices forwarded to a CPU, we need to name the device
> and its MSIs by mapping them to an ITS.
> Since this involves queueing commands to the ITS command queue, we can't
> really afford to do this during the guest's runtime, as this would open
> up a denial-of-service attack vector.
> So we require every device with MSI interrupts to be mapped explicitly by
> Dom0. For Dom0 itself we can just use the existing PCI physdev_op
> hypercalls, which the existing Linux kernel issues already.
> So upon receipt of this hypercall we map the device to the hardware ITS
> and prepare it to be later mapped by the virtual ITS by using the very
> same device ID (for Dom0 only).
> Also we ask for mapping 32 LPIs to cover 32 MSIs that the device may
> use.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/physdev.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/xen/arch/arm/physdev.c b/xen/arch/arm/physdev.c
> index 27bbbda..d9e6be3 100644
> --- a/xen/arch/arm/physdev.c
> +++ b/xen/arch/arm/physdev.c
> @@ -9,11 +9,35 @@
>  #include <xen/lib.h>
>  #include <xen/errno.h>
>  #include <xen/sched.h>
> +#include <xen/guest_access.h>
> +#include <asm/gic-its.h>
>  #include <asm/hypercall.h>
>  
>  
>  int do_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>  {
> +    struct physdev_manage_pci manage;
> +    struct domain *dom0;
> +    u32 devid;
> +    int ret;
> +
> +    switch (cmd)
> +    {
> +        case PHYSDEVOP_manage_pci_add:
> +        case PHYSDEVOP_manage_pci_remove:
> +            if ( copy_from_guest(&manage, arg, 1) != 0 )
> +                return -EFAULT;
> +
> +            dom0 = hardware_domain;

Please don't effectively name dom0 hardware_domain, just use
hardware_domain directly.


> +            devid = manage.bus << 8 | manage.devfn;
> +            /* Allocate an ITS device table with space for 32 MSIs */
> +            ret = gicv3_its_map_device(dom0, devid, devid, 5,
> +                                       cmd == PHYSDEVOP_manage_pci_add);

Why 32? Is it an arbitrary number? Shouldn't we figure out exactly the
number of events the device need?


> +            put_domain(dom0);

Where is the correspondent get_domain?


> +            return ret;
> +    }
> +
>      gdprintk(XENLOG_DEBUG, "PHYSDEVOP cmd=%d: not implemented\n", cmd);
>      return -ENOSYS;
>  }
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs
  2016-12-22 18:24 ` [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
@ 2017-01-05 21:36   ` Stefano Stabellini
  2017-01-12 18:24     ` Andre Przywara
  0 siblings, 1 reply; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 21:36 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> For the same reason that allocating a struct irq_desc for each
> possible LPI is not an option, having a struct pending_irq for each LPI
> is also not feasible. However we actually only need those when an
> interrupt is on a vCPU (or is about to be injected).
> Maintain a list of those structs that we can use for the lifecycle of
> a guest LPI. We allocate new entries if necessary, however reuse
> pre-owned entries whenever possible.
> I added some locking around this list here, however my gut feeling is
> that we don't need one because this a per-VCPU structure anyway.
> If someone could confirm this, I'd be grateful.

I don't think the list should be per-VCPU, because the underlying LPIs
are global. Similarly, the struct pending_irq array is per-domain, only
the first 32 (PPIs) are per vcpu. Besides, it shouldn't be a list :-)


> Teach the existing VGIC functions to find the right pointer when being
> given a virtual LPI number.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Most of my comments on the previous version of the patch are still
unaddressed.


> ---
>  xen/arch/arm/gic.c           |  3 +++
>  xen/arch/arm/vgic-v3.c       | 11 ++++++++
>  xen/arch/arm/vgic.c          | 64 +++++++++++++++++++++++++++++++++++++++++---
>  xen/include/asm-arm/domain.h |  2 ++
>  xen/include/asm-arm/vgic.h   | 10 +++++++
>  5 files changed, 87 insertions(+), 3 deletions(-)
> 
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index a5348f2..6f25501 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -509,6 +509,9 @@ static void gic_update_one_lr(struct vcpu *v, int i)
>                  struct vcpu *v_target = vgic_get_target_vcpu(v, irq);
>                  irq_set_affinity(p->desc, cpumask_of(v_target->processor));
>              }
> +            /* If this was an LPI, mark this struct as available again. */
> +            if ( p->irq >= 8192 )
> +                p->irq = 0;
>          }
>      }
>  }
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index d61479d..0ffde74 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -331,6 +331,14 @@ read_unknown:
>      return 1;
>  }
>  
> +int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi)
> +{
> +    if ( vlpi >= d->arch.vgic.nr_lpis )
> +        return GIC_PRI_IRQ;
> +
> +    return d->arch.vgic.proptable[vlpi - 8192] & 0xfc;
> +}
> +
>  static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>                                            uint32_t gicr_reg,
>                                            register_t r)
> @@ -1426,6 +1434,9 @@ static int vgic_v3_vcpu_init(struct vcpu *v)
>      if ( v->vcpu_id == last_cpu || (v->vcpu_id == (d->max_vcpus - 1)) )
>          v->arch.vgic.flags |= VGIC_V3_RDIST_LAST;
>  
> +    spin_lock_init(&v->arch.vgic.pending_lpi_list_lock);
> +    INIT_LIST_HEAD(&v->arch.vgic.pending_lpi_list);
> +
>      return 0;
>  }
>  
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index de77aaa..f15eb3e 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -31,6 +31,8 @@
>  #include <asm/mmio.h>
>  #include <asm/gic.h>
>  #include <asm/vgic.h>
> +#include <asm/gic_v3_defs.h>
> +#include <asm/gic-its.h>
>  
>  static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v, int rank)
>  {
> @@ -61,7 +63,7 @@ struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq)
>      return vgic_get_rank(v, rank);
>  }
>  
> -static void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq)
> +void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq)
>  {
>      INIT_LIST_HEAD(&p->inflight);
>      INIT_LIST_HEAD(&p->lr_queue);
> @@ -247,10 +249,14 @@ struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq)
>  
>  static int vgic_get_virq_priority(struct vcpu *v, unsigned int virq)
>  {
> -    struct vgic_irq_rank *rank = vgic_rank_irq(v, virq);
> +    struct vgic_irq_rank *rank;
>      unsigned long flags;
>      int priority;
>  
> +    if ( virq >= 8192 )
> +        return vgic_lpi_get_priority(v->domain, virq);
> +
> +    rank = vgic_rank_irq(v, virq);
>      vgic_lock_rank(v, rank, flags);
>      priority = rank->priority[virq & INTERRUPT_RANK_MASK];
>      vgic_unlock_rank(v, rank, flags);
> @@ -449,13 +455,63 @@ bool vgic_to_sgi(struct vcpu *v, register_t sgir, enum gic_sgi_mode irqmode,
>      return true;
>  }
>  
> +/*
> + * Holding struct pending_irq's for each possible virtual LPI in each domain
> + * requires too much Xen memory, also a malicious guest could potentially
> + * spam Xen with LPI map requests. We cannot cover those with (guest allocated)
> + * ITS memory, so we use a dynamic scheme of allocating struct pending_irq's
> + * on demand.
> + */
> +struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int lpi,
> +                                   bool allocate)
> +{
> +    struct lpi_pending_irq *lpi_irq, *empty = NULL;
> +
> +    spin_lock(&v->arch.vgic.pending_lpi_list_lock);
> +    list_for_each_entry(lpi_irq, &v->arch.vgic.pending_lpi_list, entry)
> +    {
> +        if ( lpi_irq->pirq.irq == lpi )
> +        {
> +            spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
> +            return &lpi_irq->pirq;
> +        }
> +
> +        if ( lpi_irq->pirq.irq == 0 && !empty )
> +            empty = lpi_irq;
> +    }
> +
> +    if ( !allocate )
> +    {
> +        spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
> +        return NULL;
> +    }
> +
> +    if ( !empty )
> +    {
> +        empty = xzalloc(struct lpi_pending_irq);
> +        vgic_init_pending_irq(&empty->pirq, lpi);
> +        list_add_tail(&empty->entry, &v->arch.vgic.pending_lpi_list);
> +    } else
> +    {
> +        empty->pirq.status = 0;
> +        empty->pirq.irq = lpi;
> +    }
> +
> +    spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
> +
> +    return &empty->pirq;
> +}
> +
>  struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq)
>  {
>      struct pending_irq *n;
> +
>      /* Pending irqs allocation strategy: the first vgic.nr_spis irqs
>       * are used for SPIs; the rests are used for per cpu irqs */
>      if ( irq < 32 )
>          n = &v->arch.vgic.pending_irqs[irq];
> +    else if ( irq >= 8192 )
> +        n = lpi_to_pending(v, irq, true);
>      else
>          n = &v->domain->arch.vgic.pending_irqs[irq - 32];
>      return n;
> @@ -483,7 +539,7 @@ void vgic_clear_pending_irqs(struct vcpu *v)
>  void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
>  {
>      uint8_t priority;
> -    struct pending_irq *iter, *n = irq_to_pending(v, virq);
> +    struct pending_irq *iter, *n;
>      unsigned long flags;
>      bool running;
>  
> @@ -491,6 +547,8 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
>  
>      spin_lock_irqsave(&v->arch.vgic.lock, flags);
>  
> +    n = irq_to_pending(v, virq);
> +
>      /* vcpu offline */
>      if ( test_bit(_VPF_down, &v->pause_flags) )
>      {
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 8ccc32a..02ae2cd 100644
> --- a/xen/include/asm-arm/domain.h
> +++ b/xen/include/asm-arm/domain.h
> @@ -256,6 +256,8 @@ struct arch_vcpu
>          paddr_t rdist_base;
>  #define VGIC_V3_RDIST_LAST  (1 << 0)        /* last vCPU of the rdist */
>          uint8_t flags;
> +        struct list_head pending_lpi_list;
> +        spinlock_t pending_lpi_list_lock;   /* protects the pending_lpi_list */
>      } vgic;
>  
>      /* Timer registers  */
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 672f649..a503321 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -83,6 +83,12 @@ struct pending_irq
>      struct list_head lr_queue;
>  };
>  
> +struct lpi_pending_irq
> +{
> +    struct list_head entry;
> +    struct pending_irq pirq;
> +};
> +
>  #define NR_INTERRUPT_PER_RANK   32
>  #define INTERRUPT_RANK_MASK (NR_INTERRUPT_PER_RANK - 1)
>  
> @@ -296,13 +302,17 @@ extern struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq);
>  extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq);
>  extern void vgic_vcpu_inject_spi(struct domain *d, unsigned int virq);
>  extern void vgic_clear_pending_irqs(struct vcpu *v);
> +extern void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq);
>  extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq);
>  extern struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq);
> +extern struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int irq,
> +                                          bool allocate);
>  extern struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
>  extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq);
>  extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
>  extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
>  extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
> +extern int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi);
>  extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
>  int vgic_v2_init(struct domain *d, int *mmio_count);
>  int vgic_v3_init(struct domain *d, int *mmio_count);

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests
  2016-12-22 18:24 ` [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests Andre Przywara
@ 2017-01-05 22:10   ` Stefano Stabellini
  2017-01-12 12:16     ` Andre Przywara
  0 siblings, 1 reply; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 22:10 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> Upon receiving an LPI, we need to find the right VCPU and virtual IRQ
> number to get this IRQ injected.
> Iterate our two-level LPI table to find this information quickly when
> the host takes an LPI. Call the existing injection function to let the
> GIC emulation deal with this interrupt.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-its.c    | 35 +++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic.c        |  6 ++++--
>  xen/include/asm-arm/irq.h |  8 ++++++++
>  3 files changed, 47 insertions(+), 2 deletions(-)
> 
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index e7ddd90..0d4ca1b 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -72,6 +72,41 @@ static union host_lpi *gic_get_host_lpi(uint32_t plpi)
>      return &lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE][plpi % HOST_LPIS_PER_PAGE];
>  }
>  
> +/* Handle incoming LPIs, which are a bit special, because they are potentially
> + * numerous and also only get injected into guests. Treat them specially here,
> + * by just looking up their target vCPU and virtual LPI number and hand it
> + * over to the injection function.
> + */
> +void do_LPI(unsigned int lpi)
> +{
> +    struct domain *d;
> +    union host_lpi *hlpip, hlpi;
> +    struct vcpu *vcpu;
> +
> +    WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
> +
> +    hlpip = gic_get_host_lpi(lpi);
> +    if ( !hlpip )
> +        return;
> +
> +    hlpi.data = hlpip->data;

Why can't we just reference hlpip directly throughout this function? Is
it for atomicity reasons?


> +    /* We may have mapped more host LPIs than the guest actually asked for. */
> +    if ( !hlpi.virt_lpi )
> +        return;
> +
> +    d = get_domain_by_id(hlpi.dom_id);
> +    if ( !d )
> +        return;
> +
> +    if ( hlpi.vcpu_id >= d->max_vcpus )
> +        return;
> +
> +    vcpu = d->vcpu[hlpi.vcpu_id];
> +
> +    vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
> +}
> +
>  #define ITS_CMD_QUEUE_SZ                                SZ_64K
>  
>  static int its_send_command(struct host_its *hw_its, void *its_cmd)
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index 6f25501..7d428dc 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -700,8 +700,10 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
>              local_irq_enable();
>              do_IRQ(regs, irq, is_fiq);
>              local_irq_disable();
> -        }
> -        else if (unlikely(irq < 16))
> +        } else if ( irq >= 8192 )
> +        {
> +            do_LPI(irq);
> +        } else if ( unlikely(irq < 16) )
>          {
>              do_sgi(regs, irq);
>          }
> diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
> index 8f7a167..ee47de8 100644
> --- a/xen/include/asm-arm/irq.h
> +++ b/xen/include/asm-arm/irq.h
> @@ -34,6 +34,14 @@ struct irq_desc *__irq_to_desc(int irq);
>  
>  void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq);
>  
> +#ifdef CONFIG_HAS_ITS
> +void do_LPI(unsigned int irq);
> +#else
> +static inline void do_LPI(unsigned int irq)
> +{
> +}
> +#endif
> +
>  #define domain_pirq_to_irq(d, pirq) (pirq)
>  
>  bool_t is_assignable_irq(unsigned int irq);
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 12/26] ARM: vGICv3: handle virtual LPI pending and property tables
  2016-12-22 18:24 ` [RFC PATCH v2 12/26] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
@ 2017-01-05 22:17   ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 22:17 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> Allow a guest to provide the address and size for the memory regions
> it has reserved for the GICv3 pending and property tables.
> We sanitise the various fields of the respective redistributor
> registers and map those pages into Xen's address space to have easy
> access.

Many comments still unaddressed


> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3.c       | 202 ++++++++++++++++++++++++++++++++++++++++---
>  xen/arch/arm/vgic.c          |   4 +
>  xen/include/asm-arm/domain.h |   8 +-
>  xen/include/asm-arm/vgic.h   |   4 +
>  4 files changed, 203 insertions(+), 15 deletions(-)
> 
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 0ffde74..b981d4e 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -20,12 +20,14 @@
>  
>  #include <xen/bitops.h>
>  #include <xen/config.h>
> +#include <xen/domain_page.h>
>  #include <xen/lib.h>
>  #include <xen/init.h>
>  #include <xen/softirq.h>
>  #include <xen/irq.h>
>  #include <xen/sched.h>
>  #include <xen/sizes.h>
> +#include <xen/vmap.h>
>  #include <asm/current.h>
>  #include <asm/mmio.h>
>  #include <asm/gic_v3_defs.h>
> @@ -229,12 +231,14 @@ static int __vgic_v3_rdistr_rd_mmio_read(struct vcpu *v, mmio_info_t *info,
>          goto read_reserved;
>  
>      case VREG64(GICR_PROPBASER):
> -        /* LPI's not implemented */
> -        goto read_as_zero_64;
> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +        *r = vgic_reg64_extract(v->domain->arch.vgic.rdist_propbase, info);
> +        return 1;
>  
>      case VREG64(GICR_PENDBASER):
> -        /* LPI's not implemented */
> -        goto read_as_zero_64;
> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +        *r = vgic_reg64_extract(v->arch.vgic.rdist_pendbase, info);
> +        return 1;
>  
>      case 0x0080:
>          goto read_reserved;
> @@ -302,11 +306,6 @@ bad_width:
>      domain_crash_synchronous();
>      return 0;
>  
> -read_as_zero_64:
> -    if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> -    *r = 0;
> -    return 1;
> -
>  read_as_zero_32:
>      if ( dabt.size != DABT_WORD ) goto bad_width;
>      *r = 0;
> @@ -331,6 +330,143 @@ read_unknown:
>      return 1;
>  }
>  
> +static uint64_t vgic_sanitise_field(uint64_t reg, uint64_t field_mask,
> +                                    int field_shift,
> +                                    uint64_t (*sanitise_fn)(uint64_t))
> +{
> +    uint64_t field = (reg & field_mask) >> field_shift;
> +
> +    field = sanitise_fn(field) << field_shift;
> +    return (reg & ~field_mask) | field;
> +}
> +
> +/* We want to avoid outer shareable. */
> +static uint64_t vgic_sanitise_shareability(uint64_t field)
> +{
> +    switch (field) {
> +    case GIC_BASER_OuterShareable:
> +        return GIC_BASER_InnerShareable;
> +    default:
> +        return field;
> +    }
> +}
> +
> +/* Avoid any inner non-cacheable mapping. */
> +static uint64_t vgic_sanitise_inner_cacheability(uint64_t field)
> +{
> +    switch (field) {
> +    case GIC_BASER_CACHE_nCnB:
> +    case GIC_BASER_CACHE_nC:
> +        return GIC_BASER_CACHE_RaWb;
> +    default:
> +        return field;
> +    }
> +}
> +
> +/* Non-cacheable or same-as-inner are OK. */
> +static uint64_t vgic_sanitise_outer_cacheability(uint64_t field)
> +{
> +    switch (field) {
> +    case GIC_BASER_CACHE_SameAsInner:
> +    case GIC_BASER_CACHE_nC:
> +        return field;
> +    default:
> +        return GIC_BASER_CACHE_nC;
> +    }
> +}
> +
> +static uint64_t sanitize_propbaser(uint64_t reg)
> +{
> +    reg = vgic_sanitise_field(reg, GICR_PROPBASER_SHAREABILITY_MASK,
> +                              GICR_PROPBASER_SHAREABILITY_SHIFT,
> +                              vgic_sanitise_shareability);
> +    reg = vgic_sanitise_field(reg, GICR_PROPBASER_INNER_CACHEABILITY_MASK,
> +                              GICR_PROPBASER_INNER_CACHEABILITY_SHIFT,
> +                              vgic_sanitise_inner_cacheability);
> +    reg = vgic_sanitise_field(reg, GICR_PROPBASER_OUTER_CACHEABILITY_MASK,
> +                              GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT,
> +                              vgic_sanitise_outer_cacheability);
> +
> +    reg &= ~PROPBASER_RES0_MASK;
> +    reg &= ~GENMASK(51, 48);
> +    return reg;
> +}
> +
> +static uint64_t sanitize_pendbaser(uint64_t reg)
> +{
> +    reg = vgic_sanitise_field(reg, GICR_PENDBASER_SHAREABILITY_MASK,
> +                              GICR_PENDBASER_SHAREABILITY_SHIFT,
> +                              vgic_sanitise_shareability);
> +    reg = vgic_sanitise_field(reg, GICR_PENDBASER_INNER_CACHEABILITY_MASK,
> +                              GICR_PENDBASER_INNER_CACHEABILITY_SHIFT,
> +                              vgic_sanitise_inner_cacheability);
> +    reg = vgic_sanitise_field(reg, GICR_PENDBASER_OUTER_CACHEABILITY_MASK,
> +                              GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT,
> +                              vgic_sanitise_outer_cacheability);
> +
> +    reg &= ~PENDBASER_RES0_MASK;
> +    reg &= ~GENMASK(51, 48);
> +    return reg;
> +}
> +
> +/*
> + * Allow mapping some parts of guest memory into Xen's VA space to have easy
> + * access to it. This is to allow ITS configuration data to be held in
> + * guest memory and avoid using Xen memory for that.
> + */
> +void *map_guest_pages(struct domain *d, paddr_t guest_addr, int nr_pages)
> +{
> +    mfn_t onepage;
> +    mfn_t *pages;
> +    int i;
> +    void *ptr;
> +
> +    /* TODO: free previous mapping, change prototype? use get-put-put? */
> +
> +    guest_addr &= PAGE_MASK;
> +
> +    if ( nr_pages == 1 )
> +    {
> +        pages = &onepage;
> +    } else
> +    {
> +        pages = xmalloc_array(mfn_t, nr_pages);
> +        if ( !pages )
> +            return NULL;
> +    }
> +
> +    for ( i = 0; i < nr_pages; i++ )
> +    {
> +        get_page_from_gfn(d, (guest_addr >> PAGE_SHIFT) + i, NULL, P2M_ALLOC);
> +        pages[i] = _mfn((guest_addr + i * PAGE_SIZE) >> PAGE_SHIFT);
> +    }
> +
> +    ptr = vmap(pages, nr_pages);
> +
> +    if ( nr_pages > 1 )
> +        xfree(pages);
> +
> +    return ptr;
> +}
> +
> +void unmap_guest_pages(void *va, int nr_pages)
> +{
> +    paddr_t pa;
> +    unsigned long i;
> +
> +    if ( !va )
> +        return;
> +
> +    va = (void *)((uintptr_t)va & PAGE_MASK);
> +    pa = virt_to_maddr(va);
> +
> +    vunmap(va);
> +    for ( i = 0; i < nr_pages; i++ )
> +        put_page(mfn_to_page((pa >> PAGE_SHIFT) + i));
> +
> +    return;
> +}
> +
>  int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi)
>  {
>      if ( vlpi >= d->arch.vgic.nr_lpis )
> @@ -339,11 +475,20 @@ int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi)
>      return d->arch.vgic.proptable[vlpi - 8192] & 0xfc;
>  }
>  
> +bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi)
> +{
> +    if ( vlpi >= d->arch.vgic.nr_lpis )
> +        return false;
> +
> +    return d->arch.vgic.proptable[vlpi - 8192] & LPI_PROP_ENABLED;
> +}
> +
>  static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>                                            uint32_t gicr_reg,
>                                            register_t r)
>  {
>      struct hsr_dabt dabt = info->dabt;
> +    uint64_t reg;
>  
>      switch ( gicr_reg )
>      {
> @@ -384,13 +529,42 @@ static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>      case 0x0050:
>          goto write_reserved;
>  
> -    case VREG64(GICR_PROPBASER):
> -        /* LPI is not implemented */
> -        goto write_ignore_64;
> +    case VREG64(GICR_PROPBASER): {
> +        int nr_pages;
> +
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        if ( v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
> +            return 1;
>  
> +        reg = v->domain->arch.vgic.rdist_propbase;
> +        if ( v->domain->arch.vgic.proptable )
> +        {
> +            nr_pages = BIT((reg & 0x1f) + 1) - 8192;
> +            nr_pages = DIV_ROUND_UP(nr_pages, PAGE_SIZE);
> +            unmap_guest_pages(v->domain->arch.vgic.proptable, nr_pages);
> +        }
> +        vgic_reg64_update(&reg, r, info);
> +        reg = sanitize_propbaser(reg);
> +        v->domain->arch.vgic.rdist_propbase = reg;
> +
> +        v->domain->arch.vgic.nr_lpis = BIT((reg & 0x1f) + 1) - 8192;
> +        nr_pages = DIV_ROUND_UP(v->domain->arch.vgic.nr_lpis, PAGE_SIZE);
> +        v->domain->arch.vgic.proptable = map_guest_pages(v->domain,
> +                                                         reg & GENMASK(47, 12),
> +                                                         nr_pages);
> +        return 1;
> +    }
>      case VREG64(GICR_PENDBASER):
> -        /* LPI is not implemented */
> -        goto write_ignore_64;
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +	reg = v->arch.vgic.rdist_pendbase;
> +	vgic_reg64_update(&reg, r, info);
> +	reg = sanitize_pendbaser(reg);
> +	v->arch.vgic.rdist_pendbase = reg;
> +
> +        unmap_guest_pages(v->arch.vgic.pendtable, 16);
> +	v->arch.vgic.pendtable = map_guest_pages(v->domain,
> +                                                 reg & GENMASK(47, 12), 16);
> +	return 1;
>  
>      case 0x0080:
>          goto write_reserved;
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index f15eb3e..2a33a0e 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -497,6 +497,10 @@ struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int lpi,
>          empty->pirq.irq = lpi;
>      }
>  
> +    /* Update the enabled status */
> +    if ( vgic_lpi_is_enabled(v->domain, lpi) )
> +        set_bit(GIC_IRQ_GUEST_ENABLED, &empty->pirq.status);
> +
>      spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
>  
>      return &empty->pirq;
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 02ae2cd..543d058 100644
> --- a/xen/include/asm-arm/domain.h
> +++ b/xen/include/asm-arm/domain.h
> @@ -109,6 +109,9 @@ struct arch_domain
>          } *rdist_regions;
>          int nr_regions;                     /* Number of rdist regions */
>          uint32_t rdist_stride;              /* Re-Distributor stride */
> +        int nr_lpis;
> +        uint64_t rdist_propbase;
> +        uint8_t *proptable;
>          struct list_head its_devices;       /* devices mapped to an ITS */
>          spinlock_t its_devices_lock;        /* protects the its_devices list */
>  #endif
> @@ -254,7 +257,10 @@ struct arch_vcpu
>  
>          /* GICv3: redistributor base and flags for this vCPU */
>          paddr_t rdist_base;
> -#define VGIC_V3_RDIST_LAST  (1 << 0)        /* last vCPU of the rdist */
> +#define VGIC_V3_RDIST_LAST      (1 << 0)        /* last vCPU of the rdist */
> +#define VGIC_V3_LPIS_ENABLED    (1 << 1)
> +        uint64_t rdist_pendbase;
> +        unsigned long *pendtable;
>          uint8_t flags;
>          struct list_head pending_lpi_list;
>          spinlock_t pending_lpi_list_lock;   /* protects the pending_lpi_list */
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index a503321..1c157d3 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -285,6 +285,9 @@ VGIC_REG_HELPERS(32, 0x3);
>  
>  #undef VGIC_REG_HELPERS
>  
> +void *map_guest_pages(struct domain *d, paddr_t guest_addr, int nr_pages);
> +void unmap_guest_pages(void *va, int nr_pages);
> +
>  enum gic_sgi_mode;
>  
>  /*
> @@ -312,6 +315,7 @@ extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq);
>  extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
>  extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
>  extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
> +extern bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi);
>  extern int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi);
>  extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
>  int vgic_v2_init(struct domain *d, int *mmio_count);
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIso
  2016-12-22 18:24 ` [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIs Andre Przywara
@ 2017-01-05 22:28   ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 22:28 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> If a guest disables an LPI, we do not forward this to the associated
> host LPI to avoid queueing commands to the host ITS command queue.
> So it may happen that an LPI fires nevertheless on the host. In this
> case we can bail out early, but have to save the pending state on the
> virtual side.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-its.c     |  8 ++++++++
>  xen/arch/arm/vgic-v3.c     | 12 ++++++++++++
>  xen/include/asm-arm/vgic.h |  1 +
>  3 files changed, 21 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> index 602e19a..54e604a 100644
> --- a/xen/arch/arm/gic-its.c
> +++ b/xen/arch/arm/gic-its.c
> @@ -104,6 +104,14 @@ void do_LPI(unsigned int lpi)
>  
>      vcpu = d->vcpu[hlpi.vcpu_id];
>  
> +    /*
> +     * We keep all host LPIs enabled, so check if it's disabled on the guest
> +     * side and just record this LPI in the virtual pending table in this case.
> +     * The guest picks it up once it gets enabled again.
> +     */
> +    if ( !vgic_can_inject_lpi(vcpu, hlpi.virt_lpi) )
> +        return;

I suggest vgic_can_inject_lpi doesn't only return true or false, but
also if the vlpi is already set to pending. In that case, I think we
should disable the plpi to avoid storms (also see
http://marc.info/?l=xen-devel&m=148055519432739).


>      vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
>  }
>  
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index b981d4e..206e00b 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -483,6 +483,18 @@ bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi)
>      return d->arch.vgic.proptable[vlpi - 8192] & LPI_PROP_ENABLED;
>  }
>  
> +bool vgic_can_inject_lpi(struct vcpu *vcpu, uint32_t vlpi)
> +{
> +    if ( vlpi >= vcpu->domain->arch.vgic.nr_lpis )
> +        return false;
> +
> +    if ( vgic_lpi_is_enabled(vcpu->domain, vlpi) )
> +        return true;
> +
> +    set_bit(vlpi - 8192, vcpu->arch.vgic.pendtable);
> +    return false;
> +}
> +
>  static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>                                            uint32_t gicr_reg,
>                                            register_t r)
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 1c157d3..73291dd 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -317,6 +317,7 @@ extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
>  extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
>  extern bool vgic_lpi_is_enabled(struct domain *d, uint32_t vlpi);
>  extern int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi);
> +extern bool vgic_can_inject_lpi(struct vcpu *v, uint32_t vlpi);
>  extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
>  int vgic_v2_init(struct domain *d, int *mmio_count);
>  int vgic_v3_init(struct domain *d, int *mmio_count);
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 23/26] ARM: vITS: handle INVALL command
  2016-12-22 18:24 ` [RFC PATCH v2 23/26] ARM: vITS: handle INVALL command Andre Przywara
@ 2017-01-05 22:50   ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-05 22:50 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> The INVALL command instructs an ITS to invalidate the configuration
> data for all LPIs associated with a given redistributor (read: VCPU).
> This is nasty to emulate exactly with our architecture, so we just scan
> the pending table and inject _every_ LPI found there that got enabled.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-its.c | 36 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
> index bcabb04..c130d98 100644
> --- a/xen/arch/arm/vgic-its.c
> +++ b/xen/arch/arm/vgic-its.c
> @@ -297,6 +297,39 @@ out_unlock:
>      return ret;
>  }
>  
> +/* INVALL updates the per-LPI configuration status for every LPI mapped to
> + * a particular redistributor. Since our property table is referenced when
> + * needed, we don't need to sync anything, really. But we have to take care
> + * of LPIs getting enabled if there is an interrupt pending.
> + * To catch every LPI without iterating through the device table we just
> + * look for set bits in our virtual pending table and check the status of
> + * the enabled bit in the respective property table entry.
> + * This actually covers every (pending) LPI from every redistributor,
> + * but update_lpi_enabled_status() is a NOP for LPIs not being mapped
> + * to the redistributor/VCPU we are interested in.
> + */
> +static int its_handle_invall(struct virt_its *its, uint64_t *cmdptr)
> +{
> +    uint32_t collid = its_cmd_get_collection(cmdptr);
> +    struct vcpu *vcpu;
> +    int vlpi = 0;
> +
> +    spin_lock(&its->its_lock);
> +    vcpu = get_vcpu_from_collection(its, collid);
> +    spin_unlock(&its->its_lock);
> +
> +    do {
> +        vlpi = find_next_bit(vcpu->arch.vgic.pendtable,
> +                             its->d->arch.vgic.nr_lpis, vlpi);
> +        if (vlpi >= its->d->arch.vgic.nr_lpis)
> +            break;
> +
> +        update_lpi_enabled_status(its, vcpu, vlpi);
> +    } while (1);

Looks much better than before


> +    return 0;
> +}
> +
>  static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
>  {
>      uint32_t collid = its_cmd_get_collection(cmdptr);
> @@ -490,6 +523,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
>          case GITS_CMD_INV:
>              its_handle_inv(its, cmdptr);
>  	    break;
> +        case GITS_CMD_INVALL:
> +            its_handle_invall(its, cmdptr);
> +	    break;
>          case GITS_CMD_MAPC:
>              its_handle_mapc(its, cmdptr);
>              break;
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array
  2017-01-05 18:56   ` Stefano Stabellini
@ 2017-01-06 17:59     ` Andre Przywara
  2017-01-06 20:20       ` Stefano Stabellini
  0 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-06 17:59 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi,

On 05/01/17 18:56, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
> 7> The number of LPIs on a host can be potentially huge (millions),
>> although in practise will be mostly reasonable. So prematurely allocating
>> an array of struct irq_desc's for each LPI is not an option.
>> However Xen itself does not care about LPIs, as every LPI will be injected
>> into a guest (Dom0 for now).
>> Create a dense data structure (8 Bytes) for each LPI which holds just
>> enough information to determine the virtual IRQ number and the VCPU into
>> which the LPI needs to be injected.
>> Also to not artificially limit the number of LPIs, we create a 2-level
>> table for holding those structures.
>> This patch introduces functions to initialize these tables and to
>> create, lookup and destroy entries for a given LPI.
>> We allocate and access LPI information in a way that does not require
>> a lock.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-its.c        | 233 +++++++++++++++++++++++++++++++++++++++++-
>>  xen/include/asm-arm/gic-its.h |   1 +
>>  2 files changed, 233 insertions(+), 1 deletion(-)
>>
>> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
>> index e157c6b..e7ddd90 100644
>> --- a/xen/arch/arm/gic-its.c
>> +++ b/xen/arch/arm/gic-its.c
>> @@ -18,21 +18,36 @@
>>  
>>  #include <xen/config.h>
>>  #include <xen/lib.h>
>> +#include <xen/sched.h>
>>  #include <xen/err.h>
>>  #include <xen/device_tree.h>
>>  #include <xen/libfdt/libfdt.h>
>>  #include <xen/sched.h>
>>  #include <xen/sizes.h>
>>  #include <asm/p2m.h>
>> +#include <asm/domain.h>
>>  #include <asm/io.h>
>>  #include <asm/gic.h>
>>  #include <asm/gic_v3_defs.h>
>>  #include <asm/gic-its.h>
>>  
>> +/* LPIs on the host always go to a guest, so no struct irq_desc for them. */
>> +union host_lpi {
>> +    uint64_t data;
>> +    struct {
>> +        uint64_t virt_lpi:32;
>> +        uint64_t dom_id:16;
>> +        uint64_t vcpu_id:16;
>> +    };
>> +};
> 
> Just go with a regular struct
> 
>     struct host_lpi {
>         uint32_t virt_lpi;
>         uint16_t dom_id;
>         uint16_t vcpu_id;
>     };
> 
> The aarch64 C ABI guarantees the alignments of the fields.

Yes, I will get rid of the bitfields. But the actual purpose of the
union is to allow lock-free atomic access. I just see now that I failed
to document that, sorry!

We can't afford to have a lock for the actual data here, so the idea was
to use the naturally atomic access a native data type would give us.
In case we want to write multiple members, we assemble them in a local
copy and then write the uint64_t variable into the actual location.
Similar for reading. Single members can be updated using the members
directly.
Since the architecture guarantees atomic access for an aligned memory
access to/from a GPR, I think this is safe.
I am not sure we need to use the atomic_<type>_{read,write} accessors
here? I tried it: the resulting assembly is identical, and the source
doesn't look too bad either, so I guess I will change them over, just to
be safe?

Please note that this not about access ordering due to concurrent
accesses (so no barriers), we just need to guarantee that a host_lpi
state is consistent in respect to its members.

>>  /* Global state */
>>  static struct {
>>      uint8_t *lpi_property;
>> +    union host_lpi **host_lpis;
>>      unsigned int host_lpi_bits;
>> +    /* Protects allocation and deallocation of host LPIs, but not the access */
>> +    spinlock_t host_lpis_lock;
>>  } lpi_data;
>>  
>>  /* Physical redistributor address */
>> @@ -43,6 +58,19 @@ static DEFINE_PER_CPU(uint64_t, rdist_id);
>>  static DEFINE_PER_CPU(void *, pending_table);
>>  
>>  #define MAX_PHYS_LPIS   (BIT_ULL(lpi_data.host_lpi_bits) - 8192)
>> +#define HOST_LPIS_PER_PAGE      (PAGE_SIZE / sizeof(union host_lpi))
>> +
>> +static union host_lpi *gic_get_host_lpi(uint32_t plpi)
>> +{
>> +    if ( plpi < 8192 || plpi >= MAX_PHYS_LPIS + 8192 )
>> +        return NULL;
>> +
>> +    plpi -= 8192;
>> +    if ( !lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE] )
>> +        return NULL;
>> +
>> +    return &lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE][plpi % HOST_LPIS_PER_PAGE];
>> +}
>>  
>>  #define ITS_CMD_QUEUE_SZ                                SZ_64K
>>  
>> @@ -96,6 +124,20 @@ static int its_send_cmd_sync(struct host_its *its, int cpu)
>>      return its_send_command(its, cmd);
>>  }
>>  
>> +static int its_send_cmd_mapti(struct host_its *its,
>> +                              uint32_t deviceid, uint32_t eventid,
>> +                              uint32_t pintid, uint16_t icid)
>> +{
>> +    uint64_t cmd[4];
>> +
>> +    cmd[0] = GITS_CMD_MAPTI | ((uint64_t)deviceid << 32);
>> +    cmd[1] = eventid | ((uint64_t)pintid << 32);
>> +    cmd[2] = icid;
>> +    cmd[3] = 0x00;
>> +
>> +    return its_send_command(its, cmd);
>> +}
>> +
>>  static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
>>  {
>>      uint64_t cmd[4];
>> @@ -124,6 +166,19 @@ static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
>>      return its_send_command(its, cmd);
>>  }
>>  
>> +static int its_send_cmd_inv(struct host_its *its,
>> +                            uint32_t deviceid, uint32_t eventid)
>> +{
>> +    uint64_t cmd[4];
>> +
>> +    cmd[0] = GITS_CMD_INV | ((uint64_t)deviceid << 32);
>> +    cmd[1] = eventid;
>> +    cmd[2] = 0x00;
>> +    cmd[3] = 0x00;
>> +
>> +    return its_send_command(its, cmd);
>> +}
>> +
>>  /* Set up the (1:1) collection mapping for the given host CPU. */
>>  void gicv3_its_setup_collection(int cpu)
>>  {
>> @@ -366,21 +421,181 @@ uint64_t gicv3_lpi_get_proptable()
>>  static unsigned int max_lpi_bits = CONFIG_MAX_HOST_LPI_BITS;
>>  integer_param("max_lpi_bits", max_lpi_bits);
>>  
>> +/* Allocate the 2nd level array for host LPIs. This one holds pointers
>> + * to the page with the actual "union host_lpi" entries. Our LPI limit
>> + * avoids excessive memory usage.
>> + */
>>  int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
>>  {
>> +    int nr_lpi_ptrs;
>> +
>>      lpi_data.host_lpi_bits = min(hw_lpi_bits, max_lpi_bits);
>>  
>> +    spin_lock_init(&lpi_data.host_lpis_lock);
>> +
>> +    nr_lpi_ptrs = MAX_PHYS_LPIS / (PAGE_SIZE / sizeof(union host_lpi));
>> +    lpi_data.host_lpis = xzalloc_array(union host_lpi *, nr_lpi_ptrs);
>> +    if ( !lpi_data.host_lpis )
>> +        return -ENOMEM;
>> +
>>      printk("GICv3: using at most %lld LPIs on the host.\n", MAX_PHYS_LPIS);
>>  
>>      return 0;
>>  }
>>  
>> +#define INVALID_DOMID ((uint16_t)~0)
>> +#define LPI_BLOCK       32
> 
> Why 32? What is the reason behind it? It should be explained in the
> commit message and in the code.

There are multiple reasons for 32:
1) maximum number of MSIs per device in (legacy) PCI devices
2) smallest reasonable size of ITT (256 Byte alignment with typically 8
Byte sized entries)
3) number of ITT entries the Linux kernel allocates normally

The previous version worked without using blocks, by allocating single
LPIs. Now our early device and LPI mapping mandates a number of events
to be allocated, which we happen to set to 32. Ideally this number would
be passed on from a guest or Dom0, but this would require changing the
(hypercall) interfaces. We may do this later, but for now just using 32
seems to be good enough.

> Please add some numbers of memory consumption and average number of
> iterations required to setup lpis on an example server system. It is
> important to understand if the current algorithm and data structs are
> sufficient for our needs.

OK, can do.

> With the new host_lpis data struct below, the memory consumption is
> 
>     ROUNDUP(nr_plpis * 8, 4096) +
>     (ROUNDUP(nr_plpis * 8, 4096) / 4096) * 8 +
>     (nr_events / 32) * 4

I am not sure I get the meanings of the lines here, since you mix
physical LPIs (on the host) with events (per mapped device in each domain).

For physical LPIs:
- One top level table using one pointer per (4K / sizeof(union
host_lpi)) LPIs, so for each 512 LPIs. The number of LPIs is the compile
time constant, possibly overridden via the command line. Default value
is 20 bits, so the top level table takes 16KB.
- For each actually allocated contiguous block of 512 LPIs another 4K
page to hold the actual union host_lpi entries. This is densely
allocated, so one 4K table covers 16 passed-through devices (with 32
LPIs á 8 bytes each), regardless of their device ID or virtual LPI. So
128 (PCI) devices take up 32 KB, for instance, the default maximum of
1024 devices needs 256KB (if every device is actually used).

This covers the data structures for our internal physical LPI forwarding
information.
Additionally the host ITS requires some memory, which is one ITT per
used device (32 events (=MSIs) á 8 bytes = 256 bytes) and nr_devices
(=1024 by default) * 8 Bytes for the device table. That adds up to 256K
(worst case) + 8K.

I guess I will either add the broken-down memory requirements into some
documentation file or into the design document.

> Which, assuming nr_events = nr_plpis = 1024, means 8192 + 16 + 128, right?
> 
> 
>> +/* Must be called with host_lpis_lock held. */
>> +static int find_unused_host_lpi(int start, uint32_t *index)
>> +{
>> +    int chunk;
>> +    uint32_t i = *index;
>> +
>> +    for ( chunk = start; chunk < MAX_PHYS_LPIS / HOST_LPIS_PER_PAGE; chunk++ )
>> +    {
>> +        /* If we hit an unallocated chunk, use entry 0 in that one. */
>> +        if ( !lpi_data.host_lpis[chunk] )
>> +        {
>> +            *index = 0;
>> +            return chunk;
>> +        }
>> +
>> +        /* Find an unallocated entry in this chunk. */
>> +        for ( ; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
>> +        {
>> +            if ( lpi_data.host_lpis[chunk][i].dom_id == INVALID_DOMID )
>> +            {
>> +                *index = i;
>> +                return chunk;
>> +            }
> 
> This assumes that plpis are always allocated 32 at a time, by not
> checking within a block.

Yes, this is fine as the only function to allocate a block (below) does
this in chunks of LPI_BLOCK.

>> +        }
>> +        i = 0;
>> +    }
>> +
>> +    return -1;
>> +}
>> +
>> +/*
>> + * Allocate a block of 32 LPIs on the given host ITS for device "devid",
>> + * starting with "eventid". Put them into the respective ITT by issuing a
>> + * MAPTI command for each of them.
>> + */
>> +static int gicv3_allocate_host_lpi_block(struct host_its *its, struct domain *d,
>> +                                         uint32_t host_devid, uint32_t eventid)
>> +{
>> +    static uint32_t next_lpi = 0;
>> +    uint32_t lpi, lpi_idx = next_lpi % HOST_LPIS_PER_PAGE;
>> +    int chunk;
>> +    int i;
>> +
>> +    spin_lock(&lpi_data.host_lpis_lock);
>> +    chunk = find_unused_host_lpi(next_lpi / HOST_LPIS_PER_PAGE, &lpi_idx);
>> +
>> +    if ( chunk == - 1 )          /* rescan for a hole from the beginning */
>> +    {
>> +        lpi_idx = 0;
>> +        chunk = find_unused_host_lpi(0, &lpi_idx);
>> +        if ( chunk == -1 )
>> +        {
>> +            spin_unlock(&lpi_data.host_lpis_lock);
>> +            return -ENOSPC;
>> +        }
>> +    }
>> +
>> +    /* If we hit an unallocated chunk, we initialize it and use entry 0. */
>> +    if ( !lpi_data.host_lpis[chunk] )
>> +    {
>> +        union host_lpi *new_chunk;
>> +
>> +        new_chunk = alloc_xenheap_pages(0, 0);
>> +        if ( !new_chunk )
>> +        {
>> +            spin_unlock(&lpi_data.host_lpis_lock);
>> +            return -ENOMEM;
>> +        }
>> +
>> +        for ( i = 0; i < HOST_LPIS_PER_PAGE; i += LPI_BLOCK )
>> +            new_chunk[i].dom_id = INVALID_DOMID;
> 
> Don't we need to properly initialize each entry in the block? I would
> use memset for initialization.

If the domain ID is invalid, the other two entries will always be
ignored. Only in case of a valid domID we ever look at the LPI number
and VCPU id.

>> +        lpi_data.host_lpis[chunk] = new_chunk;
>> +        lpi_idx = 0;
>> +    }
>> +
>> +    lpi = chunk * HOST_LPIS_PER_PAGE + lpi_idx;
>> +
>> +    for ( i = 0; i < LPI_BLOCK; i++ )
>> +    {
>> +        union host_lpi hlpi;
>> +
>> +        /*
>> +         * Mark this host LPI as belonging to the domain, but don't assign
>> +         * any virtual LPI or a VCPU yet.
>> +         */
>> +        hlpi.virt_lpi = 0;
> 
>  #define 0 to invalid vlpi
> 
> 
>> +        hlpi.dom_id = d->domain_id;
>> +        hlpi.vcpu_id = ~0;
> 
>  #define ~0 to invalid vcpu

Sure.

>> +        lpi_data.host_lpis[chunk][lpi_idx + i].data = hlpi.data;
>> +
>> +        /*
>> +         * Enable this host LPI, so we don't have to do this during the
>> +         * guest's runtime.
>> +         */
>> +        lpi_data.lpi_property[lpi + i] |= LPI_PROP_ENABLED;
>> +    }
>> +
>> +    /* We have allocated and initialized the host LPI entries, so it's safe
>> +     * to drop the lock now. Access to the structures can be done concurrently
>> +     * as it involves only an atomic uint64_t access.
>> +     */
>> +    spin_unlock(&lpi_data.host_lpis_lock);
>> +
>> +    __flush_dcache_area(&lpi_data.lpi_property[lpi], LPI_BLOCK);
>> +
>> +    /* Tell the redistributor about the changed enabled bits and map the LPIs */
>> +    for ( i = 0; i < LPI_BLOCK; i++ )
>> +    {
>> +        its_send_cmd_mapti(its, host_devid, eventid + i, lpi + i + 8192, 0);
>> +        its_send_cmd_inv(its, host_devid, eventid + i);
>> +    }
>> +
>> +    its_send_cmd_sync(its, 0);
>> +
>> +    next_lpi = lpi + LPI_BLOCK;
>> +    return lpi + 8192;
>> +}
>> +
>> +static int gicv3_free_host_lpi_block(struct host_its *its, uint32_t lpi)
>> +{
>> +    union host_lpi *hlpi, empty_lpi = { .dom_id = INVALID_DOMID };
>> +    int i;
>> +
>> +    hlpi = gic_get_host_lpi(lpi);
>> +    if ( !hlpi )
>> +        return -ENOENT;
>> +
>> +    spin_lock(&lpi_data.host_lpis_lock);
>> +
>> +    for ( i = 0; i < LPI_BLOCK; i++ )
>> +        hlpi[i].data = empty_lpi.data;
>> +
>> +    spin_unlock(&lpi_data.host_lpis_lock);
>> +
>> +    return 0;
>> +}
>> +
>>  static void remove_mapped_guest_device(struct its_devices *dev)
>>  {
>> +    int i;
>> +
>>      if ( dev->hw_its )
>>          its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
>>  
>> +    for ( i = 0; i < dev->eventids / 32; i++ )
>> +        gicv3_free_host_lpi_block(dev->hw_its, dev->host_lpis[i]);
>> +
>>      xfree(dev->itt_addr);
>> +    xfree(dev->host_lpis);
>>      xfree(dev);
>>  }
>>  
>> @@ -390,7 +605,7 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>>      void *itt_addr = NULL;
>>      struct its_devices *dev, *temp;
>>      struct host_its *hw_its;
>> -    int ret;
>> +    int ret, i;
>>  
>>      /* check for already existing mappings */
>>      spin_lock(&d->arch.vgic.its_devices_lock);
>> @@ -435,9 +650,18 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>>          return -ENOMEM;
>>      }
>>  
>> +    dev->host_lpis = xzalloc_array(uint32_t, BIT(bits) / 32);
>> +    if ( !dev->host_lpis )
>> +    {
>> +        xfree(dev);
>> +        xfree(itt_addr);
>> +        return -ENOMEM;
>> +    }
>> +
>>      ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
>>                              virt_to_maddr(itt_addr), true);
>>      if (ret) {
>> +        xfree(dev->host_lpis);
>>          xfree(itt_addr);
>>          xfree(dev);
>>          return ret;
>> @@ -453,6 +677,13 @@ int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>>      list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
>>      spin_unlock(&d->arch.vgic.its_devices_lock);
>>  
>> +    /* Map all host LPIs within this device already. We can't afford to queue
>> +     * any host ITS commands later on during the guest's runtime.
>> +     */
>> +    for ( i = 0; i < BIT(bits) / 32; i++ )
>> +        dev->host_lpis[i] = gicv3_allocate_host_lpi_block(hw_its, d, host_devid,
>> +                                                          i * 32);
> 
> Shouldn't we check for errors returned by gicv3_allocate_host_lpi_block?

I just see that passing less than 5 bits will skip this loop at all :-(
Will fix it (also applies to the allocation a bit earlier).

> If BIT(bits) < 32, it is possible to allocate more plpis than necessary.

Yes, but this is not a problem. They won't have a virtual LPI assigned
(=0), so any incoming LPI will be just ignored.

> It is also possible to end up calling mapti with an inexistent eventid
> for host_devid. Could that be a problem?

Not at all. Actually there is no such thing as a "nonexistent event ID",
because the event ID will be written by the device as the payload to the
MSI doorbell address, probably because it learned about it by the
driver. So if we provision an ITTE with an event ID which the device
will never send, that LPI will just never fire.
Since Xen (in contrast to the driver in the domain) has no idea how many
and which MSIs the device will use, we just allocate a bunch of them.
The upper limit (32 atm) is something we probably need to still think
about, though.
I tried to learn a limit from Linux ("nvecs" in its_create_device()
seems to be the source), but couldn't find anything useful other than 32.
We will learn about exceeding this as soon as a domain tries to map a
virtual LPI with an event ID higher than 31, however it's too late to
fix this then. We can bark when this happens to learn if any device ever
does this during our testing to get some heuristic data.

Eventually all boils down to Xen getting more information from Dom0
about the required number of MSIs. We could then even limit the
allocation to less than 32, if that helps.

Cheers,
Andre.

> 
>>      return 0;
>>  }
>>  
>> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
>> index 525a29d..d1ebc19 100644
>> --- a/xen/include/asm-arm/gic-its.h
>> +++ b/xen/include/asm-arm/gic-its.h
>> @@ -104,6 +104,7 @@ struct its_devices {
>>      uint32_t guest_devid;
>>      uint32_t host_devid;
>>      uint32_t eventids;
>> +    uint32_t *host_lpis;
>>  };
>>  
>>  extern struct list_head host_its_list;
>> -- 
>> 2.9.0
>>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array
  2017-01-06 17:59     ` Andre Przywara
@ 2017-01-06 20:20       ` Stefano Stabellini
  2017-01-20 12:00         ` Julien Grall
  0 siblings, 1 reply; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-06 20:20 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Fri, 6 Jan 2017, Andre Przywara wrote:
> >> +/* LPIs on the host always go to a guest, so no struct irq_desc for them. */
> >> +union host_lpi {
> >> +    uint64_t data;
> >> +    struct {
> >> +        uint64_t virt_lpi:32;
> >> +        uint64_t dom_id:16;
> >> +        uint64_t vcpu_id:16;
> >> +    };
> >> +};
> > 
> > Just go with a regular struct
> > 
> >     struct host_lpi {
> >         uint32_t virt_lpi;
> >         uint16_t dom_id;
> >         uint16_t vcpu_id;
> >     };
> > 
> > The aarch64 C ABI guarantees the alignments of the fields.
> 
> Yes, I will get rid of the bitfields. But the actual purpose of the
> union is to allow lock-free atomic access. I just see now that I failed
> to document that, sorry!
> 
> We can't afford to have a lock for the actual data here, so the idea was
> to use the naturally atomic access a native data type would give us.
> In case we want to write multiple members, we assemble them in a local
> copy and then write the uint64_t variable into the actual location.
> Similar for reading. Single members can be updated using the members
> directly.
> Since the architecture guarantees atomic access for an aligned memory
> access to/from a GPR, I think this is safe.
> I am not sure we need to use the atomic_<type>_{read,write} accessors
> here? I tried it: the resulting assembly is identical, and the source
> doesn't look too bad either, so I guess I will change them over, just to
> be safe?

Yes, it will also work as documentation


> > It is also possible to end up calling mapti with an inexistent eventid
> > for host_devid. Could that be a problem?
> 
> Not at all. Actually there is no such thing as a "nonexistent event ID",
> because the event ID will be written by the device as the payload to the
> MSI doorbell address, probably because it learned about it by the
> driver. So if we provision an ITTE with an event ID which the device
> will never send, that LPI will just never fire.
> Since Xen (in contrast to the driver in the domain) has no idea how many
> and which MSIs the device will use, we just allocate a bunch of them.
> The upper limit (32 atm) is something we probably need to still think
> about, though.
> I tried to learn a limit from Linux ("nvecs" in its_create_device()
> seems to be the source), but couldn't find anything useful other than 32.
> We will learn about exceeding this as soon as a domain tries to map a
> virtual LPI with an event ID higher than 31, however it's too late to
> fix this then. We can bark when this happens to learn if any device ever
> does this during our testing to get some heuristic data.
> 
> Eventually all boils down to Xen getting more information from Dom0
> about the required number of MSIs. We could then even limit the
> allocation to less than 32, if that helps.

Originally Julien and I thought that Xen should map events up to the
theoretically maximum for each device, but we realized that they were
too many: an MSI-X capable device can generate up to 2048 different
events.

Xen needs to find out the exact number of events for each device. The
information can either be provided by the guest, or the hypervisor needs
to figure it out on its own.

With Julien's PCI Passthrough work, Xen will become able to read the
amount of events a device is capable of generating, so in the long term
this problem should be easy to solve. But Julien's work might land one
or two Xen releases after ITS.

In the meantime, we can extend an existing PHYSDEVOP hypercall or add a
new one. Julien, do you agree?

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests
  2017-01-05 22:10   ` Stefano Stabellini
@ 2017-01-12 12:16     ` Andre Przywara
  2017-01-12 18:57       ` Stefano Stabellini
  0 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-12 12:16 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi,

On 05/01/17 22:10, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
>> Upon receiving an LPI, we need to find the right VCPU and virtual IRQ
>> number to get this IRQ injected.
>> Iterate our two-level LPI table to find this information quickly when
>> the host takes an LPI. Call the existing injection function to let the
>> GIC emulation deal with this interrupt.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-its.c    | 35 +++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic.c        |  6 ++++--
>>  xen/include/asm-arm/irq.h |  8 ++++++++
>>  3 files changed, 47 insertions(+), 2 deletions(-)
>>
>> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
>> index e7ddd90..0d4ca1b 100644
>> --- a/xen/arch/arm/gic-its.c
>> +++ b/xen/arch/arm/gic-its.c
>> @@ -72,6 +72,41 @@ static union host_lpi *gic_get_host_lpi(uint32_t plpi)
>>      return &lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE][plpi % HOST_LPIS_PER_PAGE];
>>  }
>>  
>> +/* Handle incoming LPIs, which are a bit special, because they are potentially
>> + * numerous and also only get injected into guests. Treat them specially here,
>> + * by just looking up their target vCPU and virtual LPI number and hand it
>> + * over to the injection function.
>> + */
>> +void do_LPI(unsigned int lpi)
>> +{
>> +    struct domain *d;
>> +    union host_lpi *hlpip, hlpi;
>> +    struct vcpu *vcpu;
>> +
>> +    WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
>> +
>> +    hlpip = gic_get_host_lpi(lpi);
>> +    if ( !hlpip )
>> +        return;
>> +
>> +    hlpi.data = hlpip->data;
> 
> Why can't we just reference hlpip directly throughout this function? Is
> it for atomicity reasons?

Yes. We have to make sure that the LPI entry is consistent, but we don't
want to (and probably can't) take a lock here and everywhere else where
we touch it. We are fine with reading an "outdated" entry (which is just
about to change), this is a benign race which can happen on real
hardware as well.

>> +    /* We may have mapped more host LPIs than the guest actually asked for. */
>> +    if ( !hlpi.virt_lpi )
>> +        return;
>> +
>> +    d = get_domain_by_id(hlpi.dom_id);
>> +    if ( !d )
>> +        return;
>> +
>> +    if ( hlpi.vcpu_id >= d->max_vcpus )

I am just seeing that I miss a put_domain(d) here and ....

>> +        return;
>> +
>> +    vcpu = d->vcpu[hlpi.vcpu_id];

... here.

Which makes me wonder if it is legal to use a VCPU reference even though
I "put back" the domain pointer?
Is there a get_vcpu()/put_vcpu() equivalent? Or is this supposed to
covered by the domain pointer as well?

Or shall I use get_domain() the moment I enter the domain ID into the
host LPI array and only "put" it when an entry gets changed or the LPI
gets somehow else invalid (VCPU destroyed, domain destroyed)?

Cheers,
Andre.

>> +
>> +    vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
>> +}
>> +
>>  #define ITS_CMD_QUEUE_SZ                                SZ_64K
>>  
>>  static int its_send_command(struct host_its *hw_its, void *its_cmd)
>> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
>> index 6f25501..7d428dc 100644
>> --- a/xen/arch/arm/gic.c
>> +++ b/xen/arch/arm/gic.c
>> @@ -700,8 +700,10 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
>>              local_irq_enable();
>>              do_IRQ(regs, irq, is_fiq);
>>              local_irq_disable();
>> -        }
>> -        else if (unlikely(irq < 16))
>> +        } else if ( irq >= 8192 )
>> +        {
>> +            do_LPI(irq);
>> +        } else if ( unlikely(irq < 16) )
>>          {
>>              do_sgi(regs, irq);
>>          }
>> diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
>> index 8f7a167..ee47de8 100644
>> --- a/xen/include/asm-arm/irq.h
>> +++ b/xen/include/asm-arm/irq.h
>> @@ -34,6 +34,14 @@ struct irq_desc *__irq_to_desc(int irq);
>>  
>>  void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq);
>>  
>> +#ifdef CONFIG_HAS_ITS
>> +void do_LPI(unsigned int irq);
>> +#else
>> +static inline void do_LPI(unsigned int irq)
>> +{
>> +}
>> +#endif
>> +
>>  #define domain_pirq_to_irq(d, pirq) (pirq)
>>  
>>  bool_t is_assignable_irq(unsigned int irq);
>> -- 
>> 2.9.0
>>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs
  2017-01-05 21:36   ` Stefano Stabellini
@ 2017-01-12 18:24     ` Andre Przywara
  2017-01-12 19:15       ` Stefano Stabellini
  0 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-12 18:24 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi Stefano,

On 05/01/17 21:36, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
>> For the same reason that allocating a struct irq_desc for each
>> possible LPI is not an option, having a struct pending_irq for each LPI
>> is also not feasible. However we actually only need those when an
>> interrupt is on a vCPU (or is about to be injected).
>> Maintain a list of those structs that we can use for the lifecycle of
>> a guest LPI. We allocate new entries if necessary, however reuse
>> pre-owned entries whenever possible.
>> I added some locking around this list here, however my gut feeling is
>> that we don't need one because this a per-VCPU structure anyway.
>> If someone could confirm this, I'd be grateful.
> 
> I don't think the list should be per-VCPU, because the underlying LPIs
> are global. 

But _pending_ IRQs (regardless of their type) are definitely a per-VCPU
thing. I consider struct pending_irq something like an LR precursor or a
software representation of it.
Also the pending bitmap is a per-redistributor table.

The problem is that struct pending_irq is pretty big, 56 Bytes on arm64
if I did the math correctly. So the structs for the 32 private
interrupts per VCPU alone account for 1792 Byte. Actually I tried to add
another list head to it to be able to reuse the structure, but that
broke the build because struct vcpu got bigger than 4K.

So the main reason I went for a dynamic pending_irq approach was that
the potentially big number of LPIs could lead to a lot of memory to be
allocated by Xen. And the ITS architecture does not provides any memory
table (provided by the guest) to be used for storing this information.
Also ...

> Similarly, the struct pending_irq array is per-domain, only
> the first 32 (PPIs) are per vcpu. Besides, it shouldn't be a list :-)

In reality the number of interrupts which are on a VCPU at any given
point in time is expected to be very low, in some previous experiments I
found never more than four. This is even more true for LPIs, which, due
to the lack of an active state, fall out of the system as soon as the
VCPU reads the ICC_IAR register.
So the list will be very short, usually, which made it very appealing to
just go with a list, especially for an RFC.

Also having potentially thousands of those structures lying around
mostly unused doesn't sound very clever to me. Actually I was thinking
about using the same approach for the other interrupts (SPI/PPI/SGI) as
well, but I guess that doesn't give us much, apart from breaking
everything ;-)

But that being said:
If we can prove that the number of LPIs actually is limited (because it
is bounded by the number of devices, which Dom0 controls), I am willing
to investigate if we can switch over to using one struct pending_irq per
LPI.
Or do you want me to just use a more advanced data structure for that?

>> Teach the existing VGIC functions to find the right pointer when being
>> given a virtual LPI number.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> 
> Most of my comments on the previous version of the patch are still
> unaddressed.

Indeed, I just found that your reply wasn't tagged in my mailer, so I
missed it. Sorry about that! Will look at it now.

Cheers,
Andre.

> 
> 
>> ---
>>  xen/arch/arm/gic.c           |  3 +++
>>  xen/arch/arm/vgic-v3.c       | 11 ++++++++
>>  xen/arch/arm/vgic.c          | 64 +++++++++++++++++++++++++++++++++++++++++---
>>  xen/include/asm-arm/domain.h |  2 ++
>>  xen/include/asm-arm/vgic.h   | 10 +++++++
>>  5 files changed, 87 insertions(+), 3 deletions(-)
>>
>> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
>> index a5348f2..6f25501 100644
>> --- a/xen/arch/arm/gic.c
>> +++ b/xen/arch/arm/gic.c
>> @@ -509,6 +509,9 @@ static void gic_update_one_lr(struct vcpu *v, int i)
>>                  struct vcpu *v_target = vgic_get_target_vcpu(v, irq);
>>                  irq_set_affinity(p->desc, cpumask_of(v_target->processor));
>>              }
>> +            /* If this was an LPI, mark this struct as available again. */
>> +            if ( p->irq >= 8192 )
>> +                p->irq = 0;
>>          }
>>      }
>>  }
>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> index d61479d..0ffde74 100644
>> --- a/xen/arch/arm/vgic-v3.c
>> +++ b/xen/arch/arm/vgic-v3.c
>> @@ -331,6 +331,14 @@ read_unknown:
>>      return 1;
>>  }
>>  
>> +int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi)
>> +{
>> +    if ( vlpi >= d->arch.vgic.nr_lpis )
>> +        return GIC_PRI_IRQ;
>> +
>> +    return d->arch.vgic.proptable[vlpi - 8192] & 0xfc;
>> +}
>> +
>>  static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>>                                            uint32_t gicr_reg,
>>                                            register_t r)
>> @@ -1426,6 +1434,9 @@ static int vgic_v3_vcpu_init(struct vcpu *v)
>>      if ( v->vcpu_id == last_cpu || (v->vcpu_id == (d->max_vcpus - 1)) )
>>          v->arch.vgic.flags |= VGIC_V3_RDIST_LAST;
>>  
>> +    spin_lock_init(&v->arch.vgic.pending_lpi_list_lock);
>> +    INIT_LIST_HEAD(&v->arch.vgic.pending_lpi_list);
>> +
>>      return 0;
>>  }
>>  
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index de77aaa..f15eb3e 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -31,6 +31,8 @@
>>  #include <asm/mmio.h>
>>  #include <asm/gic.h>
>>  #include <asm/vgic.h>
>> +#include <asm/gic_v3_defs.h>
>> +#include <asm/gic-its.h>
>>  
>>  static inline struct vgic_irq_rank *vgic_get_rank(struct vcpu *v, int rank)
>>  {
>> @@ -61,7 +63,7 @@ struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq)
>>      return vgic_get_rank(v, rank);
>>  }
>>  
>> -static void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq)
>> +void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq)
>>  {
>>      INIT_LIST_HEAD(&p->inflight);
>>      INIT_LIST_HEAD(&p->lr_queue);
>> @@ -247,10 +249,14 @@ struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq)
>>  
>>  static int vgic_get_virq_priority(struct vcpu *v, unsigned int virq)
>>  {
>> -    struct vgic_irq_rank *rank = vgic_rank_irq(v, virq);
>> +    struct vgic_irq_rank *rank;
>>      unsigned long flags;
>>      int priority;
>>  
>> +    if ( virq >= 8192 )
>> +        return vgic_lpi_get_priority(v->domain, virq);
>> +
>> +    rank = vgic_rank_irq(v, virq);
>>      vgic_lock_rank(v, rank, flags);
>>      priority = rank->priority[virq & INTERRUPT_RANK_MASK];
>>      vgic_unlock_rank(v, rank, flags);
>> @@ -449,13 +455,63 @@ bool vgic_to_sgi(struct vcpu *v, register_t sgir, enum gic_sgi_mode irqmode,
>>      return true;
>>  }
>>  
>> +/*
>> + * Holding struct pending_irq's for each possible virtual LPI in each domain
>> + * requires too much Xen memory, also a malicious guest could potentially
>> + * spam Xen with LPI map requests. We cannot cover those with (guest allocated)
>> + * ITS memory, so we use a dynamic scheme of allocating struct pending_irq's
>> + * on demand.
>> + */
>> +struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int lpi,
>> +                                   bool allocate)
>> +{
>> +    struct lpi_pending_irq *lpi_irq, *empty = NULL;
>> +
>> +    spin_lock(&v->arch.vgic.pending_lpi_list_lock);
>> +    list_for_each_entry(lpi_irq, &v->arch.vgic.pending_lpi_list, entry)
>> +    {
>> +        if ( lpi_irq->pirq.irq == lpi )
>> +        {
>> +            spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
>> +            return &lpi_irq->pirq;
>> +        }
>> +
>> +        if ( lpi_irq->pirq.irq == 0 && !empty )
>> +            empty = lpi_irq;
>> +    }
>> +
>> +    if ( !allocate )
>> +    {
>> +        spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
>> +        return NULL;
>> +    }
>> +
>> +    if ( !empty )
>> +    {
>> +        empty = xzalloc(struct lpi_pending_irq);
>> +        vgic_init_pending_irq(&empty->pirq, lpi);
>> +        list_add_tail(&empty->entry, &v->arch.vgic.pending_lpi_list);
>> +    } else
>> +    {
>> +        empty->pirq.status = 0;
>> +        empty->pirq.irq = lpi;
>> +    }
>> +
>> +    spin_unlock(&v->arch.vgic.pending_lpi_list_lock);
>> +
>> +    return &empty->pirq;
>> +}
>> +
>>  struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq)
>>  {
>>      struct pending_irq *n;
>> +
>>      /* Pending irqs allocation strategy: the first vgic.nr_spis irqs
>>       * are used for SPIs; the rests are used for per cpu irqs */
>>      if ( irq < 32 )
>>          n = &v->arch.vgic.pending_irqs[irq];
>> +    else if ( irq >= 8192 )
>> +        n = lpi_to_pending(v, irq, true);
>>      else
>>          n = &v->domain->arch.vgic.pending_irqs[irq - 32];
>>      return n;
>> @@ -483,7 +539,7 @@ void vgic_clear_pending_irqs(struct vcpu *v)
>>  void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
>>  {
>>      uint8_t priority;
>> -    struct pending_irq *iter, *n = irq_to_pending(v, virq);
>> +    struct pending_irq *iter, *n;
>>      unsigned long flags;
>>      bool running;
>>  
>> @@ -491,6 +547,8 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
>>  
>>      spin_lock_irqsave(&v->arch.vgic.lock, flags);
>>  
>> +    n = irq_to_pending(v, virq);
>> +
>>      /* vcpu offline */
>>      if ( test_bit(_VPF_down, &v->pause_flags) )
>>      {
>> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
>> index 8ccc32a..02ae2cd 100644
>> --- a/xen/include/asm-arm/domain.h
>> +++ b/xen/include/asm-arm/domain.h
>> @@ -256,6 +256,8 @@ struct arch_vcpu
>>          paddr_t rdist_base;
>>  #define VGIC_V3_RDIST_LAST  (1 << 0)        /* last vCPU of the rdist */
>>          uint8_t flags;
>> +        struct list_head pending_lpi_list;
>> +        spinlock_t pending_lpi_list_lock;   /* protects the pending_lpi_list */
>>      } vgic;
>>  
>>      /* Timer registers  */
>> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
>> index 672f649..a503321 100644
>> --- a/xen/include/asm-arm/vgic.h
>> +++ b/xen/include/asm-arm/vgic.h
>> @@ -83,6 +83,12 @@ struct pending_irq
>>      struct list_head lr_queue;
>>  };
>>  
>> +struct lpi_pending_irq
>> +{
>> +    struct list_head entry;
>> +    struct pending_irq pirq;
>> +};
>> +
>>  #define NR_INTERRUPT_PER_RANK   32
>>  #define INTERRUPT_RANK_MASK (NR_INTERRUPT_PER_RANK - 1)
>>  
>> @@ -296,13 +302,17 @@ extern struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq);
>>  extern void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq);
>>  extern void vgic_vcpu_inject_spi(struct domain *d, unsigned int virq);
>>  extern void vgic_clear_pending_irqs(struct vcpu *v);
>> +extern void vgic_init_pending_irq(struct pending_irq *p, unsigned int virq);
>>  extern struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq);
>>  extern struct pending_irq *spi_to_pending(struct domain *d, unsigned int irq);
>> +extern struct pending_irq *lpi_to_pending(struct vcpu *v, unsigned int irq,
>> +                                          bool allocate);
>>  extern struct vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
>>  extern struct vgic_irq_rank *vgic_rank_irq(struct vcpu *v, unsigned int irq);
>>  extern bool vgic_emulate(struct cpu_user_regs *regs, union hsr hsr);
>>  extern void vgic_disable_irqs(struct vcpu *v, uint32_t r, int n);
>>  extern void vgic_enable_irqs(struct vcpu *v, uint32_t r, int n);
>> +extern int vgic_lpi_get_priority(struct domain *d, uint32_t vlpi);
>>  extern void register_vgic_ops(struct domain *d, const struct vgic_ops *ops);
>>  int vgic_v2_init(struct domain *d, int *mmio_count);
>>  int vgic_v3_init(struct domain *d, int *mmio_count);

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests
  2017-01-12 12:16     ` Andre Przywara
@ 2017-01-12 18:57       ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-12 18:57 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 12 Jan 2017, Andre Przywara wrote:
> On 05/01/17 22:10, Stefano Stabellini wrote:
> > On Thu, 22 Dec 2016, Andre Przywara wrote:
> >> Upon receiving an LPI, we need to find the right VCPU and virtual IRQ
> >> number to get this IRQ injected.
> >> Iterate our two-level LPI table to find this information quickly when
> >> the host takes an LPI. Call the existing injection function to let the
> >> GIC emulation deal with this interrupt.
> >>
> >> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> >> ---
> >>  xen/arch/arm/gic-its.c    | 35 +++++++++++++++++++++++++++++++++++
> >>  xen/arch/arm/gic.c        |  6 ++++--
> >>  xen/include/asm-arm/irq.h |  8 ++++++++
> >>  3 files changed, 47 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> >> index e7ddd90..0d4ca1b 100644
> >> --- a/xen/arch/arm/gic-its.c
> >> +++ b/xen/arch/arm/gic-its.c
> >> @@ -72,6 +72,41 @@ static union host_lpi *gic_get_host_lpi(uint32_t plpi)
> >>      return &lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE][plpi % HOST_LPIS_PER_PAGE];
> >>  }
> >>  
> >> +/* Handle incoming LPIs, which are a bit special, because they are potentially
> >> + * numerous and also only get injected into guests. Treat them specially here,
> >> + * by just looking up their target vCPU and virtual LPI number and hand it
> >> + * over to the injection function.
> >> + */
> >> +void do_LPI(unsigned int lpi)
> >> +{
> >> +    struct domain *d;
> >> +    union host_lpi *hlpip, hlpi;
> >> +    struct vcpu *vcpu;
> >> +
> >> +    WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
> >> +
> >> +    hlpip = gic_get_host_lpi(lpi);
> >> +    if ( !hlpip )
> >> +        return;
> >> +
> >> +    hlpi.data = hlpip->data;
> > 
> > Why can't we just reference hlpip directly throughout this function? Is
> > it for atomicity reasons?
> 
> Yes. We have to make sure that the LPI entry is consistent, but we don't
> want to (and probably can't) take a lock here and everywhere else where
> we touch it. We are fine with reading an "outdated" entry (which is just
> about to change), this is a benign race which can happen on real
> hardware as well.

In that case, it's best to use atomic functions.


> >> +    /* We may have mapped more host LPIs than the guest actually asked for. */
> >> +    if ( !hlpi.virt_lpi )
> >> +        return;
> >> +
> >> +    d = get_domain_by_id(hlpi.dom_id);
> >> +    if ( !d )
> >> +        return;
> >> +
> >> +    if ( hlpi.vcpu_id >= d->max_vcpus )
> 
> I am just seeing that I miss a put_domain(d) here and ....
> 
> >> +        return;
> >> +
> >> +    vcpu = d->vcpu[hlpi.vcpu_id];
> 
> ... here.

Oops, you are right.


> Which makes me wonder if it is legal to use a VCPU reference even though
> I "put back" the domain pointer?

I don't think so.


> Is there a get_vcpu()/put_vcpu() equivalent? Or is this supposed to
> covered by the domain pointer as well?
> 
> Or shall I use get_domain() the moment I enter the domain ID into the
> host LPI array and only "put" it when an entry gets changed or the LPI
> gets somehow else invalid (VCPU destroyed, domain destroyed)?

The pattern is to get_domain at the beginning of the implementation of
the function in the hypervisor and put_domain at the end of it. In this
case, I think you should replace the returns in this function with goto
out, and have an out label with put_domain.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs
  2017-01-12 18:24     ` Andre Przywara
@ 2017-01-12 19:15       ` Stefano Stabellini
  2017-01-12 19:28         ` Andre Przywara
  0 siblings, 1 reply; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-12 19:15 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 12 Jan 2017, Andre Przywara wrote:
> Hi Stefano,
> 
> On 05/01/17 21:36, Stefano Stabellini wrote:
> > On Thu, 22 Dec 2016, Andre Przywara wrote:
> >> For the same reason that allocating a struct irq_desc for each
> >> possible LPI is not an option, having a struct pending_irq for each LPI
> >> is also not feasible. However we actually only need those when an
> >> interrupt is on a vCPU (or is about to be injected).
> >> Maintain a list of those structs that we can use for the lifecycle of
> >> a guest LPI. We allocate new entries if necessary, however reuse
> >> pre-owned entries whenever possible.
> >> I added some locking around this list here, however my gut feeling is
> >> that we don't need one because this a per-VCPU structure anyway.
> >> If someone could confirm this, I'd be grateful.
> > 
> > I don't think the list should be per-VCPU, because the underlying LPIs
> > are global. 
> 
> But _pending_ IRQs (regardless of their type) are definitely a per-VCPU
> thing. I consider struct pending_irq something like an LR precursor or a
> software representation of it.
> Also the pending bitmap is a per-redistributor table.
> 
> The problem is that struct pending_irq is pretty big, 56 Bytes on arm64
> if I did the math correctly. So the structs for the 32 private
> interrupts per VCPU alone account for 1792 Byte. Actually I tried to add
> another list head to it to be able to reuse the structure, but that
> broke the build because struct vcpu got bigger than 4K.
> 
> So the main reason I went for a dynamic pending_irq approach was that
> the potentially big number of LPIs could lead to a lot of memory to be
> allocated by Xen. And the ITS architecture does not provides any memory
> table (provided by the guest) to be used for storing this information.
> Also ...

Dynamic pending_irqs are good, but why one list per vcpu, rather than
one list per domain, given that in our current design they can only be
targeting one vcpu at any given time?


> > Similarly, the struct pending_irq array is per-domain, only
> > the first 32 (PPIs) are per vcpu. Besides, it shouldn't be a list :-)
> 
> In reality the number of interrupts which are on a VCPU at any given
> point in time is expected to be very low, in some previous experiments I
> found never more than four. This is even more true for LPIs, which, due
> to the lack of an active state, fall out of the system as soon as the
> VCPU reads the ICC_IAR register.
> So the list will be very short, usually, which made it very appealing to
> just go with a list, especially for an RFC.
> 
> Also having potentially thousands of those structures lying around
> mostly unused doesn't sound very clever to me. Actually I was thinking
> about using the same approach for the other interrupts (SPI/PPI/SGI) as
> well, but I guess that doesn't give us much, apart from breaking
> everything ;-)
> 
> But that being said:
> If we can prove that the number of LPIs actually is limited (because it
> is bounded by the number of devices, which Dom0 controls), I am willing
> to investigate if we can switch over to using one struct pending_irq per
> LPI.
> Or do you want me to just use a more advanced data structure for that?

I think we misunderstood each other. I am definitely not suggesting to
have one struct pending_irq per LPI. Your idea of dynamically allocating
them is good.

The things I am concerned about are:

1) the choice of a list as a data structure, instead of an hashtable or
   a tree (see alpine.DEB.2.10.1610271725280.9978@sstabellini-ThinkPad-X260)
2) the choice of having one data structure (list or whatever) per vcpu,
   rather than one per domain

In both cases, it's not a matter of opinion but a matter of numbers and
performance. I would like to see some numbers to prove our choices right
or wrong.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs
  2017-01-12 19:15       ` Stefano Stabellini
@ 2017-01-12 19:28         ` Andre Przywara
  0 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2017-01-12 19:28 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi,

On 12/01/17 19:15, Stefano Stabellini wrote:
> On Thu, 12 Jan 2017, Andre Przywara wrote:
>> Hi Stefano,
>>
>> On 05/01/17 21:36, Stefano Stabellini wrote:
>>> On Thu, 22 Dec 2016, Andre Przywara wrote:
>>>> For the same reason that allocating a struct irq_desc for each
>>>> possible LPI is not an option, having a struct pending_irq for each LPI
>>>> is also not feasible. However we actually only need those when an
>>>> interrupt is on a vCPU (or is about to be injected).
>>>> Maintain a list of those structs that we can use for the lifecycle of
>>>> a guest LPI. We allocate new entries if necessary, however reuse
>>>> pre-owned entries whenever possible.
>>>> I added some locking around this list here, however my gut feeling is
>>>> that we don't need one because this a per-VCPU structure anyway.
>>>> If someone could confirm this, I'd be grateful.
>>>
>>> I don't think the list should be per-VCPU, because the underlying LPIs
>>> are global. 
>>
>> But _pending_ IRQs (regardless of their type) are definitely a per-VCPU
>> thing. I consider struct pending_irq something like an LR precursor or a
>> software representation of it.
>> Also the pending bitmap is a per-redistributor table.
>>
>> The problem is that struct pending_irq is pretty big, 56 Bytes on arm64
>> if I did the math correctly. So the structs for the 32 private
>> interrupts per VCPU alone account for 1792 Byte. Actually I tried to add
>> another list head to it to be able to reuse the structure, but that
>> broke the build because struct vcpu got bigger than 4K.
>>
>> So the main reason I went for a dynamic pending_irq approach was that
>> the potentially big number of LPIs could lead to a lot of memory to be
>> allocated by Xen. And the ITS architecture does not provides any memory
>> table (provided by the guest) to be used for storing this information.
>> Also ...
> 
> Dynamic pending_irqs are good, but why one list per vcpu, rather than
> one list per domain, given that in our current design they can only be
> targeting one vcpu at any given time?

I believe the specs demands that: one LPI can only be pending at one
redistributor for any given point in time, but I will ask Marc tomorrow.
I believe it isn't spelled out in the spec, but can be deducted.
But as the pending table is per-redistributor, I was assuming that
modelling this per VCPU is the right thing.
I will think about it, your rationale of the LPIs being global makes
some sense ...

> 
>>> Similarly, the struct pending_irq array is per-domain, only
>>> the first 32 (PPIs) are per vcpu. Besides, it shouldn't be a list :-)
>>
>> In reality the number of interrupts which are on a VCPU at any given
>> point in time is expected to be very low, in some previous experiments I
>> found never more than four. This is even more true for LPIs, which, due
>> to the lack of an active state, fall out of the system as soon as the
>> VCPU reads the ICC_IAR register.
>> So the list will be very short, usually, which made it very appealing to
>> just go with a list, especially for an RFC.
>>
>> Also having potentially thousands of those structures lying around
>> mostly unused doesn't sound very clever to me. Actually I was thinking
>> about using the same approach for the other interrupts (SPI/PPI/SGI) as
>> well, but I guess that doesn't give us much, apart from breaking
>> everything ;-)
>>
>> But that being said:
>> If we can prove that the number of LPIs actually is limited (because it
>> is bounded by the number of devices, which Dom0 controls), I am willing
>> to investigate if we can switch over to using one struct pending_irq per
>> LPI.
>> Or do you want me to just use a more advanced data structure for that?
> 
> I think we misunderstood each other. I am definitely not suggesting to
> have one struct pending_irq per LPI. Your idea of dynamically allocating
> them is good.

Sorry for the misunderstanding - I am relieved that I don't have to
change that ;-)

> The things I am concerned about are:
> 
> 1) the choice of a list as a data structure, instead of an hashtable or
>    a tree (see alpine.DEB.2.10.1610271725280.9978@sstabellini-ThinkPad-X260)
> 2) the choice of having one data structure (list or whatever) per vcpu,
>    rather than one per domain
> 
> In both cases, it's not a matter of opinion but a matter of numbers and
> performance. I would like to see some numbers to prove our choices right
> or wrong.

Got it. I will see what I can do. In the moment my
number-and-performance gathering capabilities are severely limited due
to me running everything on the model.

Cheers,
Andre.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping
  2017-01-05  0:13   ` Stefano Stabellini
@ 2017-01-13 12:31     ` Andre Przywara
  2017-01-13 19:22       ` Stefano Stabellini
  2017-01-13 12:31     ` Andre Przywara
  1 sibling, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-13 12:31 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi Stefano,

On 05/01/17 00:13, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
>> The ITS uses device IDs to map LPIs to a device. Dom0 will later use
>> those IDs, which we directly pass on to the host.
>> For this we have to map each device that Dom0 may request to a host
>> ITS device with the same identifier.
>> Allocate the respective memory and enter each device into a list to
>> later be able to iterate over it or to easily teardown guests.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-its.c        | 118 ++++++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/vgic.c           |   3 ++
>>  xen/include/asm-arm/domain.h  |   2 +
>>  xen/include/asm-arm/gic-its.h |  22 ++++++++
>>  4 files changed, 145 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
>> index d0f5fd1..e157c6b 100644
>> --- a/xen/arch/arm/gic-its.c
>> +++ b/xen/arch/arm/gic-its.c
>> @@ -21,6 +21,7 @@
>>  #include <xen/err.h>
>>  #include <xen/device_tree.h>
>>  #include <xen/libfdt/libfdt.h>
>> +#include <xen/sched.h>
>>  #include <xen/sizes.h>
>>  #include <asm/p2m.h>
>>  #include <asm/io.h>
>> @@ -108,6 +109,21 @@ static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
>>      return its_send_command(its, cmd);
>>  }
>>
>> +static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
>> +                             int size, uint64_t itt_addr, bool valid)
>> +{
>> +    uint64_t cmd[4];
>> +
>> +    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
>> +    cmd[1] = size & GENMASK(4, 0);
>> +    cmd[2] = itt_addr & GENMASK(51, 8);
>> +    if ( valid )
>> +        cmd[2] |= BIT_ULL(63);
>> +    cmd[3] = 0x00;
>> +
>> +    return its_send_command(its, cmd);
>> +}
>> +
>>  /* Set up the (1:1) collection mapping for the given host CPU. */
>>  void gicv3_its_setup_collection(int cpu)
>>  {
>> @@ -237,6 +253,7 @@ int gicv3_its_init(struct host_its *hw_its)
>>
>>      reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
>>      hw_its->pta = !!(reg & GITS_TYPER_PTA);
>> +    hw_its->itte_size = ((reg >> 4) & 0xf) + 1;
>
> Please #define all numbers
>
>
>>      for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
>>      {
>> @@ -358,6 +375,107 @@ int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
>>      return 0;
>>  }
>>
>> +static void remove_mapped_guest_device(struct its_devices *dev)
>> +{
>> +    if ( dev->hw_its )
>> +        its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
>> +
>> +    xfree(dev->itt_addr);
>> +    xfree(dev);
>> +}
>> +
>> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>> +                         int bits, bool valid)
>> +{
>> +    void *itt_addr = NULL;
>> +    struct its_devices *dev, *temp;
>> +    struct host_its *hw_its;
>> +    int ret;
>> +
>> +    /* check for already existing mappings */
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
>> +    {
>> +        if ( dev->guest_devid != guest_devid )
>> +            continue;
>> +
>> +        if ( !valid )
>> +            list_del(&dev->entry);
>> +
>> +        spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +        if ( valid )
>> +            return -EBUSY;
>> +
>> +        remove_mapped_guest_device(dev);
>> +
>> +        return 0;
>> +    }
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>
> Compared to the previous version, now the list is per-domain, which is
> better for DomUs, but not for Dom0. In the case of Dom0 the number of
> iterations will be the same.
>
> I suggest we move to a different data structure, such as an hashtable or
> an rbtree. If devids were guaranteed to be dense, then we could store
> struct its_devices pointers in an array and direct access them, but I
> don't think they are?

They are indeed quite sparse, as they effectively are PCI IDs.
But AFAICS this list is never iterated in a hot path. There are three
users of the list:
- gicv3_its_map_device(), which is only called by the physdev_ops
handler, so only once at Dom0 boot, basically.
- its_remove_domain(), which has no caller at the moment and is just
here for the sake of completeness and to make sure we remove anything
that we created. But it isn't performance critical at all.
- find_guest_lpi(), which is called by gicv3_assign_guest_event() and
gicv3_lpi_change_vcpu(). Those are called by the MAPTI, DISCARD and MOVI
virtual ITS command handlers, but those commands are expected to be rare.

So going away from a list is not strictly necessary, IMHO.
But frankly I just chose a list because I know how it works by heart. I
guess I have to check how easy rbtrees or hash tables are to use in Xen,
as it sounds indeed sensible to use one of them (probably an rbtree)
instead.

> If devids are sparse, using a per-domain structure could be a good idea,
> but we still need to prevent a device from being assigned to two domains
> simultaneously. Is there a check to avoid that?

Not at the moment, and I wouldn't expect it to be the task of the ITS
emulation to take care of that. That sounds like something for the PCI
passthrough subsystem to check. This function here will eventually get
called by that layer once it has sanitized the request and prepares the
device for triggering MSIs.

For this series there is only one domain to consider (Dom0) and we check
the per-domain device list to avoid double assignment, so this is safe.

Cheers,
Andre.

>> +    if ( !valid )
>> +        return -ENOENT;
>> +
>> +    /* TODO: Work out the correct hardware ITS to use here.
>> +     * Maybe a per-platform function: devid -> ITS?
>> +     * Or parsing the DT to find the msi_parent?
>> +     * Or get Dom0 to give us this information?
>> +     * For now just use the first ITS.
>> +     */
>> +    hw_its = list_first_entry(&host_its_list, struct host_its, entry);
>> +
>> +    itt_addr = _xmalloc(BIT(bits) * hw_its->itte_size, 256);
>> +    if ( !itt_addr )
>> +        return -ENOMEM;
>> +
>> +    dev = xmalloc(struct its_devices);
>> +    if ( !dev )
>> +    {
>> +        xfree(itt_addr);
>> +        return -ENOMEM;
>> +    }
>> +
>> +    ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
>> +                            virt_to_maddr(itt_addr), true);
>> +    if (ret) {
>> +        xfree(itt_addr);
>> +        xfree(dev);
>> +        return ret;
>> +    }
>> +
>> +    dev->itt_addr = itt_addr;
>> +    dev->hw_its = hw_its;
>> +    dev->guest_devid = guest_devid;
>> +    dev->host_devid = host_devid;
>> +    dev->eventids = BIT(bits);
>> +
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +    return 0;
>> +}
>> +
>> +/* Removing any connections a domain had to any ITS in the system. */
>> +int its_remove_domain(struct domain *d)
>> +{
>> +    struct its_devices *dev, *temp;
>> +
>> +retry:
>> +
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
>> +    {
>> +        list_del(&dev->entry);
>> +        spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +        remove_mapped_guest_device(dev);
>> +        goto retry;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>  /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
>>  void gicv3_its_dt_init(const struct dt_device_node *node)
>>  {
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index 364d5f0..de77aaa 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -156,6 +156,9 @@ int domain_vgic_init(struct domain *d, unsigned int nr_spis)
>>      for ( i = 0; i < NR_GIC_SGI; i++ )
>>          set_bit(i, d->arch.vgic.allocated_irqs);
>>
>> +    spin_lock_init(&d->arch.vgic.its_devices_lock);
>> +    INIT_LIST_HEAD(&d->arch.vgic.its_devices);
>> +
>>      return 0;
>>  }
>>
>> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
>> index 2d6fbb1..8ccc32a 100644
>> --- a/xen/include/asm-arm/domain.h
>> +++ b/xen/include/asm-arm/domain.h
>> @@ -109,6 +109,8 @@ struct arch_domain
>>          } *rdist_regions;
>>          int nr_regions;                     /* Number of rdist regions */
>>          uint32_t rdist_stride;              /* Re-Distributor stride */
>> +        struct list_head its_devices;       /* devices mapped to an ITS */
>> +        spinlock_t its_devices_lock;        /* protects the its_devices list */
>>  #endif
>>      } vgic;
>>
>> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
>> index 68e5f63..525a29d 100644
>> --- a/xen/include/asm-arm/gic-its.h
>> +++ b/xen/include/asm-arm/gic-its.h
>> @@ -93,9 +93,19 @@ struct host_its {
>>      void __iomem *its_base;
>>      spinlock_t cmd_lock;
>>      void *cmd_buf;
>> +    int itte_size;
>>      bool pta;
>>  };
>>
>> +struct its_devices {
>> +    struct list_head entry;
>> +    struct host_its *hw_its;
>> +    void *itt_addr;
>> +    uint32_t guest_devid;
>> +    uint32_t host_devid;
>> +    uint32_t eventids;
>> +};
>> +
>>  extern struct list_head host_its_list;
>>
>>  #ifdef CONFIG_HAS_ITS
>> @@ -119,6 +129,13 @@ void gicv3_set_redist_addr(paddr_t address, int redist_id);
>>  /* Map a collection for this host CPU to each host ITS. */
>>  void gicv3_its_setup_collection(int cpu);
>>
>> +/* Map a device on the host by allocating an ITT on the host (ITS).
>> + * "bits" specifies how many events (interrupts) this device will need.
>> + * Setting "valid" to false deallocates the device.
>> + */
>> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>> +                         int bits, bool valid);
>> +
>>  #else
>>
>>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
>> @@ -146,6 +163,11 @@ static inline void gicv3_set_redist_addr(paddr_t address, int redist_id)
>>  static inline void gicv3_its_setup_collection(int cpu)
>>  {
>>  }
>> +static inline int gicv3_its_map_device(struct domain *d, int host_devid,
>> +                         int guest_devid, int bits, bool valid)
>> +{
>> +    return -ENODEV;
>> +}
>>
>>  #endif /* CONFIG_HAS_ITS */
>>
>> --
>> 2.9.0
>>
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping
  2017-01-05  0:13   ` Stefano Stabellini
  2017-01-13 12:31     ` Andre Przywara
@ 2017-01-13 12:31     ` Andre Przywara
  1 sibling, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2017-01-13 12:31 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, Vijay Kilari

Hi Stefano,

On 05/01/17 00:13, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
>> The ITS uses device IDs to map LPIs to a device. Dom0 will later use
>> those IDs, which we directly pass on to the host.
>> For this we have to map each device that Dom0 may request to a host
>> ITS device with the same identifier.
>> Allocate the respective memory and enter each device into a list to
>> later be able to iterate over it or to easily teardown guests.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-its.c        | 118 ++++++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/vgic.c           |   3 ++
>>  xen/include/asm-arm/domain.h  |   2 +
>>  xen/include/asm-arm/gic-its.h |  22 ++++++++
>>  4 files changed, 145 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
>> index d0f5fd1..e157c6b 100644
>> --- a/xen/arch/arm/gic-its.c
>> +++ b/xen/arch/arm/gic-its.c
>> @@ -21,6 +21,7 @@
>>  #include <xen/err.h>
>>  #include <xen/device_tree.h>
>>  #include <xen/libfdt/libfdt.h>
>> +#include <xen/sched.h>
>>  #include <xen/sizes.h>
>>  #include <asm/p2m.h>
>>  #include <asm/io.h>
>> @@ -108,6 +109,21 @@ static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
>>      return its_send_command(its, cmd);
>>  }
>>  
>> +static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
>> +                             int size, uint64_t itt_addr, bool valid)
>> +{
>> +    uint64_t cmd[4];
>> +
>> +    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
>> +    cmd[1] = size & GENMASK(4, 0);
>> +    cmd[2] = itt_addr & GENMASK(51, 8);
>> +    if ( valid )
>> +        cmd[2] |= BIT_ULL(63);
>> +    cmd[3] = 0x00;
>> +
>> +    return its_send_command(its, cmd);
>> +}
>> +
>>  /* Set up the (1:1) collection mapping for the given host CPU. */
>>  void gicv3_its_setup_collection(int cpu)
>>  {
>> @@ -237,6 +253,7 @@ int gicv3_its_init(struct host_its *hw_its)
>>  
>>      reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
>>      hw_its->pta = !!(reg & GITS_TYPER_PTA);
>> +    hw_its->itte_size = ((reg >> 4) & 0xf) + 1;
> 
> Please #define all numbers
> 
> 
>>      for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
>>      {
>> @@ -358,6 +375,107 @@ int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
>>      return 0;
>>  }
>>  
>> +static void remove_mapped_guest_device(struct its_devices *dev)
>> +{
>> +    if ( dev->hw_its )
>> +        its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
>> +
>> +    xfree(dev->itt_addr);
>> +    xfree(dev);
>> +}
>> +
>> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>> +                         int bits, bool valid)
>> +{
>> +    void *itt_addr = NULL;
>> +    struct its_devices *dev, *temp;
>> +    struct host_its *hw_its;
>> +    int ret;
>> +
>> +    /* check for already existing mappings */
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
>> +    {
>> +        if ( dev->guest_devid != guest_devid )
>> +            continue;
>> +
>> +        if ( !valid )
>> +            list_del(&dev->entry);
>> +
>> +        spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +        if ( valid )
>> +            return -EBUSY;
>> +
>> +        remove_mapped_guest_device(dev);
>> +
>> +        return 0;
>> +    }
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> 
> Compared to the previous version, now the list is per-domain, which is
> better for DomUs, but not for Dom0. In the case of Dom0 the number of
> iterations will be the same.
> 
> I suggest we move to a different data structure, such as an hashtable or
> an rbtree. If devids were guaranteed to be dense, then we could store
> struct its_devices pointers in an array and direct access them, but I
> don't think they are?

They are indeed quite sparse, as they effectively are PCI IDs.
But AFAICS this list is never iterated in a hot path. There are three
users of the list:
- gicv3_its_map_device(), which is only called by the physdev_ops
handler, so only once at Dom0 boot, basically.
- its_remove_domain(), which has no caller at the moment and is just
here for the sake of completeness and to make sure we remove anything
that we created. But it isn't performance critical at all.
- find_guest_lpi(), which is called by gicv3_assign_guest_event() and
gicv3_lpi_change_vcpu(). Those are called by the MAPTI, DISCARD and MOVI
virtual ITS command handlers, but those commands are expected to be rare.

So going away from a list is not strictly necessary, IMHO.
But frankly I just chose a list because I know how it works by heart. I
guess I have to check how easy rbtrees or hash tables are to use in Xen,
as it sounds indeed sensible to use one of them (probably an rbtree)
instead.

> If devids are sparse, using a per-domain structure could be a good idea,
> but we still need to prevent a device from being assigned to two domains
> simultaneously. Is there a check to avoid that?

Not at the moment, and I wouldn't expect it to be the task of the ITS
emulation to take care of that. That sounds like something for the PCI
passthrough subsystem to check. This function here will eventually get
called by that layer once it has sanitized the request and prepares the
device for triggering MSIs.

For this series there is only one domain to consider (Dom0) and we check
the per-domain device list to avoid double assignment, so this is safe.

Cheers,
Andre.

>> +    if ( !valid )
>> +        return -ENOENT;
>> +
>> +    /* TODO: Work out the correct hardware ITS to use here.
>> +     * Maybe a per-platform function: devid -> ITS?
>> +     * Or parsing the DT to find the msi_parent?
>> +     * Or get Dom0 to give us this information?
>> +     * For now just use the first ITS.
>> +     */
>> +    hw_its = list_first_entry(&host_its_list, struct host_its, entry);
>> +
>> +    itt_addr = _xmalloc(BIT(bits) * hw_its->itte_size, 256);
>> +    if ( !itt_addr )
>> +        return -ENOMEM;
>> +
>> +    dev = xmalloc(struct its_devices);
>> +    if ( !dev )
>> +    {
>> +        xfree(itt_addr);
>> +        return -ENOMEM;
>> +    }
>> +
>> +    ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
>> +                            virt_to_maddr(itt_addr), true);
>> +    if (ret) {
>> +        xfree(itt_addr);
>> +        xfree(dev);
>> +        return ret;
>> +    }
>> +
>> +    dev->itt_addr = itt_addr;
>> +    dev->hw_its = hw_its;
>> +    dev->guest_devid = guest_devid;
>> +    dev->host_devid = host_devid;
>> +    dev->eventids = BIT(bits);
>> +
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +    return 0;
>> +}
>> +
>> +/* Removing any connections a domain had to any ITS in the system. */
>> +int its_remove_domain(struct domain *d)
>> +{
>> +    struct its_devices *dev, *temp;
>> +
>> +retry:
>> +
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
>> +    {
>> +        list_del(&dev->entry);
>> +        spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +        remove_mapped_guest_device(dev);
>> +        goto retry;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>  /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
>>  void gicv3_its_dt_init(const struct dt_device_node *node)
>>  {
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index 364d5f0..de77aaa 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -156,6 +156,9 @@ int domain_vgic_init(struct domain *d, unsigned int nr_spis)
>>      for ( i = 0; i < NR_GIC_SGI; i++ )
>>          set_bit(i, d->arch.vgic.allocated_irqs);
>>  
>> +    spin_lock_init(&d->arch.vgic.its_devices_lock);
>> +    INIT_LIST_HEAD(&d->arch.vgic.its_devices);
>> +
>>      return 0;
>>  }
>>  
>> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
>> index 2d6fbb1..8ccc32a 100644
>> --- a/xen/include/asm-arm/domain.h
>> +++ b/xen/include/asm-arm/domain.h
>> @@ -109,6 +109,8 @@ struct arch_domain
>>          } *rdist_regions;
>>          int nr_regions;                     /* Number of rdist regions */
>>          uint32_t rdist_stride;              /* Re-Distributor stride */
>> +        struct list_head its_devices;       /* devices mapped to an ITS */
>> +        spinlock_t its_devices_lock;        /* protects the its_devices list */
>>  #endif
>>      } vgic;
>>  
>> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
>> index 68e5f63..525a29d 100644
>> --- a/xen/include/asm-arm/gic-its.h
>> +++ b/xen/include/asm-arm/gic-its.h
>> @@ -93,9 +93,19 @@ struct host_its {
>>      void __iomem *its_base;
>>      spinlock_t cmd_lock;
>>      void *cmd_buf;
>> +    int itte_size;
>>      bool pta;
>>  };
>>  
>> +struct its_devices {
>> +    struct list_head entry;
>> +    struct host_its *hw_its;
>> +    void *itt_addr;
>> +    uint32_t guest_devid;
>> +    uint32_t host_devid;
>> +    uint32_t eventids;
>> +};
>> +
>>  extern struct list_head host_its_list;
>>  
>>  #ifdef CONFIG_HAS_ITS
>> @@ -119,6 +129,13 @@ void gicv3_set_redist_addr(paddr_t address, int redist_id);
>>  /* Map a collection for this host CPU to each host ITS. */
>>  void gicv3_its_setup_collection(int cpu);
>>  
>> +/* Map a device on the host by allocating an ITT on the host (ITS).
>> + * "bits" specifies how many events (interrupts) this device will need.
>> + * Setting "valid" to false deallocates the device.
>> + */
>> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
>> +                         int bits, bool valid);
>> +
>>  #else
>>  
>>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
>> @@ -146,6 +163,11 @@ static inline void gicv3_set_redist_addr(paddr_t address, int redist_id)
>>  static inline void gicv3_its_setup_collection(int cpu)
>>  {
>>  }
>> +static inline int gicv3_its_map_device(struct domain *d, int host_devid,
>> +                         int guest_devid, int bits, bool valid)
>> +{
>> +    return -ENODEV;
>> +}
>>  
>>  #endif /* CONFIG_HAS_ITS */
>>  
>> -- 
>> 2.9.0
>>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping
  2017-01-13 12:31     ` Andre Przywara
@ 2017-01-13 19:22       ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-13 19:22 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Fri, 13 Jan 2017, Andre Przywara wrote:
> Hi Stefano,
> 
> On 05/01/17 00:13, Stefano Stabellini wrote:
> > On Thu, 22 Dec 2016, Andre Przywara wrote:
> >> The ITS uses device IDs to map LPIs to a device. Dom0 will later use
> >> those IDs, which we directly pass on to the host.
> >> For this we have to map each device that Dom0 may request to a host
> >> ITS device with the same identifier.
> >> Allocate the respective memory and enter each device into a list to
> >> later be able to iterate over it or to easily teardown guests.
> >>
> >> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> >> ---
> >>  xen/arch/arm/gic-its.c        | 118 ++++++++++++++++++++++++++++++++++++++++++
> >>  xen/arch/arm/vgic.c           |   3 ++
> >>  xen/include/asm-arm/domain.h  |   2 +
> >>  xen/include/asm-arm/gic-its.h |  22 ++++++++
> >>  4 files changed, 145 insertions(+)
> >>
> >> diff --git a/xen/arch/arm/gic-its.c b/xen/arch/arm/gic-its.c
> >> index d0f5fd1..e157c6b 100644
> >> --- a/xen/arch/arm/gic-its.c
> >> +++ b/xen/arch/arm/gic-its.c
> >> @@ -21,6 +21,7 @@
> >>  #include <xen/err.h>
> >>  #include <xen/device_tree.h>
> >>  #include <xen/libfdt/libfdt.h>
> >> +#include <xen/sched.h>
> >>  #include <xen/sizes.h>
> >>  #include <asm/p2m.h>
> >>  #include <asm/io.h>
> >> @@ -108,6 +109,21 @@ static int its_send_cmd_mapc(struct host_its *its, int collection_id, int cpu)
> >>      return its_send_command(its, cmd);
> >>  }
> >>
> >> +static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
> >> +                             int size, uint64_t itt_addr, bool valid)
> >> +{
> >> +    uint64_t cmd[4];
> >> +
> >> +    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
> >> +    cmd[1] = size & GENMASK(4, 0);
> >> +    cmd[2] = itt_addr & GENMASK(51, 8);
> >> +    if ( valid )
> >> +        cmd[2] |= BIT_ULL(63);
> >> +    cmd[3] = 0x00;
> >> +
> >> +    return its_send_command(its, cmd);
> >> +}
> >> +
> >>  /* Set up the (1:1) collection mapping for the given host CPU. */
> >>  void gicv3_its_setup_collection(int cpu)
> >>  {
> >> @@ -237,6 +253,7 @@ int gicv3_its_init(struct host_its *hw_its)
> >>
> >>      reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
> >>      hw_its->pta = !!(reg & GITS_TYPER_PTA);
> >> +    hw_its->itte_size = ((reg >> 4) & 0xf) + 1;
> >
> > Please #define all numbers
> >
> >
> >>      for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
> >>      {
> >> @@ -358,6 +375,107 @@ int gicv3_lpi_init_host_lpis(unsigned int hw_lpi_bits)
> >>      return 0;
> >>  }
> >>
> >> +static void remove_mapped_guest_device(struct its_devices *dev)
> >> +{
> >> +    if ( dev->hw_its )
> >> +        its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
> >> +
> >> +    xfree(dev->itt_addr);
> >> +    xfree(dev);
> >> +}
> >> +
> >> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
> >> +                         int bits, bool valid)
> >> +{
> >> +    void *itt_addr = NULL;
> >> +    struct its_devices *dev, *temp;
> >> +    struct host_its *hw_its;
> >> +    int ret;
> >> +
> >> +    /* check for already existing mappings */
> >> +    spin_lock(&d->arch.vgic.its_devices_lock);
> >> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
> >> +    {
> >> +        if ( dev->guest_devid != guest_devid )
> >> +            continue;
> >> +
> >> +        if ( !valid )
> >> +            list_del(&dev->entry);
> >> +
> >> +        spin_unlock(&d->arch.vgic.its_devices_lock);
> >> +
> >> +        if ( valid )
> >> +            return -EBUSY;
> >> +
> >> +        remove_mapped_guest_device(dev);
> >> +
> >> +        return 0;
> >> +    }
> >> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> >
> > Compared to the previous version, now the list is per-domain, which is
> > better for DomUs, but not for Dom0. In the case of Dom0 the number of
> > iterations will be the same.
> >
> > I suggest we move to a different data structure, such as an hashtable or
> > an rbtree. If devids were guaranteed to be dense, then we could store
> > struct its_devices pointers in an array and direct access them, but I
> > don't think they are?
> 
> They are indeed quite sparse, as they effectively are PCI IDs.
> But AFAICS this list is never iterated in a hot path. There are three
> users of the list:
> - gicv3_its_map_device(), which is only called by the physdev_ops
> handler, so only once at Dom0 boot, basically.
> - its_remove_domain(), which has no caller at the moment and is just
> here for the sake of completeness and to make sure we remove anything
> that we created. But it isn't performance critical at all.
> - find_guest_lpi(), which is called by gicv3_assign_guest_event() and
> gicv3_lpi_change_vcpu(). Those are called by the MAPTI, DISCARD and MOVI
> virtual ITS command handlers, but those commands are expected to be rare.
> 
> So going away from a list is not strictly necessary, IMHO.
> But frankly I just chose a list because I know how it works by heart. I
> guess I have to check how easy rbtrees or hash tables are to use in Xen,
> as it sounds indeed sensible to use one of them (probably an rbtree)
> instead.

We do have rbtrees.


> > If devids are sparse, using a per-domain structure could be a good idea,
> > but we still need to prevent a device from being assigned to two domains
> > simultaneously. Is there a check to avoid that?
> 
> Not at the moment, and I wouldn't expect it to be the task of the ITS
> emulation to take care of that. That sounds like something for the PCI
> passthrough subsystem to check. This function here will eventually get
> called by that layer once it has sanitized the request and prepares the
> device for triggering MSIs.
> 
> For this series there is only one domain to consider (Dom0) and we check
> the per-domain device list to avoid double assignment, so this is safe.

If we expect the next engineer, the one that is going to work on
enabling and testing ITS support for DomUs, to add a check for this or
make sure that such a check already exists, we need an /* XXX in the
code somewhere.


> >> +    if ( !valid )
> >> +        return -ENOENT;
> >> +
> >> +    /* TODO: Work out the correct hardware ITS to use here.
> >> +     * Maybe a per-platform function: devid -> ITS?
> >> +     * Or parsing the DT to find the msi_parent?
> >> +     * Or get Dom0 to give us this information?
> >> +     * For now just use the first ITS.
> >> +     */
> >> +    hw_its = list_first_entry(&host_its_list, struct host_its, entry);
> >> +
> >> +    itt_addr = _xmalloc(BIT(bits) * hw_its->itte_size, 256);
> >> +    if ( !itt_addr )
> >> +        return -ENOMEM;
> >> +
> >> +    dev = xmalloc(struct its_devices);
> >> +    if ( !dev )
> >> +    {
> >> +        xfree(itt_addr);
> >> +        return -ENOMEM;
> >> +    }
> >> +
> >> +    ret = its_send_cmd_mapd(hw_its, host_devid, bits - 1,
> >> +                            virt_to_maddr(itt_addr), true);
> >> +    if (ret) {
> >> +        xfree(itt_addr);
> >> +        xfree(dev);
> >> +        return ret;
> >> +    }
> >> +
> >> +    dev->itt_addr = itt_addr;
> >> +    dev->hw_its = hw_its;
> >> +    dev->guest_devid = guest_devid;
> >> +    dev->host_devid = host_devid;
> >> +    dev->eventids = BIT(bits);
> >> +
> >> +    spin_lock(&d->arch.vgic.its_devices_lock);
> >> +    list_add_tail(&dev->entry, &d->arch.vgic.its_devices);
> >> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +/* Removing any connections a domain had to any ITS in the system. */
> >> +int its_remove_domain(struct domain *d)
> >> +{
> >> +    struct its_devices *dev, *temp;
> >> +
> >> +retry:
> >> +
> >> +    spin_lock(&d->arch.vgic.its_devices_lock);
> >> +    list_for_each_entry_safe(dev, temp, &d->arch.vgic.its_devices, entry)
> >> +    {
> >> +        list_del(&dev->entry);
> >> +        spin_unlock(&d->arch.vgic.its_devices_lock);
> >> +
> >> +        remove_mapped_guest_device(dev);
> >> +        goto retry;
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >>  /* Scan the DT for any ITS nodes and create a list of host ITSes out of it. */
> >>  void gicv3_its_dt_init(const struct dt_device_node *node)
> >>  {
> >> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> >> index 364d5f0..de77aaa 100644
> >> --- a/xen/arch/arm/vgic.c
> >> +++ b/xen/arch/arm/vgic.c
> >> @@ -156,6 +156,9 @@ int domain_vgic_init(struct domain *d, unsigned int nr_spis)
> >>      for ( i = 0; i < NR_GIC_SGI; i++ )
> >>          set_bit(i, d->arch.vgic.allocated_irqs);
> >>
> >> +    spin_lock_init(&d->arch.vgic.its_devices_lock);
> >> +    INIT_LIST_HEAD(&d->arch.vgic.its_devices);
> >> +
> >>      return 0;
> >>  }
> >>
> >> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> >> index 2d6fbb1..8ccc32a 100644
> >> --- a/xen/include/asm-arm/domain.h
> >> +++ b/xen/include/asm-arm/domain.h
> >> @@ -109,6 +109,8 @@ struct arch_domain
> >>          } *rdist_regions;
> >>          int nr_regions;                     /* Number of rdist regions */
> >>          uint32_t rdist_stride;              /* Re-Distributor stride */
> >> +        struct list_head its_devices;       /* devices mapped to an ITS */
> >> +        spinlock_t its_devices_lock;        /* protects the its_devices list */
> >>  #endif
> >>      } vgic;
> >>
> >> diff --git a/xen/include/asm-arm/gic-its.h b/xen/include/asm-arm/gic-its.h
> >> index 68e5f63..525a29d 100644
> >> --- a/xen/include/asm-arm/gic-its.h
> >> +++ b/xen/include/asm-arm/gic-its.h
> >> @@ -93,9 +93,19 @@ struct host_its {
> >>      void __iomem *its_base;
> >>      spinlock_t cmd_lock;
> >>      void *cmd_buf;
> >> +    int itte_size;
> >>      bool pta;
> >>  };
> >>
> >> +struct its_devices {
> >> +    struct list_head entry;
> >> +    struct host_its *hw_its;
> >> +    void *itt_addr;
> >> +    uint32_t guest_devid;
> >> +    uint32_t host_devid;
> >> +    uint32_t eventids;
> >> +};
> >> +
> >>  extern struct list_head host_its_list;
> >>
> >>  #ifdef CONFIG_HAS_ITS
> >> @@ -119,6 +129,13 @@ void gicv3_set_redist_addr(paddr_t address, int redist_id);
> >>  /* Map a collection for this host CPU to each host ITS. */
> >>  void gicv3_its_setup_collection(int cpu);
> >>
> >> +/* Map a device on the host by allocating an ITT on the host (ITS).
> >> + * "bits" specifies how many events (interrupts) this device will need.
> >> + * Setting "valid" to false deallocates the device.
> >> + */
> >> +int gicv3_its_map_device(struct domain *d, int host_devid, int guest_devid,
> >> +                         int bits, bool valid);
> >> +
> >>  #else
> >>
> >>  static inline void gicv3_its_dt_init(const struct dt_device_node *node)
> >> @@ -146,6 +163,11 @@ static inline void gicv3_set_redist_addr(paddr_t address, int redist_id)
> >>  static inline void gicv3_its_setup_collection(int cpu)
> >>  {
> >>  }
> >> +static inline int gicv3_its_map_device(struct domain *d, int host_devid,
> >> +                         int guest_devid, int bits, bool valid)
> >> +{
> >> +    return -ENODEV;
> >> +}
> >>
> >>  #endif /* CONFIG_HAS_ITS */
> >>
> >> --
> >> 2.9.0
> >>
> IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xen.org
> https://lists.xen.org/xen-devel
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (25 preceding siblings ...)
  2016-12-22 18:24 ` [RFC PATCH v2 26/26] ARM: vGIC: advertising LPI support Andre Przywara
@ 2017-01-18  8:13 ` Vijay Kilari
  2017-01-18  9:55   ` Julien Grall
  2017-01-19 12:26 ` Vijay Kilari
  27 siblings, 1 reply; 66+ messages in thread
From: Vijay Kilari @ 2017-01-18  8:13 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini

Hi Andre,

On Thu, Dec 22, 2016 at 11:54 PM, Andre Przywara <andre.przywara@arm.com> wrote:
> Hi,
>
> this is a reworked version of the Dom0 GICv3-ITS emulation series.
> This is still not fully where I want it and has some loose bits and
> pieces still, but since there are significant changes in the architecture
> I wanted to have an opinion before going ahead and replacing every single
> number with a named constant ;-) If that smells like a "send out before
> the end of the year", you are spot on.
>
> This series introduces ARM GICv3 ITS emulation, for now restricted to
> Dom0 only. The ITS is an interrupt controller widget providing a
> sophisticated way to deal with MSIs in a scalable manner.
> For hardware which relies on the ITS to provide interrupts for its
> peripherals this code is needed to get a machine booted into Dom0 at all.
> ITS emulation for DomUs is only really useful with PCI passthrough,
> which is not yet available for ARM. It is expected that this feature
> will be co-developed with the ITS DomU code. However this code drop here
> considered DomU emulation already, to keep later architectural changes
> to a minimum.
>
> Some generic design principles:
>
> * The current GIC code statically allocates structures for each supported
> IRQ (both for the host and the guest), which due to the potentially
> millions of LPI interrupts is not feasible to copy for the ITS.
> So we refrain from introducing the ITS as a first class Xen interrupt
> controller, also we don't hold struct irq_desc's or struct pending_irq's
> for each possible LPI.
> Fortunately LPIs are only interesting to guests, so we get away with
> storing only the virtual IRQ number and the guest VCPU for each allocated
> host LPI, which can be stashed into one uint64_t. This data is stored in
> a two-level table, which is both memory efficient and quick to access.
> We hook into the existing IRQ handling and VGIC code to avoid accessing
> the normal structures, providing alternative methods for getting the
> needed information (priority, is enabled?) for LPIs.
> For interrupts which are queued to or are actually in a guest we
> allocate struct pending_irq's on demand. As it is expected that only a
> very small number of interrupts is ever on a VCPU at the same time, this
> seems like the best approach. For now allocated structs are re-used and
> held in a linked list.
>
> * On the guest side we (later will) have to deal with malicious guests
> trying to hog Xen with mapping requests for a lot of LPIs, for instance.
> As the ITS actually uses system memory for storing status information,
> we use this memory (which the guest has to provide) to naturally limit
> a guest. For those tables which are page sized (devices, collections (CPUs),
> LPI properties) we map those pages into Xen, so we can easily access
> them from the virtual GIC code.
> Unfortunately the actual interrupt mapping tables are not necessarily
> page aligned, also can be much smaller than a page, so mapping all of
> them permanently is fiddly. As ITS commands in need to iterate those
> tables are pretty rare after all, we for now map them on demand upon
> emulating a virtual ITS command.
>
> * An obvious approach to handling some guest ITS commands would be to
> propagate them to the host, for instance to map devices and LPIs and
> to enable or disable LPIs.
> However this (later with DomU support) will create an attack vector, as
> a malicious guest could try to fill the host command queue with
> propagated commands.
> So in contrast to the previous RFC post this version now completely avoids
> this situation. For mapping devices and LPIs we rely on this being done
> via a hypercall prior to the actual guest run. For enabling and disabling
> LPIs we keep this bit on the virtual side and let LPIs always be enabled
> on the host side, dealing with the consequences this approach creates.
>
> This series is still a draft, with some known and many unknown issues.
> I made ITS support a Kconfig option, also it is only supported on arm64.
> This leads to some hideous constructs like an #ifdef'ed header file with
> empty function stubs, but I guess we can clean this up later in the
> upstreaming process.
>
> There are numerous changes compared to the last post, mainly affecting
> the now missing ITS command progagation. I also added locking to the
> "usual suspects" data structures.
> I picked some low hanging fruits from the review comments.
> Things I haven't addresses well is the whole memory management, in terms
> of marking pages r/o for a guest or allocating Xen memory from the proper
> bucket. This will be addresses with the next post.
>
> For now this code happens to boot Dom0 on an ARM fast model with ITS
> support. I still haven't had the chance to get hold of a Xen supported
> hardware platform with an ITS yet, so running on real hardware is a bit
> terra incognita.
>
> The code can also be found on the its/rfc-v2 branch here:
> git://linux-arm.org/xen-ap.git
> http://www.linux-arm.org/git?p=xen-ap.git;a=shortlog;h=refs/heads/its/rfc-v2

What is the kernel version that you tried to boot dom0?.
Have you tried with smmu and pci devices?.  Please share your kernel config.

>
> Cheers,
> Andre
>
> Andre Przywara (26):
>   ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
>   ARM: GICv3: allocate LPI pending and property table
>   ARM: GICv3 ITS: allocate device and collection table
>   ARM: GICv3 ITS: map ITS command buffer
>   ARM: GICv3 ITS: introduce ITS command handling
>   ARM: GICv3 ITS: introduce device mapping
>   ARM: GICv3 ITS: introduce host LPI array
>   ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall
>   ARM: GICv3: introduce separate pending_irq structs for LPIs
>   ARM: GICv3: forward pending LPIs to guests
>   ARM: GICv3: enable ITS and LPIs on the host
>   ARM: vGICv3: handle virtual LPI pending and property tables
>   ARM: vGICv3: Handle disabled LPIs
>   ARM: vGICv3: introduce basic ITS emulation bits
>   ARM: vITS: handle CLEAR command
>   ARM: vITS: handle INT command
>   ARM: vITS: handle MAPC command
>   ARM: vITS: handle MAPD command
>   ARM: vITS: handle MAPTI command
>   ARM: vITS: handle MOVI command
>   ARM: vITS: handle DISCARD command
>   ARM: vITS: handle INV command
>   ARM: vITS: handle INVALL command
>   ARM: vITS: create and initialize virtual ITSes for Dom0
>   ARM: vITS: create ITS subnodes for Dom0 DT
>   ARM: vGIC: advertising LPI support
>
>  xen/arch/arm/Kconfig              |  20 +
>  xen/arch/arm/Makefile             |   2 +
>  xen/arch/arm/efi/efi-boot.h       |   1 -
>  xen/arch/arm/gic-its.c            | 934 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c             |  92 +++-
>  xen/arch/arm/gic.c                |   9 +-
>  xen/arch/arm/physdev.c            |  24 +
>  xen/arch/arm/vgic-its.c           | 842 ++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3.c            | 274 +++++++++--
>  xen/arch/arm/vgic.c               |  71 ++-
>  xen/include/asm-arm/bitops.h      |   2 +
>  xen/include/asm-arm/cache.h       |   4 +
>  xen/include/asm-arm/domain.h      |  13 +-
>  xen/include/asm-arm/gic-its.h     | 217 +++++++++
>  xen/include/asm-arm/gic_v3_defs.h |  67 ++-
>  xen/include/asm-arm/irq.h         |   8 +
>  xen/include/asm-arm/vgic.h        |  15 +
>  17 files changed, 2558 insertions(+), 37 deletions(-)
>  create mode 100644 xen/arch/arm/gic-its.c
>  create mode 100644 xen/arch/arm/vgic-its.c
>  create mode 100644 xen/include/asm-arm/gic-its.h
>
> --
> 2.9.0
>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation
  2017-01-18  8:13 ` [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Vijay Kilari
@ 2017-01-18  9:55   ` Julien Grall
  0 siblings, 0 replies; 66+ messages in thread
From: Julien Grall @ 2017-01-18  9:55 UTC (permalink / raw)
  To: Vijay Kilari, Andre Przywara; +Cc: xen-devel, Stefano Stabellini

Hello Vijay,

On 18/01/2017 08:13, Vijay Kilari wrote:
> On Thu, Dec 22, 2016 at 11:54 PM, Andre Przywara <andre.przywara@arm.com> wrote:
>> Hi,
>>
>> this is a reworked version of the Dom0 GICv3-ITS emulation series.
>> This is still not fully where I want it and has some loose bits and
>> pieces still, but since there are significant changes in the architecture
>> I wanted to have an opinion before going ahead and replacing every single
>> number with a named constant ;-) If that smells like a "send out before
>> the end of the year", you are spot on.
>>
>> This series introduces ARM GICv3 ITS emulation, for now restricted to
>> Dom0 only. The ITS is an interrupt controller widget providing a
>> sophisticated way to deal with MSIs in a scalable manner.
>> For hardware which relies on the ITS to provide interrupts for its
>> peripherals this code is needed to get a machine booted into Dom0 at all.
>> ITS emulation for DomUs is only really useful with PCI passthrough,
>> which is not yet available for ARM. It is expected that this feature
>> will be co-developed with the ITS DomU code. However this code drop here
>> considered DomU emulation already, to keep later architectural changes
>> to a minimum.
>>
>> Some generic design principles:
>>
>> * The current GIC code statically allocates structures for each supported
>> IRQ (both for the host and the guest), which due to the potentially
>> millions of LPI interrupts is not feasible to copy for the ITS.
>> So we refrain from introducing the ITS as a first class Xen interrupt
>> controller, also we don't hold struct irq_desc's or struct pending_irq's
>> for each possible LPI.
>> Fortunately LPIs are only interesting to guests, so we get away with
>> storing only the virtual IRQ number and the guest VCPU for each allocated
>> host LPI, which can be stashed into one uint64_t. This data is stored in
>> a two-level table, which is both memory efficient and quick to access.
>> We hook into the existing IRQ handling and VGIC code to avoid accessing
>> the normal structures, providing alternative methods for getting the
>> needed information (priority, is enabled?) for LPIs.
>> For interrupts which are queued to or are actually in a guest we
>> allocate struct pending_irq's on demand. As it is expected that only a
>> very small number of interrupts is ever on a VCPU at the same time, this
>> seems like the best approach. For now allocated structs are re-used and
>> held in a linked list.
>>
>> * On the guest side we (later will) have to deal with malicious guests
>> trying to hog Xen with mapping requests for a lot of LPIs, for instance.
>> As the ITS actually uses system memory for storing status information,
>> we use this memory (which the guest has to provide) to naturally limit
>> a guest. For those tables which are page sized (devices, collections (CPUs),
>> LPI properties) we map those pages into Xen, so we can easily access
>> them from the virtual GIC code.
>> Unfortunately the actual interrupt mapping tables are not necessarily
>> page aligned, also can be much smaller than a page, so mapping all of
>> them permanently is fiddly. As ITS commands in need to iterate those
>> tables are pretty rare after all, we for now map them on demand upon
>> emulating a virtual ITS command.
>>
>> * An obvious approach to handling some guest ITS commands would be to
>> propagate them to the host, for instance to map devices and LPIs and
>> to enable or disable LPIs.
>> However this (later with DomU support) will create an attack vector, as
>> a malicious guest could try to fill the host command queue with
>> propagated commands.
>> So in contrast to the previous RFC post this version now completely avoids
>> this situation. For mapping devices and LPIs we rely on this being done
>> via a hypercall prior to the actual guest run. For enabling and disabling
>> LPIs we keep this bit on the virtual side and let LPIs always be enabled
>> on the host side, dealing with the consequences this approach creates.
>>
>> This series is still a draft, with some known and many unknown issues.
>> I made ITS support a Kconfig option, also it is only supported on arm64.
>> This leads to some hideous constructs like an #ifdef'ed header file with
>> empty function stubs, but I guess we can clean this up later in the
>> upstreaming process.
>>
>> There are numerous changes compared to the last post, mainly affecting
>> the now missing ITS command progagation. I also added locking to the
>> "usual suspects" data structures.
>> I picked some low hanging fruits from the review comments.
>> Things I haven't addresses well is the whole memory management, in terms
>> of marking pages r/o for a guest or allocating Xen memory from the proper
>> bucket. This will be addresses with the next post.
>>
>> For now this code happens to boot Dom0 on an ARM fast model with ITS
>> support. I still haven't had the chance to get hold of a Xen supported
>> hardware platform with an ITS yet, so running on real hardware is a bit
>> terra incognita.
>>
>> The code can also be found on the its/rfc-v2 branch here:
>> git://linux-arm.org/xen-ap.git
>> http://www.linux-arm.org/git?p=xen-ap.git;a=shortlog;h=refs/heads/its/rfc-v2
>
> What is the kernel version that you tried to boot dom0?.

I haven't tried the ITS series, but any kernel version which boot 
baremetal on your platform should boot on Xen. If not, you need to 
figure out why.

Regarding the kernel config, it will depend on your platform. In general 
your platform options + classic xen options should work.

> Have you tried with smmu and pci devices?.  Please share your kernel config.

The SMMU driver in Xen does not yet support PCI devices.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation
  2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
                   ` (26 preceding siblings ...)
  2017-01-18  8:13 ` [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Vijay Kilari
@ 2017-01-19 12:26 ` Vijay Kilari
  2017-01-19 13:50   ` Andre Przywara
  27 siblings, 1 reply; 66+ messages in thread
From: Vijay Kilari @ 2017-01-19 12:26 UTC (permalink / raw)
  To: Andre Przywara
  Cc: prasun.kapoor, Jayachandran.Nair, Stefano Stabellini,
	manish.jaggi, Julien Grall, xen-devel

Hi Andre,

   I see following issues when running on ThunderX platform with your patches.
I have debugged and patched/workaround few issues. For issue (5) I
need your  inputs.

1) Your code base fails to boot xen. Fails at dom0 memory allocation.
    To overcome this I have rebased your patches on top of 4.8 stable
release and this issue is not seen.

2)  ITS is not initialized if GICv2 info is not found in GICv3 dt
node. But having GICv2 info is not
   mandatory. So in the below code if GICv2 info is not found ITS is
not initialized.

static void __init gicv3_dt_init (void)
{
   ...
    /*
     * For GICv3 supporting GICv2, GICC and GICV base address will be
     * provided.
     */
    res = dt_device_get_address(node, 1 + gicv3.rdist_count,
                                &cbase, &csize);

-    if ( res )    // <==  This check needs to be managed properly?
-        return;

    dt_device_get_address(node, 1 + gicv3.rdist_count + 2,
                          &vbase, &vsize);

    /* Check for ITS child nodes and build the host ITS list accordingly. */
    gicv3_its_dt_init(node);

}

3) Dom0 is not calling PHYSDEVOP_manage_pci_add for all the pci devices
   instead it is making PHYSDEVOP_pci_device_add when segment exists.
   So I have made below changes so that ITS mapping is called for all
the devices.

4) Also put_domain() is throws panic. So I have dropped it in below code.

--- a/xen/arch/arm/physdev.c
+++ b/xen/arch/arm/physdev.c
@@ -17,27 +17,68 @@
 int do_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
 {
     struct physdev_manage_pci manage;
+    struct physdev_pci_device_add pci_add;
     struct domain *dom0;
     u32 devid;
     int ret;

     switch (cmd)
     {
         case PHYSDEVOP_manage_pci_add:
         case PHYSDEVOP_manage_pci_remove:
             if ( copy_from_guest(&manage, arg, 1) != 0 )
                 return -EFAULT;

             dom0 = hardware_domain;
             devid = manage.bus << 8 | manage.devfn;
             /* Allocate an ITS device table with space for 32 MSIs */
             ret = gicv3_its_map_device(dom0, devid, devid, 5,
                                        cmd == PHYSDEVOP_manage_pci_add);

-            put_domain(dom0);
             return ret;
+       case PHYSDEVOP_pci_device_add:
+            if ( copy_from_guest(&pci_add, arg, 1) != 0 )
+                return -EFAULT;
+            dom0 = hardware_domain;
+            devid = pci_add.seg << 16 | pci_add.bus << 8 | pci_add.devfn;
+
+            /* Allocate an ITS device table with space for 32 MSIs */
+            ret = gicv3_its_map_device(dom0, devid, devid, 5,
+                                       cmd == PHYSDEVOP_pci_device_add);
+
+            return ret;
     }

     gdprintk(XENLOG_DEBUG, "PHYSDEVOP cmd=%d: not implemented\n", cmd);
     return -ENOSYS;
 }


5)  With the above patch, Now I see ITS HW is throwing up Error 10801
where in it shows
devid is out of range in MAPD command.

See below log with some debug prints, where I have printed
GITS_IMP_CSER register for
every MAPD command.
(XEN) GITS_IMP_CSER 0x5e4000010801

This error is seen when seg is 1. This was not the case with previous
version of patches.
However GITS_TYPER from HW shows it support upto 20 bits.

pci 0000:01:10.1: [177d:a026] type 00 class 0x028000
pci 0000:01:10.1: BAR 0: [mem 0x87e0e1000000-0x87e0e13fffff 64bit]
(from Enhanced Allocation, properties 0x0)
pci 0000:01:10.1: BAR 4: [mem 0x87e0e1400000-0x87e0e17fffff 64bit]
(from Enhanced Allocation, properties 0x0)
(XEN) In do_physdev_op PHYSDEVOP_pci_device_add
(XEN) In do_physdev_op dev id 385 seg 0 bus 1 devfn 129
(XEN) In gicv3_its_map_device send MAPD for  dev id 385
(XEN) CMD[0] 0x18100000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044200 CMD[3] 0x0
(XEN) GITS_IMP_TSER 0x0
(XEN) GITS_IMP_CSER 0x0
iommu: Adding device 0000:01:10.1 to group 41
pci 0000:01:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
enable it with 'pcie_aspm=force'
pci_bus 0000:01: busn_res: [bus 01-ff] end is updated to 01
pci_bus 0000:02: busn_res: can not insert [bus 02-ff] under [bus
00-1f] (conflicts with (null) [bus 00-1f])
pci 0000:02:00.0: [177d:a01d] type 00 class 0x010400
pci 0000:02:00.0: BAR 0: [mem 0x870000000000-0x8700007fffff 64bit]
(from Enhanced Allocation, properties 0x0)
pci 0000:02:00.0: BAR 4: [mem 0x870000f00000-0x870000ffffff 64bit]
(from Enhanced Allocation, properties 0x0)
(XEN) In do_physdev_op PHYSDEVOP_pci_device_add
(XEN) In do_physdev_op dev id 512 seg 0 bus 2 devfn 0
(XEN) In gicv3_its_map_device send MAPD for  dev id 512
(XEN) CMD[0] 0x20000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044400 CMD[3] 0x0
(XEN) GITS_IMP_TSER 0x0
(XEN) GITS_IMP_CSER 0x0
iommu: Adding device 0000:02:00.0 to group 42
pci 0000:02:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
enable it with 'pcie_aspm=force'
pci_bus 0000:02: busn_res: [bus 02-ff] end is updated to 02
pci_bus 0000:03: busn_res: can not insert [bus 03-ff] under [bus
00-1f] (conflicts with (null) [bus 00-1f])
pci 0000:03:00.0: [177d:a01a] type 00 class 0x120000
pci 0000:03:00.0: BAR 0: [mem 0x838000000000-0x8380003fffff 64bit]
(from Enhanced Allocation, properties 0x0)
pci 0000:03:00.0: BAR 4: [mem 0x838000f00000-0x838000ffffff 64bit]
(from Enhanced Allocation, properties 0x0)
(XEN) In do_physdev_op PHYSDEVOP_pci_device_add
(XEN) In do_physdev_op dev id 768 seg 0 bus 3 devfn 0
(XEN) In gicv3_its_map_device send MAPD for  dev id 768
(XEN) CMD[0] 0x30000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044500 CMD[3] 0x0
(XEN) GITS_IMP_TSER 0x0
(XEN) GITS_IMP_CSER 0x0
iommu: Adding device 0000:03:00.0 to group 43
pci 0000:03:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
enable it with 'pcie_aspm=force'
pci_bus 0000:03: busn_res: [bus 03-ff] end is updated to 03
pci_bus 0000:04: busn_res: can not insert [bus 04-ff] under [bus
00-1f] (conflicts with (null) [bus 00-1f])
pci 0000:04:00.0: [177d:a019] type 00 class 0x120000
pci 0000:04:00.0: BAR 0: [mem 0x846000000000-0x8467ffffffff 64bit]
(from Enhanced Allocation, properties 0x0)
pci 0000:04:00.0: BAR 4: [mem 0x846a00000000-0x846a000fffff 64bit]
(from Enhanced Allocation, properties 0x0)
(XEN) In do_physdev_op PHYSDEVOP_pci_device_add
(XEN) In do_physdev_op dev id 1024 seg 0 bus 4 devfn 0
(XEN) In gicv3_its_map_device send MAPD for  dev id 1024
(XEN) CMD[0] 0x40000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044700 CMD[3] 0x0
(XEN) GITS_IMP_TSER 0x0
(XEN) GITS_IMP_CSER 0x0
iommu: Adding device 0000:04:00.0 to group 44
pci 0000:04:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
enable it with 'pcie_aspm=force'
pci_bus 0000:04: busn_res: [bus 04-ff] end is updated to 04
pci 0000:00:01.0: PCI bridge to [bus 01]
pci 0000:00:14.0: PCI bridge to [bus 02]
pci 0000:00:15.0: PCI bridge to [bus 03]
pci 0000:00:16.0: PCI bridge to [bus 04]
OF: PCI: host bridge /soc@0/pci@849000000000 ranges:
OF: PCI:   MEM 0x810000000000..0x817fffffffff -> 0x810000000000
pci-host-generic 849000000000.pci: ECAM at [mem
0x849000000000-0x849001ffffff] for [bus 00-1f]
pci-host-generic 849000000000.pci: PCI host bridge to bus 0001:00
pci_bus 0001:00: root bus resource [bus 00-1f]
pci_bus 0001:00: root bus resource [mem 0x810000000000-0x817fffffffff]
pci 0001:00:08.0: [177d:a01c] type 00 class 0x010601
pci 0001:00:08.0: BAR 0: [mem 0x814000000000-0x8140001fffff 64bit]
(from Enhanced Allocation, properties 0x0)
pci 0001:00:08.0: BAR 4: [mem 0x814000200000-0x8140002fffff 64bit]
(from Enhanced Allocation, properties 0x0)
(XEN) In do_physdev_op PHYSDEVOP_pci_device_add
(XEN) In do_physdev_op dev id 65600 seg 1 bus 0 devfn 64
(XEN) In gicv3_its_map_device send MAPD for  dev id 65600
(XEN) CMD[0] 0x1004000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044900 CMD[3] 0x0
(XEN) GITS_IMP_TSER 0x0
(XEN) GITS_IMP_CSER 0x5e4000010801  // <== MAPD deviceid Out of Range error.

6) I have also seen panic in gicv3_assign_guest_event() when called
from dom0 DISCARD ITS command.
   Not debugged much.

Regards
Vijay

On Thu, Dec 22, 2016 at 11:54 PM, Andre Przywara <andre.przywara@arm.com> wrote:
> Hi,
>
> this is a reworked version of the Dom0 GICv3-ITS emulation series.
> This is still not fully where I want it and has some loose bits and
> pieces still, but since there are significant changes in the architecture
> I wanted to have an opinion before going ahead and replacing every single
> number with a named constant ;-) If that smells like a "send out before
> the end of the year", you are spot on.
>
> This series introduces ARM GICv3 ITS emulation, for now restricted to
> Dom0 only. The ITS is an interrupt controller widget providing a
> sophisticated way to deal with MSIs in a scalable manner.
> For hardware which relies on the ITS to provide interrupts for its
> peripherals this code is needed to get a machine booted into Dom0 at all.
> ITS emulation for DomUs is only really useful with PCI passthrough,
> which is not yet available for ARM. It is expected that this feature
> will be co-developed with the ITS DomU code. However this code drop here
> considered DomU emulation already, to keep later architectural changes
> to a minimum.
>
> Some generic design principles:
>
> * The current GIC code statically allocates structures for each supported
> IRQ (both for the host and the guest), which due to the potentially
> millions of LPI interrupts is not feasible to copy for the ITS.
> So we refrain from introducing the ITS as a first class Xen interrupt
> controller, also we don't hold struct irq_desc's or struct pending_irq's
> for each possible LPI.
> Fortunately LPIs are only interesting to guests, so we get away with
> storing only the virtual IRQ number and the guest VCPU for each allocated
> host LPI, which can be stashed into one uint64_t. This data is stored in
> a two-level table, which is both memory efficient and quick to access.
> We hook into the existing IRQ handling and VGIC code to avoid accessing
> the normal structures, providing alternative methods for getting the
> needed information (priority, is enabled?) for LPIs.
> For interrupts which are queued to or are actually in a guest we
> allocate struct pending_irq's on demand. As it is expected that only a
> very small number of interrupts is ever on a VCPU at the same time, this
> seems like the best approach. For now allocated structs are re-used and
> held in a linked list.
>
> * On the guest side we (later will) have to deal with malicious guests
> trying to hog Xen with mapping requests for a lot of LPIs, for instance.
> As the ITS actually uses system memory for storing status information,
> we use this memory (which the guest has to provide) to naturally limit
> a guest. For those tables which are page sized (devices, collections (CPUs),
> LPI properties) we map those pages into Xen, so we can easily access
> them from the virtual GIC code.
> Unfortunately the actual interrupt mapping tables are not necessarily
> page aligned, also can be much smaller than a page, so mapping all of
> them permanently is fiddly. As ITS commands in need to iterate those
> tables are pretty rare after all, we for now map them on demand upon
> emulating a virtual ITS command.
>
> * An obvious approach to handling some guest ITS commands would be to
> propagate them to the host, for instance to map devices and LPIs and
> to enable or disable LPIs.
> However this (later with DomU support) will create an attack vector, as
> a malicious guest could try to fill the host command queue with
> propagated commands.
> So in contrast to the previous RFC post this version now completely avoids
> this situation. For mapping devices and LPIs we rely on this being done
> via a hypercall prior to the actual guest run. For enabling and disabling
> LPIs we keep this bit on the virtual side and let LPIs always be enabled
> on the host side, dealing with the consequences this approach creates.
>
> This series is still a draft, with some known and many unknown issues.
> I made ITS support a Kconfig option, also it is only supported on arm64.
> This leads to some hideous constructs like an #ifdef'ed header file with
> empty function stubs, but I guess we can clean this up later in the
> upstreaming process.
>
> There are numerous changes compared to the last post, mainly affecting
> the now missing ITS command progagation. I also added locking to the
> "usual suspects" data structures.
> I picked some low hanging fruits from the review comments.
> Things I haven't addresses well is the whole memory management, in terms
> of marking pages r/o for a guest or allocating Xen memory from the proper
> bucket. This will be addresses with the next post.
>
> For now this code happens to boot Dom0 on an ARM fast model with ITS
> support. I still haven't had the chance to get hold of a Xen supported
> hardware platform with an ITS yet, so running on real hardware is a bit
> terra incognita.
>
> The code can also be found on the its/rfc-v2 branch here:
> git://linux-arm.org/xen-ap.git
> http://www.linux-arm.org/git?p=xen-ap.git;a=shortlog;h=refs/heads/its/rfc-v2
>
> Cheers,
> Andre
>
> Andre Przywara (26):
>   ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
>   ARM: GICv3: allocate LPI pending and property table
>   ARM: GICv3 ITS: allocate device and collection table
>   ARM: GICv3 ITS: map ITS command buffer
>   ARM: GICv3 ITS: introduce ITS command handling
>   ARM: GICv3 ITS: introduce device mapping
>   ARM: GICv3 ITS: introduce host LPI array
>   ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall
>   ARM: GICv3: introduce separate pending_irq structs for LPIs
>   ARM: GICv3: forward pending LPIs to guests
>   ARM: GICv3: enable ITS and LPIs on the host
>   ARM: vGICv3: handle virtual LPI pending and property tables
>   ARM: vGICv3: Handle disabled LPIs
>   ARM: vGICv3: introduce basic ITS emulation bits
>   ARM: vITS: handle CLEAR command
>   ARM: vITS: handle INT command
>   ARM: vITS: handle MAPC command
>   ARM: vITS: handle MAPD command
>   ARM: vITS: handle MAPTI command
>   ARM: vITS: handle MOVI command
>   ARM: vITS: handle DISCARD command
>   ARM: vITS: handle INV command
>   ARM: vITS: handle INVALL command
>   ARM: vITS: create and initialize virtual ITSes for Dom0
>   ARM: vITS: create ITS subnodes for Dom0 DT
>   ARM: vGIC: advertising LPI support
>
>  xen/arch/arm/Kconfig              |  20 +
>  xen/arch/arm/Makefile             |   2 +
>  xen/arch/arm/efi/efi-boot.h       |   1 -
>  xen/arch/arm/gic-its.c            | 934 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c             |  92 +++-
>  xen/arch/arm/gic.c                |   9 +-
>  xen/arch/arm/physdev.c            |  24 +
>  xen/arch/arm/vgic-its.c           | 842 ++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3.c            | 274 +++++++++--
>  xen/arch/arm/vgic.c               |  71 ++-
>  xen/include/asm-arm/bitops.h      |   2 +
>  xen/include/asm-arm/cache.h       |   4 +
>  xen/include/asm-arm/domain.h      |  13 +-
>  xen/include/asm-arm/gic-its.h     | 217 +++++++++
>  xen/include/asm-arm/gic_v3_defs.h |  67 ++-
>  xen/include/asm-arm/irq.h         |   8 +
>  xen/include/asm-arm/vgic.h        |  15 +
>  17 files changed, 2558 insertions(+), 37 deletions(-)
>  create mode 100644 xen/arch/arm/gic-its.c
>  create mode 100644 xen/arch/arm/vgic-its.c
>  create mode 100644 xen/include/asm-arm/gic-its.h
>
> --
> 2.9.0
>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation
  2017-01-19 12:26 ` Vijay Kilari
@ 2017-01-19 13:50   ` Andre Przywara
  2017-01-19 14:32     ` Julien Grall
  0 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-19 13:50 UTC (permalink / raw)
  To: Vijay Kilari
  Cc: prasun.kapoor, Jayachandran.Nair, Stefano Stabellini,
	manish.jaggi, Julien Grall, xen-devel

Hi Vijay,

On 19/01/17 12:26, Vijay Kilari wrote:
> Hi Andre,
> 
>    I see following issues when running on ThunderX platform with your patches.
> I have debugged and patched/workaround few issues. For issue (5) I
> need your  inputs.

thanks for the testing and the input. It seems that my anticipation of
issues when booting on hardware were right ;-)
It seems that some of the issues got fixed already by me rewriting some
parts, triggered by comments from Stefano.

> 1) Your code base fails to boot xen. Fails at dom0 memory allocation.
>     To overcome this I have rebased your patches on top of 4.8 stable
> release and this issue is not seen.

Can you try to rebase on latest xen master instead and see if that's
fixed there by any chance? Because that's where we eventually need to
base against anyway. 4.8 stable will not help.

> 2)  ITS is not initialized if GICv2 info is not found in GICv3 dt
> node. But having GICv2 info is not
>    mandatory. So in the below code if GICv2 info is not found ITS is
> not initialized.

Ah, right, good point. The model is v2 compatible.
As this looks like an issue independent from the ITS emulation, can you
make a patch? Or does Xen indeed rely on having v2 compat support at the
moment? I think you should be able to boot Dom0 with an initrd on
ThunderX already, can't you?

> static void __init gicv3_dt_init (void)
> {
>    ...
>     /*
>      * For GICv3 supporting GICv2, GICC and GICV base address will be
>      * provided.
>      */
>     res = dt_device_get_address(node, 1 + gicv3.rdist_count,
>                                 &cbase, &csize);
> 
> -    if ( res )    // <==  This check needs to be managed properly?
> -        return;
> 
>     dt_device_get_address(node, 1 + gicv3.rdist_count + 2,
>                           &vbase, &vsize);
> 
>     /* Check for ITS child nodes and build the host ITS list accordingly. */
>     gicv3_its_dt_init(node);
> 
> }
> 
> 3) Dom0 is not calling PHYSDEVOP_manage_pci_add for all the pci devices
>    instead it is making PHYSDEVOP_pci_device_add when segment exists.
>    So I have made below changes so that ITS mapping is called for all
> the devices.

Ah, OK. I didn't look at the code, just observed the physdevops and
implemented them. Will check this again.

> 
> 4) Also put_domain() is throws panic. So I have dropped it in below code.

Indeed, I found this by code inspection already.

> 
> --- a/xen/arch/arm/physdev.c
> +++ b/xen/arch/arm/physdev.c
> @@ -17,27 +17,68 @@
>  int do_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
>  {
>      struct physdev_manage_pci manage;
> +    struct physdev_pci_device_add pci_add;
>      struct domain *dom0;
>      u32 devid;
>      int ret;
> 
>      switch (cmd)
>      {
>          case PHYSDEVOP_manage_pci_add:
>          case PHYSDEVOP_manage_pci_remove:
>              if ( copy_from_guest(&manage, arg, 1) != 0 )
>                  return -EFAULT;
> 
>              dom0 = hardware_domain;
>              devid = manage.bus << 8 | manage.devfn;
>              /* Allocate an ITS device table with space for 32 MSIs */
>              ret = gicv3_its_map_device(dom0, devid, devid, 5,
>                                         cmd == PHYSDEVOP_manage_pci_add);
> 
> -            put_domain(dom0);
>              return ret;
> +       case PHYSDEVOP_pci_device_add:
> +            if ( copy_from_guest(&pci_add, arg, 1) != 0 )
> +                return -EFAULT;
> +            dom0 = hardware_domain;
> +            devid = pci_add.seg << 16 | pci_add.bus << 8 | pci_add.devfn;
> +
> +            /* Allocate an ITS device table with space for 32 MSIs */
> +            ret = gicv3_its_map_device(dom0, devid, devid, 5,
> +                                       cmd == PHYSDEVOP_pci_device_add);
> +
> +            return ret;
>      }
> 
>      gdprintk(XENLOG_DEBUG, "PHYSDEVOP cmd=%d: not implemented\n", cmd);
>      return -ENOSYS;
>  }
> 
> 
> 5)  With the above patch, Now I see ITS HW is throwing up Error 10801
> where in it shows
> devid is out of range in MAPD command.
> 
> See below log with some debug prints, where I have printed
> GITS_IMP_CSER register for
> every MAPD command.
> (XEN) GITS_IMP_CSER 0x5e4000010801
> 
> This error is seen when seg is 1. This was not the case with previous
> version of patches.
> However GITS_TYPER from HW shows it support upto 20 bits.
> 
> pci 0000:01:10.1: [177d:a026] type 00 class 0x028000
> pci 0000:01:10.1: BAR 0: [mem 0x87e0e1000000-0x87e0e13fffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> pci 0000:01:10.1: BAR 4: [mem 0x87e0e1400000-0x87e0e17fffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> (XEN) In do_physdev_op PHYSDEVOP_pci_device_add
> (XEN) In do_physdev_op dev id 385 seg 0 bus 1 devfn 129
> (XEN) In gicv3_its_map_device send MAPD for  dev id 385
> (XEN) CMD[0] 0x18100000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044200 CMD[3] 0x0
> (XEN) GITS_IMP_TSER 0x0
> (XEN) GITS_IMP_CSER 0x0
> iommu: Adding device 0000:01:10.1 to group 41
> pci 0000:01:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
> enable it with 'pcie_aspm=force'
> pci_bus 0000:01: busn_res: [bus 01-ff] end is updated to 01
> pci_bus 0000:02: busn_res: can not insert [bus 02-ff] under [bus
> 00-1f] (conflicts with (null) [bus 00-1f])
> pci 0000:02:00.0: [177d:a01d] type 00 class 0x010400
> pci 0000:02:00.0: BAR 0: [mem 0x870000000000-0x8700007fffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> pci 0000:02:00.0: BAR 4: [mem 0x870000f00000-0x870000ffffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> (XEN) In do_physdev_op PHYSDEVOP_pci_device_add
> (XEN) In do_physdev_op dev id 512 seg 0 bus 2 devfn 0
> (XEN) In gicv3_its_map_device send MAPD for  dev id 512
> (XEN) CMD[0] 0x20000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044400 CMD[3] 0x0
> (XEN) GITS_IMP_TSER 0x0
> (XEN) GITS_IMP_CSER 0x0
> iommu: Adding device 0000:02:00.0 to group 42
> pci 0000:02:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
> enable it with 'pcie_aspm=force'
> pci_bus 0000:02: busn_res: [bus 02-ff] end is updated to 02
> pci_bus 0000:03: busn_res: can not insert [bus 03-ff] under [bus
> 00-1f] (conflicts with (null) [bus 00-1f])
> pci 0000:03:00.0: [177d:a01a] type 00 class 0x120000
> pci 0000:03:00.0: BAR 0: [mem 0x838000000000-0x8380003fffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> pci 0000:03:00.0: BAR 4: [mem 0x838000f00000-0x838000ffffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> (XEN) In do_physdev_op PHYSDEVOP_pci_device_add
> (XEN) In do_physdev_op dev id 768 seg 0 bus 3 devfn 0
> (XEN) In gicv3_its_map_device send MAPD for  dev id 768
> (XEN) CMD[0] 0x30000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044500 CMD[3] 0x0
> (XEN) GITS_IMP_TSER 0x0
> (XEN) GITS_IMP_CSER 0x0
> iommu: Adding device 0000:03:00.0 to group 43
> pci 0000:03:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
> enable it with 'pcie_aspm=force'
> pci_bus 0000:03: busn_res: [bus 03-ff] end is updated to 03
> pci_bus 0000:04: busn_res: can not insert [bus 04-ff] under [bus
> 00-1f] (conflicts with (null) [bus 00-1f])
> pci 0000:04:00.0: [177d:a019] type 00 class 0x120000
> pci 0000:04:00.0: BAR 0: [mem 0x846000000000-0x8467ffffffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> pci 0000:04:00.0: BAR 4: [mem 0x846a00000000-0x846a000fffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> (XEN) In do_physdev_op PHYSDEVOP_pci_device_add
> (XEN) In do_physdev_op dev id 1024 seg 0 bus 4 devfn 0
> (XEN) In gicv3_its_map_device send MAPD for  dev id 1024
> (XEN) CMD[0] 0x40000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044700 CMD[3] 0x0
> (XEN) GITS_IMP_TSER 0x0
> (XEN) GITS_IMP_CSER 0x0
> iommu: Adding device 0000:04:00.0 to group 44
> pci 0000:04:00.0: disabling ASPM on pre-1.1 PCIe device.  You can
> enable it with 'pcie_aspm=force'
> pci_bus 0000:04: busn_res: [bus 04-ff] end is updated to 04
> pci 0000:00:01.0: PCI bridge to [bus 01]
> pci 0000:00:14.0: PCI bridge to [bus 02]
> pci 0000:00:15.0: PCI bridge to [bus 03]
> pci 0000:00:16.0: PCI bridge to [bus 04]
> OF: PCI: host bridge /soc@0/pci@849000000000 ranges:
> OF: PCI:   MEM 0x810000000000..0x817fffffffff -> 0x810000000000
> pci-host-generic 849000000000.pci: ECAM at [mem
> 0x849000000000-0x849001ffffff] for [bus 00-1f]
> pci-host-generic 849000000000.pci: PCI host bridge to bus 0001:00
> pci_bus 0001:00: root bus resource [bus 00-1f]
> pci_bus 0001:00: root bus resource [mem 0x810000000000-0x817fffffffff]
> pci 0001:00:08.0: [177d:a01c] type 00 class 0x010601
> pci 0001:00:08.0: BAR 0: [mem 0x814000000000-0x8140001fffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> pci 0001:00:08.0: BAR 4: [mem 0x814000200000-0x8140002fffff 64bit]
> (from Enhanced Allocation, properties 0x0)
> (XEN) In do_physdev_op PHYSDEVOP_pci_device_add
> (XEN) In do_physdev_op dev id 65600 seg 1 bus 0 devfn 64
> (XEN) In gicv3_its_map_device send MAPD for  dev id 65600
> (XEN) CMD[0] 0x1004000000008 CMD[1] 0x4 CMD [2] 0x8000001ff6044900 CMD[3] 0x0
> (XEN) GITS_IMP_TSER 0x0
> (XEN) GITS_IMP_CSER 0x5e4000010801  // <== MAPD deviceid Out of Range error.
> 
> 6) I have also seen panic in gicv3_assign_guest_event() when called
> from dom0 DISCARD ITS command.
>    Not debugged much.

I think my local fixes here address some of that already. I will send a
new version hopefully later this month, so just stay tuned.

Cheers,
Andre.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation
  2017-01-19 13:50   ` Andre Przywara
@ 2017-01-19 14:32     ` Julien Grall
  2017-01-19 14:45       ` Andre Przywara
  0 siblings, 1 reply; 66+ messages in thread
From: Julien Grall @ 2017-01-19 14:32 UTC (permalink / raw)
  To: Andre Przywara, Vijay Kilari
  Cc: xen-devel, Jayachandran.Nair, Stefano Stabellini, manish.jaggi,
	prasun.kapoor

Hi Andre,

On 19/01/2017 13:50, Andre Przywara wrote:
> On 19/01/17 12:26, Vijay Kilari wrote:
>>    I see following issues when running on ThunderX platform with your patches.
>> I have debugged and patched/workaround few issues. For issue (5) I
>> need your  inputs.
>
> thanks for the testing and the input. It seems that my anticipation of
> issues when booting on hardware were right ;-)
> It seems that some of the issues got fixed already by me rewriting some
> parts, triggered by comments from Stefano.
>
>> 1) Your code base fails to boot xen. Fails at dom0 memory allocation.
>>     To overcome this I have rebased your patches on top of 4.8 stable
>> release and this issue is not seen.
>
> Can you try to rebase on latest xen master instead and see if that's
> fixed there by any chance? Because that's where we eventually need to
> base against anyway. 4.8 stable will not help.
>
>> 2)  ITS is not initialized if GICv2 info is not found in GICv3 dt
>> node. But having GICv2 info is not
>>    mandatory. So in the below code if GICv2 info is not found ITS is
>> not initialized.
>
> Ah, right, good point. The model is v2 compatible.
> As this looks like an issue independent from the ITS emulation, can you
> make a patch? Or does Xen indeed rely on having v2 compat support at the
> moment? I think you should be able to boot Dom0 with an initrd on
> ThunderX already, can't you?

This is a bug in your series and not the current tree. If you look at it 
the if (res) return; will avoid to read the next GICv2 region if the 
first one does not exist.

So the problem is where you added the call to the ITS initialization. 
This should be fixed in your series and not separately.

Cheers,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation
  2017-01-19 14:32     ` Julien Grall
@ 2017-01-19 14:45       ` Andre Przywara
  0 siblings, 0 replies; 66+ messages in thread
From: Andre Przywara @ 2017-01-19 14:45 UTC (permalink / raw)
  To: Julien Grall, Vijay Kilari
  Cc: xen-devel, Jayachandran.Nair, Stefano Stabellini, manish.jaggi,
	prasun.kapoor

Hi,

On 19/01/17 14:32, Julien Grall wrote:
> Hi Andre,
> 
> On 19/01/2017 13:50, Andre Przywara wrote:
>> On 19/01/17 12:26, Vijay Kilari wrote:
>>>    I see following issues when running on ThunderX platform with your
>>> patches.
>>> I have debugged and patched/workaround few issues. For issue (5) I
>>> need your  inputs.
>>
>> thanks for the testing and the input. It seems that my anticipation of
>> issues when booting on hardware were right ;-)
>> It seems that some of the issues got fixed already by me rewriting some
>> parts, triggered by comments from Stefano.
>>
>>> 1) Your code base fails to boot xen. Fails at dom0 memory allocation.
>>>     To overcome this I have rebased your patches on top of 4.8 stable
>>> release and this issue is not seen.
>>
>> Can you try to rebase on latest xen master instead and see if that's
>> fixed there by any chance? Because that's where we eventually need to
>> base against anyway. 4.8 stable will not help.
>>
>>> 2)  ITS is not initialized if GICv2 info is not found in GICv3 dt
>>> node. But having GICv2 info is not
>>>    mandatory. So in the below code if GICv2 info is not found ITS is
>>> not initialized.
>>
>> Ah, right, good point. The model is v2 compatible.
>> As this looks like an issue independent from the ITS emulation, can you
>> make a patch? Or does Xen indeed rely on having v2 compat support at the
>> moment? I think you should be able to boot Dom0 with an initrd on
>> ThunderX already, can't you?
> 
> This is a bug in your series and not the current tree. If you look at it
> the if (res) return; will avoid to read the next GICv2 region if the
> first one does not exist.

Ah, right, I misread the code snippet.

> So the problem is where you added the call to the ITS initialization.
> This should be fixed in your series and not separately.

So yes, I will fix it in my next drop.
I guess it should simply read:
    if ( !res )
        dt_device_get_address(node, 1 + gicv3.rdist_count + 2,
...

Thanks for pointing this out.

Cheers,
Andre.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2017-01-04 21:47   ` Stefano Stabellini
  2017-01-05 17:56     ` Andre Przywara
@ 2017-01-20 11:12     ` Julien Grall
  2017-01-20 11:27       ` Andre Przywara
  1 sibling, 1 reply; 66+ messages in thread
From: Julien Grall @ 2017-01-20 11:12 UTC (permalink / raw)
  To: Stefano Stabellini, Andre Przywara; +Cc: xen-devel, Vijay Kilari

Hello,

On 04/01/2017 22:47, Stefano Stabellini wrote:
> On Thu, 22 Dec 2016, Andre Przywara wrote:
>> Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
>> an EventID (the MSI payload or interrupt ID) to a pair of LPI number
>> and collection ID, which points to the target CPU.
>> This mapping is stored in the device and collection tables, which software
>> has to provide for the ITS to use.
>> Allocate the required memory and hand it the ITS.
>> The maximum number of devices is limited to a compile-time constant
>> exposed in Kconfig.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/Kconfig          |   6 +++
>>  xen/arch/arm/gic-its.c        | 114 ++++++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic-v3.c         |   5 ++
>>  xen/include/asm-arm/bitops.h  |   1 +
>>  xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
>>  5 files changed, 176 insertions(+), 1 deletion(-)
>>
>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>> index a7d941c..a369305 100644
>> --- a/xen/arch/arm/Kconfig
>> +++ b/xen/arch/arm/Kconfig
>> @@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
>>            This can be overriden on the command line with the max_lpi_bits
>>            parameter.
>>
>> +config MAX_ITS_PCI_BUSSES
>> +        depends on HAS_ITS
>> +        int "Number of PCI busses the ITS supports (4)"
>> +        range 1 1024
>> +        default "4"
>
> Given that any kind of devices can be behind an ITS, including non-PCI
> devices, I think it is best to call this MAX_PHYS_ITS_DEVICES.

I don't think we should limit the number of devices supported at 
compilation time. We aim to have a single Xen binary running on any 
board. The best would be to guess the number at runtime, but I would be 
fine with a command line options.

Cheers,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2017-01-20 11:12     ` Julien Grall
@ 2017-01-20 11:27       ` Andre Przywara
  2017-01-20 12:18         ` Julien Grall
  0 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-20 11:27 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Hi,

On 20/01/17 11:12, Julien Grall wrote:
> Hello,
> 
> On 04/01/2017 22:47, Stefano Stabellini wrote:
>> On Thu, 22 Dec 2016, Andre Przywara wrote:
>>> Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
>>> an EventID (the MSI payload or interrupt ID) to a pair of LPI number
>>> and collection ID, which points to the target CPU.
>>> This mapping is stored in the device and collection tables, which
>>> software
>>> has to provide for the ITS to use.
>>> Allocate the required memory and hand it the ITS.
>>> The maximum number of devices is limited to a compile-time constant
>>> exposed in Kconfig.
>>>
>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>> ---
>>>  xen/arch/arm/Kconfig          |   6 +++
>>>  xen/arch/arm/gic-its.c        | 114
>>> ++++++++++++++++++++++++++++++++++++++++++
>>>  xen/arch/arm/gic-v3.c         |   5 ++
>>>  xen/include/asm-arm/bitops.h  |   1 +
>>>  xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
>>>  5 files changed, 176 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>>> index a7d941c..a369305 100644
>>> --- a/xen/arch/arm/Kconfig
>>> +++ b/xen/arch/arm/Kconfig
>>> @@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
>>>            This can be overriden on the command line with the
>>> max_lpi_bits
>>>            parameter.
>>>
>>> +config MAX_ITS_PCI_BUSSES
>>> +        depends on HAS_ITS
>>> +        int "Number of PCI busses the ITS supports (4)"
>>> +        range 1 1024
>>> +        default "4"
>>
>> Given that any kind of devices can be behind an ITS, including non-PCI
>> devices, I think it is best to call this MAX_PHYS_ITS_DEVICES.
> 
> I don't think we should limit the number of devices supported at
> compilation time. We aim to have a single Xen binary running on any
> board. The best would be to guess the number at runtime, but I would be
> fine with a command line options.

Yes, so I changed this already to specify the number of devices and will
now also add a command line option.
So do you want to get rid of the Kconfig entirely to just go with
#define-ing the default value in the code?
Or do we still want to have this easily accessible way of changing the
default, at least for the first time to simplify experimentation?

Cheers,
Andre.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array
  2017-01-06 20:20       ` Stefano Stabellini
@ 2017-01-20 12:00         ` Julien Grall
  2017-01-20 22:58           ` Stefano Stabellini
  0 siblings, 1 reply; 66+ messages in thread
From: Julien Grall @ 2017-01-20 12:00 UTC (permalink / raw)
  To: Stefano Stabellini, Andre Przywara; +Cc: xen-devel, Vijay Kilari

Hi Stefano,

Sorry for the late answer, still going through my e-mail backlog.

On 06/01/2017 21:20, Stefano Stabellini wrote:
> On Fri, 6 Jan 2017, Andre Przywara wrote:
>>> It is also possible to end up calling mapti with an inexistent eventid
>>> for host_devid. Could that be a problem?
>>
>> Not at all. Actually there is no such thing as a "nonexistent event ID",
>> because the event ID will be written by the device as the payload to the
>> MSI doorbell address, probably because it learned about it by the
>> driver. So if we provision an ITTE with an event ID which the device
>> will never send, that LPI will just never fire.
>> Since Xen (in contrast to the driver in the domain) has no idea how many
>> and which MSIs the device will use, we just allocate a bunch of them.
>> The upper limit (32 atm) is something we probably need to still think
>> about, though.
>> I tried to learn a limit from Linux ("nvecs" in its_create_device()
>> seems to be the source), but couldn't find anything useful other than 32.
>> We will learn about exceeding this as soon as a domain tries to map a
>> virtual LPI with an event ID higher than 31, however it's too late to
>> fix this then. We can bark when this happens to learn if any device ever
>> does this during our testing to get some heuristic data.
>>
>> Eventually all boils down to Xen getting more information from Dom0
>> about the required number of MSIs. We could then even limit the
>> allocation to less than 32, if that helps.
>
> Originally Julien and I thought that Xen should map events up to the
> theoretically maximum for each device, but we realized that they were
> too many: an MSI-X capable device can generate up to 2048 different
> events.
>
> Xen needs to find out the exact number of events for each device. The
> information can either be provided by the guest, or the hypervisor needs
> to figure it out on its own.
>
> With Julien's PCI Passthrough work, Xen will become able to read the
> amount of events a device is capable of generating, so in the long term
> this problem should be easy to solve. But Julien's work might land one
> or two Xen releases after ITS.
>
> In the meantime, we can extend an existing PHYSDEVOP hypercall or add a
> new one. Julien, do you agree?

PHYSDEVOP are stable ABI and some are already being in used by Linux 
even on ARM.

I am not in favor of adding a PHYSDEVOP which may not be necessary in a 
couple of the release. Furthermore, there are already some issues with 
how device are being added in the ITS. Indeed, the PHYSDEVOP operations 
will provide the RID (can be deduced from the BDF), but the deviceID may 
not be equal to the RID on some platform. This is where IORT (on ACPI) 
and msi-map (on DT) comes from. The plumbing will be added during the 
PCI work.

So I would be more in favor to hardcode the device info (DeviceID, 
number of max number of MSIs) per platform until we get PCI passthrough 
working.

Note that the PCI work will not include any thoughts regarding platform 
device supporting MSI. So we may end up to add a new PHYSDEVO.

Cheers,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2017-01-20 11:27       ` Andre Przywara
@ 2017-01-20 12:18         ` Julien Grall
  2017-01-20 16:05           ` Andre Przywara
  0 siblings, 1 reply; 66+ messages in thread
From: Julien Grall @ 2017-01-20 12:18 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Hi Andre,

On 20/01/2017 12:27, Andre Przywara wrote:
> On 20/01/17 11:12, Julien Grall wrote:
>> Hello,
>>
>> On 04/01/2017 22:47, Stefano Stabellini wrote:
>>> On Thu, 22 Dec 2016, Andre Przywara wrote:
>>>> Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
>>>> an EventID (the MSI payload or interrupt ID) to a pair of LPI number
>>>> and collection ID, which points to the target CPU.
>>>> This mapping is stored in the device and collection tables, which
>>>> software
>>>> has to provide for the ITS to use.
>>>> Allocate the required memory and hand it the ITS.
>>>> The maximum number of devices is limited to a compile-time constant
>>>> exposed in Kconfig.
>>>>
>>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>>> ---
>>>>  xen/arch/arm/Kconfig          |   6 +++
>>>>  xen/arch/arm/gic-its.c        | 114
>>>> ++++++++++++++++++++++++++++++++++++++++++
>>>>  xen/arch/arm/gic-v3.c         |   5 ++
>>>>  xen/include/asm-arm/bitops.h  |   1 +
>>>>  xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
>>>>  5 files changed, 176 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>>>> index a7d941c..a369305 100644
>>>> --- a/xen/arch/arm/Kconfig
>>>> +++ b/xen/arch/arm/Kconfig
>>>> @@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
>>>>            This can be overriden on the command line with the
>>>> max_lpi_bits
>>>>            parameter.
>>>>
>>>> +config MAX_ITS_PCI_BUSSES
>>>> +        depends on HAS_ITS
>>>> +        int "Number of PCI busses the ITS supports (4)"
>>>> +        range 1 1024
>>>> +        default "4"
>>>
>>> Given that any kind of devices can be behind an ITS, including non-PCI
>>> devices, I think it is best to call this MAX_PHYS_ITS_DEVICES.
>>
>> I don't think we should limit the number of devices supported at
>> compilation time. We aim to have a single Xen binary running on any
>> board. The best would be to guess the number at runtime, but I would be
>> fine with a command line options.
>
> Yes, so I changed this already to specify the number of devices and will
> now also add a command line option.
> So do you want to get rid of the Kconfig entirely to just go with
> #define-ing the default value in the code?
> Or do we still want to have this easily accessible way of changing the
> default, at least for the first time to simplify experimentation?

Before answering to your questions, I'd like to understand why you need 
to have this value hardcoded or coming from the command line? Can't this 
be figured out at runtime?

Also what is this issue to use the number of ID bits presented in TYPER? 
Is it because your series is only handling flat table, so a large amount 
of memory would be used?

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2017-01-20 12:18         ` Julien Grall
@ 2017-01-20 16:05           ` Andre Przywara
  2017-01-20 18:14             ` Julien Grall
  0 siblings, 1 reply; 66+ messages in thread
From: Andre Przywara @ 2017-01-20 16:05 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Hi,

On 20/01/17 12:18, Julien Grall wrote:
> Hi Andre,
> 
> On 20/01/2017 12:27, Andre Przywara wrote:
>> On 20/01/17 11:12, Julien Grall wrote:
>>> Hello,
>>>
>>> On 04/01/2017 22:47, Stefano Stabellini wrote:
>>>> On Thu, 22 Dec 2016, Andre Przywara wrote:
>>>>> Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
>>>>> an EventID (the MSI payload or interrupt ID) to a pair of LPI number
>>>>> and collection ID, which points to the target CPU.
>>>>> This mapping is stored in the device and collection tables, which
>>>>> software
>>>>> has to provide for the ITS to use.
>>>>> Allocate the required memory and hand it the ITS.
>>>>> The maximum number of devices is limited to a compile-time constant
>>>>> exposed in Kconfig.
>>>>>
>>>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>>>> ---
>>>>>  xen/arch/arm/Kconfig          |   6 +++
>>>>>  xen/arch/arm/gic-its.c        | 114
>>>>> ++++++++++++++++++++++++++++++++++++++++++
>>>>>  xen/arch/arm/gic-v3.c         |   5 ++
>>>>>  xen/include/asm-arm/bitops.h  |   1 +
>>>>>  xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
>>>>>  5 files changed, 176 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>>>>> index a7d941c..a369305 100644
>>>>> --- a/xen/arch/arm/Kconfig
>>>>> +++ b/xen/arch/arm/Kconfig
>>>>> @@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
>>>>>            This can be overriden on the command line with the
>>>>> max_lpi_bits
>>>>>            parameter.
>>>>>
>>>>> +config MAX_ITS_PCI_BUSSES
>>>>> +        depends on HAS_ITS
>>>>> +        int "Number of PCI busses the ITS supports (4)"
>>>>> +        range 1 1024
>>>>> +        default "4"
>>>>
>>>> Given that any kind of devices can be behind an ITS, including non-PCI
>>>> devices, I think it is best to call this MAX_PHYS_ITS_DEVICES.
>>>
>>> I don't think we should limit the number of devices supported at
>>> compilation time. We aim to have a single Xen binary running on any
>>> board. The best would be to guess the number at runtime, but I would be
>>> fine with a command line options.
>>
>> Yes, so I changed this already to specify the number of devices and will
>> now also add a command line option.
>> So do you want to get rid of the Kconfig entirely to just go with
>> #define-ing the default value in the code?
>> Or do we still want to have this easily accessible way of changing the
>> default, at least for the first time to simplify experimentation?
> 
> Before answering to your questions, I'd like to understand why you need
> to have this value hardcoded or coming from the command line? Can't this
> be figured out at runtime?
> 
> Also what is this issue to use the number of ID bits presented in TYPER?
> Is it because your series is only handling flat table, so a large amount
> of memory would be used?

My understanding is that is the TYPER value just specifies the number of
device ID bits the ITS supports, without necessarily requiring software
to allocate tables to cover all theoretically possible devices. Instead
the expectation is that an ITS driver has some clue about how many
devices the system has or will need - which is not true in the (current)
Xen case, I am afraid.
A good ITS implementation might just implement all architected 32 bits,
which would require 32GB of a flat device table :-(
Also the indirect table feature is optional - and it happens to be that
it is _not_ available on the Cavium ThunderX, IIRC, despite it having
sparse device IDs.
And Linux limits all BASER tables to be at most 16MB.

So I think having some kind of upper limit would be good to have in any
case, whether this has to be a compile time option or not is another
question.

Cheers,
Andre.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table
  2017-01-20 16:05           ` Andre Przywara
@ 2017-01-20 18:14             ` Julien Grall
  0 siblings, 0 replies; 66+ messages in thread
From: Julien Grall @ 2017-01-20 18:14 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini; +Cc: xen-devel, Vijay Kilari

Hi Andre,

On 20/01/2017 17:05, Andre Przywara wrote:
> On 20/01/17 12:18, Julien Grall wrote:
>> Hi Andre,
>>
>> On 20/01/2017 12:27, Andre Przywara wrote:
>>> On 20/01/17 11:12, Julien Grall wrote:
>>>> Hello,
>>>>
>>>> On 04/01/2017 22:47, Stefano Stabellini wrote:
>>>>> On Thu, 22 Dec 2016, Andre Przywara wrote:
>>>>>> Each ITS maps a pair of a DeviceID (usually the PCI b/d/f triplet) and
>>>>>> an EventID (the MSI payload or interrupt ID) to a pair of LPI number
>>>>>> and collection ID, which points to the target CPU.
>>>>>> This mapping is stored in the device and collection tables, which
>>>>>> software
>>>>>> has to provide for the ITS to use.
>>>>>> Allocate the required memory and hand it the ITS.
>>>>>> The maximum number of devices is limited to a compile-time constant
>>>>>> exposed in Kconfig.
>>>>>>
>>>>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>>>>> ---
>>>>>>  xen/arch/arm/Kconfig          |   6 +++
>>>>>>  xen/arch/arm/gic-its.c        | 114
>>>>>> ++++++++++++++++++++++++++++++++++++++++++
>>>>>>  xen/arch/arm/gic-v3.c         |   5 ++
>>>>>>  xen/include/asm-arm/bitops.h  |   1 +
>>>>>>  xen/include/asm-arm/gic-its.h |  51 ++++++++++++++++++-
>>>>>>  5 files changed, 176 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>>>>>> index a7d941c..a369305 100644
>>>>>> --- a/xen/arch/arm/Kconfig
>>>>>> +++ b/xen/arch/arm/Kconfig
>>>>>> @@ -59,6 +59,12 @@ config MAX_HOST_LPI_BITS
>>>>>>            This can be overriden on the command line with the
>>>>>> max_lpi_bits
>>>>>>            parameter.
>>>>>>
>>>>>> +config MAX_ITS_PCI_BUSSES
>>>>>> +        depends on HAS_ITS
>>>>>> +        int "Number of PCI busses the ITS supports (4)"
>>>>>> +        range 1 1024
>>>>>> +        default "4"
>>>>>
>>>>> Given that any kind of devices can be behind an ITS, including non-PCI
>>>>> devices, I think it is best to call this MAX_PHYS_ITS_DEVICES.
>>>>
>>>> I don't think we should limit the number of devices supported at
>>>> compilation time. We aim to have a single Xen binary running on any
>>>> board. The best would be to guess the number at runtime, but I would be
>>>> fine with a command line options.
>>>
>>> Yes, so I changed this already to specify the number of devices and will
>>> now also add a command line option.
>>> So do you want to get rid of the Kconfig entirely to just go with
>>> #define-ing the default value in the code?
>>> Or do we still want to have this easily accessible way of changing the
>>> default, at least for the first time to simplify experimentation?
>>
>> Before answering to your questions, I'd like to understand why you need
>> to have this value hardcoded or coming from the command line? Can't this
>> be figured out at runtime?
>>
>> Also what is this issue to use the number of ID bits presented in TYPER?
>> Is it because your series is only handling flat table, so a large amount
>> of memory would be used?
>
> My understanding is that is the TYPER value just specifies the number of
> device ID bits the ITS supports, without necessarily requiring software
> to allocate tables to cover all theoretically possible devices. Instead
> the expectation is that an ITS driver has some clue about how many
> devices the system has or will need - which is not true in the (current)
> Xen case, I am afraid.

Looking at Linux code (see its_parse_baser_device), the number of bits 
in TYPER is used to computer the number of device supported. The number 
of device supported will be clamped if it is too big.

I think it is a quite sensible solution as a user may not know the last 
DeviceID.

> A good ITS implementation might just implement all architected 32 bits,
> which would require 32GB of a flat device table :-(
> Also the indirect table feature is optional - and it happens to be that
> it is _not_ available on the Cavium ThunderX, IIRC, despite it having
> sparse device IDs.
> And Linux limits all BASER tables to be at most 16MB.

Do you know why the 16MB?

>
> So I think having some kind of upper limit would be good to have in any
> case, whether this has to be a compile time option or not is another
> question.

Could not we have a size handling all the tables rather than being 
specific to the device table?

Cheers,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array
  2017-01-20 12:00         ` Julien Grall
@ 2017-01-20 22:58           ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-01-20 22:58 UTC (permalink / raw)
  To: Julien Grall; +Cc: Andre Przywara, Stefano Stabellini, Vijay Kilari, xen-devel

On Fri, 20 Jan 2017, Julien Grall wrote:
> Hi Stefano,
> 
> Sorry for the late answer, still going through my e-mail backlog.
> 
> On 06/01/2017 21:20, Stefano Stabellini wrote:
> > On Fri, 6 Jan 2017, Andre Przywara wrote:
> > > > It is also possible to end up calling mapti with an inexistent eventid
> > > > for host_devid. Could that be a problem?
> > > 
> > > Not at all. Actually there is no such thing as a "nonexistent event ID",
> > > because the event ID will be written by the device as the payload to the
> > > MSI doorbell address, probably because it learned about it by the
> > > driver. So if we provision an ITTE with an event ID which the device
> > > will never send, that LPI will just never fire.
> > > Since Xen (in contrast to the driver in the domain) has no idea how many
> > > and which MSIs the device will use, we just allocate a bunch of them.
> > > The upper limit (32 atm) is something we probably need to still think
> > > about, though.
> > > I tried to learn a limit from Linux ("nvecs" in its_create_device()
> > > seems to be the source), but couldn't find anything useful other than 32.
> > > We will learn about exceeding this as soon as a domain tries to map a
> > > virtual LPI with an event ID higher than 31, however it's too late to
> > > fix this then. We can bark when this happens to learn if any device ever
> > > does this during our testing to get some heuristic data.
> > > 
> > > Eventually all boils down to Xen getting more information from Dom0
> > > about the required number of MSIs. We could then even limit the
> > > allocation to less than 32, if that helps.
> > 
> > Originally Julien and I thought that Xen should map events up to the
> > theoretically maximum for each device, but we realized that they were
> > too many: an MSI-X capable device can generate up to 2048 different
> > events.
> > 
> > Xen needs to find out the exact number of events for each device. The
> > information can either be provided by the guest, or the hypervisor needs
> > to figure it out on its own.
> > 
> > With Julien's PCI Passthrough work, Xen will become able to read the
> > amount of events a device is capable of generating, so in the long term
> > this problem should be easy to solve. But Julien's work might land one
> > or two Xen releases after ITS.
> > 
> > In the meantime, we can extend an existing PHYSDEVOP hypercall or add a
> > new one. Julien, do you agree?
> 
> PHYSDEVOP are stable ABI and some are already being in used by Linux even on
> ARM.
> 
> I am not in favor of adding a PHYSDEVOP which may not be necessary in a couple
> of the release. Furthermore, there are already some issues with how device are
> being added in the ITS. Indeed, the PHYSDEVOP operations will provide the RID
> (can be deduced from the BDF), but the deviceID may not be equal to the RID on
> some platform. This is where IORT (on ACPI) and msi-map (on DT) comes from.
> The plumbing will be added during the PCI work.
> 
> So I would be more in favor to hardcode the device info (DeviceID, number of
> max number of MSIs) per platform until we get PCI passthrough working.

I think that would be OK for a first ITS implementation in Xen. We can
defer the physdevop to later, after you complete PCI passthrough. I also
agree that we'll probably end up with a physdevop anyway, but at least
we'll have a better idea about what we need.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [RFC PATCH v2 14/26] ARM: vGICv3: introduce basic ITS emulation bits
  2016-12-22 18:24 ` [RFC PATCH v2 14/26] ARM: vGICv3: introduce basic ITS emulation bits Andre Przywara
@ 2017-02-15  0:02   ` Stefano Stabellini
  0 siblings, 0 replies; 66+ messages in thread
From: Stefano Stabellini @ 2017-02-15  0:02 UTC (permalink / raw)
  To: Andre Przywara; +Cc: xen-devel, Julien Grall, Stefano Stabellini, Vijay Kilari

On Thu, 22 Dec 2016, Andre Przywara wrote:
> Create a new file to hold the emulation code for the ITS widget.
> For now we emulate the memory mapped ITS registers and provide a stub
> to introduce the ITS command handling framework (but without actually
> emulating any commands at this time).
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Please see alpine.DEB.2.10.1611081507410.3491@sstabellini-ThinkPad-X260

> ---
>  xen/arch/arm/Makefile             |   1 +
>  xen/arch/arm/vgic-its.c           | 377 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3.c            |   9 -
>  xen/include/asm-arm/gic_v3_defs.h |  19 ++
>  4 files changed, 397 insertions(+), 9 deletions(-)
>  create mode 100644 xen/arch/arm/vgic-its.c
> 
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index 80c2951..346454d 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -45,6 +45,7 @@ obj-y += traps.o
>  obj-y += vgic.o
>  obj-y += vgic-v2.o
>  obj-$(CONFIG_HAS_GICV3) += vgic-v3.o
> +obj-$(CONFIG_HAS_ITS) += vgic-its.o
>  obj-y += vm_event.o
>  obj-y += vtimer.o
>  obj-y += vpsci.o
> diff --git a/xen/arch/arm/vgic-its.c b/xen/arch/arm/vgic-its.c
> new file mode 100644
> index 0000000..e5e9ae4
> --- /dev/null
> +++ b/xen/arch/arm/vgic-its.c
> @@ -0,0 +1,377 @@
> +/*
> + * xen/arch/arm/vgic-its.c
> + *
> + * ARM Interrupt Translation Service (ITS) emulation
> + *
> + * Andre Przywara <andre.przywara@arm.com>
> + * Copyright (c) 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 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 <xen/bitops.h>
> +#include <xen/config.h>
> +#include <xen/domain_page.h>
> +#include <xen/lib.h>
> +#include <xen/init.h>
> +#include <xen/softirq.h>
> +#include <xen/irq.h>
> +#include <xen/sched.h>
> +#include <xen/sizes.h>
> +#include <asm/current.h>
> +#include <asm/mmio.h>
> +#include <asm/gic_v3_defs.h>
> +#include <asm/gic-its.h>
> +#include <asm/vgic.h>
> +#include <asm/vgic-emul.h>
> +
> +/* Data structure to describe a virtual ITS */
> +struct virt_its {
> +    struct domain *d;
> +    struct host_its *hw_its;
> +    spinlock_t vcmd_lock;       /* protects the virtual command buffer */
> +    uint64_t cbaser;
> +    uint64_t *cmdbuf;
> +    int cwriter;
> +    int creadr;
> +    spinlock_t its_lock;        /* protects the collection and device tables */
> +    uint64_t baser0, baser1;
> +    uint16_t *coll_table;
> +    int max_collections;
> +    uint64_t *dev_table;
> +    int max_devices;
> +    bool enabled;
> +};
> +
> +/* An Interrupt Translation Table Entry: this is indexed by a
> + * DeviceID/EventID pair and is located in guest memory.
> + */
> +struct vits_itte
> +{
> +    uint32_t vlpi;
> +    uint16_t collection;
> +};
> +
> +/**************************************
> + * Functions that handle ITS commands *
> + **************************************/
> +
> +static uint64_t its_cmd_mask_field(uint64_t *its_cmd,
> +                                   int word, int shift, int size)
> +{
> +    return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT(size) - 1);
> +}
> +
> +#define its_cmd_get_command(cmd)        its_cmd_mask_field(cmd, 0,  0,  8)
> +#define its_cmd_get_deviceid(cmd)       its_cmd_mask_field(cmd, 0, 32, 32)
> +#define its_cmd_get_size(cmd)           its_cmd_mask_field(cmd, 1,  0,  5)
> +#define its_cmd_get_id(cmd)             its_cmd_mask_field(cmd, 1,  0, 32)
> +#define its_cmd_get_physical_id(cmd)    its_cmd_mask_field(cmd, 1, 32, 32)
> +#define its_cmd_get_collection(cmd)     its_cmd_mask_field(cmd, 2,  0, 16)
> +#define its_cmd_get_target_addr(cmd)    its_cmd_mask_field(cmd, 2, 16, 32)
> +#define its_cmd_get_validbit(cmd)       its_cmd_mask_field(cmd, 2, 63,  1)
> +
> +#define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
> +
> +static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its,
> +                                uint32_t writer)
> +{
> +    uint64_t *cmdptr;
> +
> +    if ( !its->cmdbuf )
> +        return -1;
> +
> +    if ( writer >= ITS_CMD_BUFFER_SIZE(its->cbaser) )
> +        return -1;
> +
> +    spin_lock(&its->vcmd_lock);
> +
> +    while ( its->creadr != writer )
> +    {
> +        cmdptr = its->cmdbuf + (its->creadr / sizeof(*its->cmdbuf));
> +        switch (its_cmd_get_command(cmdptr))
> +        {
> +        case GITS_CMD_SYNC:
> +            /* We handle ITS commands synchronously, so we ignore SYNC. */
> +	    break;
> +        default:
> +            gdprintk(XENLOG_G_WARNING, "ITS: unhandled ITS command %ld\n",
> +                   its_cmd_get_command(cmdptr));
> +            break;
> +        }
> +
> +        its->creadr += ITS_CMD_SIZE;
> +        if ( its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser) )
> +            its->creadr = 0;
> +    }
> +    its->cwriter = writer;
> +
> +    spin_unlock(&its->vcmd_lock);
> +
> +    return 0;
> +}
> +
> +/*****************************
> + * ITS registers read access *
> + *****************************/
> +
> +/* The physical address is encoded slightly differently depending on
> + * the used page size: the highest four bits are stored in the lowest
> + * four bits of the field for 64K pages.
> + */
> +static paddr_t get_baser_phys_addr(uint64_t reg)
> +{
> +    if ( reg & BIT(9) )
> +        return (reg & GENMASK(47, 16)) | ((reg & GENMASK(15, 12)) << 36);
> +    else
> +        return reg & GENMASK(47, 12);
> +}
> +
> +static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
> +                                 register_t *r, void *priv)
> +{
> +    struct virt_its *its = priv;
> +
> +    switch ( info->gpa & 0xffff )
> +    {
> +    case VREG32(GITS_CTLR):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +        *r = vgic_reg32_extract(its->enabled | BIT(31), info);
> +	break;
> +    case VREG32(GITS_IIDR):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +        *r = vgic_reg32_extract(GITS_IIDR_VALUE, info);
> +        break;
> +    case VREG64(GITS_TYPER):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        *r = vgic_reg64_extract(0x1eff1, info);
> +        break;
> +    case VREG64(GITS_CBASER):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        *r = vgic_reg64_extract(its->cbaser, info);
> +        break;
> +    case VREG64(GITS_CWRITER):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        *r = vgic_reg64_extract(its->cwriter, info);
> +        break;
> +    case VREG64(GITS_CREADR):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        *r = vgic_reg64_extract(its->creadr, info);
> +        break;
> +    case VREG64(GITS_BASER0):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        *r = vgic_reg64_extract(its->baser0, info);
> +        break;
> +    case VREG64(GITS_BASER1):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        *r = vgic_reg64_extract(its->baser1, info);
> +        break;
> +    case VRANGE64(GITS_BASER2, GITS_BASER7):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        *r = vgic_reg64_extract(0, info);
> +        break;
> +    case VREG32(GICD_PIDR2):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +        *r = vgic_reg32_extract(GICV3_GICD_PIDR2, info);
> +        break;
> +    }
> +
> +    return 1;
> +
> +bad_width:
> +    domain_crash_synchronous();
> +
> +    return 0;
> +}
> +
> +/******************************
> + * ITS registers write access *
> + ******************************/
> +
> +static int its_baser_table_size(uint64_t baser)
> +{
> +    int page_size = 0;
> +
> +    switch ( (baser >> 8) & 3 )
> +    {
> +    case 0: page_size = SZ_4K; break;
> +    case 1: page_size = SZ_16K; break;
> +    case 2:
> +    case 3: page_size = SZ_64K; break;
> +    }
> +
> +    return page_size * ((baser & GENMASK(7, 0)) + 1);
> +}
> +
> +static int its_baser_nr_entries(uint64_t baser)
> +{
> +    int entry_size = ((baser & GENMASK(52, 48)) >> 48) + 1;
> +
> +    return its_baser_table_size(baser) / entry_size;
> +}
> +
> +static int vgic_v3_its_mmio_write(struct vcpu *v, mmio_info_t *info,
> +                                  register_t r, void *priv)
> +{
> +    struct domain *d = v->domain;
> +    struct virt_its *its = priv;
> +    uint64_t reg;
> +    uint32_t ctlr;
> +
> +    switch ( info->gpa & 0xffff )
> +    {
> +    case VREG32(GITS_CTLR):
> +        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +	vgic_reg32_update(&ctlr, r, info);
> +	its->enabled = ctlr & GITS_CTLR_ENABLE;
> +	/* TODO: trigger something ... */
> +        return 1;
> +    case VREG32(GITS_IIDR):
> +        goto write_ignore_32;
> +    case VREG32(GITS_TYPER):
> +        goto write_ignore_32;
> +    case VREG64(GITS_CBASER):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +
> +        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
> +        if ( its->enabled )
> +            return 1;
> +
> +        reg = its->cbaser;
> +        vgic_reg64_update(&reg, r, info);
> +        /* TODO: sanitise! */
> +        its->cbaser = reg;
> +
> +        if ( reg & BIT(63) )
> +        {
> +            its->cmdbuf = map_guest_pages(d, reg & GENMASK(51, 12), 1);
> +        }
> +        else
> +        {
> +            unmap_guest_pages(its->cmdbuf, 1);
> +            its->cmdbuf = NULL;
> +        }
> +
> +	return 1;
> +    case VREG64(GITS_CWRITER):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +        reg = its->cwriter;
> +        vgic_reg64_update(&reg, r, info);
> +        vgic_its_handle_cmds(d, its, reg);
> +        return 1;
> +    case VREG64(GITS_CREADR):
> +        goto write_ignore_64;
> +    case VREG64(GITS_BASER0):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +
> +        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
> +        if ( its->enabled )
> +            return 1;
> +
> +        reg = its->baser0;
> +        vgic_reg64_update(&reg, r, info);
> +
> +        reg &= ~GITS_BASER_RO_MASK;
> +        reg |= (sizeof(uint64_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
> +        reg |= GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT;
> +        /* TODO: sanitise! */
> +        /* TODO: locking(?) */
> +
> +        if ( reg & GITS_BASER_VALID )
> +        {
> +            its->dev_table = map_guest_pages(d,
> +                                             get_baser_phys_addr(reg),
> +                                             its_baser_table_size(reg) >> PAGE_SHIFT);
> +            its->max_devices = its_baser_nr_entries(reg);
> +            memset(its->dev_table, 0, its->max_devices * sizeof(uint64_t));
> +        }
> +        else
> +        {
> +            unmap_guest_pages(its->dev_table,
> +                              its_baser_table_size(reg) >> PAGE_SHIFT);
> +            its->max_devices = 0;
> +        }
> +
> +        its->baser0 = reg;
> +        return 1;
> +    case VREG64(GITS_BASER1):
> +        if ( info->dabt.size < DABT_WORD ) goto bad_width;
> +
> +        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
> +        if ( its->enabled )
> +            return 1;
> +
> +        reg = its->baser1;
> +        vgic_reg64_update(&reg, r, info);
> +        reg &= ~GITS_BASER_RO_MASK;
> +        reg |= (sizeof(uint16_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
> +        reg |= GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT;
> +        /* TODO: sanitise! */
> +
> +        /* TODO: sort out locking */
> +        /* TODO: repeated calls: free old mapping */
> +        if ( reg & GITS_BASER_VALID )
> +        {
> +            its->coll_table = map_guest_pages(d, get_baser_phys_addr(reg),
> +                                              its_baser_table_size(reg) >> PAGE_SHIFT);
> +            its->max_collections = its_baser_nr_entries(reg);
> +            memset(its->coll_table, 0xff,
> +                   its->max_collections * sizeof(uint16_t));
> +        }
> +        else
> +        {
> +            unmap_guest_pages(its->coll_table,
> +                              its_baser_table_size(reg) >> PAGE_SHIFT);
> +            its->max_collections = 0;
> +        }
> +        its->baser1 = reg;
> +        return 1;
> +    case VRANGE64(GITS_BASER2, GITS_BASER7):
> +        goto write_ignore_64;
> +    default:
> +        gdprintk(XENLOG_G_WARNING, "ITS: unhandled ITS register 0x%lx\n",
> +                 info->gpa & 0xffff);
> +        return 0;
> +    }
> +
> +    return 1;
> +
> +write_ignore_64:
> +    if ( ! vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +    return 1;
> +
> +write_ignore_32:
> +    if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +    return 1;
> +
> +bad_width:
> +    printk(XENLOG_G_ERR "%pv vGICR: bad read width %d r%d offset %#08lx\n",
> +           v, info->dabt.size, info->dabt.reg, info->gpa & 0xffff);
> +
> +    domain_crash_synchronous();
> +
> +    return 0;
> +}
> +
> +static const struct mmio_handler_ops vgic_its_mmio_handler = {
> +    .read  = vgic_v3_its_mmio_read,
> +    .write = vgic_v3_its_mmio_write,
> +};
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 206e00b..a9b04ab 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -159,15 +159,6 @@ static void vgic_store_irouter(struct domain *d, struct vgic_irq_rank *rank,
>      rank->vcpu[offset] = new_vcpu->vcpu_id;
>  }
>  
> -static inline bool vgic_reg64_check_access(struct hsr_dabt dabt)
> -{
> -    /*
> -     * 64 bits registers can be accessible using 32-bit and 64-bit unless
> -     * stated otherwise (See 8.1.3 ARM IHI 0069A).
> -     */
> -    return ( dabt.size == DABT_DOUBLE_WORD || dabt.size == DABT_WORD );
> -}
> -
>  static int __vgic_v3_rdistr_rd_mmio_read(struct vcpu *v, mmio_info_t *info,
>                                           uint32_t gicr_reg,
>                                           register_t *r)
> diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
> index da5fb77..6a91f5b 100644
> --- a/xen/include/asm-arm/gic_v3_defs.h
> +++ b/xen/include/asm-arm/gic_v3_defs.h
> @@ -147,6 +147,16 @@
>  #define LPI_PROP_RES1                (1 << 1)
>  #define LPI_PROP_ENABLED             (1 << 0)
>  
> +/*
> + * PIDR2: Only bits[7:4] are not implementation defined. We are
> + * emulating a GICv3 ([7:4] = 0x3).
> + *
> + * We don't emulate a specific registers scheme so implement the others
> + * bits as RES0 as recommended by the spec (see 8.1.13 in ARM IHI 0069A).
> + */
> +#define GICV3_GICD_PIDR2  0x30
> +#define GICV3_GICR_PIDR2  GICV3_GICD_PIDR2
> +
>  #define GICH_VMCR_EOI                (1 << 9)
>  #define GICH_VMCR_VENG1              (1 << 1)
>  
> @@ -190,6 +200,15 @@ struct rdist_region {
>      bool single_rdist;
>  };
>  
> +/*
> + * 64 bits registers can be accessible using 32-bit and 64-bit unless
> + * stated otherwise (See 8.1.3 ARM IHI 0069A).
> + */
> +static inline bool vgic_reg64_check_access(struct hsr_dabt dabt)
> +{
> +    return ( dabt.size == DABT_DOUBLE_WORD || dabt.size == DABT_WORD );
> +}
> +
>  #endif /* __ASM_ARM_GIC_V3_DEFS_H__ */
>  
>  /*
> -- 
> 2.9.0
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 66+ messages in thread

end of thread, other threads:[~2017-02-15  0:02 UTC | newest]

Thread overview: 66+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-22 18:24 [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 01/26] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 02/26] ARM: GICv3: allocate LPI pending and property table Andre Przywara
2017-01-04 21:09   ` Stefano Stabellini
2017-01-05 17:13     ` Andre Przywara
2017-01-05 17:52       ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 03/26] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
2017-01-04 21:47   ` Stefano Stabellini
2017-01-05 17:56     ` Andre Przywara
2017-01-20 11:12     ` Julien Grall
2017-01-20 11:27       ` Andre Przywara
2017-01-20 12:18         ` Julien Grall
2017-01-20 16:05           ` Andre Przywara
2017-01-20 18:14             ` Julien Grall
2016-12-22 18:24 ` [RFC PATCH v2 04/26] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
2017-01-04 21:53   ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 05/26] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
2017-01-04 22:08   ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 06/26] ARM: GICv3 ITS: introduce device mapping Andre Przywara
2017-01-05  0:13   ` Stefano Stabellini
2017-01-13 12:31     ` Andre Przywara
2017-01-13 19:22       ` Stefano Stabellini
2017-01-13 12:31     ` Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 07/26] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
2017-01-05 18:56   ` Stefano Stabellini
2017-01-06 17:59     ` Andre Przywara
2017-01-06 20:20       ` Stefano Stabellini
2017-01-20 12:00         ` Julien Grall
2017-01-20 22:58           ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 08/26] ARM: GICv3 ITS: map device and LPIs to the ITS on physdev_op hypercall Andre Przywara
2017-01-05 21:23   ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 09/26] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
2017-01-05 21:36   ` Stefano Stabellini
2017-01-12 18:24     ` Andre Przywara
2017-01-12 19:15       ` Stefano Stabellini
2017-01-12 19:28         ` Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 10/26] ARM: GICv3: forward pending LPIs to guests Andre Przywara
2017-01-05 22:10   ` Stefano Stabellini
2017-01-12 12:16     ` Andre Przywara
2017-01-12 18:57       ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 11/26] ARM: GICv3: enable ITS and LPIs on the host Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 12/26] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
2017-01-05 22:17   ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIs Andre Przywara
2017-01-05 22:28   ` [RFC PATCH v2 13/26] ARM: vGICv3: Handle disabled LPIso Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 14/26] ARM: vGICv3: introduce basic ITS emulation bits Andre Przywara
2017-02-15  0:02   ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 15/26] ARM: vITS: handle CLEAR command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 16/26] ARM: vITS: handle INT command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 17/26] ARM: vITS: handle MAPC command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 18/26] ARM: vITS: handle MAPD command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 19/26] ARM: vITS: handle MAPTI command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 20/26] ARM: vITS: handle MOVI command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 21/26] ARM: vITS: handle DISCARD command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 22/26] ARM: vITS: handle INV command Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 23/26] ARM: vITS: handle INVALL command Andre Przywara
2017-01-05 22:50   ` Stefano Stabellini
2016-12-22 18:24 ` [RFC PATCH v2 24/26] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 25/26] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
2016-12-22 18:24 ` [RFC PATCH v2 26/26] ARM: vGIC: advertising LPI support Andre Przywara
2017-01-18  8:13 ` [RFC PATCH v2 00/26] arm64: Dom0 ITS emulation Vijay Kilari
2017-01-18  9:55   ` Julien Grall
2017-01-19 12:26 ` Vijay Kilari
2017-01-19 13:50   ` Andre Przywara
2017-01-19 14:32     ` Julien Grall
2017-01-19 14:45       ` Andre Przywara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.