All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/30] arm64: Dom0 ITS emulation
@ 2017-04-05 23:18 Andre Przywara
  2017-04-05 23:18 ` [PATCH v5 01/30] bitops: add GENMASK_ULL Andre Przywara
                   ` (30 more replies)
  0 siblings, 31 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

another round with lots of fixes for the ITS emulation series.
The access to guest memory has been reworked, we now use a routine to
copy to and from guest memory to also guarantee atomic access.
This is courtesy of Vijaya Kumar, from a previous series.
For a detailed changelog see below.

Open questions:
- Do we really need the GENMASK_ULL and BIT_ULL version ? This series is
  really only for ARM64 (which doesn't need those), so we could get rid
  of this, possibly introduce them later should we *really* need an ARM32
  version. I think this will be our least problem then.
- I was thinking about MOVALL and the spec is really confusing here.
  It needs some discussion whether we need to emulate it or not. However
  Linux does not use this command, so we might get away with it now.
  An implementation might be doable along the lines of INVALL.
- Julien asked for more ITS documentation, which is quite hard to come up
  with. I will try to explain things in more details tomorrow in commit
  messages, if there are still things missing.

Cheers,
Andre

----------------------------------
This series adds support for emulation of an ARM GICv3 ITS interrupt
controller. 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. Should it emerge that traversing a linked list
is a performance issue, this can be changed to use a hash table.

* 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. We use a function to copy parts of the guest memory to be able
to access those tables.
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. This is
acceptable because "mapping" them is actually very cheap on arm64. Also as
we can't properly protect those areas due to their sub-page-size property,
we validate the data in there before actually using it. The vITS code
basically just stores the data in there which the guest has actually
transferred via the virtual ITS command queue before, so there is no secret
revealed nor does it create an attack vector for a malicious guest.

* 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 we try to avoid this situation: Dom0 sending a device mapping (MAPD)
command is the only time we allow queuing commands to the host ITS command
queue, as this seems to be the only reliable way of getting the
required information at the moment. However at the same time we map all
events to LPIs already, also enable them. This avoids sending commands
later at runtime, as we can deal with mappings and LPI enabling/disabling
internally.

As it is expected that the ITS support will become a tech preview in the
first release, there is a Kconfig option to enable it. Also it is
supported on arm64 only, which will most likely not change in the future.
This leads to some hideous constructs like an #ifdef'ed header file with
empty function stubs, I have some hope we can still clean this up.
The number of LPIs supported on the host is a command line option, to
limit memory usage in case hardware offers a very high number.

This code boots Dom0 on an ARM Fast Model with ITS support. I tried to
address the issues seen by people running the previous version on real
hardware, though couldn't verify this here for myself.
So any testing, bug reports (and possibly even fixes) are very welcome.

The code can also be found on the its/v5 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/v5

Cheers,
Andre

Changelog v4 ... v5:
- adding many comments
- spinlock asserts
- rename r_host_lpis to max_host_lpi_ids
- remove max_its_device_bits command line
- add warning on high number of LPIs
- avoid potential leak on host MAPD
- properly handle nr_events rounding
- remove unmap_all_devices(), replace with ASSERT
- add barriers for (lockless) host LPI lookups
- add proper locking in ITS and redist MMIO register handling
- rollback failing device mapping
- fix various printks
- add vgic_access_guest_memory() and use it
- (getting rid of page mapping functions and helpers)
- drop table mapping / unmapping on redist/ITS enable/disable
- minor reworks in functions as per review comments
- fix ITS enablement check
- move lpi_to_pending() and lpi_get_priority() to vgic_ops
- move do_LPI() to gic_hw_ops
- whitespace and hard tabs fixes
- introduce ITS domain init function (and use it for the rbtree)
- enable IRQs around do_LPI
- implement TODOs for later optimizations
- add "v" prefix to variables holding virtual properties
- provide locked and normal versions of read/write_itte
- only CLEAR LPI if not already guest visible (plus comment)
- update LPI property on MAPTI
- store vcpu_id in pending_irq for LPIs (helps INVALL)
- improve INVALL implementation to only cover LPIs on this VCPU
- improve virtual BASE register initialization
- limit number of virtual LPIs to 24 bits (Linux bug at 32??)
- only inject LPIs if redistributor is actually enabled

Changelog v3 .. v4:
- make HAS_ITS depend on EXPERT
- introduce new patch 02 to initialize host ITS early
- fix cmd_lock init position
- introduce warning on high number of LPI allocations
- various int -> unsigned fixes
- adding and improving comments
- rate limit ITS command queue full msg
- drop unneeded checks
- validate against allowed number of device IDs
- avoid memory leaks when removing devices
- improve algorithm for finding free host LPI
- convert unmap_all_devices from goto to while loop
- add message on remapping ITS device
- name virtual device / event IDs properly
- use atomic read when reading ITT entry

Changelog v2 .. v3:
- preallocate struct pending_irq's
- map ITS and redistributor tables only on demand
- store property, enable and pending bit in struct pending_irq
- improve error checking and handling
- add comments

Changelog v1 .. v2:
- clean up header file inclusion
- rework host ITS table allocation: observe attributes, many fixes
- remove patch 1 to export __flush_dcache_area, use existing function instead
- use number of LPIs internally instead of number of bits
- keep host_its_list as private as possible
- keep struct its_devices private
- rework gicv3_its_map_guest_devices
- fix rbtree issues
- more error handling and propagation
- cope with GICv4 implementations (but no virtual LPI features!)
- abstract host and guest ITSes by using doorbell addresses
- join per-redistributor variables into one per-CPU structure
- fix data types (unsigned int)
- many minor bug fixes

(Rough) changelog RFC-v2 .. v1:
- split host ITS driver into gic-v3-lpi.c and gic-v3-its.c part
- rename virtual ITS driver file to vgic-v3-its.c
- use macros and named constants for all magic numbers
- use atomic accessors for accessing the host LPI data
- remove leftovers from connecting virtual and host ITSes
- bail out if host ITS is disabled in the DT
- rework map/unmap_guest_pages():
    - split off p2m part as get/put_guest_pages (to be done on allocation)
    - get rid of vmap, using map_domain_page() instead
- delay allocation of virtual tables until actual LPI/ITS enablement
- properly size both virtual and physical tables upon allocation
- fix put_domain() locking issues in physdev_op and LPI handling code
- add and extend comments in various areas
- fix lotsa coding style and white space issues, including comment style
- add locking to data structures not yet covered
- fix various locking issues
- use an rbtree to deal with ITS devices (instead of a list)
- properly handle memory attributes for ITS tables
- handle cacheable/non-cacheable ITS table mappings
- sanitize guest provided ITS/LPI table attributes
- fix breakage on non-GICv2 compatible host GICv3 controllers
- add command line parameters on top of Kconfig options
- properly wait for an ITS to become quiescient before enabling it
- handle host ITS command queue errors
- actually wait for host ITS command completion (READR==WRITER)
- fix ARM32 compilation
- various patch splits and reorderings

*** BLURB HERE ***

Andre Przywara (30):
  bitops: add GENMASK_ULL
  bitops: add BIT_ULL variant
  ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  ARM: GICv3 ITS: initialize host ITS
  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 host LPI array
  ARM: vGICv3: introduce ITS emulation stub
  ARM: GICv3 ITS: introduce device mapping
  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: vITS: add command handling stub and MMIO emulation
  ARM: vITS: introduce translation table walks
  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: advertise LPI support

 docs/misc/xen-command-line.markdown |    9 +
 xen/arch/arm/Kconfig                |    5 +
 xen/arch/arm/Makefile               |    3 +
 xen/arch/arm/gic-v2.c               |    7 +
 xen/arch/arm/gic-v3-its.c           |  997 ++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3-lpi.c           |  589 +++++++++++++++++
 xen/arch/arm/gic-v3.c               |   81 ++-
 xen/arch/arm/gic.c                  |   10 +-
 xen/arch/arm/vgic-v2.c              |   15 +
 xen/arch/arm/vgic-v3-its.c          | 1213 +++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-v3.c              |  261 +++++++-
 xen/arch/arm/vgic.c                 |   50 +-
 xen/include/asm-arm/bitops.h        |    1 +
 xen/include/asm-arm/config.h        |    2 +
 xen/include/asm-arm/domain.h        |   13 +-
 xen/include/asm-arm/gic.h           |    2 +
 xen/include/asm-arm/gic_v3_defs.h   |   63 ++
 xen/include/asm-arm/gic_v3_its.h    |  276 ++++++++
 xen/include/asm-arm/irq.h           |   16 +
 xen/include/asm-arm/vgic.h          |   10 +
 xen/include/xen/bitops.h            |    5 +-
 21 files changed, 3586 insertions(+), 42 deletions(-)
 create mode 100644 xen/arch/arm/gic-v3-its.c
 create mode 100644 xen/arch/arm/gic-v3-lpi.c
 create mode 100644 xen/arch/arm/vgic-v3-its.c
 create mode 100644 xen/include/asm-arm/gic_v3_its.h

-- 
2.8.2


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

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

* [PATCH v5 01/30] bitops: add GENMASK_ULL
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
@ 2017-04-05 23:18 ` Andre Przywara
  2017-04-05 23:25   ` Stefano Stabellini
  2017-04-05 23:18 ` [PATCH v5 02/30] bitops: add BIT_ULL variant Andre Przywara
                   ` (29 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

To safely handle 64-bit registers even on 32-bit systems, introduce
a GENMASK_ULL variant (lifted from Linux).
This adds a BITS_PER_LONG_LONG define as well.
Also fix a bug in the comment for the existing GENMASK variant.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/include/asm-arm/config.h | 2 ++
 xen/include/xen/bitops.h     | 5 ++++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
index b2edf95..1f730ce 100644
--- a/xen/include/asm-arm/config.h
+++ b/xen/include/asm-arm/config.h
@@ -19,6 +19,8 @@
 #define BITS_PER_LONG (BYTES_PER_LONG << 3)
 #define POINTER_ALIGN BYTES_PER_LONG
 
+#define BITS_PER_LONG_LONG (sizeof (long long) * BITS_PER_BYTE)
+
 /* xen_ulong_t is always 64 bits */
 #define BITS_PER_XEN_ULONG 64
 
diff --git a/xen/include/xen/bitops.h b/xen/include/xen/bitops.h
index bd0883a..9261e06 100644
--- a/xen/include/xen/bitops.h
+++ b/xen/include/xen/bitops.h
@@ -5,11 +5,14 @@
 /*
  * Create a contiguous bitmask starting at bit position @l and ending at
  * position @h. For example
- * GENMASK(30, 21) gives us the 32bit vector 0x01fe00000.
+ * GENMASK(30, 21) gives us the 32bit vector 0x7fe00000.
  */
 #define GENMASK(h, l) \
     (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
 
+#define GENMASK_ULL(h, l) \
+    (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
+
 /*
  * ffs: find first bit set. This is defined the same way as
  * the libc and compiler builtin ffs routines, therefore
-- 
2.8.2


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

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

* [PATCH v5 02/30] bitops: add BIT_ULL variant
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
  2017-04-05 23:18 ` [PATCH v5 01/30] bitops: add GENMASK_ULL Andre Przywara
@ 2017-04-05 23:18 ` Andre Przywara
  2017-04-05 23:26   ` Stefano Stabellini
  2017-04-05 23:18 ` [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
                   ` (28 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

To safely handle 64-bit registers even on 32-bit systems, introduce
a BIT_ULL variant (lifted from Linux).

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/include/asm-arm/bitops.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
index bda8898..1cbfb9e 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 ADDR (*(volatile int *) addr)
-- 
2.8.2


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

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

* [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
  2017-04-05 23:18 ` [PATCH v5 01/30] bitops: add GENMASK_ULL Andre Przywara
  2017-04-05 23:18 ` [PATCH v5 02/30] bitops: add BIT_ULL variant Andre Przywara
@ 2017-04-05 23:18 ` Andre Przywara
  2017-04-05 23:26   ` Stefano Stabellini
  2017-04-05 23:18 ` [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS Andre Przywara
                   ` (27 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Parse the GIC subnodes in the device tree 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 (as an EXPERT option), use
XEN_CONFIG_EXPERT=y on the make command line to see and use the option.

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

diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index 43123e6..d46b98c 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -45,6 +45,11 @@ config ACPI
 config HAS_GICV3
 	bool
 
+config HAS_ITS
+        bool
+        prompt "GICv3 ITS MSI controller support" if EXPERT = "y"
+        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 0ce94a8..39c0a03 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-v3-its.o
 obj-y += guestcopy.o
 obj-y += hvm.o
 obj-y += io.o
diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
new file mode 100644
index 0000000..6b02349
--- /dev/null
+++ b/xen/arch/arm/gic-v3-its.c
@@ -0,0 +1,77 @@
+/*
+ * xen/arch/arm/gic-v3-its.c
+ *
+ * ARM GICv3 Interrupt Translation Service (ITS) support
+ *
+ * Copyright (C) 2016,2017 - 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/lib.h>
+#include <asm/gic_v3_defs.h>
+#include <asm/gic_v3_its.h>
+
+/*
+ * No lock here, as this list gets only populated upon boot while scanning
+ * firmware tables for all host ITSes, and only gets iterated afterwards.
+ */
+LIST_HEAD(host_its_list);
+
+bool gicv3_its_host_has_its(void)
+{
+    return !list_empty(&host_its_list);
+}
+
+/* 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)
+    {
+        uint64_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 695f01f..b626298 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -42,6 +42,7 @@
 #include <asm/device.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
+#include <asm/gic_v3_its.h>
 #include <asm/cpufeature.h>
 #include <asm/acpi.h>
 
@@ -1227,11 +1228,12 @@ static void __init gicv3_dt_init(void)
      */
     res = dt_device_get_address(node, 1 + gicv3.rdist_count,
                                 &cbase, &csize);
-    if ( res )
-        return;
+    if ( !res )
+        dt_device_get_address(node, 1 + gicv3.rdist_count + 2,
+                              &vbase, &vsize);
 
-    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_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
new file mode 100644
index 0000000..765a655
--- /dev/null
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -0,0 +1,67 @@
+/*
+ * ARM GICv3 ITS support
+ *
+ * Andre Przywara <andre.przywara@arm.com>
+ * Copyright (c) 2016,2017 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ASM_ARM_ITS_H__
+#define __ASM_ARM_ITS_H__
+
+#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;
+};
+
+
+#ifdef CONFIG_HAS_ITS
+
+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);
+
+bool gicv3_its_host_has_its(void);
+
+#else
+
+static LIST_HEAD(host_its_list);
+
+static inline void gicv3_its_dt_init(const struct dt_device_node *node)
+{
+}
+
+static inline bool gicv3_its_host_has_its(void)
+{
+    return false;
+}
+
+#endif /* CONFIG_HAS_ITS */
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
-- 
2.8.2


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

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

* [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (2 preceding siblings ...)
  2017-04-05 23:18 ` [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
@ 2017-04-05 23:18 ` Andre Przywara
  2017-04-05 23:27   ` Stefano Stabellini
  2017-04-06 12:41   ` Julien Grall
  2017-04-05 23:18 ` [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table Andre Przywara
                   ` (26 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Map the registers frame for each host ITS and populate the host ITS
structure with some parameters describing the size of certain properties
like the number of bits for device IDs.

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

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index 6b02349..0298866 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -19,8 +19,10 @@
  */
 
 #include <xen/lib.h>
+#include <xen/mm.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic_v3_its.h>
+#include <asm/io.h>
 
 /*
  * No lock here, as this list gets only populated upon boot while scanning
@@ -33,6 +35,37 @@ bool gicv3_its_host_has_its(void)
     return !list_empty(&host_its_list);
 }
 
+static int gicv3_its_init_single_its(struct host_its *hw_its)
+{
+    uint64_t reg;
+
+    hw_its->its_base = ioremap_nocache(hw_its->addr, hw_its->size);
+    if ( !hw_its->its_base )
+        return -ENOMEM;
+
+    reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
+    hw_its->devid_bits = GITS_TYPER_DEVICE_ID_BITS(reg);
+    hw_its->evid_bits = GITS_TYPER_EVENT_ID_BITS(reg);
+    hw_its->itte_size = GITS_TYPER_ITT_SIZE(reg);
+
+    return 0;
+}
+
+int gicv3_its_init(void)
+{
+    struct host_its *hw_its;
+    int ret;
+
+    list_for_each_entry(hw_its, &host_its_list, entry)
+    {
+        ret = gicv3_its_init_single_its(hw_its);
+        if ( ret )
+            return ret;
+    }
+
+    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 b626298..d3d5784 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -1590,6 +1590,11 @@ static int __init gicv3_init(void)
     spin_lock(&gicv3.lock);
 
     gicv3_dist_init();
+
+    res = gicv3_its_init();
+    if ( res )
+        panic("GICv3: ITS: initialization failed: %d\n", res);
+
     res = gicv3_cpu_init();
     gicv3_hyp_init();
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 765a655..7d88987 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -20,6 +20,38 @@
 #ifndef __ASM_ARM_ITS_H__
 #define __ASM_ARM_ITS_H__
 
+#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_TYPER_DEVIDS_SHIFT         13
+#define GITS_TYPER_DEVIDS_MASK          (0x1fUL << GITS_TYPER_DEVIDS_SHIFT)
+#define GITS_TYPER_DEVICE_ID_BITS(r)    (((r & GITS_TYPER_DEVIDS_MASK) >> \
+                                               GITS_TYPER_DEVIDS_SHIFT) + 1)
+
+#define GITS_TYPER_IDBITS_SHIFT         8
+#define GITS_TYPER_IDBITS_MASK          (0x1fUL << GITS_TYPER_IDBITS_SHIFT)
+#define GITS_TYPER_EVENT_ID_BITS(r)     (((r & GITS_TYPER_IDBITS_MASK) >> \
+                                               GITS_TYPER_IDBITS_SHIFT) + 1)
+
+#define GITS_TYPER_ITT_SIZE_SHIFT       4
+#define GITS_TYPER_ITT_SIZE_MASK        (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
+#define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
+                                                 GITS_TYPER_ITT_SIZE_SHIFT) + 1)
+
 #include <xen/device_tree.h>
 
 /* data structure for each hardware ITS */
@@ -28,6 +60,10 @@ struct host_its {
     const struct dt_device_node *dt_node;
     paddr_t addr;
     paddr_t size;
+    void __iomem *its_base;
+    unsigned int devid_bits;
+    unsigned int evid_bits;
+    unsigned int itte_size;
 };
 
 
@@ -40,6 +76,9 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
 
 bool gicv3_its_host_has_its(void);
 
+/* Initialize the host structures for the host ITSes. */
+int gicv3_its_init(void);
+
 #else
 
 static LIST_HEAD(host_its_list);
@@ -53,6 +92,11 @@ static inline bool gicv3_its_host_has_its(void)
     return false;
 }
 
+static inline int gicv3_its_init(void)
+{
+    return 0;
+}
+
 #endif /* CONFIG_HAS_ITS */
 
 #endif
-- 
2.8.2


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

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

* [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (3 preceding siblings ...)
  2017-04-05 23:18 ` [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS Andre Przywara
@ 2017-04-05 23:18 ` Andre Przywara
  2017-04-05 23:30   ` Stefano Stabellini
  2017-04-06 12:58   ` Julien Grall
  2017-04-05 23:18 ` [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
                   ` (25 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

The ARM GICv3 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
redistributor. The maximum number of LPIs to be used can be adjusted with
the command line option "max_lpi_bits", which defaults to 20 bits,
covering about one million LPIs.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 docs/misc/xen-command-line.markdown |   9 ++
 xen/arch/arm/Makefile               |   1 +
 xen/arch/arm/gic-v3-lpi.c           | 221 ++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c               |  17 +++
 xen/include/asm-arm/gic_v3_defs.h   |  52 +++++++++
 xen/include/asm-arm/gic_v3_its.h    |  15 ++-
 xen/include/asm-arm/irq.h           |   8 ++
 7 files changed, 322 insertions(+), 1 deletion(-)
 create mode 100644 xen/arch/arm/gic-v3-lpi.c

diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown
index 9eb85d6..6ef8633 100644
--- a/docs/misc/xen-command-line.markdown
+++ b/docs/misc/xen-command-line.markdown
@@ -1172,6 +1172,15 @@ based interrupts. Any higher IRQs will be available for use via PCI MSI.
 ### maxcpus
 > `= <integer>`
 
+### max\_lpi\_bits
+> `= <integer>`
+
+Specifies the number of ARM GICv3 LPI interrupts to allocate on the host,
+presented as the number of bits needed to encode it. This must be at least
+14 and not exceed 32, and each LPI requires one byte (configuration) and
+one pending bit to be allocated.
+Defaults to 20 bits (to cover at most 1048576 interrupts).
+
 ### mce
 > `= <integer>`
 
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 39c0a03..6be85ab 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -19,6 +19,7 @@ obj-y += gic.o
 obj-y += gic-v2.o
 obj-$(CONFIG_HAS_GICV3) += gic-v3.o
 obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
+obj-$(CONFIG_HAS_ITS) += gic-v3-lpi.o
 obj-y += guestcopy.o
 obj-y += hvm.o
 obj-y += io.o
diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
new file mode 100644
index 0000000..7f99ec6
--- /dev/null
+++ b/xen/arch/arm/gic-v3-lpi.c
@@ -0,0 +1,221 @@
+/*
+ * xen/arch/arm/gic-v3-lpi.c
+ *
+ * ARM GICv3 Locality-specific Peripheral Interrupts (LPI) support
+ *
+ * Copyright (C) 2016,2017 - 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/lib.h>
+#include <xen/mm.h>
+#include <xen/sizes.h>
+#include <xen/warning.h>
+#include <asm/gic.h>
+#include <asm/gic_v3_defs.h>
+#include <asm/gic_v3_its.h>
+#include <asm/io.h>
+#include <asm/page.h>
+
+#define LPI_PROPTABLE_NEEDS_FLUSHING    (1U << 0)
+
+/* Global state */
+static struct {
+    /* The global LPI property table, shared by all redistributors. */
+    uint8_t *lpi_property;
+    /*
+     * Number of physical LPIs the host supports. This is a property of
+     * the GIC hardware. We depart from the habit of naming these things
+     * "physical" in Xen, as the GICv3/4 spec uses the term "physical LPI"
+     * in a different context to differentiate them from "virtual LPIs".
+     */
+    unsigned long long int max_host_lpi_ids;
+    unsigned int flags;
+} lpi_data;
+
+struct lpi_redist_data {
+    void                *pending_table;
+};
+
+static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);
+
+#define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
+
+static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
+{
+    uint64_t val;
+    void *pendtable;
+
+    if ( this_cpu(lpi_redist).pending_table )
+        return -EBUSY;
+
+    val  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
+    val |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
+    val |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
+
+    /*
+     * The pending table holds one bit per LPI and even covers bits for
+     * interrupt IDs below 8192, so we allocate the full range.
+     * The GICv3 imposes a 64KB alignment requirement, also requires
+     * physically contiguous memory.
+     */
+    pendtable = _xzalloc(lpi_data.max_host_lpi_ids / 8, SZ_64K);
+    if ( !pendtable )
+        return -ENOMEM;
+
+    /* Make sure the physical address can be encoded in the register. */
+    if ( virt_to_maddr(pendtable) & ~GENMASK_ULL(51, 16) )
+    {
+        xfree(pendtable);
+        return -ERANGE;
+    }
+    clean_and_invalidate_dcache_va_range(pendtable,
+                                         lpi_data.max_host_lpi_ids / 8);
+
+    this_cpu(lpi_redist).pending_table = pendtable;
+
+    val |= GICR_PENDBASER_PTZ;
+
+    val |= virt_to_maddr(pendtable);
+
+    *reg = val;
+
+    return 0;
+}
+
+/*
+ * Tell a redistributor about the (shared) property table, allocating one
+ * if not already done.
+ */
+static int gicv3_lpi_set_proptable(void __iomem * rdist_base)
+{
+    uint64_t reg;
+
+    reg  = GIC_BASER_CACHE_RaWaWb << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT;
+    reg |= GIC_BASER_CACHE_SameAsInner << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT;
+    reg |= GIC_BASER_InnerShareable << GICR_PROPBASER_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 = _xmalloc(lpi_data.max_host_lpi_ids, SZ_4K);
+
+        if ( !table )
+            return -ENOMEM;
+
+        /* Make sure the physical address can be encoded in the register. */
+        if ( (virt_to_maddr(table) & ~GENMASK_ULL(51, 12)) )
+        {
+            xfree(table);
+            return -ERANGE;
+        }
+        memset(table, GIC_PRI_IRQ | LPI_PROP_RES1, MAX_NR_HOST_LPIS);
+        clean_and_invalidate_dcache_va_range(table, MAX_NR_HOST_LPIS);
+        lpi_data.lpi_property = table;
+    }
+
+    /* Encode the number of bits needed, minus one */
+    reg |= fls(lpi_data.max_host_lpi_ids - 1) - 1;
+
+    reg |= virt_to_maddr(lpi_data.lpi_property);
+
+    writeq_relaxed(reg, rdist_base + GICR_PROPBASER);
+    reg = readq_relaxed(rdist_base + GICR_PROPBASER);
+
+    /* If we can't do shareable, we have to drop cacheability as well. */
+    if ( !(reg & GICR_PROPBASER_SHAREABILITY_MASK) )
+    {
+        reg &= ~GICR_PROPBASER_INNER_CACHEABILITY_MASK;
+        reg |= GIC_BASER_CACHE_nC << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT;
+    }
+
+    /* Remember that we have to flush the property table if non-cacheable. */
+    if ( (reg & GICR_PROPBASER_INNER_CACHEABILITY_MASK) <= GIC_BASER_CACHE_nC )
+    {
+        lpi_data.flags |= LPI_PROPTABLE_NEEDS_FLUSHING;
+        /* Update the redistributors knowledge about the attributes. */
+        writeq_relaxed(reg, rdist_base + GICR_PROPBASER);
+    }
+
+    return 0;
+}
+
+int gicv3_lpi_init_rdist(void __iomem * rdist_base)
+{
+    uint32_t reg;
+    uint64_t table_reg;
+    int ret;
+
+    /* We don't support LPIs without an ITS. */
+    if ( !gicv3_its_host_has_its() )
+        return -ENODEV;
+
+    /* Make sure LPIs are disabled before setting up the tables. */
+    reg = readl_relaxed(rdist_base + GICR_CTLR);
+    if ( reg & GICR_CTLR_ENABLE_LPIS )
+        return -EBUSY;
+
+    ret = gicv3_lpi_allocate_pendtable(&table_reg);
+    if ( ret )
+        return ret;
+    writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
+    table_reg = readq_relaxed(rdist_base + GICR_PENDBASER);
+
+    /* If the hardware reports non-shareable, drop cacheability as well. */
+    if ( !(table_reg & GICR_PENDBASER_SHAREABILITY_MASK) )
+    {
+        table_reg &= GICR_PENDBASER_SHAREABILITY_MASK;
+        table_reg &= GICR_PENDBASER_INNER_CACHEABILITY_MASK;
+        table_reg |= GIC_BASER_CACHE_nC << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
+
+        writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
+    }
+
+    return gicv3_lpi_set_proptable(rdist_base);
+}
+
+static unsigned int max_lpi_bits = 20;
+integer_param("max_lpi_bits", max_lpi_bits);
+
+int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
+{
+    /* An implementation needs to support at least 14 bits of LPI IDs. */
+    max_lpi_bits = max(max_lpi_bits, 14U);
+    lpi_data.max_host_lpi_ids = BIT_ULL(min(host_lpi_bits, max_lpi_bits));
+
+    /*
+     * Warn if the number of LPIs are quite high, as the user might not want
+     * to waste megabytes of memory for a mostly empty table.
+     * It's very unlikely that we need more than 24 bits worth of LPIs.
+     */
+    if ( lpi_data.max_host_lpi_ids > BIT(24) )
+        warning_add("Using high number of LPIs, limit memory usage with max_lpi_bits\n");
+
+    printk("GICv3: using at most %llu LPIs on the host.\n", MAX_NR_HOST_LPIS);
+
+    return 0;
+}
+
+/*
+ * 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 d3d5784..54d2235 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -547,6 +547,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(GICD_TYPE_ID_BITS(type));
+
     printk("GICv3: %d lines, (IID %8.8x).\n",
            nr_lines, readl_relaxed(GICD + GICD_IIDR));
 
@@ -659,6 +662,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_lpi_init_rdist(ptr);
+                    if ( ret && ret != -ENODEV )
+                    {
+                        printk("GICv3: CPU%d: Cannot initialize LPIs: %u\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/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
index 6bd25a5..6f02bb0 100644
--- a/xen/include/asm-arm/gic_v3_defs.h
+++ b/xen/include/asm-arm/gic_v3_defs.h
@@ -45,6 +45,9 @@
 
 /* Additional bits in GICD_TYPER defined by GICv3 */
 #define GICD_TYPE_ID_BITS_SHIFT 19
+#define GICD_TYPE_ID_BITS(r)    ((((r) >> GICD_TYPE_ID_BITS_SHIFT) & 0x1f) + 1)
+
+#define GICD_TYPE_LPIS               (1U << 17)
 
 #define GICD_CTLR_RWP                (1UL << 31)
 #define GICD_CTLR_ARE_NS             (1U << 4)
@@ -95,12 +98,61 @@
 #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)
 
+/* For specifying the inner cacheability type only */
+#define GIC_BASER_CACHE_nCnB         0ULL
+/* For specifying the outer cacheability type only */
+#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_OUTER_CACHEABILITY_SHIFT         56
+#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK               \
+        (7UL << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT)
+#define GICR_PROPBASER_SHAREABILITY_SHIFT               10
+#define GICR_PROPBASER_SHAREABILITY_MASK                     \
+        (3UL << GICR_PROPBASER_SHAREABILITY_SHIFT)
+#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT         7
+#define GICR_PROPBASER_INNER_CACHEABILITY_MASK               \
+        (7UL << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT)
+#define GICR_PROPBASER_RES0_MASK                             \
+        (GENMASK_ULL(63, 59) | GENMASK_ULL(55, 52) | GENMASK_ULL(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 GICR_PENDBASER_RES0_MASK                             \
+        (BIT(63) | GENMASK_ULL(61, 59) | GENMASK_ULL(55, 52) |       \
+         GENMASK_ULL(15, 12) | GENMASK_ULL(6, 0))
+
 #define DEFAULT_PMR_VALUE            0xff
 
+#define LPI_PROP_PRIO_MASK           0xfc
+#define LPI_PROP_RES1                (1 << 1)
+#define LPI_PROP_ENABLED             (1 << 0)
+
 #define GICH_VMCR_EOI                (1 << 9)
 #define GICH_VMCR_VENG1              (1 << 1)
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 7d88987..295eb22 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -76,7 +76,10 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
 
 bool gicv3_its_host_has_its(void);
 
-/* Initialize the host structures for the host ITSes. */
+int gicv3_lpi_init_rdist(void __iomem * rdist_base);
+
+/* Initialize the host structures for LPIs and the host ITSes. */
+int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits);
 int gicv3_its_init(void);
 
 #else
@@ -92,6 +95,16 @@ static inline bool gicv3_its_host_has_its(void)
     return false;
 }
 
+static inline int gicv3_lpi_init_rdist(void __iomem * rdist_base)
+{
+    return -ENODEV;
+}
+
+static inline int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
+{
+    return 0;
+}
+
 static inline int gicv3_its_init(void)
 {
     return 0;
diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
index 4849f16..f940092 100644
--- a/xen/include/asm-arm/irq.h
+++ b/xen/include/asm-arm/irq.h
@@ -18,8 +18,16 @@ struct arch_irq_desc {
 };
 
 #define NR_LOCAL_IRQS	32
+
+/*
+ * This only covers the interrupts that Xen cares about, so SGIs, PPIs and
+ * SPIs. LPIs are too numerous, also only propagated to guests, so they are
+ * not included in this number.
+ */
 #define NR_IRQS		1024
 
+#define LPI_OFFSET      8192
+
 #define nr_irqs NR_IRQS
 #define nr_static_irqs NR_IRQS
 #define arch_hwdom_irqs(domid) NR_IRQS
-- 
2.8.2


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

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

* [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (4 preceding siblings ...)
  2017-04-05 23:18 ` [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table Andre Przywara
@ 2017-04-05 23:18 ` Andre Przywara
  2017-04-06 14:44   ` Julien Grall
  2017-04-06 22:19   ` Julien Grall
  2017-04-05 23:18 ` [PATCH v5 07/30] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
                   ` (24 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Each ITS maps a pair of a DeviceID (for instance derived from a 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 over to the ITS.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
---
 xen/arch/arm/gic-v3-its.c        | 132 +++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic_v3_its.h |  32 ++++++++++
 2 files changed, 164 insertions(+)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index 0298866..eef2933 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -35,9 +35,109 @@ bool gicv3_its_host_has_its(void)
     return !list_empty(&host_its_list);
 }
 
+#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_ULL(58, 56) | GENMASK_ULL(52, 48))
+
+/* Check that the physical address can be encoded in the PROPBASER register. */
+static bool check_baser_phys_addr(void *vaddr, unsigned int page_bits)
+{
+    paddr_t paddr = virt_to_maddr(vaddr);
+
+    return (!(paddr & ~GENMASK_ULL(page_bits < 16 ? 47 : 51, page_bits)));
+}
+
+static uint64_t encode_baser_phys_addr(paddr_t addr, unsigned int page_bits)
+{
+    uint64_t ret = addr & GENMASK_ULL(47, page_bits);
+
+    if ( page_bits < 16 )
+        return ret;
+
+    /* For 64K pages address bits 51-48 are encoded in bits 15-12. */
+    return ret | ((addr & GENMASK_ULL(51, 48)) >> (48 - 12));
+}
+
+/* The ITS BASE registers work with page sizes of 4K, 16K or 64K. */
+#define BASER_PAGE_BITS(sz) ((sz) * 2 + 12)
+
+static int its_map_baser(void __iomem *basereg, uint64_t regc,
+                         unsigned int nr_items)
+{
+    uint64_t attr, reg;
+    unsigned int entry_size = GITS_BASER_ENTRY_SIZE(regc);
+    unsigned int pagesz = 2;    /* try 64K pages first, then go down. */
+    unsigned int table_size;
+    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;
+
+    /*
+     * Setup the BASE register with the attributes that we like. Then read
+     * it back and see what sticks (page size, cacheability and shareability
+     * attributes), retrying if necessary.
+     */
+retry:
+    table_size = ROUNDUP(nr_items * entry_size, BIT(BASER_PAGE_BITS(pagesz)));
+    /* The BASE registers support at most 256 pages. */
+    table_size = min(table_size, 256U << BASER_PAGE_BITS(pagesz));
+
+    buffer = _xzalloc(table_size, BIT(BASER_PAGE_BITS(pagesz)));
+    if ( !buffer )
+        return -ENOMEM;
+
+    if ( !check_baser_phys_addr(buffer, BASER_PAGE_BITS(pagesz)) )
+    {
+        xfree(buffer);
+        return -ERANGE;
+    }
+
+    reg  = attr;
+    reg |= (pagesz << GITS_BASER_PAGE_SIZE_SHIFT);
+    reg |= (table_size >> BASER_PAGE_BITS(pagesz)) - 1;
+    reg |= regc & BASER_RO_MASK;
+    reg |= GITS_VALID_BIT;
+    reg |= encode_baser_phys_addr(virt_to_maddr(buffer),
+                                  BASER_PAGE_BITS(pagesz));
+
+    writeq_relaxed(reg, basereg);
+    regc = readq_relaxed(basereg);
+
+    /* The host didn't like our attributes, just use what it returned. */
+    if ( (regc & BASER_ATTR_MASK) != attr )
+    {
+        /* If we can't map it shareable, drop cacheability as well. */
+        if ( (regc & GITS_BASER_SHAREABILITY_MASK) == GIC_BASER_NonShareable )
+        {
+            regc &= ~GITS_BASER_INNER_CACHEABILITY_MASK;
+            writeq_relaxed(regc, basereg);
+        }
+        attr = regc & BASER_ATTR_MASK;
+    }
+    if ( (regc & GITS_BASER_INNER_CACHEABILITY_MASK) <= GIC_BASER_CACHE_nC )
+        clean_and_invalidate_dcache_va_range(buffer, table_size);
+
+    /* If the host accepted our page size, we are done. */
+    if ( ((regc >> GITS_BASER_PAGE_SIZE_SHIFT) & 0x3UL) == pagesz )
+        return 0;
+
+    xfree(buffer);
+
+    if ( pagesz-- > 0 )
+        goto retry;
+
+    /* None of the page sizes was accepted, give up */
+    return -EINVAL;
+}
+
 static int gicv3_its_init_single_its(struct host_its *hw_its)
 {
     uint64_t reg;
+    int i, ret;
 
     hw_its->its_base = ioremap_nocache(hw_its->addr, hw_its->size);
     if ( !hw_its->its_base )
@@ -48,6 +148,38 @@ static int gicv3_its_init_single_its(struct host_its *hw_its)
     hw_its->evid_bits = GITS_TYPER_EVENT_ID_BITS(reg);
     hw_its->itte_size = GITS_TYPER_ITT_SIZE(reg);
 
+    for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
+    {
+        void __iomem *basereg = hw_its->its_base + GITS_BASER0 + i * 8;
+        unsigned 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:
+            ret = its_map_baser(basereg, reg, BIT(hw_its->devid_bits));
+            if ( ret )
+                return ret;
+            break;
+        case GITS_BASER_TYPE_COLLECTION:
+            ret = its_map_baser(basereg, reg, num_possible_cpus());
+            if ( ret )
+                return ret;
+            break;
+        /* In case this is a GICv4, provide a (dummy) vPE table as well. */
+        case GITS_BASER_TYPE_VCPU:
+            ret = its_map_baser(basereg, reg, 1);
+            if ( ret )
+                return ret;
+            break;
+        default:
+            continue;
+        }
+    }
+
     return 0;
 }
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 295eb22..6e51b98 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -37,6 +37,11 @@
 #define GITS_BASER7                     0x138
 
 /* Register bits */
+#define GITS_VALID_BIT                  BIT_ULL(63)
+
+#define GITS_CTLR_QUIESCENT             BIT(31)
+#define GITS_CTLR_ENABLE                BIT(0)
+
 #define GITS_TYPER_DEVIDS_SHIFT         13
 #define GITS_TYPER_DEVIDS_MASK          (0x1fUL << GITS_TYPER_DEVIDS_SHIFT)
 #define GITS_TYPER_DEVICE_ID_BITS(r)    (((r & GITS_TYPER_DEVIDS_MASK) >> \
@@ -52,6 +57,33 @@
 #define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
                                                  GITS_TYPER_ITT_SIZE_SHIFT) + 1)
 
+#define GITS_IIDR_VALUE                 0x34c
+
+#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_ENTRY_SIZE(reg)                                       \
+                        (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
+#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)
+#define GITS_BASER_SHAREABILITY_MASK   (0x3ULL << GITS_BASER_SHAREABILITY_SHIFT)
+#define GITS_BASER_OUTER_CACHEABILITY_MASK   (0x7ULL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)
+#define GITS_BASER_INNER_CACHEABILITY_MASK   (0x7ULL << GITS_BASER_INNER_CACHEABILITY_SHIFT)
+
 #include <xen/device_tree.h>
 
 /* data structure for each hardware ITS */
-- 
2.8.2


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

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

* [PATCH v5 07/30] ARM: GICv3 ITS: map ITS command buffer
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (5 preceding siblings ...)
  2017-04-05 23:18 ` [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
@ 2017-04-05 23:18 ` Andre Przywara
  2017-04-06 14:49   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
                   ` (23 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:18 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Instead of directly manipulating the tables in memory, an ITS driver
sends commands via a ring buffer in normal system memory 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>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
---
 xen/arch/arm/gic-v3-its.c        | 53 ++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic_v3_its.h |  6 +++++
 2 files changed, 59 insertions(+)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index eef2933..d8e978a 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -20,10 +20,13 @@
 
 #include <xen/lib.h>
 #include <xen/mm.h>
+#include <xen/sizes.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic_v3_its.h>
 #include <asm/io.h>
 
+#define ITS_CMD_QUEUE_SZ                SZ_1M
+
 /*
  * No lock here, as this list gets only populated upon boot while scanning
  * firmware tables for all host ITSes, and only gets iterated afterwards.
@@ -60,6 +63,51 @@ static uint64_t encode_baser_phys_addr(paddr_t addr, unsigned int page_bits)
     return ret | ((addr & GENMASK_ULL(51, 48)) >> (48 - 12));
 }
 
+static void *its_map_cbaser(struct host_its *its)
+{
+    void __iomem *cbasereg = its->its_base + GITS_CBASER;
+    uint64_t reg;
+    void *buffer;
+
+    reg  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
+    reg |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
+    reg |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
+
+    buffer = _xzalloc(ITS_CMD_QUEUE_SZ, SZ_64K);
+    if ( !buffer )
+        return NULL;
+
+    if ( virt_to_maddr(buffer) & ~GENMASK_ULL(51, 12) )
+    {
+        xfree(buffer);
+        return NULL;
+    }
+
+    reg |= GITS_VALID_BIT | virt_to_maddr(buffer);
+    reg |= ((ITS_CMD_QUEUE_SZ / SZ_4K) - 1) & GITS_CBASER_SIZE_MASK;
+    writeq_relaxed(reg, cbasereg);
+    reg = readq_relaxed(cbasereg);
+
+    /* If the ITS dropped shareability, drop cacheability as well. */
+    if ( (reg & GITS_BASER_SHAREABILITY_MASK) == 0 )
+    {
+        reg &= ~GITS_BASER_INNER_CACHEABILITY_MASK;
+        writeq_relaxed(reg, cbasereg);
+    }
+
+    /*
+     * If the command queue memory is mapped as uncached, we need to flush
+     * it on every access.
+     */
+    if ( !(reg & GITS_BASER_INNER_CACHEABILITY_MASK) )
+    {
+        its->flags |= HOST_ITS_FLUSH_CMD_QUEUE;
+        printk(XENLOG_WARNING "using non-cacheable ITS command queue\n");
+    }
+
+    return buffer;
+}
+
 /* The ITS BASE registers work with page sizes of 4K, 16K or 64K. */
 #define BASER_PAGE_BITS(sz) ((sz) * 2 + 12)
 
@@ -180,6 +228,11 @@ static int gicv3_its_init_single_its(struct host_its *hw_its)
         }
     }
 
+    hw_its->cmd_buf = its_map_cbaser(hw_its);
+    if ( !hw_its->cmd_buf )
+        return -ENOMEM;
+    writeq_relaxed(0, hw_its->its_base + GITS_CWRITER);
+
     return 0;
 }
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 6e51b98..a68ccf3 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -84,8 +84,12 @@
 #define GITS_BASER_OUTER_CACHEABILITY_MASK   (0x7ULL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)
 #define GITS_BASER_INNER_CACHEABILITY_MASK   (0x7ULL << GITS_BASER_INNER_CACHEABILITY_SHIFT)
 
+#define GITS_CBASER_SIZE_MASK           0xff
+
 #include <xen/device_tree.h>
 
+#define HOST_ITS_FLUSH_CMD_QUEUE        (1U << 0)
+
 /* data structure for each hardware ITS */
 struct host_its {
     struct list_head entry;
@@ -96,6 +100,8 @@ struct host_its {
     unsigned int devid_bits;
     unsigned int evid_bits;
     unsigned int itte_size;
+    void *cmd_buf;
+    unsigned int flags;
 };
 
 
-- 
2.8.2


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

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

* [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (6 preceding siblings ...)
  2017-04-05 23:18 ` [PATCH v5 07/30] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:33   ` Stefano Stabellini
  2017-04-06 14:55   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
                   ` (22 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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.
As an ITS might choose between *two* ways of addressing a redistributor,
we store both the MMIO base address as well as the processor number in
a per-CPU variable to give each ITS what it wants.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-v3-its.c         | 199 ++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3-lpi.c         |  28 ++++++
 xen/arch/arm/gic-v3.c             |  26 ++++-
 xen/include/asm-arm/gic_v3_defs.h |   2 +
 xen/include/asm-arm/gic_v3_its.h  |  37 +++++++
 5 files changed, 291 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index d8e978a..1ecd63b 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -19,11 +19,14 @@
  */
 
 #include <xen/lib.h>
+#include <xen/delay.h>
 #include <xen/mm.h>
 #include <xen/sizes.h>
+#include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic_v3_its.h>
 #include <asm/io.h>
+#include <asm/page.h>
 
 #define ITS_CMD_QUEUE_SZ                SZ_1M
 
@@ -38,6 +41,160 @@ bool gicv3_its_host_has_its(void)
     return !list_empty(&host_its_list);
 }
 
+#define BUFPTR_MASK                     GENMASK_ULL(19, 5)
+static int its_send_command(struct host_its *hw_its, const void *its_cmd)
+{
+    /*
+     * The command queue should actually never become full, if it does anyway
+     * and this situation is not resolved quickly, this points to a much
+     * bigger problem, probably an hardware error.
+     * So to cover the one-off case where we actually hit a full command
+     * queue, we introduce a small grace period to not give up too quickly.
+     * Given the usual multi-hundred MHz frequency the ITS usually runs with,
+     * one millisecond (for a single command) seem to be more than enough.
+     * But this value is rather arbitrarily chosen based on theoretical
+     * considerations.
+     */
+    s_time_t deadline = NOW() + MILLISECS(1);
+    uint64_t readp, writep;
+    int ret = -EBUSY;
+
+    /* No ITS commands from an interrupt handler (at the moment). */
+    ASSERT(!in_irq());
+
+    spin_lock(&hw_its->cmd_lock);
+
+    do {
+        readp = readq_relaxed(hw_its->its_base + GITS_CREADR) & BUFPTR_MASK;
+        writep = readq_relaxed(hw_its->its_base + GITS_CWRITER) & BUFPTR_MASK;
+
+        if ( ((writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ) != readp )
+        {
+            ret = 0;
+            break;
+        }
+
+        /*
+         * If the command queue is full, wait for a bit in the hope it drains
+         * before giving up.
+         */
+        spin_unlock(&hw_its->cmd_lock);
+        cpu_relax();
+        udelay(1);
+        spin_lock(&hw_its->cmd_lock);
+    } while ( NOW() <= deadline );
+
+    if ( ret )
+    {
+        spin_unlock(&hw_its->cmd_lock);
+        if ( printk_ratelimit() )
+            printk(XENLOG_WARNING "host ITS: command queue full.\n");
+        return ret;
+    }
+
+    memcpy(hw_its->cmd_buf + writep, its_cmd, ITS_CMD_SIZE);
+    if ( hw_its->flags & HOST_ITS_FLUSH_CMD_QUEUE )
+        clean_and_invalidate_dcache_va_range(hw_its->cmd_buf + writep,
+                                             ITS_CMD_SIZE);
+    else
+        dsb(ishst);
+
+    writep = (writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ;
+    writeq_relaxed(writep & BUFPTR_MASK, hw_its->its_base + GITS_CWRITER);
+
+    spin_unlock(&hw_its->cmd_lock);
+
+    return 0;
+}
+
+/* Wait for an ITS to finish processing all commands. */
+static int gicv3_its_wait_commands(struct host_its *hw_its)
+{
+    /*
+     * As there could be quite a number of commands in a queue, we will
+     * wait a bit longer than the one millisecond for a single command above.
+     * Again this value is based on theoretical considerations, actually the
+     * command queue should drain much faster.
+     */
+    s_time_t deadline = NOW() + MILLISECS(100);
+    uint64_t readp, writep;
+
+    do {
+        spin_lock(&hw_its->cmd_lock);
+        readp = readq_relaxed(hw_its->its_base + GITS_CREADR) & BUFPTR_MASK;
+        writep = readq_relaxed(hw_its->its_base + GITS_CWRITER) & BUFPTR_MASK;
+        spin_unlock(&hw_its->cmd_lock);
+
+        if ( readp == writep )
+            return 0;
+
+        cpu_relax();
+        udelay(1);
+    } while ( NOW() <= deadline );
+
+    return -ETIMEDOUT;
+}
+
+static uint64_t encode_rdbase(struct host_its *hw_its, unsigned int cpu,
+                              uint64_t reg)
+{
+    reg &= ~GENMASK_ULL(51, 16);
+
+    reg |= gicv3_get_redist_address(cpu, hw_its->flags & HOST_ITS_USES_PTA);
+
+    return reg;
+}
+
+static int its_send_cmd_sync(struct host_its *its, unsigned 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, uint32_t collection_id,
+                             unsigned int cpu)
+{
+    uint64_t cmd[4];
+
+    cmd[0] = GITS_CMD_MAPC;
+    cmd[1] = 0x00;
+    cmd[2] = encode_rdbase(its, cpu, collection_id);
+    cmd[2] |= GITS_VALID_BIT;
+    cmd[3] = 0x00;
+
+    return its_send_command(its, cmd);
+}
+
+/* Set up the (1:1) collection mapping for the given host CPU. */
+int gicv3_its_setup_collection(unsigned int cpu)
+{
+    struct host_its *its;
+    int ret;
+
+    list_for_each_entry(its, &host_its_list, entry)
+    {
+        ret = its_send_cmd_mapc(its, cpu, cpu);
+        if ( ret )
+            return ret;
+
+        ret = its_send_cmd_sync(its, cpu);
+        if ( ret )
+            return ret;
+
+        ret = gicv3_its_wait_commands(its);
+        if ( ret )
+            return ret;
+    }
+
+    return 0;
+}
+
 #define BASER_ATTR_MASK                                           \
         ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
          (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
@@ -182,6 +339,41 @@ retry:
     return -EINVAL;
 }
 
+/*
+ * Before an ITS gets initialized, it should be in a quiescent state, where
+ * all outstanding commands and transactions have finished.
+ * So if the ITS is already enabled, turn it off and wait for all outstanding
+ * operations to get processed by polling the QUIESCENT bit.
+ */
+static int gicv3_disable_its(struct host_its *hw_its)
+{
+    uint32_t reg;
+    /*
+     * As we also need to wait for the command queue to drain, we use the same
+     * (arbitrary) timeout value as above for gicv3_its_wait_commands().
+     */
+    s_time_t deadline = NOW() + MILLISECS(100);
+
+    reg = readl_relaxed(hw_its->its_base + GITS_CTLR);
+    if ( !(reg & GITS_CTLR_ENABLE) && (reg & GITS_CTLR_QUIESCENT) )
+        return 0;
+
+    writel_relaxed(reg & ~GITS_CTLR_ENABLE, hw_its->its_base + GITS_CTLR);
+
+    do {
+        reg = readl_relaxed(hw_its->its_base + GITS_CTLR);
+        if ( reg & GITS_CTLR_QUIESCENT )
+            return 0;
+
+        cpu_relax();
+        udelay(1);
+    } while ( NOW() <= deadline );
+
+    printk(XENLOG_ERR "ITS@%lx not quiescent.\n", hw_its->addr);
+
+    return -ETIMEDOUT;
+}
+
 static int gicv3_its_init_single_its(struct host_its *hw_its)
 {
     uint64_t reg;
@@ -191,10 +383,17 @@ static int gicv3_its_init_single_its(struct host_its *hw_its)
     if ( !hw_its->its_base )
         return -ENOMEM;
 
+    ret = gicv3_disable_its(hw_its);
+    if ( ret )
+        return ret;
+
     reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
     hw_its->devid_bits = GITS_TYPER_DEVICE_ID_BITS(reg);
     hw_its->evid_bits = GITS_TYPER_EVENT_ID_BITS(reg);
     hw_its->itte_size = GITS_TYPER_ITT_SIZE(reg);
+    if ( reg & GITS_TYPER_PTA )
+        hw_its->flags |= HOST_ITS_USES_PTA;
+    spin_lock_init(&hw_its->cmd_lock);
 
     for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
     {
diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
index 7f99ec6..9d3df7f 100644
--- a/xen/arch/arm/gic-v3-lpi.c
+++ b/xen/arch/arm/gic-v3-lpi.c
@@ -45,6 +45,8 @@ static struct {
 } lpi_data;
 
 struct lpi_redist_data {
+    paddr_t             redist_addr;
+    unsigned int        redist_id;
     void                *pending_table;
 };
 
@@ -52,6 +54,32 @@ static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);
 
 #define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
 
+/*
+ * An ITS can refer to redistributors in two ways: either by an ID (possibly
+ * the CPU number) or by its MMIO address. This is a hardware implementation
+ * choice, so we have to cope with both approaches. The GICv3 code calculates
+ * both values and calls this function to let the ITS store them when it's
+ * later required to provide them. This is done in a per-CPU variable.
+ */
+void gicv3_set_redist_address(paddr_t address, unsigned int redist_id)
+{
+    this_cpu(lpi_redist).redist_addr = address;
+    this_cpu(lpi_redist).redist_id = redist_id;
+}
+
+/*
+ * Returns a redistributor's ID (either as an address or as an ID).
+ * This must be (and is) called only after it has been setup by the above
+ * function.
+ */
+uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
+{
+    if ( use_pta )
+        return per_cpu(lpi_redist, cpu).redist_addr & GENMASK_ULL(51, 16);
+    else
+        return per_cpu(lpi_redist, cpu).redist_id << 16;
+}
+
 static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
 {
     uint64_t val;
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index 54d2235..a559e5e 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -665,8 +665,25 @@ static int __init gicv3_populate_rdist(void)
 
                 if ( typer & GICR_TYPER_PLPIS )
                 {
+                    paddr_t rdist_addr;
+                    unsigned int procnum;
                     int ret;
 
+                    /*
+                     * The ITS refers to redistributors either by their physical
+                     * address or by their ID. Which one to use is an ITS
+                     * choice. So determine those two values here (which we
+                     * can do only here in GICv3 code) and tell the
+                     * ITS code about it, so it can use them later to be able
+                     * to address those redistributors accordingly.
+                     */
+                    rdist_addr = gicv3.rdist_regions[i].base;
+                    rdist_addr += ptr - gicv3.rdist_regions[i].map_base;
+                    procnum = (typer & GICR_TYPER_PROC_NUM_MASK);
+                    procnum >>= GICR_TYPER_PROC_NUM_SHIFT;
+
+                    gicv3_set_redist_address(rdist_addr, procnum);
+
                     ret = gicv3_lpi_init_rdist(ptr);
                     if ( ret && ret != -ENODEV )
                     {
@@ -704,7 +721,7 @@ static int __init gicv3_populate_rdist(void)
 
 static int gicv3_cpu_init(void)
 {
-    int i;
+    int i, ret;
     uint32_t priority;
 
     /* Register ourselves with the rest of the world */
@@ -714,6 +731,13 @@ static int gicv3_cpu_init(void)
     if ( gicv3_enable_redist() )
         return -ENODEV;
 
+    if ( gicv3_its_host_has_its() )
+    {
+        ret = gicv3_its_setup_collection(smp_processor_id());
+        if ( ret )
+            return ret;
+    }
+
     /* Set priority on PPI and SGI interrupts */
     priority = (GIC_PRI_IPI << 24 | GIC_PRI_IPI << 16 | GIC_PRI_IPI << 8 |
                 GIC_PRI_IPI);
diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
index 6f02bb0..b7e5a47 100644
--- a/xen/include/asm-arm/gic_v3_defs.h
+++ b/xen/include/asm-arm/gic_v3_defs.h
@@ -103,6 +103,8 @@
 #define GICR_TYPER_PLPIS             (1U << 0)
 #define GICR_TYPER_VLPIS             (1U << 1)
 #define GICR_TYPER_LAST              (1U << 4)
+#define GICR_TYPER_PROC_NUM_SHIFT    8
+#define GICR_TYPER_PROC_NUM_MASK     (0xffff << GICR_TYPER_PROC_NUM_SHIFT)
 
 /* For specifying the inner cacheability type only */
 #define GIC_BASER_CACHE_nCnB         0ULL
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index a68ccf3..7d7ec32 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -42,6 +42,7 @@
 #define GITS_CTLR_QUIESCENT             BIT(31)
 #define GITS_CTLR_ENABLE                BIT(0)
 
+#define GITS_TYPER_PTA                  BIT_ULL(19)
 #define GITS_TYPER_DEVIDS_SHIFT         13
 #define GITS_TYPER_DEVIDS_MASK          (0x1fUL << GITS_TYPER_DEVIDS_SHIFT)
 #define GITS_TYPER_DEVICE_ID_BITS(r)    (((r & GITS_TYPER_DEVIDS_MASK) >> \
@@ -86,9 +87,26 @@
 
 #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
+
 #include <xen/device_tree.h>
 
 #define HOST_ITS_FLUSH_CMD_QUEUE        (1U << 0)
+#define HOST_ITS_USES_PTA               (1U << 1)
 
 /* data structure for each hardware ITS */
 struct host_its {
@@ -100,6 +118,7 @@ struct host_its {
     unsigned int devid_bits;
     unsigned int evid_bits;
     unsigned int itte_size;
+    spinlock_t cmd_lock;
     void *cmd_buf;
     unsigned int flags;
 };
@@ -120,6 +139,13 @@ int gicv3_lpi_init_rdist(void __iomem * rdist_base);
 int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits);
 int gicv3_its_init(void);
 
+/* Store the physical address and ID for each redistributor as read from DT. */
+void gicv3_set_redist_address(paddr_t address, unsigned int redist_id);
+uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta);
+
+/* Map a collection for this host CPU to each host ITS. */
+int gicv3_its_setup_collection(unsigned int cpu);
+
 #else
 
 static LIST_HEAD(host_its_list);
@@ -148,6 +174,17 @@ static inline int gicv3_its_init(void)
     return 0;
 }
 
+static inline void gicv3_set_redist_address(paddr_t address,
+                                            unsigned int redist_id)
+{
+}
+
+static inline int gicv3_its_setup_collection(unsigned int cpu)
+{
+    /* We should never get here without an ITS. */
+    BUG();
+}
+
 #endif /* CONFIG_HAS_ITS */
 
 #endif
-- 
2.8.2


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

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

* [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (7 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:37   ` Stefano Stabellini
  2017-04-05 23:19 ` [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub Andre Przywara
                   ` (21 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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.
By using the naturally atomic access guarantee the native uint64_t data
type gives us, 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-v3-its.c        |  60 +++++++++++
 xen/arch/arm/gic-v3-lpi.c        | 227 +++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic_v3_its.h |   6 ++
 xen/include/asm-arm/irq.h        |   8 ++
 4 files changed, 301 insertions(+)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index 1ecd63b..eb47c9d 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -157,6 +157,20 @@ static int its_send_cmd_sync(struct host_its *its, unsigned 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, uint32_t collection_id,
                              unsigned int cpu)
 {
@@ -171,6 +185,19 @@ static int its_send_cmd_mapc(struct host_its *its, uint32_t collection_id,
     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. */
 int gicv3_its_setup_collection(unsigned int cpu)
 {
@@ -450,6 +477,39 @@ int gicv3_its_init(void)
     return 0;
 }
 
+/*
+ * On the host ITS @its, map @nr_events consecutive LPIs.
+ * The mapping connects a device @devid and event @eventid pair to LPI @lpi,
+ * increasing both @eventid and @lpi to cover the number of requested LPIs.
+ */
+static int gicv3_its_map_host_events(struct host_its *its,
+                                     uint32_t devid, uint32_t eventid,
+                                     uint32_t lpi, uint32_t nr_events)
+{
+    uint32_t i;
+    int ret;
+
+    for ( i = 0; i < nr_events; i++ )
+    {
+        /* For now we map every host LPI to host CPU 0 */
+        ret = its_send_cmd_mapti(its, devid, eventid + i, lpi + i, 0);
+        if ( ret )
+            return ret;
+
+        ret = its_send_cmd_inv(its, devid, eventid + i);
+        if ( ret )
+            return ret;
+    }
+
+    /* TODO: Consider using INVALL here. Didn't work on the model, though. */
+
+    ret = its_send_cmd_sync(its, 0);
+    if ( ret )
+        return ret;
+
+    return gicv3_its_wait_commands(its);
+}
+
 /* 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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
index 9d3df7f..0785701 100644
--- a/xen/arch/arm/gic-v3-lpi.c
+++ b/xen/arch/arm/gic-v3-lpi.c
@@ -20,14 +20,37 @@
 
 #include <xen/lib.h>
 #include <xen/mm.h>
+#include <xen/sched.h>
 #include <xen/sizes.h>
 #include <xen/warning.h>
+#include <asm/atomic.h>
+#include <asm/domain.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic_v3_its.h>
 #include <asm/io.h>
 #include <asm/page.h>
 
+/*
+ * There could be a lot of LPIs on the host side, and they always go to
+ * a guest. So having a struct irq_desc for each of them would be wasteful
+ * and useless.
+ * Instead just store enough information to find the right VCPU to inject
+ * those LPIs into, which just requires the virtual LPI number.
+ * To avoid a global lock on this data structure, this is using a lockless
+ * approach relying on the architectural atomicity of native data types:
+ * We read or write the "data" view of this union atomically, then can
+ * access the broken-down fields in our local copy.
+ */
+union host_lpi {
+    uint64_t data;
+    struct {
+        uint32_t virt_lpi;
+        uint16_t dom_id;
+        uint16_t vcpu_id;
+    };
+};
+
 #define LPI_PROPTABLE_NEEDS_FLUSHING    (1U << 0)
 
 /* Global state */
@@ -35,12 +58,23 @@ static struct {
     /* The global LPI property table, shared by all redistributors. */
     uint8_t *lpi_property;
     /*
+     * A two-level table to lookup LPIs firing on the host and look up the
+     * VCPU and virtual LPI number to inject into.
+     */
+    union host_lpi **host_lpis;
+    /*
      * Number of physical LPIs the host supports. This is a property of
      * the GIC hardware. We depart from the habit of naming these things
      * "physical" in Xen, as the GICv3/4 spec uses the term "physical LPI"
      * in a different context to differentiate them from "virtual LPIs".
      */
     unsigned long long int max_host_lpi_ids;
+    /*
+     * Protects allocation and deallocation of host LPIs and next_free_lpi,
+     * but not the actual data stored in the host_lpi entry.
+     */
+    spinlock_t host_lpis_lock;
+    uint32_t next_free_lpi;
     unsigned int flags;
 } lpi_data;
 
@@ -53,6 +87,28 @@ struct lpi_redist_data {
 static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);
 
 #define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
+#define HOST_LPIS_PER_PAGE      (PAGE_SIZE / sizeof(union host_lpi))
+
+static union host_lpi *gic_get_host_lpi(uint32_t plpi)
+{
+    union host_lpi *block;
+
+    if ( !is_lpi(plpi) || plpi >= MAX_NR_HOST_LPIS + LPI_OFFSET )
+        return NULL;
+
+    ASSERT(plpi >= LPI_OFFSET);
+
+    plpi -= LPI_OFFSET;
+
+    block = lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE];
+    if ( !block )
+        return NULL;
+
+    /* Matches the write barrier in allocation code. */
+    smp_rmb();
+
+    return &block[plpi % HOST_LPIS_PER_PAGE];
+}
 
 /*
  * An ITS can refer to redistributors in two ways: either by an ID (possibly
@@ -220,8 +276,18 @@ int gicv3_lpi_init_rdist(void __iomem * rdist_base)
 static unsigned int max_lpi_bits = 20;
 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 host_lpi_bits)
 {
+    unsigned int nr_lpi_ptrs;
+
+    /* We rely on the data structure being atomically accessible. */
+    BUILD_BUG_ON(sizeof(union host_lpi) > sizeof(unsigned long));
+
     /* An implementation needs to support at least 14 bits of LPI IDs. */
     max_lpi_bits = max(max_lpi_bits, 14U);
     lpi_data.max_host_lpi_ids = BIT_ULL(min(host_lpi_bits, max_lpi_bits));
@@ -234,11 +300,172 @@ int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
     if ( lpi_data.max_host_lpi_ids > BIT(24) )
         warning_add("Using high number of LPIs, limit memory usage with max_lpi_bits\n");
 
+    spin_lock_init(&lpi_data.host_lpis_lock);
+    lpi_data.next_free_lpi = 0;
+
+    nr_lpi_ptrs = MAX_NR_HOST_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 %llu LPIs on the host.\n", MAX_NR_HOST_LPIS);
 
     return 0;
 }
 
+static int find_unused_host_lpi(uint32_t start, uint32_t *index)
+{
+    unsigned int chunk;
+    uint32_t i = *index;
+
+    ASSERT(spin_is_locked(&lpi_data.host_lpis_lock));
+
+    for ( chunk = start;
+          chunk < MAX_NR_HOST_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 == DOMID_INVALID )
+            {
+                *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.
+ */
+int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi)
+{
+    uint32_t lpi, lpi_idx;
+    int chunk;
+    int i;
+
+    spin_lock(&lpi_data.host_lpis_lock);
+    lpi_idx = lpi_data.next_free_lpi % HOST_LPIS_PER_PAGE;
+    chunk = find_unused_host_lpi(lpi_data.next_free_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;
+
+        /* TODO: NUMA locality for quicker IRQ path? */
+        new_chunk = alloc_xenheap_page();
+        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 = DOMID_INVALID;
+
+        /*
+         * Make sure all slots are really marked empty before publishing the
+         * new chunk.
+         */
+        smp_wmb();
+
+        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 = INVALID_LPI;
+        hlpi.dom_id = d->domain_id;
+        hlpi.vcpu_id = INVALID_VCPU_ID;
+        write_u64_atomic(&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;
+    }
+
+    lpi_data.next_free_lpi = lpi + LPI_BLOCK;
+
+    /*
+     * 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);
+
+    if ( lpi_data.flags & LPI_PROPTABLE_NEEDS_FLUSHING )
+        clean_and_invalidate_dcache_va_range(&lpi_data.lpi_property[lpi],
+                                             LPI_BLOCK);
+
+    *first_lpi = lpi + LPI_OFFSET;
+
+    return 0;
+}
+
+void gicv3_free_host_lpi_block(uint32_t first_lpi)
+{
+    union host_lpi *hlpi, empty_lpi = { .dom_id = DOMID_INVALID };
+    int i;
+
+    hlpi = gic_get_host_lpi(first_lpi);
+    if ( !hlpi )
+        return;         /* Nothing to free here. */
+
+    spin_lock(&lpi_data.host_lpis_lock);
+
+    for ( i = 0; i < LPI_BLOCK; i++ )
+        write_u64_atomic(&hlpi[i].data, empty_lpi.data);
+
+    /*
+     * Make sure the next allocation can reuse this block, as we do only
+     * forward scanning when finding an unused block.
+     */
+    if ( lpi_data.next_free_lpi > first_lpi )
+        lpi_data.next_free_lpi = first_lpi;
+
+    spin_unlock(&lpi_data.host_lpis_lock);
+
+    return;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 7d7ec32..13b3e14 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -108,6 +108,9 @@
 #define HOST_ITS_FLUSH_CMD_QUEUE        (1U << 0)
 #define HOST_ITS_USES_PTA               (1U << 1)
 
+/* We allocate LPIs on the hosts in chunks of 32 to reduce handling overhead. */
+#define LPI_BLOCK                       32
+
 /* data structure for each hardware ITS */
 struct host_its {
     struct list_head entry;
@@ -146,6 +149,9 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta);
 /* Map a collection for this host CPU to each host ITS. */
 int gicv3_its_setup_collection(unsigned int cpu);
 
+int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
+void gicv3_free_host_lpi_block(uint32_t first_lpi);
+
 #else
 
 static LIST_HEAD(host_its_list);
diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
index f940092..7c76626 100644
--- a/xen/include/asm-arm/irq.h
+++ b/xen/include/asm-arm/irq.h
@@ -28,6 +28,9 @@ struct arch_irq_desc {
 
 #define LPI_OFFSET      8192
 
+/* LPIs are always numbered starting at 8192, so 0 is a good invalid case. */
+#define INVALID_LPI     0
+
 #define nr_irqs NR_IRQS
 #define nr_static_irqs NR_IRQS
 #define arch_hwdom_irqs(domid) NR_IRQS
@@ -41,6 +44,11 @@ struct irq_desc *__irq_to_desc(int irq);
 
 void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq);
 
+static inline bool is_lpi(unsigned int irq)
+{
+    return irq >= LPI_OFFSET;
+}
+
 #define domain_pirq_to_irq(d, pirq) (pirq)
 
 bool_t is_assignable_irq(unsigned int irq);
-- 
2.8.2


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

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

* [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (8 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06 15:17   ` Julien Grall
  2017-04-07 14:31   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping Andre Przywara
                   ` (20 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Create a new file to hold the emulation code for the ITS widget.
This just holds the data structure and a init and free function for now.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/Makefile            |  1 +
 xen/arch/arm/vgic-v3-its.c       | 85 ++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-v3.c           |  3 ++
 xen/include/asm-arm/gic_v3_its.h | 12 ++++++
 4 files changed, 101 insertions(+)
 create mode 100644 xen/arch/arm/vgic-v3-its.c

diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 6be85ab..49e1fb2 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -47,6 +47,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-v3-its.o
 obj-y += vm_event.o
 obj-y += vtimer.o
 obj-y += vpsci.o
diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
new file mode 100644
index 0000000..94c3c44
--- /dev/null
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -0,0 +1,85 @@
+/*
+ * xen/arch/arm/vgic-v3-its.c
+ *
+ * ARM Interrupt Translation Service (ITS) emulation
+ *
+ * Andre Przywara <andre.przywara@arm.com>
+ * Copyright (c) 2016,2017 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; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <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_v3_its.h>
+#include <asm/vgic.h>
+#include <asm/vgic-emul.h>
+
+/*
+ * Data structure to describe a virtual ITS.
+ * If both the vcmd_lock and the its_lock are required, the vcmd_lock must
+ * be taken first.
+ */
+struct virt_its {
+    struct domain *d;
+    unsigned int devid_bits;
+    unsigned int intid_bits;
+    spinlock_t vcmd_lock;       /* Protects the virtual command buffer, which */
+    uint64_t cwriter;           /* consists of CBASER and CWRITER and those   */
+    uint64_t creadr;            /* shadow variables cwriter and creadr. */
+    /* Protects the rest of this structure, including the ITS tables. */
+    spinlock_t its_lock;
+    uint64_t cbaser;
+    uint64_t baser_dev, baser_coll;     /* BASER0 and BASER1 for the guest */
+    unsigned int max_collections;
+    unsigned 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;
+    uint16_t pad;
+};
+
+void vgic_v3_its_init_domain(struct domain *d)
+{
+}
+
+void vgic_v3_its_free_domain(struct domain *d)
+{
+}
+
+/*
+ * 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 0679e76..142eb64 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -1449,6 +1449,8 @@ static int vgic_v3_domain_init(struct domain *d)
     d->arch.vgic.nr_regions = rdist_count;
     d->arch.vgic.rdist_regions = rdist_regions;
 
+    vgic_v3_its_init_domain(d);
+
     /*
      * Domain 0 gets the hardware address.
      * Guests get the virtual platform layout.
@@ -1521,6 +1523,7 @@ static int vgic_v3_domain_init(struct domain *d)
 
 static void vgic_v3_domain_free(struct domain *d)
 {
+    vgic_v3_its_free_domain(d);
     xfree(d->arch.vgic.rdist_regions);
 }
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 13b3e14..9b0c0ec 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -149,6 +149,10 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta);
 /* Map a collection for this host CPU to each host ITS. */
 int gicv3_its_setup_collection(unsigned int cpu);
 
+/* Initialize and destroy the per-domain parts of the virtual ITS support. */
+void vgic_v3_its_init_domain(struct domain *d);
+void vgic_v3_its_free_domain(struct domain *d);
+
 int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
 void gicv3_free_host_lpi_block(uint32_t first_lpi);
 
@@ -191,6 +195,14 @@ static inline int gicv3_its_setup_collection(unsigned int cpu)
     BUG();
 }
 
+static inline void vgic_v3_its_init_domain(struct domain *d)
+{
+}
+
+static inline void vgic_v3_its_free_domain(struct domain *d)
+{
+}
+
 #endif /* CONFIG_HAS_ITS */
 
 #endif
-- 
2.8.2


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

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

* [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (9 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:41   ` Stefano Stabellini
                     ` (2 more replies)
  2017-04-05 23:19 ` [PATCH v5 12/30] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
                   ` (19 subsequent siblings)
  30 siblings, 3 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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 an rbtree to
later be able to iterate over it or to easily teardown guests.
Because device IDs are per ITS, we need to identify a virtual ITS. We
use the doorbell address for that purpose, as it is a nice architectural
MSI property and spares us handling with opaque pointer or break
the VGIC abstraction.

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

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index eb47c9d..45bbfa7 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -21,6 +21,8 @@
 #include <xen/lib.h>
 #include <xen/delay.h>
 #include <xen/mm.h>
+#include <xen/rbtree.h>
+#include <xen/sched.h>
 #include <xen/sizes.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
@@ -36,6 +38,26 @@
  */
 LIST_HEAD(host_its_list);
 
+/*
+ * Describes a device which is using the ITS and is used by a guest.
+ * Since device IDs are per ITS (in contrast to vLPIs, which are per
+ * guest), we have to differentiate between different virtual ITSes.
+ * We use the doorbell address here, since this is a nice architectural
+ * property of MSIs in general and we can easily get to the base address
+ * of the ITS and look that up.
+ */
+struct its_devices {
+    struct rb_node rbnode;
+    struct host_its *hw_its;
+    void *itt_addr;
+    paddr_t guest_doorbell;             /* Identifies the virtual ITS */
+    uint32_t host_devid;
+    uint32_t guest_devid;
+    uint32_t eventids;                  /* Number of event IDs (MSIs) */
+    uint32_t *host_lpi_blocks;          /* Which LPIs are used on the host */
+    struct pending_irq *pend_irqs;      /* One struct per event */
+};
+
 bool gicv3_its_host_has_its(void)
 {
     return !list_empty(&host_its_list);
@@ -185,6 +207,30 @@ static int its_send_cmd_mapc(struct host_its *its, uint32_t collection_id,
     return its_send_command(its, cmd);
 }
 
+static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
+                             uint8_t size_bits, paddr_t itt_addr, bool valid)
+{
+    uint64_t cmd[4];
+
+    if ( valid )
+    {
+        ASSERT(size_bits <= its->evid_bits);
+        ASSERT(size_bits > 0);
+        ASSERT(!(itt_addr & ~GENMASK_ULL(51, 8)));
+
+        /* The number of events is encoded as "number of bits minus one". */
+        size_bits--;
+    }
+    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
+    cmd[1] = size_bits;
+    cmd[2] = itt_addr;
+    if ( valid )
+        cmd[2] |= GITS_VALID_BIT;
+    cmd[3] = 0x00;
+
+    return its_send_command(its, cmd);
+}
+
 static int its_send_cmd_inv(struct host_its *its,
                             uint32_t deviceid, uint32_t eventid)
 {
@@ -477,6 +523,66 @@ int gicv3_its_init(void)
     return 0;
 }
 
+static int remove_mapped_guest_device(struct its_devices *dev)
+{
+    int ret = 0;
+    unsigned int i;
+
+    if ( dev->hw_its )
+        /* MAPD also discards all events with this device ID. */
+        ret = its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
+
+    for ( i = 0; i < dev->eventids / LPI_BLOCK; i++ )
+        gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
+
+    /* Make sure the MAPD command above is really executed. */
+    if ( !ret )
+        ret = gicv3_its_wait_commands(dev->hw_its);
+
+    /* This should never happen, but just in case ... */
+    if ( ret )
+        printk(XENLOG_WARNING "Can't unmap host ITS device 0x%x\n",
+               dev->host_devid);
+
+    xfree(dev->itt_addr);
+    xfree(dev->pend_irqs);
+    xfree(dev->host_lpi_blocks);
+    xfree(dev);
+
+    return 0;
+}
+
+static struct host_its *gicv3_its_find_by_doorbell(paddr_t doorbell_address)
+{
+    struct host_its *hw_its;
+
+    list_for_each_entry(hw_its, &host_its_list, entry)
+    {
+        if ( hw_its->addr + ITS_DOORBELL_OFFSET == doorbell_address )
+            return hw_its;
+    }
+
+    return NULL;
+}
+
+static int compare_its_guest_devices(struct its_devices *dev,
+                                     paddr_t vdoorbell, uint32_t vdevid)
+{
+    if ( dev->guest_doorbell < vdoorbell )
+        return -1;
+
+    if ( dev->guest_doorbell > vdoorbell )
+        return 1;
+
+    if ( dev->guest_devid < vdevid )
+        return -1;
+
+    if ( dev->guest_devid > vdevid )
+        return 1;
+
+    return 0;
+}
+
 /*
  * On the host ITS @its, map @nr_events consecutive LPIs.
  * The mapping connects a device @devid and event @eventid pair to LPI @lpi,
@@ -510,6 +616,163 @@ static int gicv3_its_map_host_events(struct host_its *its,
     return gicv3_its_wait_commands(its);
 }
 
+/*
+ * Map a hardware device, identified by a certain host ITS and its device ID
+ * to domain d, a guest ITS (identified by its doorbell address) and device ID.
+ * Also provide the number of events (MSIs) needed for that device.
+ * This does not check if this particular hardware device is already mapped
+ * at another domain, it is expected that this would be done by the caller.
+ */
+int gicv3_its_map_guest_device(struct domain *d,
+                               paddr_t host_doorbell, uint32_t host_devid,
+                               paddr_t guest_doorbell, uint32_t guest_devid,
+                               uint32_t nr_events, bool valid)
+{
+    void *itt_addr = NULL;
+    struct host_its *hw_its;
+    struct its_devices *dev = NULL;
+    struct rb_node **new = &d->arch.vgic.its_devices.rb_node, *parent = NULL;
+    unsigned int i;
+    int ret = -ENOENT;
+
+    hw_its = gicv3_its_find_by_doorbell(host_doorbell);
+    if ( !hw_its )
+        return ret;
+
+    /* Sanitise the provided hardware values against the host ITS. */
+    if ( host_devid >= BIT(hw_its->devid_bits) )
+        return -EINVAL;
+
+    /* We allocate events and LPIs in chunks of LPI_BLOCK (=32). */
+    nr_events = ROUNDUP(nr_events, LPI_BLOCK);
+    if ( nr_events >= BIT(hw_its->evid_bits) )
+        return -EINVAL;
+
+    /* check for already existing mappings */
+    spin_lock(&d->arch.vgic.its_devices_lock);
+    while ( *new )
+    {
+        struct its_devices *temp;
+        int cmp;
+
+        temp = rb_entry(*new, struct its_devices, rbnode);
+
+        parent = *new;
+        cmp = compare_its_guest_devices(temp, guest_doorbell, guest_devid);
+        if ( !cmp )
+        {
+            if ( !valid )
+                rb_erase(&temp->rbnode, &d->arch.vgic.its_devices);
+
+            spin_unlock(&d->arch.vgic.its_devices_lock);
+
+            if ( valid )
+            {
+                printk(XENLOG_G_WARNING "d%d tried to remap guest ITS device 0x%x to host device 0x%x\n",
+                        d->domain_id, guest_devid, host_devid);
+                return -EBUSY;
+            }
+
+            return remove_mapped_guest_device(temp);
+        }
+
+        if ( cmp > 0 )
+            new = &((*new)->rb_left);
+        else
+            new = &((*new)->rb_right);
+    }
+
+    if ( !valid )
+        goto out_unlock;
+
+    ret = -ENOMEM;
+
+    /* An Interrupt Translation Table needs to be 256-byte aligned. */
+    itt_addr = _xzalloc(nr_events * hw_its->itte_size, 256);
+    if ( !itt_addr )
+        goto out_unlock;
+
+    dev = xzalloc(struct its_devices);
+    if ( !dev )
+        goto out_unlock;
+
+    /*
+     * Allocate the pending_irqs for each virtual LPI. They will be put
+     * into the domain's radix tree upon the guest's MAPTI command.
+     */
+    dev->pend_irqs = xzalloc_array(struct pending_irq, nr_events);
+    if ( !dev->pend_irqs )
+        goto out_unlock;
+
+    dev->host_lpi_blocks = xzalloc_array(uint32_t, nr_events);
+    if ( !dev->host_lpi_blocks )
+        goto out_unlock;
+
+    ret = its_send_cmd_mapd(hw_its, host_devid, fls(nr_events - 1),
+                            virt_to_maddr(itt_addr), true);
+    if ( ret )
+        goto out_unlock;
+
+    dev->itt_addr = itt_addr;
+    dev->hw_its = hw_its;
+    dev->guest_doorbell = guest_doorbell;
+    dev->guest_devid = guest_devid;
+    dev->host_devid = host_devid;
+    dev->eventids = nr_events;
+
+    rb_link_node(&dev->rbnode, parent, new);
+    rb_insert_color(&dev->rbnode, &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 < nr_events / LPI_BLOCK; i++ )
+    {
+        ret = gicv3_allocate_host_lpi_block(d, &dev->host_lpi_blocks[i]);
+        if ( ret < 0 )
+            break;
+
+        ret = gicv3_its_map_host_events(hw_its, host_devid, i * LPI_BLOCK,
+                                        dev->host_lpi_blocks[i], LPI_BLOCK);
+        if ( ret < 0 )
+            break;
+    }
+
+    if ( ret )
+    {
+        do {
+            i--;
+            gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
+            if ( i == 0 )
+                break;
+        } while (1);
+
+        /* Unmapping the device will discard all LPIs mapped so far. */
+        its_send_cmd_mapd(hw_its, host_devid, 1, 0, false);
+
+        goto out;
+    }
+
+    return 0;
+
+out_unlock:
+    spin_unlock(&d->arch.vgic.its_devices_lock);
+
+out:
+    if ( dev )
+    {
+        xfree(dev->pend_irqs);
+        xfree(dev->host_lpi_blocks);
+    }
+    xfree(itt_addr);
+    xfree(dev);
+
+    return ret;
+}
+
 /* 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-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 94c3c44..9dfda59 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -69,10 +69,13 @@ struct vits_itte
 
 void vgic_v3_its_init_domain(struct domain *d)
 {
+    spin_lock_init(&d->arch.vgic.its_devices_lock);
+    d->arch.vgic.its_devices = RB_ROOT;
 }
 
 void vgic_v3_its_free_domain(struct domain *d)
 {
+    ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
 }
 
 /*
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 09fe502..503a3cf 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -10,6 +10,7 @@
 #include <asm/gic.h>
 #include <public/hvm/params.h>
 #include <xen/serial.h>
+#include <xen/rbtree.h>
 
 struct hvm_domain
 {
@@ -108,6 +109,8 @@ struct arch_domain
         } *rdist_regions;
         int nr_regions;                     /* Number of rdist regions */
         uint32_t rdist_stride;              /* Re-Distributor stride */
+        struct rb_root its_devices;         /* Devices mapped to an ITS */
+        spinlock_t its_devices_lock;        /* Protects the its_devices tree */
 #endif
     } vgic;
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 9b0c0ec..3b7c724 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -103,7 +103,10 @@
 #define GITS_CMD_MOVALL                 0x0e
 #define GITS_CMD_DISCARD                0x0f
 
+#define ITS_DOORBELL_OFFSET             0x10040
+
 #include <xen/device_tree.h>
+#include <xen/rbtree.h>
 
 #define HOST_ITS_FLUSH_CMD_QUEUE        (1U << 0)
 #define HOST_ITS_USES_PTA               (1U << 1)
@@ -153,6 +156,16 @@ int gicv3_its_setup_collection(unsigned int cpu);
 void vgic_v3_its_init_domain(struct domain *d);
 void vgic_v3_its_free_domain(struct domain *d);
 
+/*
+ * Map a device on the host by allocating an ITT on the host (ITS).
+ * "nr_event" specifies how many events (interrupts) this device will need.
+ * Setting "valid" to false deallocates the device.
+ */
+int gicv3_its_map_guest_device(struct domain *d,
+                               paddr_t host_doorbell, uint32_t host_devid,
+                               paddr_t guest_doorbell, uint32_t guest_devid,
+                               uint32_t nr_events, bool valid);
+
 int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
 void gicv3_free_host_lpi_block(uint32_t first_lpi);
 
-- 
2.8.2


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

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

* [PATCH v5 12/30] ARM: GICv3: introduce separate pending_irq structs for LPIs
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (10 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06 15:59   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests Andre Przywara
                   ` (18 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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. We only care about mapped LPIs, so we can get away
with having struct pending_irq's only for them.
Maintain a radix tree per domain where we drop the pointer to the
respective pending_irq. The index used is the virtual LPI number.
The memory for the actual structures has been allocated already per
device at device mapping time.
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/vgic-v2.c       |  8 ++++++++
 xen/arch/arm/vgic-v3.c       | 22 ++++++++++++++++++++++
 xen/arch/arm/vgic.c          |  2 ++
 xen/include/asm-arm/domain.h |  2 ++
 xen/include/asm-arm/vgic.h   |  2 ++
 5 files changed, 36 insertions(+)

diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
index 0674f7b..3cad68f 100644
--- a/xen/arch/arm/vgic-v2.c
+++ b/xen/arch/arm/vgic-v2.c
@@ -703,10 +703,18 @@ static void vgic_v2_domain_free(struct domain *d)
     /* Nothing to be cleanup for this driver */
 }
 
+static struct pending_irq *vgic_v2_lpi_to_pending(struct domain *d,
+                                                  unsigned int vlpi)
+{
+    /* Dummy function, no LPIs on a VGICv2. */
+    BUG();
+}
+
 static const struct vgic_ops vgic_v2_ops = {
     .vcpu_init   = vgic_v2_vcpu_init,
     .domain_init = vgic_v2_domain_init,
     .domain_free = vgic_v2_domain_free,
+    .lpi_to_pending = vgic_v2_lpi_to_pending,
     .max_vcpus = 8,
 };
 
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 142eb64..5128f13 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -1451,6 +1451,9 @@ static int vgic_v3_domain_init(struct domain *d)
 
     vgic_v3_its_init_domain(d);
 
+    rwlock_init(&d->arch.vgic.pend_lpi_tree_lock);
+    radix_tree_init(&d->arch.vgic.pend_lpi_tree);
+
     /*
      * Domain 0 gets the hardware address.
      * Guests get the virtual platform layout.
@@ -1524,14 +1527,33 @@ static int vgic_v3_domain_init(struct domain *d)
 static void vgic_v3_domain_free(struct domain *d)
 {
     vgic_v3_its_free_domain(d);
+    radix_tree_destroy(&d->arch.vgic.pend_lpi_tree, NULL);
     xfree(d->arch.vgic.rdist_regions);
 }
 
+/*
+ * Looks up a virtual LPI number in our tree of mapped LPIs. This will return
+ * the corresponding struct pending_irq, which we also use to store the
+ * enabled and pending bit plus the priority.
+ * Returns NULL if an LPI cannot be found (or no LPIs are supported).
+ */
+struct pending_irq *vgic_v3_lpi_to_pending(struct domain *d, unsigned int lpi)
+{
+    struct pending_irq *pirq;
+
+    read_lock(&d->arch.vgic.pend_lpi_tree_lock);
+    pirq = radix_tree_lookup(&d->arch.vgic.pend_lpi_tree, lpi);
+    read_unlock(&d->arch.vgic.pend_lpi_tree_lock);
+
+    return pirq;
+}
+
 static const struct vgic_ops v3_ops = {
     .vcpu_init   = vgic_v3_vcpu_init,
     .domain_init = vgic_v3_domain_init,
     .domain_free = vgic_v3_domain_free,
     .emulate_reg  = vgic_v3_emulate_reg,
+    .lpi_to_pending = vgic_v3_lpi_to_pending,
     /*
      * We use both AFF1 and AFF0 in (v)MPIDR. Thus, the max number of CPU
      * that can be supported is up to 4096(==256*16) in theory.
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index 67d75a6..d704d7c 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -435,6 +435,8 @@ struct pending_irq *irq_to_pending(struct vcpu *v, unsigned int irq)
      * are used for SPIs; the rests are used for per cpu irqs */
     if ( irq < 32 )
         n = &v->arch.vgic.pending_irqs[irq];
+    else if ( is_lpi(irq) )
+        n = v->domain->arch.vgic.handler->lpi_to_pending(v->domain, irq);
     else
         n = &v->domain->arch.vgic.pending_irqs[irq - 32];
     return n;
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 503a3cf..6ee7538 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -111,6 +111,8 @@ struct arch_domain
         uint32_t rdist_stride;              /* Re-Distributor stride */
         struct rb_root its_devices;         /* Devices mapped to an ITS */
         spinlock_t its_devices_lock;        /* Protects the its_devices tree */
+        struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
+        rwlock_t pend_lpi_tree_lock;        /* Protects the pend_lpi_tree */
 #endif
     } vgic;
 
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 894c3f1..7c86f5b 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -134,6 +134,8 @@ struct vgic_ops {
     void (*domain_free)(struct domain *d);
     /* vGIC sysreg/cpregs emulate */
     bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
+    /* lookup the struct pending_irq for a given LPI interrupt */
+    struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int vlpi);
     /* Maximum number of vCPU supported */
     const unsigned int max_vcpus;
 };
-- 
2.8.2


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

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

* [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (11 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 12/30] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:45   ` Stefano Stabellini
  2017-04-06 18:10   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 14/30] ARM: GICv3: enable ITS and LPIs on the host Andre Przywara
                   ` (17 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Upon receiving an LPI on the host, 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.
Also we enhance struct pending_irq to cache the pending bit and the
priority information for LPIs, as we can't afford to walk the tables in
guest memory every time we handle an incoming LPI.
This introduces a do_LPI() as a hardware gic_ops and a function to
retrieve the (cached) priority value of an LPI and a vgic_ops.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-v2.c            |  7 +++++
 xen/arch/arm/gic-v3-lpi.c        | 56 ++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c            |  1 +
 xen/arch/arm/gic.c               |  8 +++++-
 xen/arch/arm/vgic-v2.c           |  7 +++++
 xen/arch/arm/vgic-v3.c           | 12 +++++++++
 xen/arch/arm/vgic.c              |  7 ++++-
 xen/include/asm-arm/gic.h        |  2 ++
 xen/include/asm-arm/gic_v3_its.h |  8 ++++++
 xen/include/asm-arm/vgic.h       |  3 +++
 10 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
index 270a136..f4d7949 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -1217,6 +1217,12 @@ static int __init gicv2_init(void)
     return 0;
 }
 
+void gicv2_do_LPI(unsigned int lpi)
+{
+    /* No LPIs in a GICv2 */
+    BUG();
+}
+
 const static struct gic_hw_operations gicv2_ops = {
     .info                = &gicv2_info,
     .init                = gicv2_init,
@@ -1244,6 +1250,7 @@ const static struct gic_hw_operations gicv2_ops = {
     .make_hwdom_madt     = gicv2_make_hwdom_madt,
     .map_hwdom_extra_mappings = gicv2_map_hwdown_extra_mappings,
     .iomem_deny_access   = gicv2_iomem_deny_access,
+    .do_LPI              = gicv2_do_LPI,
 };
 
 /* Set up the GIC */
diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
index 0785701..d8baebc 100644
--- a/xen/arch/arm/gic-v3-lpi.c
+++ b/xen/arch/arm/gic-v3-lpi.c
@@ -136,6 +136,62 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
         return per_cpu(lpi_redist, cpu).redist_id << 16;
 }
 
+/*
+ * 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.
+ * Please note that LPIs are edge-triggered only, also have no active state,
+ * so spurious interrupts on the host side are no issue (we can just ignore
+ * them).
+ * Also a guest cannot expect that firing interrupts that haven't been
+ * fully configured yet will reach the CPU, so we don't need to care about
+ * this special case.
+ */
+void gicv3_do_LPI(unsigned int lpi)
+{
+    struct domain *d;
+    union host_lpi *hlpip, hlpi;
+    struct vcpu *vcpu;
+
+    /* EOI the LPI already. */
+    WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
+
+    /* Find out if a guest mapped something to this physical LPI. */
+    hlpip = gic_get_host_lpi(lpi);
+    if ( !hlpip )
+        return;
+
+    hlpi.data = read_u64_atomic(&hlpip->data);
+
+    /*
+     * Unmapped events are marked with an invalid LPI ID. We can safely
+     * ignore them, as they have no further state and no-one can expect
+     * to see them if they have not been mapped.
+     */
+    if ( hlpi.virt_lpi == INVALID_LPI )
+        return;
+
+    d = rcu_lock_domain_by_id(hlpi.dom_id);
+    if ( !d )
+        return;
+
+    /* Make sure we don't step beyond the vcpu array. */
+    if ( hlpi.vcpu_id >= d->max_vcpus )
+    {
+        rcu_unlock_domain(d);
+        return;
+    }
+
+    vcpu = d->vcpu[hlpi.vcpu_id];
+
+    /* Check if the VCPU is ready to receive LPIs. */
+    if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
+        vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
+
+    rcu_unlock_domain(d);
+}
+
 static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
 {
     uint64_t val;
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index a559e5e..63dbc21 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -1670,6 +1670,7 @@ static const struct gic_hw_operations gicv3_ops = {
     .make_hwdom_dt_node  = gicv3_make_hwdom_dt_node,
     .make_hwdom_madt     = gicv3_make_hwdom_madt,
     .iomem_deny_access   = gicv3_iomem_deny_access,
+    .do_LPI              = gicv3_do_LPI,
 };
 
 static int __init gicv3_dt_preinit(struct dt_device_node *node, const void *data)
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 9522c6c..a56be34 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -697,7 +697,13 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
             do_IRQ(regs, irq, is_fiq);
             local_irq_disable();
         }
-        else if (unlikely(irq < 16))
+        else if ( is_lpi(irq) )
+        {
+            local_irq_enable();
+            gic_hw_ops->do_LPI(irq);
+            local_irq_disable();
+        }
+        else if ( unlikely(irq < 16) )
         {
             do_sgi(regs, irq);
         }
diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
index 3cad68f..5f4c5ad 100644
--- a/xen/arch/arm/vgic-v2.c
+++ b/xen/arch/arm/vgic-v2.c
@@ -710,11 +710,18 @@ static struct pending_irq *vgic_v2_lpi_to_pending(struct domain *d,
     BUG();
 }
 
+static int vgic_v2_lpi_get_priority(struct domain *d, unsigned int vlpi)
+{
+    /* Dummy function, no LPIs on a VGICv2. */
+    BUG();
+}
+
 static const struct vgic_ops vgic_v2_ops = {
     .vcpu_init   = vgic_v2_vcpu_init,
     .domain_init = vgic_v2_domain_init,
     .domain_free = vgic_v2_domain_free,
     .lpi_to_pending = vgic_v2_lpi_to_pending,
+    .lpi_get_priority = vgic_v2_lpi_get_priority,
     .max_vcpus = 8,
 };
 
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 5128f13..2a14305 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -1548,12 +1548,24 @@ struct pending_irq *vgic_v3_lpi_to_pending(struct domain *d, unsigned int lpi)
     return pirq;
 }
 
+/* Retrieve the priority of an LPI from its struct pending_irq. */
+int vgic_v3_lpi_get_priority(struct domain *d, uint32_t vlpi)
+{
+    struct pending_irq *p = vgic_v3_lpi_to_pending(d, vlpi);
+
+    if ( !p )
+        return GIC_PRI_IRQ;
+
+    return p->lpi_priority;
+}
+
 static const struct vgic_ops v3_ops = {
     .vcpu_init   = vgic_v3_vcpu_init,
     .domain_init = vgic_v3_domain_init,
     .domain_free = vgic_v3_domain_free,
     .emulate_reg  = vgic_v3_emulate_reg,
     .lpi_to_pending = vgic_v3_lpi_to_pending,
+    .lpi_get_priority = vgic_v3_lpi_get_priority,
     /*
      * We use both AFF1 and AFF0 in (v)MPIDR. Thus, the max number of CPU
      * that can be supported is up to 4096(==256*16) in theory.
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index d704d7c..cd9a2a5 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -226,10 +226,15 @@ 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;
 
+    /* LPIs don't have a rank, also store their priority separately. */
+    if ( is_lpi(virq) )
+        return v->domain->arch.vgic.handler->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);
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index 836a103..42963c0 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -366,6 +366,8 @@ struct gic_hw_operations {
     int (*map_hwdom_extra_mappings)(struct domain *d);
     /* Deny access to GIC regions */
     int (*iomem_deny_access)(const struct domain *d);
+    /* Handle LPIs, which require special handling */
+    void (*do_LPI)(unsigned int lpi);
 };
 
 void register_gic_ops(const struct gic_hw_operations *ops);
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 3b7c724..eb71fbd 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -139,6 +139,8 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
 
 bool gicv3_its_host_has_its(void);
 
+void gicv3_do_LPI(unsigned int lpi);
+
 int gicv3_lpi_init_rdist(void __iomem * rdist_base);
 
 /* Initialize the host structures for LPIs and the host ITSes. */
@@ -182,6 +184,12 @@ static inline bool gicv3_its_host_has_its(void)
     return false;
 }
 
+static inline void gicv3_do_LPI(unsigned int lpi)
+{
+    /* We don't enable LPIs without an ITS. */
+    BUG();
+}
+
 static inline int gicv3_lpi_init_rdist(void __iomem * rdist_base)
 {
     return -ENODEV;
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 7c86f5b..08d6294 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -66,12 +66,14 @@ struct pending_irq
 #define GIC_IRQ_GUEST_VISIBLE  2
 #define GIC_IRQ_GUEST_ENABLED  3
 #define GIC_IRQ_GUEST_MIGRATING   4
+#define GIC_IRQ_GUEST_LPI_PENDING 5     /* Caches the pending bit of an LPI. */
     unsigned long status;
     struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
     unsigned int irq;
 #define GIC_INVALID_LR         (uint8_t)~0
     uint8_t lr;
     uint8_t priority;
+    uint8_t lpi_priority;       /* Caches the priority if this is an LPI. */
     /* inflight is used to append instances of pending_irq to
      * vgic.inflight_irqs */
     struct list_head inflight;
@@ -136,6 +138,7 @@ struct vgic_ops {
     bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
     /* lookup the struct pending_irq for a given LPI interrupt */
     struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int vlpi);
+    int (*lpi_get_priority)(struct domain *d, uint32_t vlpi);
     /* Maximum number of vCPU supported */
     const unsigned int max_vcpus;
 };
-- 
2.8.2


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

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

* [PATCH v5 14/30] ARM: GICv3: enable ITS and LPIs on the host
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (12 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:19 ` [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
                   ` (16 subsequent siblings)
  30 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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-v3-its.c |  4 ++++
 xen/arch/arm/gic-v3.c     | 18 ++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index 45bbfa7..76b0316 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -505,6 +505,10 @@ static int gicv3_its_init_single_its(struct host_its *hw_its)
         return -ENOMEM;
     writeq_relaxed(0, hw_its->its_base + GITS_CWRITER);
 
+    /* Now enable interrupt translation and command processing 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 63dbc21..54fbb19 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -620,6 +620,21 @@ static int gicv3_enable_redist(void)
     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;
@@ -731,11 +746,14 @@ static int gicv3_cpu_init(void)
     if ( gicv3_enable_redist() )
         return -ENODEV;
 
+    /* If the host has any ITSes, enable LPIs now. */
     if ( gicv3_its_host_has_its() )
     {
         ret = gicv3_its_setup_collection(smp_processor_id());
         if ( ret )
             return ret;
+        if ( !gicv3_enable_lpis() )
+            return -EBUSY;
     }
 
     /* Set priority on PPI and SGI interrupts */
-- 
2.8.2


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

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

* [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (13 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 14/30] ARM: GICv3: enable ITS and LPIs on the host Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:55   ` Stefano Stabellini
  2017-04-06 20:33   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs Andre Przywara
                   ` (15 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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.
This introduces a function to read and write from and to guest memory,
to be later able to access the tables located there.
This vgic_access_guest_memory() function has been written by Vijaya Kumar
as part of an earlier series.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-v3.c       | 152 ++++++++++++++++++++++++++++++++++++++-----
 xen/arch/arm/vgic.c          |  39 +++++++++++
 xen/include/asm-arm/domain.h |   6 +-
 xen/include/asm-arm/vgic.h   |   3 +
 4 files changed, 182 insertions(+), 18 deletions(-)

diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 2a14305..0623803 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -19,12 +19,14 @@
  */
 
 #include <xen/bitops.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>
@@ -228,12 +230,21 @@ 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;
+
+        spin_lock(&v->arch.vgic.lock);
+        *r = vgic_reg64_extract(v->domain->arch.vgic.rdist_propbase, info);
+        spin_unlock(&v->arch.vgic.lock);
+        return 1;
 
     case VREG64(GICR_PENDBASER):
-        /* LPI's not implemented */
-        goto read_as_zero_64;
+        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
+
+        spin_lock(&v->arch.vgic.lock);
+        *r = vgic_reg64_extract(v->arch.vgic.rdist_pendbase, info);
+        *r &= ~GICR_PENDBASER_PTZ;       /* WO, reads as 0 */
+        spin_unlock(&v->arch.vgic.lock);
+        return 1;
 
     case 0x0080:
         goto read_reserved;
@@ -301,11 +312,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;
@@ -330,11 +336,95 @@ 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 &= ~GICR_PROPBASER_RES0_MASK;
+
+    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 &= ~GICR_PENDBASER_RES0_MASK;
+
+    return reg;
+}
+
 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 )
     {
@@ -365,36 +455,64 @@ static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
         goto write_impl_defined;
 
     case VREG64(GICR_SETLPIR):
-        /* LPI is not implemented */
+        /* LPIs without an ITS are not implemented */
         goto write_ignore_64;
 
     case VREG64(GICR_CLRLPIR):
-        /* LPI is not implemented */
+        /* LPIs without an ITS are not implemented */
         goto write_ignore_64;
 
     case 0x0050:
         goto write_reserved;
 
     case VREG64(GICR_PROPBASER):
-        /* LPI is not implemented */
-        goto write_ignore_64;
+        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
+
+        spin_lock(&v->arch.vgic.lock);
+
+        /* Writing PROPBASER with LPIs enabled is UNPREDICTABLE. */
+        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
+        {
+            reg = v->domain->arch.vgic.rdist_propbase;
+            vgic_reg64_update(&reg, r, info);
+            reg = sanitize_propbaser(reg);
+            v->domain->arch.vgic.rdist_propbase = reg;
+        }
+
+        spin_unlock(&v->arch.vgic.lock);
+
+        return 1;
 
     case VREG64(GICR_PENDBASER):
-        /* LPI is not implemented */
-        goto write_ignore_64;
+        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
+
+        spin_lock(&v->arch.vgic.lock);
+
+        /* Writing PENDBASER with LPIs enabled is UNPREDICTABLE. */
+        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
+        {
+            reg = v->arch.vgic.rdist_pendbase;
+            vgic_reg64_update(&reg, r, info);
+            reg = sanitize_pendbaser(reg);
+            v->arch.vgic.rdist_pendbase = reg;
+        }
+
+        spin_unlock(&v->arch.vgic.lock);
+
+        return 1;
 
     case 0x0080:
         goto write_reserved;
 
     case VREG64(GICR_INVLPIR):
-        /* LPI is not implemented */
+        /* LPIs without an ITS are not implemented */
         goto write_ignore_64;
 
     case 0x00A8:
         goto write_reserved;
 
     case VREG64(GICR_INVALLR):
-        /* LPI is not implemented */
+        /* LPIs without an ITS are not implemented */
         goto write_ignore_64;
 
     case 0x00B8:
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index cd9a2a5..9b0dc3d 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -20,6 +20,7 @@
 #include <xen/bitops.h>
 #include <xen/lib.h>
 #include <xen/init.h>
+#include <xen/domain_page.h>
 #include <xen/softirq.h>
 #include <xen/irq.h>
 #include <xen/sched.h>
@@ -589,6 +590,44 @@ void vgic_free_virq(struct domain *d, unsigned int virq)
     clear_bit(virq, d->arch.vgic.allocated_irqs);
 }
 
+int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
+                             uint32_t size, bool_t is_write)
+{
+    struct page_info *page;
+    uint64_t offset;
+    p2m_type_t p2mt;
+    void *p;
+
+    page = get_page_from_gfn(d, paddr_to_pfn(gpa), &p2mt, P2M_ALLOC);
+    if ( !page )
+    {
+        printk(XENLOG_G_ERR "d%d: vITS: Failed to get table entry\n",
+               d->domain_id);
+        return -EINVAL;
+    }
+
+    if ( !p2m_is_ram(p2mt) )
+    {
+        put_page(page);
+        printk(XENLOG_G_ERR "d%d: vITS: with wrong attributes\n", d->domain_id);
+        return -EINVAL;
+    }
+
+    p = __map_domain_page(page);
+    /* Offset within the mapped page */
+    offset = gpa & ~PAGE_MASK;
+
+    if ( is_write )
+        memcpy(p + offset, addr, size);
+    else
+        memcpy(addr, p + offset, size);
+
+    unmap_domain_page(p);
+    put_page(page);
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index 6ee7538..f993292 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 */
+        unsigned long long int nr_lpis;
+        uint64_t rdist_propbase;
         struct rb_root its_devices;         /* Devices mapped to an ITS */
         spinlock_t its_devices_lock;        /* Protects the its_devices tree */
         struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
@@ -256,7 +258,9 @@ 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 */
+        uint64_t rdist_pendbase;
+#define VGIC_V3_RDIST_LAST      (1 << 0)        /* last vCPU of the rdist */
+#define VGIC_V3_LPIS_ENABLED    (1 << 1)
         uint8_t flags;
     } vgic;
 
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 08d6294..2371960 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -314,6 +314,9 @@ 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);
 
+int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
+                             uint32_t size, bool_t is_write);
+
 extern int domain_vgic_register(struct domain *d, int *mmio_count);
 extern int vcpu_vgic_free(struct vcpu *v);
 extern bool vgic_to_sgi(struct vcpu *v, register_t sgir,
-- 
2.8.2


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

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

* [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (14 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:58   ` Stefano Stabellini
  2017-04-06 21:20   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation Andre Przywara
                   ` (14 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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. We do this by storing the pending bit in struct
pending_irq, which is assiociated with mapped LPIs.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-v3-lpi.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
index d8baebc..7d20986 100644
--- a/xen/arch/arm/gic-v3-lpi.c
+++ b/xen/arch/arm/gic-v3-lpi.c
@@ -136,6 +136,22 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
         return per_cpu(lpi_redist, cpu).redist_id << 16;
 }
 
+static bool vgic_can_inject_lpi(struct vcpu *vcpu, uint32_t vlpi)
+{
+    struct pending_irq *p;
+
+    p = vcpu->domain->arch.vgic.handler->lpi_to_pending(vcpu->domain, vlpi);
+    if ( !p )
+        return false;
+
+    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
+        return true;
+
+    set_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);
+
+    return false;
+}
+
 /*
  * Handle incoming LPIs, which are a bit special, because they are potentially
  * numerous and also only get injected into guests. Treat them specially here,
@@ -187,7 +203,15 @@ void gicv3_do_LPI(unsigned int lpi)
 
     /* Check if the VCPU is ready to receive LPIs. */
     if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
-        vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
+    {
+        /*
+         * 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) )
+            vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
+    }
 
     rcu_unlock_domain(d);
 }
-- 
2.8.2


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

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

* [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (15 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  0:21   ` Stefano Stabellini
                     ` (3 more replies)
  2017-04-05 23:19 ` [PATCH v5 18/30] ARM: vITS: introduce translation table walks Andre Przywara
                   ` (13 subsequent siblings)
  30 siblings, 4 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

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/vgic-v3-its.c        | 416 ++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-v3.c            |   9 -
 xen/include/asm-arm/gic_v3_defs.h |   9 +
 xen/include/asm-arm/gic_v3_its.h  |   3 +
 4 files changed, 428 insertions(+), 9 deletions(-)

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 9dfda59..f6bf1ee 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -78,6 +78,422 @@ void vgic_v3_its_free_domain(struct domain *d)
     ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
 }
 
+/**************************************
+ * Functions that handle ITS commands *
+ **************************************/
+
+static uint64_t its_cmd_mask_field(uint64_t *its_cmd, unsigned int word,
+                                   unsigned int shift, unsigned 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_get_ittaddr(cmd)        (its_cmd_mask_field(cmd, 2, 8, 44) << 8)
+
+#define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
+
+/*
+ * Requires the vcmd_lock to be held.
+ * TODO: Investigate whether we can be smarter here and don't need to hold
+ * the lock all of the time.
+ */
+static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
+{
+    paddr_t addr = its->cbaser & GENMASK_ULL(51, 12);
+    uint64_t command[4];
+
+    ASSERT(spin_is_locked(&its->vcmd_lock));
+
+    if ( its->cwriter >= ITS_CMD_BUFFER_SIZE(its->cbaser) )
+        return -1;
+
+    while ( its->creadr != its->cwriter )
+    {
+        int ret;
+
+        ret = vgic_access_guest_memory(d, addr + its->creadr,
+                                       command, sizeof(command), false);
+        if ( ret )
+            return ret;
+
+        switch ( its_cmd_get_command(command) )
+        {
+        case GITS_CMD_SYNC:
+            /* We handle ITS commands synchronously, so we ignore SYNC. */
+            break;
+        default:
+            gdprintk(XENLOG_WARNING, "ITS: unhandled ITS command %lu\n",
+                     its_cmd_get_command(command));
+            break;
+        }
+
+        its->creadr += ITS_CMD_SIZE;
+        if ( its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser) )
+            its->creadr = 0;
+
+        if ( ret )
+            gdprintk(XENLOG_WARNING,
+                     "ITS: ITS command error %d while handling command %lu\n",
+                     ret, its_cmd_get_command(command));
+    }
+
+    return 0;
+}
+
+/*****************************
+ * ITS registers read access *
+ *****************************/
+
+static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
+                                 register_t *r, void *priv)
+{
+    struct virt_its *its = priv;
+    uint64_t reg;
+
+    switch ( info->gpa & 0xffff )
+    {
+    case VREG32(GITS_CTLR):
+        if ( info->dabt.size != DABT_WORD ) goto bad_width;
+
+        spin_lock(&its->vcmd_lock);
+        spin_lock(&its->its_lock);
+        if ( its->enabled )
+            reg = GITS_CTLR_ENABLE;
+        else
+            reg = 0;
+
+        if ( its->cwriter == its->creadr )
+            reg |= GITS_CTLR_QUIESCENT;
+        spin_unlock(&its->its_lock);
+        spin_unlock(&its->vcmd_lock);
+
+        *r = vgic_reg32_extract(reg, 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 ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+
+        reg = GITS_TYPER_PHYSICAL;
+        reg |= (sizeof(struct vits_itte) - 1) << GITS_TYPER_ITT_SIZE_SHIFT;
+        reg |= (its->intid_bits - 1) << GITS_TYPER_IDBITS_SHIFT;
+        reg |= (its->devid_bits - 1) << GITS_TYPER_DEVIDS_SHIFT;
+        *r = vgic_reg64_extract(reg, info);
+        break;
+    case VREG64(GITS_CBASER):
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+        spin_lock(&its->its_lock);
+        *r = vgic_reg64_extract(its->cbaser, info);
+        spin_unlock(&its->its_lock);
+        break;
+    case VREG64(GITS_CWRITER):
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+        spin_lock(&its->vcmd_lock);
+        *r = vgic_reg64_extract(its->cwriter, info);
+        spin_unlock(&its->vcmd_lock);
+        break;
+    case VREG64(GITS_CREADR):
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+        spin_lock(&its->vcmd_lock);
+        *r = vgic_reg64_extract(its->creadr, info);
+        spin_unlock(&its->vcmd_lock);
+        break;
+    case VREG64(GITS_BASER0):           /* device table */
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+        spin_lock(&its->its_lock);
+        *r = vgic_reg64_extract(its->baser_dev, info);
+        spin_unlock(&its->its_lock);
+        break;
+    case VREG64(GITS_BASER1):           /* collection table */
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+        spin_lock(&its->its_lock);
+        *r = vgic_reg64_extract(its->baser_coll, info);
+        spin_unlock(&its->its_lock);
+        break;
+    case VRANGE64(GITS_BASER2, GITS_BASER7):
+        goto read_as_zero_64;
+
+    case VREG32(GITS_PIDR2):
+        if ( info->dabt.size != DABT_WORD ) goto bad_width;
+        *r = vgic_reg32_extract(GIC_PIDR2_ARCH_GICv3, info);
+        break;
+    }
+
+    return 1;
+
+read_as_zero_64:
+    if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+    *r = 0;
+
+    return 1;
+
+bad_width:
+    printk(XENLOG_G_ERR "vGIIS: bad read width %d r%d offset %#08lx\n",
+           info->dabt.size, info->dabt.reg, (unsigned long)info->gpa & 0xffff);
+    domain_crash_synchronous();
+
+    return 0;
+}
+
+/******************************
+ * ITS registers write access *
+ ******************************/
+
+static unsigned int its_baser_table_size(uint64_t baser)
+{
+    unsigned int ret, page_size[4] = {SZ_4K, SZ_16K, SZ_64K, SZ_64K};
+
+    ret = page_size[(baser >> GITS_BASER_PAGE_SIZE_SHIFT) & 3];
+
+    return ret * ((baser & GITS_BASER_SIZE_MASK) + 1);
+}
+
+static int its_baser_nr_entries(uint64_t baser)
+{
+    int entry_size = GITS_BASER_ENTRY_SIZE(baser);
+
+    return its_baser_table_size(baser) / entry_size;
+}
+
+/* Must be called with the ITS lock held. */
+static bool vgic_v3_verify_its_status(struct virt_its *its, bool status)
+{
+    ASSERT(spin_is_locked(&its->its_lock));
+
+    if ( !status )
+        return false;
+
+    if ( !(its->cbaser & GITS_VALID_BIT) ||
+         !(its->baser_dev & GITS_VALID_BIT) ||
+         !(its->baser_coll & GITS_VALID_BIT) )
+        return false;
+
+    return true;
+}
+
+static void sanitize_its_base_reg(uint64_t *reg)
+{
+    uint64_t r = *reg;
+
+    /* Avoid outer shareable. */
+    switch ( (r >> GITS_BASER_SHAREABILITY_SHIFT) & 0x03 )
+    {
+    case GIC_BASER_OuterShareable:
+        r = r & ~GITS_BASER_SHAREABILITY_MASK;
+        r |= GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
+        break;
+    default:
+        break;
+    }
+
+    /* Avoid any inner non-cacheable mapping. */
+    switch ( (r >> GITS_BASER_INNER_CACHEABILITY_SHIFT) & 0x07 )
+    {
+    case GIC_BASER_CACHE_nCnB:
+    case GIC_BASER_CACHE_nC:
+        r = r & ~GITS_BASER_INNER_CACHEABILITY_MASK;
+        r |= GIC_BASER_CACHE_RaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
+        break;
+    default:
+        break;
+    }
+
+    /* Only allow non-cacheable or same-as-inner. */
+    switch ( (r >> GITS_BASER_OUTER_CACHEABILITY_SHIFT) & 0x07 )
+    {
+    case GIC_BASER_CACHE_SameAsInner:
+    case GIC_BASER_CACHE_nC:
+        break;
+    default:
+        r = r & ~GITS_BASER_OUTER_CACHEABILITY_MASK;
+        r |= GIC_BASER_CACHE_nC << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
+        break;
+    }
+
+    *reg = r;
+}
+
+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 reg32, ctlr;
+
+    switch ( info->gpa & 0xffff )
+    {
+    case VREG32(GITS_CTLR):
+        if ( info->dabt.size != DABT_WORD ) goto bad_width;
+
+        spin_lock(&its->vcmd_lock);
+        spin_lock(&its->its_lock);
+        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
+        reg32 = ctlr;
+        vgic_reg32_update(&reg32, r, info);
+
+        if ( ctlr ^ reg32 )
+            its->enabled = vgic_v3_verify_its_status(its,
+                                                     reg32 & GITS_CTLR_ENABLE);
+        spin_unlock(&its->its_lock);
+        spin_unlock(&its->vcmd_lock);
+        return 1;
+
+    case VREG32(GITS_IIDR):
+        goto write_ignore_32;
+    case VREG32(GITS_TYPER):
+        goto write_ignore_32;
+    case VREG64(GITS_CBASER):
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+
+        spin_lock(&its->vcmd_lock);
+        spin_lock(&its->its_lock);
+        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
+        if ( its->enabled )
+        {
+            spin_unlock(&its->its_lock);
+            spin_unlock(&its->vcmd_lock);
+            gdprintk(XENLOG_WARNING,
+                     "ITS: tried to change CBASER with the ITS enabled.\n");
+            return 1;
+        }
+
+        reg = its->cbaser;
+        vgic_reg64_update(&reg, r, info);
+        sanitize_its_base_reg(&reg);
+
+        its->cbaser = reg;
+        its->creadr = 0;
+        spin_unlock(&its->its_lock);
+        spin_unlock(&its->vcmd_lock);
+
+        return 1;
+
+    case VREG64(GITS_CWRITER):
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+
+        spin_lock(&its->vcmd_lock);
+        reg = its->cwriter & 0xfffe0;
+        vgic_reg64_update(&reg, r, info);
+        its->cwriter = reg & 0xfffe0;
+
+        if ( its->enabled )
+        {
+            int ret = vgic_its_handle_cmds(d, its);
+
+            if ( ret )
+                printk(XENLOG_G_WARNING "error handling ITS commands\n");
+        }
+        spin_unlock(&its->vcmd_lock);
+
+        return 1;
+
+    case VREG64(GITS_CREADR):
+        goto write_ignore_64;
+    case VREG64(GITS_BASER0):           /* device table */
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+
+        spin_lock(&its->its_lock);
+
+        /*
+         * Changing base registers with the ITS enabled is UNPREDICTABLE,
+         * we choose to ignore it, but warn.
+         */
+        if ( its->enabled )
+        {
+            spin_unlock(&its->its_lock);
+            gdprintk(XENLOG_WARNING, "ITS: tried to change BASER with the ITS enabled.\n");
+
+            return 1;
+        }
+
+        reg = its->baser_dev;
+        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;
+        sanitize_its_base_reg(&reg);
+
+        if ( reg & GITS_VALID_BIT )
+            its->max_devices = its_baser_nr_entries(reg);
+        else
+            its->max_devices = 0;
+
+        its->baser_dev = reg;
+        spin_unlock(&its->its_lock);
+        return 1;
+    case VREG64(GITS_BASER1):           /* collection table */
+        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
+
+        spin_lock(&its->its_lock);
+        /*
+         * Changing base registers with the ITS enabled is UNPREDICTABLE,
+         * we choose to ignore it, but warn.
+         */
+        if ( its->enabled )
+        {
+            spin_unlock(&its->its_lock);
+            gdprintk(XENLOG_INFO, "ITS: tried to change BASER with the ITS enabled.\n");
+            return 1;
+        }
+
+        reg = its->baser_coll;
+        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;
+        sanitize_its_base_reg(&reg);
+
+        if ( reg & GITS_VALID_BIT )
+            its->max_collections = its_baser_nr_entries(reg);
+        else
+            its->max_collections = 0;
+        its->baser_coll = reg;
+        spin_unlock(&its->its_lock);
+        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 "vGITS: bad write width %d r%d offset %#08lx\n",
+           info->dabt.size, info->dabt.reg, (unsigned long)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
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 0623803..e6a33d0 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -158,15 +158,6 @@ static void vgic_store_irouter(struct domain *d, struct vgic_irq_rank *rank,
     write_atomic(&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 b7e5a47..e60dea5 100644
--- a/xen/include/asm-arm/gic_v3_defs.h
+++ b/xen/include/asm-arm/gic_v3_defs.h
@@ -198,6 +198,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__ */
 
 /*
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index eb71fbd..d3f393f 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -35,6 +35,7 @@
 #define GITS_BASER5                     0x128
 #define GITS_BASER6                     0x130
 #define GITS_BASER7                     0x138
+#define GITS_PIDR2                      GICR_PIDR2
 
 /* Register bits */
 #define GITS_VALID_BIT                  BIT_ULL(63)
@@ -57,6 +58,7 @@
 #define GITS_TYPER_ITT_SIZE_MASK        (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
 #define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
                                                  GITS_TYPER_ITT_SIZE_SHIFT) + 1)
+#define GITS_TYPER_PHYSICAL             (1U << 0)
 
 #define GITS_IIDR_VALUE                 0x34c
 
@@ -78,6 +80,7 @@
                         (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
 #define GITS_BASER_SHAREABILITY_SHIFT   10
 #define GITS_BASER_PAGE_SIZE_SHIFT      8
+#define GITS_BASER_SIZE_MASK            0xff
 #define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
                                         (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
                                         GITS_BASER_INDIRECT)
-- 
2.8.2


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

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

* [PATCH v5 18/30] ARM: vITS: introduce translation table walks
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (16 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06 22:41   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 19/30] ARM: vITS: handle CLEAR command Andre Przywara
                   ` (12 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

The ITS stores the target (v)CPU and the (virtual) LPI number in tables.
Introduce functions to walk those tables and translate an device ID -
event ID pair into a pair of virtual LPI and vCPU.
We map those tables on demand - which is cheap on arm64. Also we take
care of the locking on the way, since we can't easily protect those ITTs
from being altered by the guest.

To allow compiling without warnings, we declare two functions as
non-static for the moment, which two later patches will fix.

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

diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index a56be34..5000b0d 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -491,6 +491,8 @@ static void gic_update_one_lr(struct vcpu *v, int i)
     {
         gic_hw_ops->clear_lr(i);
         clear_bit(i, &this_cpu(lr_mask));
+        if ( is_lpi(irq) )
+            clear_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);
 
         if ( p->desc != NULL )
             clear_bit(_IRQ_INPROGRESS, &p->desc->status);
diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index f6bf1ee..a145666 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -67,6 +67,8 @@ struct vits_itte
     uint16_t pad;
 };
 
+#define UNMAPPED_COLLECTION      ((uint16_t)~0)
+
 void vgic_v3_its_init_domain(struct domain *d)
 {
     spin_lock_init(&d->arch.vgic.its_devices_lock);
@@ -78,6 +80,183 @@ void vgic_v3_its_free_domain(struct domain *d)
     ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
 }
 
+/*
+ * 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_ULL(47, 16)) |
+                ((reg & GENMASK_ULL(15, 12)) << 36);
+    else
+        return reg & GENMASK_ULL(47, 12);
+}
+
+/* Must be called with the ITS lock held. */
+static struct vcpu *get_vcpu_from_collection(struct virt_its *its,
+                                             uint16_t collid)
+{
+    paddr_t addr = get_baser_phys_addr(its->baser_coll);
+    uint16_t vcpu_id;
+    int ret;
+
+    ASSERT(spin_is_locked(&its->its_lock));
+
+    if ( collid >= its->max_collections )
+        return NULL;
+
+    ret = vgic_access_guest_memory(its->d, addr + collid * sizeof(uint16_t),
+                                   &vcpu_id, sizeof(vcpu_id), false);
+    if ( ret )
+        return NULL;
+
+    if ( vcpu_id == UNMAPPED_COLLECTION || vcpu_id >= its->d->max_vcpus )
+        return NULL;
+
+    return its->d->vcpu[vcpu_id];
+}
+
+/*
+ * Our device table encodings:
+ * Contains the guest physical address of the Interrupt Translation Table in
+ * bits [51:8], and the size of it is encoded as the number of bits minus one
+ * in the lowest 8 bits of the word.
+ */
+#define DEV_TABLE_ITT_ADDR(x) ((x) & GENMASK_ULL(51, 8))
+#define DEV_TABLE_ITT_SIZE(x) (BIT(((x) & GENMASK_ULL(7, 0)) + 1))
+#define DEV_TABLE_ENTRY(addr, bits)                     \
+        (((addr) & GENMASK_ULL(51, 8)) | (((bits) - 1) & GENMASK_ULL(7, 0)))
+
+/*
+ * Lookup the address of the Interrupt Translation Table associated with
+ * a device ID and return the address of the ITTE belonging to the event ID
+ * (which is an index into that table).
+ */
+static paddr_t its_get_itte_address(struct virt_its *its,
+                                    uint32_t devid, uint32_t evid)
+{
+    paddr_t addr = get_baser_phys_addr(its->baser_dev);
+    uint64_t itt;
+
+    if ( devid >= its->max_devices )
+        return INVALID_PADDR;
+
+    if ( vgic_access_guest_memory(its->d, addr + devid * sizeof(uint64_t),
+                                  &itt, sizeof(itt), false) )
+        return INVALID_PADDR;
+
+    if ( evid >= DEV_TABLE_ITT_SIZE(itt) ||
+         DEV_TABLE_ITT_ADDR(itt) == INVALID_PADDR )
+        return INVALID_PADDR;
+
+    return DEV_TABLE_ITT_ADDR(itt) + evid * sizeof(struct vits_itte);
+}
+
+/*
+ * Queries the collection and device tables to get the vCPU and virtual
+ * LPI number for a given guest event. This first accesses the guest memory
+ * to resolve the address of the ITTE, then reads the ITTE entry at this
+ * address and puts the result in vcpu_ptr and vlpi_ptr.
+ * Requires the ITS lock to be held.
+ */
+static bool read_itte_locked(struct virt_its *its, uint32_t devid,
+                             uint32_t evid, struct vcpu **vcpu_ptr,
+                             uint32_t *vlpi_ptr)
+{
+    paddr_t addr;
+    struct vits_itte itte;
+    struct vcpu *vcpu;
+
+    ASSERT(spin_is_locked(&its->its_lock));
+
+    addr = its_get_itte_address(its, devid, evid);
+    if ( addr == INVALID_PADDR )
+        return false;
+
+    if ( vgic_access_guest_memory(its->d, addr, &itte, sizeof(itte), false) )
+        return false;
+
+    vcpu = get_vcpu_from_collection(its, itte.collection);
+    if ( !vcpu )
+        return false;
+
+    *vcpu_ptr = vcpu;
+    *vlpi_ptr = itte.vlpi;
+    return true;
+}
+
+/*
+ * This function takes care of the locking by taking the its_lock itself, so
+ * a caller shall not hold this. Before returning, the lock is dropped again.
+ */
+bool read_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
+               struct vcpu **vcpu_ptr, uint32_t *vlpi_ptr)
+{
+    bool ret;
+
+    spin_lock(&its->its_lock);
+    ret = read_itte_locked(its, devid, evid, vcpu_ptr, vlpi_ptr);
+    spin_unlock(&its->its_lock);
+
+    return ret;
+}
+
+/*
+ * Queries the collection and device tables to translate the device ID and
+ * event ID and find the appropriate ITTE. The given collection ID and the
+ * virtual LPI number are then stored into that entry.
+ * If vcpu_ptr is provided, returns the VCPU belonging to that collection.
+ * Requires the ITS lock to be held.
+ */
+static bool write_itte_locked(struct virt_its *its, uint32_t devid,
+                              uint32_t evid, uint32_t collid, uint32_t vlpi,
+                              struct vcpu **vcpu_ptr)
+{
+    paddr_t addr;
+    struct vits_itte itte;
+
+    ASSERT(spin_is_locked(&its->its_lock));
+
+    if ( collid >= its->max_collections )
+        return false;
+
+    if ( vlpi >= its->d->arch.vgic.nr_lpis )
+        return false;
+
+    addr = its_get_itte_address(its, devid, evid);
+    if ( addr == INVALID_PADDR )
+        return false;
+
+    itte.collection = collid;
+    itte.vlpi = vlpi;
+
+    if ( vgic_access_guest_memory(its->d, addr, &itte, sizeof(itte), true) )
+        return false;
+
+    if ( vcpu_ptr )
+        *vcpu_ptr = get_vcpu_from_collection(its, collid);
+
+    return true;
+}
+
+/*
+ * This function takes care of the locking by taking the its_lock itself, so
+ * a caller shall not hold this. Before returning, the lock is dropped again.
+ */
+bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
+                uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
+{
+    bool ret;
+
+    spin_lock(&its->its_lock);
+    ret = write_itte_locked(its, devid, evid, collid, vlpi, vcpu_ptr);
+    spin_unlock(&its->its_lock);
+
+    return ret;
+}
+
 /**************************************
  * Functions that handle ITS commands *
  **************************************/
-- 
2.8.2


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

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

* [PATCH v5 19/30] ARM: vITS: handle CLEAR command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (17 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 18/30] ARM: vITS: introduce translation table walks Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  0:31   ` Stefano Stabellini
  2017-04-05 23:19 ` [PATCH v5 20/30] ARM: vITS: handle INT command Andre Przywara
                   ` (11 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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.
As read_itte() is now eventually used, we add the static keyword.

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

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index a145666..71bc08a 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -191,8 +191,8 @@ static bool read_itte_locked(struct virt_its *its, uint32_t devid,
  * This function takes care of the locking by taking the its_lock itself, so
  * a caller shall not hold this. Before returning, the lock is dropped again.
  */
-bool read_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
-               struct vcpu **vcpu_ptr, uint32_t *vlpi_ptr)
+static bool read_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
+                      struct vcpu **vcpu_ptr, uint32_t *vlpi_ptr)
 {
     bool ret;
 
@@ -277,6 +277,48 @@ static uint64_t its_cmd_mask_field(uint64_t *its_cmd, unsigned int word,
 #define its_cmd_get_validbit(cmd)       its_cmd_mask_field(cmd, 2, 63,  1)
 #define its_cmd_get_ittaddr(cmd)        (its_cmd_mask_field(cmd, 2, 8, 44) << 8)
 
+/*
+ * CLEAR removes the pending state from an LPI. */
+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 *p;
+    struct vcpu *vcpu;
+    uint32_t vlpi;
+    unsigned long flags;
+
+    /* Translate the DevID/EvID pair into a vCPU/vLPI pair. */
+    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
+        return -1;
+
+    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
+    if ( !p )
+        return -1;
+
+    spin_lock_irqsave(&vcpu->arch.vgic.lock, flags);
+
+    /* We store the pending bit for LPIs in our struct pending_irq. */
+    clear_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);
+
+    /*
+     * If the LPI is already visible on the guest, it is too late to
+     * clear the pending state. However this is a benign race that can
+     * happen on real hardware, too: If the LPI has already been forwarded
+     * to a CPU interface, a CLEAR request reaching the redistributor has
+     * no effect on that LPI anymore. Since LPIs are edge triggered and
+     * have no active state, we don't need to care about this here.
+     */
+    if ( !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
+    {
+        /* Remove a pending, but not yet injected guest IRQ. */
+        clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
+        gic_remove_from_queues(vcpu, vlpi);
+    }
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 /*
@@ -305,6 +347,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
 
         switch ( its_cmd_get_command(command) )
         {
+        case GITS_CMD_CLEAR:
+            ret = its_handle_clear(its, command);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
             break;
-- 
2.8.2


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

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

* [PATCH v5 20/30] ARM: vITS: handle INT command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (18 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 19/30] ARM: vITS: handle CLEAR command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:19 ` [PATCH v5 21/30] ARM: vITS: handle MAPC command Andre Przywara
                   ` (10 subsequent siblings)
  30 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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-v3-its.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 71bc08a..36f942a 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -319,6 +319,33 @@ 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 pending_irq *p;
+    struct vcpu *vcpu;
+    uint32_t vlpi;
+
+    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
+        return -1;
+
+    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
+    if ( !p )
+        return -1;
+
+    /*
+     * If the LPI is enabled, inject it.
+     * If not, store the pending state to inject it once it gets enabled later.
+     */
+    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
+        vgic_vcpu_inject_irq(vcpu, vlpi);
+    else
+        set_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 /*
@@ -350,6 +377,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_CLEAR:
             ret = its_handle_clear(its, command);
             break;
+        case GITS_CMD_INT:
+            ret = its_handle_int(its, command);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
             break;
-- 
2.8.2


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

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

* [PATCH v5 21/30] ARM: vITS: handle MAPC command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (19 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 20/30] ARM: vITS: handle INT command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:19 ` [PATCH v5 22/30] ARM: vITS: handle MAPD command Andre Przywara
                   ` (9 subsequent siblings)
  30 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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-v3-its.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 36f942a..bbca5cf 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -95,6 +95,21 @@ static paddr_t get_baser_phys_addr(uint64_t reg)
 }
 
 /* Must be called with the ITS lock held. */
+static int its_set_collection(struct virt_its *its, uint16_t collid,
+                              uint16_t vcpu_id)
+{
+    paddr_t addr = get_baser_phys_addr(its->baser_coll);
+
+    ASSERT(spin_is_locked(&its->its_lock));
+
+    if ( collid >= its->max_collections )
+        return -ENOENT;
+
+    return vgic_access_guest_memory(its->d, addr + collid * sizeof(uint16_t),
+                                    &vcpu_id, sizeof(vcpu_id), true);
+}
+
+/* Must be called with the ITS lock held. */
 static struct vcpu *get_vcpu_from_collection(struct virt_its *its,
                                              uint16_t collid)
 {
@@ -346,6 +361,29 @@ static int its_handle_int(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+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);
+
+    if ( collid >= its->max_collections )
+        return -1;
+
+    if ( rdbase >= its->d->max_vcpus )
+        return -1;
+
+    spin_lock(&its->its_lock);
+
+    if ( its_cmd_get_validbit(cmdptr) )
+        its_set_collection(its, collid, rdbase);
+    else
+        its_set_collection(its, collid, UNMAPPED_COLLECTION);
+
+    spin_unlock(&its->its_lock);
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 /*
@@ -380,6 +418,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_INT:
             ret = its_handle_int(its, command);
             break;
+        case GITS_CMD_MAPC:
+            ret = its_handle_mapc(its, command);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
             break;
-- 
2.8.2


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

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

* [PATCH v5 22/30] ARM: vITS: handle MAPD command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (20 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 21/30] ARM: vITS: handle MAPC command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  0:39   ` Stefano Stabellini
  2017-04-07 12:41   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 23/30] ARM: vITS: handle MAPTI command Andre Przywara
                   ` (8 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

The MAPD command maps a device by associating a memory region for
storing ITEs with a certain device ID.
We store the given guest physical address in the device table, and, if
this command comes from Dom0, tell the host ITS driver about this new
mapping, so it can issue the corresponding host MAPD command and create
the required tables.
We simply use our existing guest memory access function to find the
right ITT entry and store the mapping there.

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

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index bbca5cf..0372ed0 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -42,6 +42,7 @@
  */
 struct virt_its {
     struct domain *d;
+    paddr_t doorbell_address;
     unsigned int devid_bits;
     unsigned int intid_bits;
     spinlock_t vcmd_lock;       /* Protects the virtual command buffer, which */
@@ -144,6 +145,20 @@ static struct vcpu *get_vcpu_from_collection(struct virt_its *its,
 #define DEV_TABLE_ENTRY(addr, bits)                     \
         (((addr) & GENMASK_ULL(51, 8)) | (((bits) - 1) & GENMASK_ULL(7, 0)))
 
+/* Set the address of an ITT for a given device ID. */
+static int its_set_itt_address(struct virt_its *its, uint32_t devid,
+                               paddr_t itt_address, uint32_t nr_bits)
+{
+    paddr_t addr = get_baser_phys_addr(its->baser_dev);
+    uint64_t itt_entry = DEV_TABLE_ENTRY(itt_address, nr_bits);
+
+    if ( devid >= its->max_devices )
+        return -ENOENT;
+
+    return vgic_access_guest_memory(its->d, addr + devid * sizeof(uint64_t),
+                                    &itt_entry, sizeof(itt_entry), true);
+}
+
 /*
  * Lookup the address of the Interrupt Translation Table associated with
  * a device ID and return the address of the ITTE belonging to the event ID
@@ -384,6 +399,47 @@ static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
+{
+    /* size and devid get validated by the functions called below. */
+    uint32_t devid = its_cmd_get_deviceid(cmdptr);
+    unsigned int size = its_cmd_get_size(cmdptr) + 1;
+    bool valid = its_cmd_get_validbit(cmdptr);
+    paddr_t itt_addr = its_cmd_get_ittaddr(cmdptr);
+    int ret;
+
+    /*
+     * There is no easy and clean way for Xen to know the ITS device ID of a
+     * particular (PCI) device, so we have to rely on the guest telling
+     * us about it. For *now* we are just using the device ID *Dom0* uses,
+     * because the driver there has the actual knowledge.
+     * Eventually this will be replaced with a dedicated hypercall to
+     * announce pass-through of devices.
+     */
+    if ( is_hardware_domain(its->d) )
+    {
+        /*
+         * Dom0's ITSes are mapped 1:1, so both addresses are the same.
+         * Also the device IDs are equal.
+         */
+        ret = gicv3_its_map_guest_device(its->d, its->doorbell_address, devid,
+                                         its->doorbell_address, devid,
+                                         BIT(size), valid);
+        if ( ret )
+            return ret;
+    }
+
+    spin_lock(&its->its_lock);
+    if ( valid )
+        ret = its_set_itt_address(its, devid, itt_addr, size);
+    else
+        ret = its_set_itt_address(its, devid, INVALID_PADDR, 1);
+
+    spin_unlock(&its->its_lock);
+
+    return ret;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 /*
@@ -421,6 +477,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_MAPC:
             ret = its_handle_mapc(its, command);
             break;
+        case GITS_CMD_MAPD:
+            ret = its_handle_mapd(its, command);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
             break;
-- 
2.8.2


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

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

* [PATCH v5 23/30] ARM: vITS: handle MAPTI command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (21 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 22/30] ARM: vITS: handle MAPD command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  0:45   ` Stefano Stabellini
                     ` (2 more replies)
  2017-04-05 23:19 ` [PATCH v5 24/30] ARM: vITS: handle MOVI command Andre Przywara
                   ` (7 subsequent siblings)
  30 siblings, 3 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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 LPI on the host can be quickly forwarded to a guest.
Beside entering the VCPU and the virtual LPI number in the respective
host LPI entry, we also initialize and add the already allocated
struct pending_irq to our radix tree, so that we can now easily find it
by its virtual LPI number.
To be able to later find the targetting VCPU for any given LPI without
having to walk *all* ITS tables, we store the VCPU ID in the pending_irq
struct as well.
This exports the vgic_init_pending_irq() function to be able to
initialize a new struct pending_irq.
As write_itte() is now eventually used, we can now add the static tag.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/gic-v3-its.c        | 74 ++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3-lpi.c        | 18 ++++++++++
 xen/arch/arm/vgic-v3-its.c       | 76 ++++++++++++++++++++++++++++++++++++++--
 xen/arch/arm/vgic.c              |  2 +-
 xen/include/asm-arm/gic_v3_its.h |  6 ++++
 xen/include/asm-arm/vgic.h       |  2 ++
 6 files changed, 175 insertions(+), 3 deletions(-)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index 76b0316..d970119 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -777,6 +777,80 @@ out:
     return ret;
 }
 
+/* Must be called with the its_device_lock held. */
+static struct its_devices *get_its_device(struct domain *d, paddr_t vdoorbell,
+                                          uint32_t vdevid)
+{
+    struct rb_node *node = d->arch.vgic.its_devices.rb_node;
+    struct its_devices *dev;
+
+    ASSERT(spin_is_locked(&d->arch.vgic.its_devices_lock));
+
+    while (node)
+    {
+        int cmp;
+
+        dev = rb_entry(node, struct its_devices, rbnode);
+        cmp = compare_its_guest_devices(dev, vdoorbell, vdevid);
+
+        if ( !cmp )
+            return dev;
+
+        if ( cmp > 0 )
+            node = node->rb_left;
+        else
+            node = node->rb_right;
+    }
+
+    return NULL;
+}
+
+static uint32_t get_host_lpi(struct its_devices *dev, uint32_t eventid)
+{
+    uint32_t host_lpi = 0;
+
+    if ( dev && (eventid < dev->eventids) )
+        host_lpi = dev->host_lpi_blocks[eventid / LPI_BLOCK] +
+                                       (eventid % LPI_BLOCK);
+
+    return host_lpi;
+}
+
+/*
+ * 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.
+ * Returns a pointer to the already allocated struct pending_irq that is
+ * meant to be used by that event.
+ */
+struct pending_irq *gicv3_assign_guest_event(struct domain *d,
+                                             paddr_t vdoorbell_address,
+                                             uint32_t vdevid, uint32_t veventid,
+                                             struct vcpu *v, uint32_t virt_lpi)
+{
+    struct its_devices *dev;
+    struct pending_irq *pirq = NULL;
+    uint32_t host_lpi = 0;
+
+    spin_lock(&d->arch.vgic.its_devices_lock);
+    dev = get_its_device(d, vdoorbell_address, vdevid);
+    if ( dev )
+    {
+        host_lpi = get_host_lpi(dev, veventid);
+        pirq = &dev->pend_irqs[veventid];
+    }
+    spin_unlock(&d->arch.vgic.its_devices_lock);
+
+    if ( !host_lpi || !pirq )
+        return NULL;
+
+    gicv3_lpi_update_host_entry(host_lpi, d->domain_id,
+                                v ? v->vcpu_id : INVALID_VCPU_ID, virt_lpi);
+
+    return pirq;
+}
+
 /* 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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
index 7d20986..c997ed5 100644
--- a/xen/arch/arm/gic-v3-lpi.c
+++ b/xen/arch/arm/gic-v3-lpi.c
@@ -216,6 +216,24 @@ void gicv3_do_LPI(unsigned int lpi)
     rcu_unlock_domain(d);
 }
 
+void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
+                                 unsigned int vcpu_id, uint32_t virt_lpi)
+{
+    union host_lpi *hlpip, hlpi;
+
+    ASSERT(host_lpi >= LPI_OFFSET);
+
+    host_lpi -= LPI_OFFSET;
+
+    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
+
+    hlpi.virt_lpi = virt_lpi;
+    hlpi.dom_id = domain_id;
+    hlpi.vcpu_id = vcpu_id;
+
+    write_u64_atomic(&hlpip->data, hlpi.data);
+}
+
 static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
 {
     uint64_t val;
diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 0372ed0..079dd44 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -275,8 +275,8 @@ static bool write_itte_locked(struct virt_its *its, uint32_t devid,
  * This function takes care of the locking by taking the its_lock itself, so
  * a caller shall not hold this. Before returning, the lock is dropped again.
  */
-bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
-                uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
+static bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
+                       uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
 {
     bool ret;
 
@@ -440,6 +440,74 @@ static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
     return ret;
 }
 
+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), _intid;
+    uint16_t collid = its_cmd_get_collection(cmdptr);
+    struct pending_irq *pirq;
+    struct vcpu *vcpu = NULL;
+    int ret = 0;
+
+    if ( its_cmd_get_command(cmdptr) == GITS_CMD_MAPI )
+        intid = eventid;
+
+    spin_lock(&its->its_lock);
+    /*
+     * Check whether there is a valid existing mapping. If yes, behavior is
+     * unpredictable, we choose to ignore this command here.
+     * This makes sure we start with a pristine pending_irq below.
+     */
+    if ( read_itte_locked(its, devid, eventid, &vcpu, &_intid) &&
+         _intid != INVALID_LPI )
+    {
+        spin_unlock(&its->its_lock);
+        return -1;
+    }
+
+    /* Enter the mapping in our virtual ITS tables. */
+    if ( !write_itte_locked(its, devid, eventid, collid, intid, &vcpu) )
+    {
+        spin_unlock(&its->its_lock);
+        return -1;
+    }
+
+    spin_unlock(&its->its_lock);
+
+    /*
+     * Connect this virtual LPI to the corresponding host LPI, which is
+     * determined by the same device ID and event ID on the host side.
+     * This returns us the corresponding, still unused pending_irq.
+     */
+    pirq = gicv3_assign_guest_event(its->d, its->doorbell_address,
+                                    devid, eventid, vcpu, intid);
+    if ( !pirq )
+        return -1;
+
+    vgic_init_pending_irq(pirq, intid);
+
+    /*
+     * Now read the guest's property table to initialize our cached state.
+     * It can't fire at this time, because it is not known to the host yet.
+     */
+    ret = update_lpi_property(its->d, intid, pirq);
+    if ( ret )
+        return ret;
+
+    pirq->vcpu_id = vcpu->vcpu_id;
+
+    /*
+     * Now insert the pending_irq into the domain's LPI tree, so that
+     * it becomes live.
+     */
+    write_lock(&its->d->arch.vgic.pend_lpi_tree_lock);
+    radix_tree_insert(&its->d->arch.vgic.pend_lpi_tree, intid, pirq);
+    write_unlock(&its->d->arch.vgic.pend_lpi_tree_lock);
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 /*
@@ -480,6 +548,10 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_MAPD:
             ret = its_handle_mapd(its, command);
             break;
+        case GITS_CMD_MAPI:
+        case GITS_CMD_MAPTI:
+            ret = its_handle_mapti(its, command);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
             break;
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index 9b0dc3d..cb1666b 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -61,7 +61,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);
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index d3f393f..30aa1ef 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -174,6 +174,12 @@ int gicv3_its_map_guest_device(struct domain *d,
 int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
 void gicv3_free_host_lpi_block(uint32_t first_lpi);
 
+struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
+                                             uint32_t devid, uint32_t eventid,
+                                             struct vcpu *v, uint32_t virt_lpi);
+void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
+                                 unsigned int vcpu_id, uint32_t virt_lpi);
+
 #else
 
 static LIST_HEAD(host_its_list);
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index 2371960..074afe4 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -83,6 +83,7 @@ struct pending_irq
      * TODO: when implementing irq migration, taking only the current
      * vgic lock is not going to be enough. */
     struct list_head lr_queue;
+    uint16_t vcpu_id;          /* The VCPU for an LPI. */
 };
 
 #define NR_INTERRUPT_PER_RANK   32
@@ -303,6 +304,7 @@ 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 vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
-- 
2.8.2


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

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

* [PATCH v5 24/30] ARM: vITS: handle MOVI command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (22 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 23/30] ARM: vITS: handle MAPTI command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  0:47   ` Stefano Stabellini
  2017-04-07 12:55   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 25/30] ARM: vITS: handle DISCARD command Andre Przywara
                   ` (6 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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-v3-its.c        | 24 ++++++++++++++++++++
 xen/arch/arm/gic-v3-lpi.c        | 15 +++++++++++++
 xen/arch/arm/vgic-v3-its.c       | 47 ++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic_v3_its.h |  4 ++++
 4 files changed, 90 insertions(+)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index d970119..a57e63a 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -851,6 +851,30 @@ struct pending_irq *gicv3_assign_guest_event(struct domain *d,
     return pirq;
 }
 
+/* Changes the target VCPU for a given host LPI assigned to a domain. */
+int gicv3_lpi_change_vcpu(struct domain *d, paddr_t vdoorbell,
+                          uint32_t vdevid, uint32_t veventid,
+                          unsigned int vcpu_id)
+{
+    uint32_t host_lpi;
+    struct its_devices *dev;
+
+    spin_lock(&d->arch.vgic.its_devices_lock);
+    dev = get_its_device(d, vdoorbell, vdevid);
+    if ( dev )
+        host_lpi = get_host_lpi(dev, veventid);
+    else
+        host_lpi = 0;
+    spin_unlock(&d->arch.vgic.its_devices_lock);
+
+    if ( !host_lpi )
+        return -ENOENT;
+
+    gicv3_lpi_update_host_vcpuid(host_lpi, vcpu_id);
+
+    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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
index c997ed5..b9960aa 100644
--- a/xen/arch/arm/gic-v3-lpi.c
+++ b/xen/arch/arm/gic-v3-lpi.c
@@ -234,6 +234,21 @@ void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
     write_u64_atomic(&hlpip->data, hlpi.data);
 }
 
+int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id)
+{
+    union host_lpi *hlpip;
+
+    ASSERT(host_lpi >= LPI_OFFSET);
+
+    host_lpi -= LPI_OFFSET;
+
+    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
+
+    write_u16_atomic(&hlpip->vcpu_id, vcpu_id);
+
+    return 0;
+}
+
 static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
 {
     uint64_t val;
diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 079dd44..6afb915 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -508,6 +508,47 @@ static int its_handle_mapti(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+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 pending_irq *p;
+    struct vcpu *vcpu;
+    uint32_t vlpi;
+    int ret = -1;
+
+    spin_lock(&its->its_lock);
+    /* Check for a mapped LPI and get the LPI number. */
+    if ( !read_itte_locked(its, devid, eventid, &vcpu, &vlpi) )
+        goto out_unlock;
+
+    /* Check the new collection ID and get the new VCPU pointer */
+    vcpu = get_vcpu_from_collection(its, collid);
+    if ( !vcpu )
+        goto out_unlock;
+
+    /* Update our cached vcpu_id in the pending_irq. */
+    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
+    p->vcpu_id = vcpu->vcpu_id;
+
+    /* Now store the new collection in the translation table. */
+    if ( !write_itte_locked(its, devid, eventid, collid, vlpi, &vcpu) )
+        goto out_unlock;
+
+    spin_unlock(&its->its_lock);
+
+    /* TODO: lookup currently-in-guest virtual IRQs and migrate them? */
+
+    return gicv3_lpi_change_vcpu(its->d, its->doorbell_address,
+                                 devid, eventid, vcpu->vcpu_id);
+
+out_unlock:
+    spin_unlock(&its->its_lock);
+
+    return ret;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 /*
@@ -552,6 +593,12 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_MAPTI:
             ret = its_handle_mapti(its, command);
             break;
+        case GITS_CMD_MOVALL:
+            gdprintk(XENLOG_G_INFO, "ITS: ignoring MOVALL command\n");
+            break;
+        case GITS_CMD_MOVI:
+            ret = its_handle_movi(its, command);
+            break;
         case GITS_CMD_SYNC:
             /* We handle ITS commands synchronously, so we ignore SYNC. */
             break;
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 30aa1ef..daae143 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -177,8 +177,12 @@ void gicv3_free_host_lpi_block(uint32_t first_lpi);
 struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
                                              uint32_t devid, uint32_t eventid,
                                              struct vcpu *v, uint32_t virt_lpi);
+int gicv3_lpi_change_vcpu(struct domain *d, paddr_t doorbell,
+                          uint32_t devid, uint32_t eventid,
+                          unsigned int vcpu_id);
 void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
                                  unsigned int vcpu_id, uint32_t virt_lpi);
+int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id);
 
 #else
 
-- 
2.8.2


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

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

* [PATCH v5 25/30] ARM: vITS: handle DISCARD command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (23 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 24/30] ARM: vITS: handle MOVI command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  0:48   ` Stefano Stabellini
  2017-04-07 13:03   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 26/30] ARM: vITS: handle INV command Andre Przywara
                   ` (5 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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-v3-its.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 6afb915..47f2884 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -549,6 +549,35 @@ out_unlock:
     return ret;
 }
 
+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 vcpu *vcpu;
+    uint32_t vlpi;
+
+    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
+        return -1;
+
+    pirq = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
+    if ( pirq )
+    {
+        clear_bit(GIC_IRQ_GUEST_QUEUED, &pirq->status);
+        gic_remove_from_queues(vcpu, vlpi);
+    }
+
+    if ( !write_itte(its, devid, eventid, UNMAPPED_COLLECTION, INVALID_LPI,
+                     NULL) )
+        return -1;
+
+    if ( !gicv3_assign_guest_event(its->d, its->doorbell_address,
+                                   devid, eventid, NULL, 0) )
+        return -1;
+
+    return 0;
+}
+
 #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
 
 /*
@@ -580,6 +609,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_CLEAR:
             ret = its_handle_clear(its, command);
             break;
+        case GITS_CMD_DISCARD:
+            ret = its_handle_discard(its, command);
+            break;
         case GITS_CMD_INT:
             ret = its_handle_int(its, command);
             break;
-- 
2.8.2


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

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

* [PATCH v5 26/30] ARM: vITS: handle INV command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (24 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 25/30] ARM: vITS: handle DISCARD command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  0:56   ` Stefano Stabellini
  2017-04-07 13:23   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 27/30] ARM: vITS: handle INVALL command Andre Przywara
                   ` (4 subsequent siblings)
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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/vgic-v3-its.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 47f2884..0d4b20d 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -376,6 +376,99 @@ static int its_handle_int(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+/*
+ * For a given virtual LPI read the enabled bit and priority from the virtual
+ * property table and update the virtual IRQ's state in the given pending_irq.
+ */
+static int update_lpi_property(struct domain *d, uint32_t vlpi,
+                               struct pending_irq *p)
+{
+    paddr_t addr;
+    uint8_t property;
+    int ret;
+
+    addr = d->arch.vgic.rdist_propbase & GENMASK_ULL(51, 12);
+
+    ret = vgic_access_guest_memory(d, addr + vlpi - LPI_OFFSET,
+                                   &property, sizeof(property), false);
+    if ( ret )
+        return ret;
+
+    p->lpi_priority = property & LPI_PROP_PRIO_MASK;
+    if ( property & LPI_PROP_ENABLED )
+        set_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+    else
+        clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+
+    return 0;
+}
+
+/*
+ * For a given virtual LPI read the enabled bit and priority 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.
+ * Also check if this LPI is due to be injected and do it, if needed.
+ */
+static int update_lpi_enabled_status(struct domain *d,
+                                     struct vcpu *vcpu, uint32_t vlpi)
+{
+    struct pending_irq *p = d->arch.vgic.handler->lpi_to_pending(d, vlpi);
+    unsigned long flags;
+    int ret;
+
+    if ( !p )
+        return -EINVAL;
+
+    spin_lock_irqsave(&vcpu->arch.vgic.lock, flags);
+    ret = update_lpi_property(d, vlpi, p);
+    if ( ret ) {
+        spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);
+        return ret;
+    }
+
+    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
+    {
+        if ( !list_empty(&p->inflight) &&
+             !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
+            gic_raise_guest_irq(vcpu, vlpi, p->lpi_priority);
+        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(GIC_IRQ_GUEST_LPI_PENDING, &p->status) )
+            vgic_vcpu_inject_irq(vcpu, vlpi);
+    }
+    else
+    {
+        clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
+        spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);
+
+        gic_remove_from_queues(vcpu, vlpi);
+    }
+
+    return 0;
+}
+
+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 vcpu *vcpu;
+    uint32_t vlpi;
+
+    /* Translate the event into a vCPU/vLPI pair. */
+    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
+        return -1;
+
+    /*
+     * Now read the property table and update our cached status. This
+     * also takes care if this LPI now needs to be injected or removed.
+     */
+    if ( update_lpi_enabled_status(its->d, vcpu, vlpi) )
+        return -1;
+
+    return 0;
+}
+
 static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
 {
     uint32_t collid = its_cmd_get_collection(cmdptr);
@@ -615,6 +708,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_INT:
             ret = its_handle_int(its, command);
             break;
+        case GITS_CMD_INV:
+            ret = its_handle_inv(its, command);
+            break;
         case GITS_CMD_MAPC:
             ret = its_handle_mapc(its, command);
             break;
-- 
2.8.2


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

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

* [PATCH v5 27/30] ARM: vITS: handle INVALL command
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (25 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 26/30] ARM: vITS: handle INV command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-05 23:19 ` [PATCH v5 28/30] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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
iterate over all mapped LPIs and filter for those from that particular
VCPU.

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

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 0d4b20d..9684b3a 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -469,6 +469,65 @@ static int its_handle_inv(struct virt_its *its, uint64_t *cmdptr)
     return 0;
 }
 
+/*
+ * INVALL updates the per-LPI configuration status for every LPI mapped to
+ * a particular redistributor.
+ * We iterate over all mapped LPIs in our radix tree and update those.
+ */
+static int its_handle_invall(struct virt_its *its, uint64_t *cmdptr)
+{
+    uint32_t collid = its_cmd_get_collection(cmdptr);
+    struct vcpu *vcpu;
+    struct pending_irq *pirqs[16];
+    uint64_t vlpi = 0;          /* 64-bit to catch overflows */
+    unsigned int nr_lpis, i;
+    int ret = 0;
+
+    /*
+     * As this implementation walks over all mapped LPIs, it might take
+     * too long for a real guest, so we might want to revisit this
+     * implementation for DomUs.
+     * However this command is very rare, also we don't expect many
+     * LPIs to be actually mapped, so it's fine for Dom0 to use.
+     */
+    ASSERT(is_hardware_domain(its->d));
+
+    spin_lock(&its->its_lock);
+    vcpu = get_vcpu_from_collection(its, collid);
+    spin_unlock(&its->its_lock);
+
+    read_lock(&its->d->arch.vgic.pend_lpi_tree_lock);
+
+    do
+    {
+        nr_lpis = radix_tree_gang_lookup(&its->d->arch.vgic.pend_lpi_tree,
+                                         (void **)pirqs, vlpi,
+                                         ARRAY_SIZE(pirqs));
+
+        for ( i = 0; i < nr_lpis; i++ )
+        {
+            /* We only care about LPIs on our VCPU. */
+            if ( pirqs[i]->vcpu_id != vcpu->vcpu_id )
+                continue;
+
+            vlpi = pirqs[i]->irq;
+            /* If that fails for a single LPI, carry on to handle the rest. */
+            if ( update_lpi_enabled_status(its->d, vcpu, vlpi) )
+                ret = -1;
+        }
+    /*
+     * Loop over the next gang of pending_irqs until we reached the end of
+     * a (fully populated) tree or the lookup function returns less LPIs than
+     * it has been asked for.
+     */
+    } while ( (++vlpi < its->d->arch.vgic.nr_lpis) &&
+              (nr_lpis == ARRAY_SIZE(pirqs)) );
+
+    read_unlock(&its->d->arch.vgic.pend_lpi_tree_lock);
+
+    return ret;
+}
+
 static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
 {
     uint32_t collid = its_cmd_get_collection(cmdptr);
@@ -711,6 +770,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
         case GITS_CMD_INV:
             ret = its_handle_inv(its, command);
             break;
+        case GITS_CMD_INVALL:
+            ret = its_handle_invall(its, command);
+            break;
         case GITS_CMD_MAPC:
             ret = its_handle_mapc(its, command);
             break;
-- 
2.8.2


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

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

* [PATCH v5 28/30] ARM: vITS: create and initialize virtual ITSes for Dom0
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (26 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 27/30] ARM: vITS: handle INVALL command Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-07 13:38   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
                   ` (2 subsequent siblings)
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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.
This introduces a function to initialize a virtual ITS.
We maintain a list of virtual ITSes, at the moment for the only
purpose of later being able to free them again.
We advertise 24 bits worth of LPIs on the guest side, using the full
32 bits seems to trigger a Linux bug (to be investigated).

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-v3-its.c       | 46 ++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/vgic-v3.c           | 17 +++++++++++++++
 xen/include/asm-arm/domain.h     |  2 ++
 xen/include/asm-arm/gic_v3_its.h | 12 +++++++++++
 4 files changed, 77 insertions(+)

diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
index 9684b3a..4e66cad 100644
--- a/xen/arch/arm/vgic-v3-its.c
+++ b/xen/arch/arm/vgic-v3-its.c
@@ -42,6 +42,7 @@
  */
 struct virt_its {
     struct domain *d;
+    struct list_head vits_list;
     paddr_t doorbell_address;
     unsigned int devid_bits;
     unsigned int intid_bits;
@@ -72,12 +73,20 @@ struct vits_itte
 
 void vgic_v3_its_init_domain(struct domain *d)
 {
+    INIT_LIST_HEAD(&d->arch.vgic.vits_list);
     spin_lock_init(&d->arch.vgic.its_devices_lock);
     d->arch.vgic.its_devices = RB_ROOT;
 }
 
 void vgic_v3_its_free_domain(struct domain *d)
 {
+    struct virt_its *pos, *temp;
+
+    list_for_each_entry_safe( pos, temp, &d->arch.vgic.vits_list, vits_list )
+    {
+        list_del(&pos->vits_list);
+        xfree(pos);
+    }
     ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
 }
 
@@ -1157,6 +1166,43 @@ 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, paddr_t guest_addr,
+                             unsigned int devid_bits, unsigned int intid_bits)
+{
+    struct virt_its *its;
+    uint64_t base_attr;
+
+    its = xzalloc(struct virt_its);
+    if ( !its )
+        return -ENOMEM;
+
+    base_attr  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
+    base_attr |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
+    base_attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
+
+    its->cbaser  = base_attr;
+    base_attr |= 0ULL << GITS_BASER_PAGE_SIZE_SHIFT;    /* 4K pages */
+    its->baser_dev = GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT;
+    its->baser_dev |= (sizeof(uint64_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
+    its->baser_dev |= base_attr;
+    its->baser_coll  = GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT;
+    its->baser_coll |= (sizeof(uint16_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
+    its->baser_coll |= base_attr;
+    its->d = d;
+    its->doorbell_address = guest_addr + ITS_DOORBELL_OFFSET;
+    its->devid_bits = devid_bits;
+    its->intid_bits = intid_bits;
+    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);
+
+    /* Register the virtual ITSes to be able to clean them up later. */
+    list_add_tail(&its->vits_list, &d->arch.vgic.vits_list);
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index e6a33d0..3b01247 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -30,6 +30,7 @@
 #include <asm/current.h>
 #include <asm/mmio.h>
 #include <asm/gic_v3_defs.h>
+#include <asm/gic_v3_its.h>
 #include <asm/vgic.h>
 #include <asm/vgic-emul.h>
 #include <asm/vreg.h>
@@ -1569,6 +1570,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;
@@ -1594,6 +1596,21 @@ 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)
+        {
+            /*
+             * For each host ITS create a virtual ITS using the same
+             * base and thus doorbell address.
+             * Use the same number of device ID bits as the host, and
+             * allow 24 bits for the interrupt ID.
+             */
+            vgic_v3_its_init_virtual(d, hw_its->addr, hw_its->devid_bits, 24);
+
+            d->arch.vgic.has_its = true;
+        }
+
     }
     else
     {
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
index f993292..cbbfb99 100644
--- a/xen/include/asm-arm/domain.h
+++ b/xen/include/asm-arm/domain.h
@@ -115,6 +115,8 @@ struct arch_domain
         spinlock_t its_devices_lock;        /* Protects the its_devices tree */
         struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
         rwlock_t pend_lpi_tree_lock;        /* Protects the pend_lpi_tree */
+        struct list_head vits_list;         /* List of virtual ITSes */
+        bool has_its;
 #endif
     } vgic;
 
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index daae143..1b8e47c 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -161,6 +161,10 @@ int gicv3_its_setup_collection(unsigned int cpu);
 void vgic_v3_its_init_domain(struct domain *d);
 void vgic_v3_its_free_domain(struct domain *d);
 
+/* Create and register a virtual ITS at the given guest address. */
+int vgic_v3_its_init_virtual(struct domain *d, paddr_t guest_addr,
+			     unsigned int devid_bits, unsigned int intid_bits);
+
 /*
  * Map a device on the host by allocating an ITT on the host (ITS).
  * "nr_event" specifies how many events (interrupts) this device will need.
@@ -237,6 +241,14 @@ static inline void vgic_v3_its_free_domain(struct domain *d)
 {
 }
 
+static inline int vgic_v3_its_init_virtual(struct domain *d,
+                                           paddr_t guest_addr,
+                                           unsigned int devid_bits,
+                                           unsigned int intid_bits)
+{
+    return 0;
+}
+
 #endif /* CONFIG_HAS_ITS */
 
 #endif
-- 
2.8.2


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

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

* [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (27 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 28/30] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-07 13:41   ` Julien Grall
  2017-04-05 23:19 ` [PATCH v5 30/30] ARM: vGIC: advertise LPI support Andre Przywara
  2017-04-06 12:31 ` [PATCH v5 00/30] arm64: Dom0 ITS emulation Julien Grall
  30 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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-v3-its.c        | 78 ++++++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c            |  4 ++-
 xen/include/asm-arm/gic_v3_its.h | 13 +++++++
 3 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
index a57e63a..a167471 100644
--- a/xen/arch/arm/gic-v3-its.c
+++ b/xen/arch/arm/gic-v3-its.c
@@ -20,6 +20,7 @@
 
 #include <xen/lib.h>
 #include <xen/delay.h>
+#include <xen/libfdt/libfdt.h>
 #include <xen/mm.h>
 #include <xen/rbtree.h>
 #include <xen/sched.h>
@@ -875,6 +876,83 @@ int gicv3_lpi_change_vcpu(struct domain *d, paddr_t vdoorbell,
     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.
+ * Giving NULL for the its_list will make it use the list of host ITSes.
+ */
+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 ( !its_list )
+        its_list = &host_its_list;
+
+    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 54fbb19..2fbcf52 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -1172,8 +1172,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(NULL, d, gic, fdt);
 }
 
 static const hw_irq_controller gicv3_host_irq_type = {
diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
index 1b8e47c..6538916 100644
--- a/xen/include/asm-arm/gic_v3_its.h
+++ b/xen/include/asm-arm/gic_v3_its.h
@@ -165,6 +165,12 @@ void vgic_v3_its_free_domain(struct domain *d);
 int vgic_v3_its_init_virtual(struct domain *d, paddr_t guest_addr,
 			     unsigned int devid_bits, unsigned int intid_bits);
 
+/* 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).
  * "nr_event" specifies how many events (interrupts) this device will need.
@@ -248,6 +254,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.8.2


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

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

* [PATCH v5 30/30] ARM: vGIC: advertise LPI support
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (28 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
@ 2017-04-05 23:19 ` Andre Przywara
  2017-04-06  1:04   ` Stefano Stabellini
  2017-04-07 13:43   ` Julien Grall
  2017-04-06 12:31 ` [PATCH v5 00/30] arm64: Dom0 ITS emulation Julien Grall
  30 siblings, 2 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-05 23:19 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: xen-devel, Shanker Donthineni, 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.
This removes a "TBD" comment, as we now populate the processor number
in the GICR_TYPE register.
Advertise 24 bits worth of LPIs to the guest.

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

diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index 3b01247..ba0e79f 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -168,8 +168,12 @@ 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;
+        spin_lock(&v->arch.vgic.lock);
+        *r = vgic_reg32_extract(!!(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED),
+                                info);
+        spin_unlock(&v->arch.vgic.lock);
+        return 1;
 
     case VREG32(GICR_IIDR):
         if ( dabt.size != DABT_WORD ) goto bad_width;
@@ -181,16 +185,20 @@ 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;
+        /* We use the VCPU ID as the redistributor ID in bits[23:8] */
+        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;
@@ -411,6 +419,17 @@ static uint64_t sanitize_pendbaser(uint64_t reg)
     return reg;
 }
 
+static void vgic_vcpu_enable_lpis(struct vcpu *v)
+{
+    uint64_t reg = v->domain->arch.vgic.rdist_propbase;
+    unsigned int nr_lpis = BIT((reg & 0x1f) + 1) - LPI_OFFSET;
+
+    if ( !v->domain->arch.vgic.nr_lpis )
+        v->domain->arch.vgic.nr_lpis = nr_lpis;
+
+    v->arch.vgic.flags |= VGIC_V3_LPIS_ENABLED;
+}
+
 static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
                                           uint32_t gicr_reg,
                                           register_t r)
@@ -421,8 +440,20 @@ 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;
+
+        spin_lock(&v->arch.vgic.lock);
+
+        /* LPIs can only be enabled once, but never disabled again. */
+        if ( (r & GICR_CTLR_ENABLE_LPIS) &&
+             !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
+            vgic_vcpu_enable_lpis(v);
+
+        spin_unlock(&v->arch.vgic.lock);
+
+        return 1;
 
     case VREG32(GICR_IIDR):
         /* RO */
@@ -1032,6 +1063,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 = 24;
+        }
         typer |= (irq_bits - 1) << GICD_TYPE_ID_BITS_SHIFT;
 
         *r = vgic_reg32_extract(typer, info);
-- 
2.8.2


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

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

* Re: [PATCH v5 01/30] bitops: add GENMASK_ULL
  2017-04-05 23:18 ` [PATCH v5 01/30] bitops: add GENMASK_ULL Andre Przywara
@ 2017-04-05 23:25   ` Stefano Stabellini
  2017-04-06  8:45     ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:25 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> To safely handle 64-bit registers even on 32-bit systems, introduce
> a GENMASK_ULL variant (lifted from Linux).
> This adds a BITS_PER_LONG_LONG define as well.
> Also fix a bug in the comment for the existing GENMASK variant.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

> ---
>  xen/include/asm-arm/config.h | 2 ++
>  xen/include/xen/bitops.h     | 5 ++++-
>  2 files changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
> index b2edf95..1f730ce 100644
> --- a/xen/include/asm-arm/config.h
> +++ b/xen/include/asm-arm/config.h
> @@ -19,6 +19,8 @@
>  #define BITS_PER_LONG (BYTES_PER_LONG << 3)
>  #define POINTER_ALIGN BYTES_PER_LONG
>  
> +#define BITS_PER_LONG_LONG (sizeof (long long) * BITS_PER_BYTE)
> +
>  /* xen_ulong_t is always 64 bits */
>  #define BITS_PER_XEN_ULONG 64
>  
> diff --git a/xen/include/xen/bitops.h b/xen/include/xen/bitops.h
> index bd0883a..9261e06 100644
> --- a/xen/include/xen/bitops.h
> +++ b/xen/include/xen/bitops.h
> @@ -5,11 +5,14 @@
>  /*
>   * Create a contiguous bitmask starting at bit position @l and ending at
>   * position @h. For example
> - * GENMASK(30, 21) gives us the 32bit vector 0x01fe00000.
> + * GENMASK(30, 21) gives us the 32bit vector 0x7fe00000.
>   */
>  #define GENMASK(h, l) \
>      (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
>  
> +#define GENMASK_ULL(h, l) \
> +    (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
> +
>  /*
>   * ffs: find first bit set. This is defined the same way as
>   * the libc and compiler builtin ffs routines, therefore
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 02/30] bitops: add BIT_ULL variant
  2017-04-05 23:18 ` [PATCH v5 02/30] bitops: add BIT_ULL variant Andre Przywara
@ 2017-04-05 23:26   ` Stefano Stabellini
  0 siblings, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:26 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> To safely handle 64-bit registers even on 32-bit systems, introduce
> a BIT_ULL variant (lifted from Linux).
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

> ---
>  xen/include/asm-arm/bitops.h | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h
> index bda8898..1cbfb9e 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 ADDR (*(volatile int *) addr)
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  2017-04-05 23:18 ` [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
@ 2017-04-05 23:26   ` Stefano Stabellini
  2017-04-06 12:39     ` Julien Grall
  2017-04-06 18:55     ` Andre Przywara
  0 siblings, 2 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:26 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> Parse the GIC subnodes in the device tree 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 (as an EXPERT option), use
> XEN_CONFIG_EXPERT=y on the make command line to see and use the option.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/Kconfig             |  5 +++
>  xen/arch/arm/Makefile            |  1 +
>  xen/arch/arm/gic-v3-its.c        | 77 ++++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c            | 10 +++---
>  xen/include/asm-arm/gic_v3_its.h | 67 ++++++++++++++++++++++++++++++++++
>  5 files changed, 156 insertions(+), 4 deletions(-)
>  create mode 100644 xen/arch/arm/gic-v3-its.c
>  create mode 100644 xen/include/asm-arm/gic_v3_its.h
> 
> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
> index 43123e6..d46b98c 100644
> --- a/xen/arch/arm/Kconfig
> +++ b/xen/arch/arm/Kconfig
> @@ -45,6 +45,11 @@ config ACPI
>  config HAS_GICV3
>  	bool
>  
> +config HAS_ITS
> +        bool
> +        prompt "GICv3 ITS MSI controller support" if EXPERT = "y"
> +        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 0ce94a8..39c0a03 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-v3-its.o
>  obj-y += guestcopy.o
>  obj-y += hvm.o
>  obj-y += io.o
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> new file mode 100644
> index 0000000..6b02349
> --- /dev/null
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -0,0 +1,77 @@
> +/*
> + * xen/arch/arm/gic-v3-its.c
> + *
> + * ARM GICv3 Interrupt Translation Service (ITS) support
> + *
> + * Copyright (C) 2016,2017 - 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; under version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <xen/lib.h>
> +#include <asm/gic_v3_defs.h>
> +#include <asm/gic_v3_its.h>
> +
> +/*
> + * No lock here, as this list gets only populated upon boot while scanning
> + * firmware tables for all host ITSes, and only gets iterated afterwards.
> + */
> +LIST_HEAD(host_its_list);
> +
> +bool gicv3_its_host_has_its(void)
> +{
> +    return !list_empty(&host_its_list);
> +}
> +
> +/* 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)
> +    {
> +        uint64_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 695f01f..b626298 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -42,6 +42,7 @@
>  #include <asm/device.h>
>  #include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
> +#include <asm/gic_v3_its.h>
>  #include <asm/cpufeature.h>
>  #include <asm/acpi.h>
>  
> @@ -1227,11 +1228,12 @@ static void __init gicv3_dt_init(void)
>       */
>      res = dt_device_get_address(node, 1 + gicv3.rdist_count,
>                                  &cbase, &csize);
> -    if ( res )
> -        return;
> +    if ( !res )
> +        dt_device_get_address(node, 1 + gicv3.rdist_count + 2,
> +                              &vbase, &vsize);
>  
> -    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_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> new file mode 100644
> index 0000000..765a655
> --- /dev/null
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -0,0 +1,67 @@
> +/*
> + * ARM GICv3 ITS support
> + *
> + * Andre Przywara <andre.przywara@arm.com>
> + * Copyright (c) 2016,2017 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; under version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ASM_ARM_ITS_H__
> +#define __ASM_ARM_ITS_H__
> +
> +#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;
> +};
> +
> +
> +#ifdef CONFIG_HAS_ITS
> +
> +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);
> +
> +bool gicv3_its_host_has_its(void);
> +
> +#else
> +
> +static LIST_HEAD(host_its_list);

This is a problem


> +static inline void gicv3_its_dt_init(const struct dt_device_node *node)
> +{
> +}
> +
> +static inline bool gicv3_its_host_has_its(void)
> +{
> +    return false;
> +}
> +
> +#endif /* CONFIG_HAS_ITS */
> +
> +#endif
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS
  2017-04-05 23:18 ` [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS Andre Przywara
@ 2017-04-05 23:27   ` Stefano Stabellini
  2017-04-06 12:41   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:27 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> Map the registers frame for each host ITS and populate the host ITS
> structure with some parameters describing the size of certain properties
> like the number of bits for device IDs.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

> ---
>  xen/arch/arm/gic-v3-its.c        | 33 ++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c            |  5 +++++
>  xen/include/asm-arm/gic_v3_its.h | 44 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 82 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index 6b02349..0298866 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -19,8 +19,10 @@
>   */
>  
>  #include <xen/lib.h>
> +#include <xen/mm.h>
>  #include <asm/gic_v3_defs.h>
>  #include <asm/gic_v3_its.h>
> +#include <asm/io.h>
>  
>  /*
>   * No lock here, as this list gets only populated upon boot while scanning
> @@ -33,6 +35,37 @@ bool gicv3_its_host_has_its(void)
>      return !list_empty(&host_its_list);
>  }
>  
> +static int gicv3_its_init_single_its(struct host_its *hw_its)
> +{
> +    uint64_t reg;
> +
> +    hw_its->its_base = ioremap_nocache(hw_its->addr, hw_its->size);
> +    if ( !hw_its->its_base )
> +        return -ENOMEM;
> +
> +    reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
> +    hw_its->devid_bits = GITS_TYPER_DEVICE_ID_BITS(reg);
> +    hw_its->evid_bits = GITS_TYPER_EVENT_ID_BITS(reg);
> +    hw_its->itte_size = GITS_TYPER_ITT_SIZE(reg);
> +
> +    return 0;
> +}
> +
> +int gicv3_its_init(void)
> +{
> +    struct host_its *hw_its;
> +    int ret;
> +
> +    list_for_each_entry(hw_its, &host_its_list, entry)
> +    {
> +        ret = gicv3_its_init_single_its(hw_its);
> +        if ( ret )
> +            return ret;
> +    }
> +
> +    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 b626298..d3d5784 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -1590,6 +1590,11 @@ static int __init gicv3_init(void)
>      spin_lock(&gicv3.lock);
>  
>      gicv3_dist_init();
> +
> +    res = gicv3_its_init();
> +    if ( res )
> +        panic("GICv3: ITS: initialization failed: %d\n", res);
> +
>      res = gicv3_cpu_init();
>      gicv3_hyp_init();
>  
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 765a655..7d88987 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -20,6 +20,38 @@
>  #ifndef __ASM_ARM_ITS_H__
>  #define __ASM_ARM_ITS_H__
>  
> +#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_TYPER_DEVIDS_SHIFT         13
> +#define GITS_TYPER_DEVIDS_MASK          (0x1fUL << GITS_TYPER_DEVIDS_SHIFT)
> +#define GITS_TYPER_DEVICE_ID_BITS(r)    (((r & GITS_TYPER_DEVIDS_MASK) >> \
> +                                               GITS_TYPER_DEVIDS_SHIFT) + 1)
> +
> +#define GITS_TYPER_IDBITS_SHIFT         8
> +#define GITS_TYPER_IDBITS_MASK          (0x1fUL << GITS_TYPER_IDBITS_SHIFT)
> +#define GITS_TYPER_EVENT_ID_BITS(r)     (((r & GITS_TYPER_IDBITS_MASK) >> \
> +                                               GITS_TYPER_IDBITS_SHIFT) + 1)
> +
> +#define GITS_TYPER_ITT_SIZE_SHIFT       4
> +#define GITS_TYPER_ITT_SIZE_MASK        (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
> +#define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
> +                                                 GITS_TYPER_ITT_SIZE_SHIFT) + 1)
> +
>  #include <xen/device_tree.h>
>  
>  /* data structure for each hardware ITS */
> @@ -28,6 +60,10 @@ struct host_its {
>      const struct dt_device_node *dt_node;
>      paddr_t addr;
>      paddr_t size;
> +    void __iomem *its_base;
> +    unsigned int devid_bits;
> +    unsigned int evid_bits;
> +    unsigned int itte_size;
>  };
>  
>  
> @@ -40,6 +76,9 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
>  
>  bool gicv3_its_host_has_its(void);
>  
> +/* Initialize the host structures for the host ITSes. */
> +int gicv3_its_init(void);
> +
>  #else
>  
>  static LIST_HEAD(host_its_list);
> @@ -53,6 +92,11 @@ static inline bool gicv3_its_host_has_its(void)
>      return false;
>  }
>  
> +static inline int gicv3_its_init(void)
> +{
> +    return 0;
> +}
> +
>  #endif /* CONFIG_HAS_ITS */
>  
>  #endif
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table
  2017-04-05 23:18 ` [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table Andre Przywara
@ 2017-04-05 23:30   ` Stefano Stabellini
  2017-04-06 12:58   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:30 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> The ARM GICv3 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
> redistributor. The maximum number of LPIs to be used can be adjusted with
> the command line option "max_lpi_bits", which defaults to 20 bits,
> covering about one million LPIs.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

> ---
>  docs/misc/xen-command-line.markdown |   9 ++
>  xen/arch/arm/Makefile               |   1 +
>  xen/arch/arm/gic-v3-lpi.c           | 221 ++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c               |  17 +++
>  xen/include/asm-arm/gic_v3_defs.h   |  52 +++++++++
>  xen/include/asm-arm/gic_v3_its.h    |  15 ++-
>  xen/include/asm-arm/irq.h           |   8 ++
>  7 files changed, 322 insertions(+), 1 deletion(-)
>  create mode 100644 xen/arch/arm/gic-v3-lpi.c
> 
> diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown
> index 9eb85d6..6ef8633 100644
> --- a/docs/misc/xen-command-line.markdown
> +++ b/docs/misc/xen-command-line.markdown
> @@ -1172,6 +1172,15 @@ based interrupts. Any higher IRQs will be available for use via PCI MSI.
>  ### maxcpus
>  > `= <integer>`
>  
> +### max\_lpi\_bits
> +> `= <integer>`
> +
> +Specifies the number of ARM GICv3 LPI interrupts to allocate on the host,
> +presented as the number of bits needed to encode it. This must be at least
> +14 and not exceed 32, and each LPI requires one byte (configuration) and
> +one pending bit to be allocated.
> +Defaults to 20 bits (to cover at most 1048576 interrupts).
> +
>  ### mce
>  > `= <integer>`
>  
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index 39c0a03..6be85ab 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -19,6 +19,7 @@ obj-y += gic.o
>  obj-y += gic-v2.o
>  obj-$(CONFIG_HAS_GICV3) += gic-v3.o
>  obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
> +obj-$(CONFIG_HAS_ITS) += gic-v3-lpi.o
>  obj-y += guestcopy.o
>  obj-y += hvm.o
>  obj-y += io.o
> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> new file mode 100644
> index 0000000..7f99ec6
> --- /dev/null
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -0,0 +1,221 @@
> +/*
> + * xen/arch/arm/gic-v3-lpi.c
> + *
> + * ARM GICv3 Locality-specific Peripheral Interrupts (LPI) support
> + *
> + * Copyright (C) 2016,2017 - 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; under version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <xen/lib.h>
> +#include <xen/mm.h>
> +#include <xen/sizes.h>
> +#include <xen/warning.h>
> +#include <asm/gic.h>
> +#include <asm/gic_v3_defs.h>
> +#include <asm/gic_v3_its.h>
> +#include <asm/io.h>
> +#include <asm/page.h>
> +
> +#define LPI_PROPTABLE_NEEDS_FLUSHING    (1U << 0)
> +
> +/* Global state */
> +static struct {
> +    /* The global LPI property table, shared by all redistributors. */
> +    uint8_t *lpi_property;
> +    /*
> +     * Number of physical LPIs the host supports. This is a property of
> +     * the GIC hardware. We depart from the habit of naming these things
> +     * "physical" in Xen, as the GICv3/4 spec uses the term "physical LPI"
> +     * in a different context to differentiate them from "virtual LPIs".
> +     */
> +    unsigned long long int max_host_lpi_ids;
> +    unsigned int flags;
> +} lpi_data;
> +
> +struct lpi_redist_data {
> +    void                *pending_table;
> +};
> +
> +static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);
> +
> +#define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
> +
> +static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
> +{
> +    uint64_t val;
> +    void *pendtable;
> +
> +    if ( this_cpu(lpi_redist).pending_table )
> +        return -EBUSY;
> +
> +    val  = GIC_BASER_CACHE_RaWaWb << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
> +    val |= GIC_BASER_CACHE_SameAsInner << GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT;
> +    val |= GIC_BASER_InnerShareable << GICR_PENDBASER_SHAREABILITY_SHIFT;
> +
> +    /*
> +     * The pending table holds one bit per LPI and even covers bits for
> +     * interrupt IDs below 8192, so we allocate the full range.
> +     * The GICv3 imposes a 64KB alignment requirement, also requires
> +     * physically contiguous memory.
> +     */
> +    pendtable = _xzalloc(lpi_data.max_host_lpi_ids / 8, SZ_64K);
> +    if ( !pendtable )
> +        return -ENOMEM;
> +
> +    /* Make sure the physical address can be encoded in the register. */
> +    if ( virt_to_maddr(pendtable) & ~GENMASK_ULL(51, 16) )
> +    {
> +        xfree(pendtable);
> +        return -ERANGE;
> +    }
> +    clean_and_invalidate_dcache_va_range(pendtable,
> +                                         lpi_data.max_host_lpi_ids / 8);
> +
> +    this_cpu(lpi_redist).pending_table = pendtable;
> +
> +    val |= GICR_PENDBASER_PTZ;
> +
> +    val |= virt_to_maddr(pendtable);
> +
> +    *reg = val;
> +
> +    return 0;
> +}
> +
> +/*
> + * Tell a redistributor about the (shared) property table, allocating one
> + * if not already done.
> + */
> +static int gicv3_lpi_set_proptable(void __iomem * rdist_base)
> +{
> +    uint64_t reg;
> +
> +    reg  = GIC_BASER_CACHE_RaWaWb << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT;
> +    reg |= GIC_BASER_CACHE_SameAsInner << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT;
> +    reg |= GIC_BASER_InnerShareable << GICR_PROPBASER_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 = _xmalloc(lpi_data.max_host_lpi_ids, SZ_4K);
> +
> +        if ( !table )
> +            return -ENOMEM;
> +
> +        /* Make sure the physical address can be encoded in the register. */
> +        if ( (virt_to_maddr(table) & ~GENMASK_ULL(51, 12)) )
> +        {
> +            xfree(table);
> +            return -ERANGE;
> +        }
> +        memset(table, GIC_PRI_IRQ | LPI_PROP_RES1, MAX_NR_HOST_LPIS);
> +        clean_and_invalidate_dcache_va_range(table, MAX_NR_HOST_LPIS);
> +        lpi_data.lpi_property = table;
> +    }
> +
> +    /* Encode the number of bits needed, minus one */
> +    reg |= fls(lpi_data.max_host_lpi_ids - 1) - 1;
> +
> +    reg |= virt_to_maddr(lpi_data.lpi_property);
> +
> +    writeq_relaxed(reg, rdist_base + GICR_PROPBASER);
> +    reg = readq_relaxed(rdist_base + GICR_PROPBASER);
> +
> +    /* If we can't do shareable, we have to drop cacheability as well. */
> +    if ( !(reg & GICR_PROPBASER_SHAREABILITY_MASK) )
> +    {
> +        reg &= ~GICR_PROPBASER_INNER_CACHEABILITY_MASK;
> +        reg |= GIC_BASER_CACHE_nC << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT;
> +    }
> +
> +    /* Remember that we have to flush the property table if non-cacheable. */
> +    if ( (reg & GICR_PROPBASER_INNER_CACHEABILITY_MASK) <= GIC_BASER_CACHE_nC )
> +    {
> +        lpi_data.flags |= LPI_PROPTABLE_NEEDS_FLUSHING;
> +        /* Update the redistributors knowledge about the attributes. */
> +        writeq_relaxed(reg, rdist_base + GICR_PROPBASER);
> +    }
> +
> +    return 0;
> +}
> +
> +int gicv3_lpi_init_rdist(void __iomem * rdist_base)
> +{
> +    uint32_t reg;
> +    uint64_t table_reg;
> +    int ret;
> +
> +    /* We don't support LPIs without an ITS. */
> +    if ( !gicv3_its_host_has_its() )
> +        return -ENODEV;
> +
> +    /* Make sure LPIs are disabled before setting up the tables. */
> +    reg = readl_relaxed(rdist_base + GICR_CTLR);
> +    if ( reg & GICR_CTLR_ENABLE_LPIS )
> +        return -EBUSY;
> +
> +    ret = gicv3_lpi_allocate_pendtable(&table_reg);
> +    if ( ret )
> +        return ret;
> +    writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
> +    table_reg = readq_relaxed(rdist_base + GICR_PENDBASER);
> +
> +    /* If the hardware reports non-shareable, drop cacheability as well. */
> +    if ( !(table_reg & GICR_PENDBASER_SHAREABILITY_MASK) )
> +    {
> +        table_reg &= GICR_PENDBASER_SHAREABILITY_MASK;
> +        table_reg &= GICR_PENDBASER_INNER_CACHEABILITY_MASK;
> +        table_reg |= GIC_BASER_CACHE_nC << GICR_PENDBASER_INNER_CACHEABILITY_SHIFT;
> +
> +        writeq_relaxed(table_reg, rdist_base + GICR_PENDBASER);
> +    }
> +
> +    return gicv3_lpi_set_proptable(rdist_base);
> +}
> +
> +static unsigned int max_lpi_bits = 20;
> +integer_param("max_lpi_bits", max_lpi_bits);
> +
> +int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
> +{
> +    /* An implementation needs to support at least 14 bits of LPI IDs. */
> +    max_lpi_bits = max(max_lpi_bits, 14U);
> +    lpi_data.max_host_lpi_ids = BIT_ULL(min(host_lpi_bits, max_lpi_bits));
> +
> +    /*
> +     * Warn if the number of LPIs are quite high, as the user might not want
> +     * to waste megabytes of memory for a mostly empty table.
> +     * It's very unlikely that we need more than 24 bits worth of LPIs.
> +     */
> +    if ( lpi_data.max_host_lpi_ids > BIT(24) )
> +        warning_add("Using high number of LPIs, limit memory usage with max_lpi_bits\n");
> +
> +    printk("GICv3: using at most %llu LPIs on the host.\n", MAX_NR_HOST_LPIS);
> +
> +    return 0;
> +}
> +
> +/*
> + * 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 d3d5784..54d2235 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -547,6 +547,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(GICD_TYPE_ID_BITS(type));
> +
>      printk("GICv3: %d lines, (IID %8.8x).\n",
>             nr_lines, readl_relaxed(GICD + GICD_IIDR));
>  
> @@ -659,6 +662,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_lpi_init_rdist(ptr);
> +                    if ( ret && ret != -ENODEV )
> +                    {
> +                        printk("GICv3: CPU%d: Cannot initialize LPIs: %u\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/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
> index 6bd25a5..6f02bb0 100644
> --- a/xen/include/asm-arm/gic_v3_defs.h
> +++ b/xen/include/asm-arm/gic_v3_defs.h
> @@ -45,6 +45,9 @@
>  
>  /* Additional bits in GICD_TYPER defined by GICv3 */
>  #define GICD_TYPE_ID_BITS_SHIFT 19
> +#define GICD_TYPE_ID_BITS(r)    ((((r) >> GICD_TYPE_ID_BITS_SHIFT) & 0x1f) + 1)
> +
> +#define GICD_TYPE_LPIS               (1U << 17)
>  
>  #define GICD_CTLR_RWP                (1UL << 31)
>  #define GICD_CTLR_ARE_NS             (1U << 4)
> @@ -95,12 +98,61 @@
>  #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)
>  
> +/* For specifying the inner cacheability type only */
> +#define GIC_BASER_CACHE_nCnB         0ULL
> +/* For specifying the outer cacheability type only */
> +#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_OUTER_CACHEABILITY_SHIFT         56
> +#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK               \
> +        (7UL << GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT)
> +#define GICR_PROPBASER_SHAREABILITY_SHIFT               10
> +#define GICR_PROPBASER_SHAREABILITY_MASK                     \
> +        (3UL << GICR_PROPBASER_SHAREABILITY_SHIFT)
> +#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT         7
> +#define GICR_PROPBASER_INNER_CACHEABILITY_MASK               \
> +        (7UL << GICR_PROPBASER_INNER_CACHEABILITY_SHIFT)
> +#define GICR_PROPBASER_RES0_MASK                             \
> +        (GENMASK_ULL(63, 59) | GENMASK_ULL(55, 52) | GENMASK_ULL(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 GICR_PENDBASER_RES0_MASK                             \
> +        (BIT(63) | GENMASK_ULL(61, 59) | GENMASK_ULL(55, 52) |       \
> +         GENMASK_ULL(15, 12) | GENMASK_ULL(6, 0))
> +
>  #define DEFAULT_PMR_VALUE            0xff
>  
> +#define LPI_PROP_PRIO_MASK           0xfc
> +#define LPI_PROP_RES1                (1 << 1)
> +#define LPI_PROP_ENABLED             (1 << 0)
> +
>  #define GICH_VMCR_EOI                (1 << 9)
>  #define GICH_VMCR_VENG1              (1 << 1)
>  
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 7d88987..295eb22 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -76,7 +76,10 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
>  
>  bool gicv3_its_host_has_its(void);
>  
> -/* Initialize the host structures for the host ITSes. */
> +int gicv3_lpi_init_rdist(void __iomem * rdist_base);
> +
> +/* Initialize the host structures for LPIs and the host ITSes. */
> +int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits);
>  int gicv3_its_init(void);
>  
>  #else
> @@ -92,6 +95,16 @@ static inline bool gicv3_its_host_has_its(void)
>      return false;
>  }
>  
> +static inline int gicv3_lpi_init_rdist(void __iomem * rdist_base)
> +{
> +    return -ENODEV;
> +}
> +
> +static inline int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
> +{
> +    return 0;
> +}
> +
>  static inline int gicv3_its_init(void)
>  {
>      return 0;
> diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
> index 4849f16..f940092 100644
> --- a/xen/include/asm-arm/irq.h
> +++ b/xen/include/asm-arm/irq.h
> @@ -18,8 +18,16 @@ struct arch_irq_desc {
>  };
>  
>  #define NR_LOCAL_IRQS	32
> +
> +/*
> + * This only covers the interrupts that Xen cares about, so SGIs, PPIs and
> + * SPIs. LPIs are too numerous, also only propagated to guests, so they are
> + * not included in this number.
> + */
>  #define NR_IRQS		1024
>  
> +#define LPI_OFFSET      8192
> +
>  #define nr_irqs NR_IRQS
>  #define nr_static_irqs NR_IRQS
>  #define arch_hwdom_irqs(domid) NR_IRQS
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling
  2017-04-05 23:19 ` [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
@ 2017-04-05 23:33   ` Stefano Stabellini
  2017-04-06 14:55   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:33 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, 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.
> As an ITS might choose between *two* ways of addressing a redistributor,
> we store both the MMIO base address as well as the processor number in
> a per-CPU variable to give each ITS what it wants.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


> ---
>  xen/arch/arm/gic-v3-its.c         | 199 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3-lpi.c         |  28 ++++++
>  xen/arch/arm/gic-v3.c             |  26 ++++-
>  xen/include/asm-arm/gic_v3_defs.h |   2 +
>  xen/include/asm-arm/gic_v3_its.h  |  37 +++++++
>  5 files changed, 291 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index d8e978a..1ecd63b 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -19,11 +19,14 @@
>   */
>  
>  #include <xen/lib.h>
> +#include <xen/delay.h>
>  #include <xen/mm.h>
>  #include <xen/sizes.h>
> +#include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
>  #include <asm/gic_v3_its.h>
>  #include <asm/io.h>
> +#include <asm/page.h>
>  
>  #define ITS_CMD_QUEUE_SZ                SZ_1M
>  
> @@ -38,6 +41,160 @@ bool gicv3_its_host_has_its(void)
>      return !list_empty(&host_its_list);
>  }
>  
> +#define BUFPTR_MASK                     GENMASK_ULL(19, 5)
> +static int its_send_command(struct host_its *hw_its, const void *its_cmd)
> +{
> +    /*
> +     * The command queue should actually never become full, if it does anyway
> +     * and this situation is not resolved quickly, this points to a much
> +     * bigger problem, probably an hardware error.
> +     * So to cover the one-off case where we actually hit a full command
> +     * queue, we introduce a small grace period to not give up too quickly.
> +     * Given the usual multi-hundred MHz frequency the ITS usually runs with,
> +     * one millisecond (for a single command) seem to be more than enough.
> +     * But this value is rather arbitrarily chosen based on theoretical
> +     * considerations.
> +     */
> +    s_time_t deadline = NOW() + MILLISECS(1);
> +    uint64_t readp, writep;
> +    int ret = -EBUSY;
> +
> +    /* No ITS commands from an interrupt handler (at the moment). */
> +    ASSERT(!in_irq());
> +
> +    spin_lock(&hw_its->cmd_lock);
> +
> +    do {
> +        readp = readq_relaxed(hw_its->its_base + GITS_CREADR) & BUFPTR_MASK;
> +        writep = readq_relaxed(hw_its->its_base + GITS_CWRITER) & BUFPTR_MASK;
> +
> +        if ( ((writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ) != readp )
> +        {
> +            ret = 0;
> +            break;
> +        }
> +
> +        /*
> +         * If the command queue is full, wait for a bit in the hope it drains
> +         * before giving up.
> +         */
> +        spin_unlock(&hw_its->cmd_lock);
> +        cpu_relax();
> +        udelay(1);
> +        spin_lock(&hw_its->cmd_lock);
> +    } while ( NOW() <= deadline );
> +
> +    if ( ret )
> +    {
> +        spin_unlock(&hw_its->cmd_lock);
> +        if ( printk_ratelimit() )
> +            printk(XENLOG_WARNING "host ITS: command queue full.\n");
> +        return ret;
> +    }
> +
> +    memcpy(hw_its->cmd_buf + writep, its_cmd, ITS_CMD_SIZE);
> +    if ( hw_its->flags & HOST_ITS_FLUSH_CMD_QUEUE )
> +        clean_and_invalidate_dcache_va_range(hw_its->cmd_buf + writep,
> +                                             ITS_CMD_SIZE);
> +    else
> +        dsb(ishst);
> +
> +    writep = (writep + ITS_CMD_SIZE) % ITS_CMD_QUEUE_SZ;
> +    writeq_relaxed(writep & BUFPTR_MASK, hw_its->its_base + GITS_CWRITER);
> +
> +    spin_unlock(&hw_its->cmd_lock);
> +
> +    return 0;
> +}
> +
> +/* Wait for an ITS to finish processing all commands. */
> +static int gicv3_its_wait_commands(struct host_its *hw_its)
> +{
> +    /*
> +     * As there could be quite a number of commands in a queue, we will
> +     * wait a bit longer than the one millisecond for a single command above.
> +     * Again this value is based on theoretical considerations, actually the
> +     * command queue should drain much faster.
> +     */
> +    s_time_t deadline = NOW() + MILLISECS(100);
> +    uint64_t readp, writep;
> +
> +    do {
> +        spin_lock(&hw_its->cmd_lock);
> +        readp = readq_relaxed(hw_its->its_base + GITS_CREADR) & BUFPTR_MASK;
> +        writep = readq_relaxed(hw_its->its_base + GITS_CWRITER) & BUFPTR_MASK;
> +        spin_unlock(&hw_its->cmd_lock);
> +
> +        if ( readp == writep )
> +            return 0;
> +
> +        cpu_relax();
> +        udelay(1);
> +    } while ( NOW() <= deadline );
> +
> +    return -ETIMEDOUT;
> +}
> +
> +static uint64_t encode_rdbase(struct host_its *hw_its, unsigned int cpu,
> +                              uint64_t reg)
> +{
> +    reg &= ~GENMASK_ULL(51, 16);
> +
> +    reg |= gicv3_get_redist_address(cpu, hw_its->flags & HOST_ITS_USES_PTA);
> +
> +    return reg;
> +}
> +
> +static int its_send_cmd_sync(struct host_its *its, unsigned 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, uint32_t collection_id,
> +                             unsigned int cpu)
> +{
> +    uint64_t cmd[4];
> +
> +    cmd[0] = GITS_CMD_MAPC;
> +    cmd[1] = 0x00;
> +    cmd[2] = encode_rdbase(its, cpu, collection_id);
> +    cmd[2] |= GITS_VALID_BIT;
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
> +/* Set up the (1:1) collection mapping for the given host CPU. */
> +int gicv3_its_setup_collection(unsigned int cpu)
> +{
> +    struct host_its *its;
> +    int ret;
> +
> +    list_for_each_entry(its, &host_its_list, entry)
> +    {
> +        ret = its_send_cmd_mapc(its, cpu, cpu);
> +        if ( ret )
> +            return ret;
> +
> +        ret = its_send_cmd_sync(its, cpu);
> +        if ( ret )
> +            return ret;
> +
> +        ret = gicv3_its_wait_commands(its);
> +        if ( ret )
> +            return ret;
> +    }
> +
> +    return 0;
> +}
> +
>  #define BASER_ATTR_MASK                                           \
>          ((0x3UL << GITS_BASER_SHAREABILITY_SHIFT)               | \
>           (0x7UL << GITS_BASER_OUTER_CACHEABILITY_SHIFT)         | \
> @@ -182,6 +339,41 @@ retry:
>      return -EINVAL;
>  }
>  
> +/*
> + * Before an ITS gets initialized, it should be in a quiescent state, where
> + * all outstanding commands and transactions have finished.
> + * So if the ITS is already enabled, turn it off and wait for all outstanding
> + * operations to get processed by polling the QUIESCENT bit.
> + */
> +static int gicv3_disable_its(struct host_its *hw_its)
> +{
> +    uint32_t reg;
> +    /*
> +     * As we also need to wait for the command queue to drain, we use the same
> +     * (arbitrary) timeout value as above for gicv3_its_wait_commands().
> +     */
> +    s_time_t deadline = NOW() + MILLISECS(100);
> +
> +    reg = readl_relaxed(hw_its->its_base + GITS_CTLR);
> +    if ( !(reg & GITS_CTLR_ENABLE) && (reg & GITS_CTLR_QUIESCENT) )
> +        return 0;
> +
> +    writel_relaxed(reg & ~GITS_CTLR_ENABLE, hw_its->its_base + GITS_CTLR);
> +
> +    do {
> +        reg = readl_relaxed(hw_its->its_base + GITS_CTLR);
> +        if ( reg & GITS_CTLR_QUIESCENT )
> +            return 0;
> +
> +        cpu_relax();
> +        udelay(1);
> +    } while ( NOW() <= deadline );
> +
> +    printk(XENLOG_ERR "ITS@%lx not quiescent.\n", hw_its->addr);
> +
> +    return -ETIMEDOUT;
> +}
> +
>  static int gicv3_its_init_single_its(struct host_its *hw_its)
>  {
>      uint64_t reg;
> @@ -191,10 +383,17 @@ static int gicv3_its_init_single_its(struct host_its *hw_its)
>      if ( !hw_its->its_base )
>          return -ENOMEM;
>  
> +    ret = gicv3_disable_its(hw_its);
> +    if ( ret )
> +        return ret;
> +
>      reg = readq_relaxed(hw_its->its_base + GITS_TYPER);
>      hw_its->devid_bits = GITS_TYPER_DEVICE_ID_BITS(reg);
>      hw_its->evid_bits = GITS_TYPER_EVENT_ID_BITS(reg);
>      hw_its->itte_size = GITS_TYPER_ITT_SIZE(reg);
> +    if ( reg & GITS_TYPER_PTA )
> +        hw_its->flags |= HOST_ITS_USES_PTA;
> +    spin_lock_init(&hw_its->cmd_lock);
>  
>      for ( i = 0; i < GITS_BASER_NR_REGS; i++ )
>      {
> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 7f99ec6..9d3df7f 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -45,6 +45,8 @@ static struct {
>  } lpi_data;
>  
>  struct lpi_redist_data {
> +    paddr_t             redist_addr;
> +    unsigned int        redist_id;
>      void                *pending_table;
>  };
>  
> @@ -52,6 +54,32 @@ static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);
>  
>  #define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
>  
> +/*
> + * An ITS can refer to redistributors in two ways: either by an ID (possibly
> + * the CPU number) or by its MMIO address. This is a hardware implementation
> + * choice, so we have to cope with both approaches. The GICv3 code calculates
> + * both values and calls this function to let the ITS store them when it's
> + * later required to provide them. This is done in a per-CPU variable.
> + */
> +void gicv3_set_redist_address(paddr_t address, unsigned int redist_id)
> +{
> +    this_cpu(lpi_redist).redist_addr = address;
> +    this_cpu(lpi_redist).redist_id = redist_id;
> +}
> +
> +/*
> + * Returns a redistributor's ID (either as an address or as an ID).
> + * This must be (and is) called only after it has been setup by the above
> + * function.
> + */
> +uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
> +{
> +    if ( use_pta )
> +        return per_cpu(lpi_redist, cpu).redist_addr & GENMASK_ULL(51, 16);
> +    else
> +        return per_cpu(lpi_redist, cpu).redist_id << 16;
> +}
> +
>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>  {
>      uint64_t val;
> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> index 54d2235..a559e5e 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -665,8 +665,25 @@ static int __init gicv3_populate_rdist(void)
>  
>                  if ( typer & GICR_TYPER_PLPIS )
>                  {
> +                    paddr_t rdist_addr;
> +                    unsigned int procnum;
>                      int ret;
>  
> +                    /*
> +                     * The ITS refers to redistributors either by their physical
> +                     * address or by their ID. Which one to use is an ITS
> +                     * choice. So determine those two values here (which we
> +                     * can do only here in GICv3 code) and tell the
> +                     * ITS code about it, so it can use them later to be able
> +                     * to address those redistributors accordingly.
> +                     */
> +                    rdist_addr = gicv3.rdist_regions[i].base;
> +                    rdist_addr += ptr - gicv3.rdist_regions[i].map_base;
> +                    procnum = (typer & GICR_TYPER_PROC_NUM_MASK);
> +                    procnum >>= GICR_TYPER_PROC_NUM_SHIFT;
> +
> +                    gicv3_set_redist_address(rdist_addr, procnum);
> +
>                      ret = gicv3_lpi_init_rdist(ptr);
>                      if ( ret && ret != -ENODEV )
>                      {
> @@ -704,7 +721,7 @@ static int __init gicv3_populate_rdist(void)
>  
>  static int gicv3_cpu_init(void)
>  {
> -    int i;
> +    int i, ret;
>      uint32_t priority;
>  
>      /* Register ourselves with the rest of the world */
> @@ -714,6 +731,13 @@ static int gicv3_cpu_init(void)
>      if ( gicv3_enable_redist() )
>          return -ENODEV;
>  
> +    if ( gicv3_its_host_has_its() )
> +    {
> +        ret = gicv3_its_setup_collection(smp_processor_id());
> +        if ( ret )
> +            return ret;
> +    }
> +
>      /* Set priority on PPI and SGI interrupts */
>      priority = (GIC_PRI_IPI << 24 | GIC_PRI_IPI << 16 | GIC_PRI_IPI << 8 |
>                  GIC_PRI_IPI);
> diff --git a/xen/include/asm-arm/gic_v3_defs.h b/xen/include/asm-arm/gic_v3_defs.h
> index 6f02bb0..b7e5a47 100644
> --- a/xen/include/asm-arm/gic_v3_defs.h
> +++ b/xen/include/asm-arm/gic_v3_defs.h
> @@ -103,6 +103,8 @@
>  #define GICR_TYPER_PLPIS             (1U << 0)
>  #define GICR_TYPER_VLPIS             (1U << 1)
>  #define GICR_TYPER_LAST              (1U << 4)
> +#define GICR_TYPER_PROC_NUM_SHIFT    8
> +#define GICR_TYPER_PROC_NUM_MASK     (0xffff << GICR_TYPER_PROC_NUM_SHIFT)
>  
>  /* For specifying the inner cacheability type only */
>  #define GIC_BASER_CACHE_nCnB         0ULL
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index a68ccf3..7d7ec32 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -42,6 +42,7 @@
>  #define GITS_CTLR_QUIESCENT             BIT(31)
>  #define GITS_CTLR_ENABLE                BIT(0)
>  
> +#define GITS_TYPER_PTA                  BIT_ULL(19)
>  #define GITS_TYPER_DEVIDS_SHIFT         13
>  #define GITS_TYPER_DEVIDS_MASK          (0x1fUL << GITS_TYPER_DEVIDS_SHIFT)
>  #define GITS_TYPER_DEVICE_ID_BITS(r)    (((r & GITS_TYPER_DEVIDS_MASK) >> \
> @@ -86,9 +87,26 @@
>  
>  #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
> +
>  #include <xen/device_tree.h>
>  
>  #define HOST_ITS_FLUSH_CMD_QUEUE        (1U << 0)
> +#define HOST_ITS_USES_PTA               (1U << 1)
>  
>  /* data structure for each hardware ITS */
>  struct host_its {
> @@ -100,6 +118,7 @@ struct host_its {
>      unsigned int devid_bits;
>      unsigned int evid_bits;
>      unsigned int itte_size;
> +    spinlock_t cmd_lock;
>      void *cmd_buf;
>      unsigned int flags;
>  };
> @@ -120,6 +139,13 @@ int gicv3_lpi_init_rdist(void __iomem * rdist_base);
>  int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits);
>  int gicv3_its_init(void);
>  
> +/* Store the physical address and ID for each redistributor as read from DT. */
> +void gicv3_set_redist_address(paddr_t address, unsigned int redist_id);
> +uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta);
> +
> +/* Map a collection for this host CPU to each host ITS. */
> +int gicv3_its_setup_collection(unsigned int cpu);
> +
>  #else
>  
>  static LIST_HEAD(host_its_list);
> @@ -148,6 +174,17 @@ static inline int gicv3_its_init(void)
>      return 0;
>  }
>  
> +static inline void gicv3_set_redist_address(paddr_t address,
> +                                            unsigned int redist_id)
> +{
> +}
> +
> +static inline int gicv3_its_setup_collection(unsigned int cpu)
> +{
> +    /* We should never get here without an ITS. */
> +    BUG();
> +}
> +
>  #endif /* CONFIG_HAS_ITS */
>  
>  #endif
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array
  2017-04-05 23:19 ` [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
@ 2017-04-05 23:37   ` Stefano Stabellini
  2017-04-06 10:31     ` Andre Przywara
  0 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:37 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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.
> By using the naturally atomic access guarantee the native uint64_t data
> type gives us, 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-v3-its.c        |  60 +++++++++++
>  xen/arch/arm/gic-v3-lpi.c        | 227 +++++++++++++++++++++++++++++++++++++++
>  xen/include/asm-arm/gic_v3_its.h |   6 ++
>  xen/include/asm-arm/irq.h        |   8 ++
>  4 files changed, 301 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index 1ecd63b..eb47c9d 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -157,6 +157,20 @@ static int its_send_cmd_sync(struct host_its *its, unsigned 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, uint32_t collection_id,
>                               unsigned int cpu)
>  {
> @@ -171,6 +185,19 @@ static int its_send_cmd_mapc(struct host_its *its, uint32_t collection_id,
>      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. */
>  int gicv3_its_setup_collection(unsigned int cpu)
>  {
> @@ -450,6 +477,39 @@ int gicv3_its_init(void)
>      return 0;
>  }
>  
> +/*
> + * On the host ITS @its, map @nr_events consecutive LPIs.
> + * The mapping connects a device @devid and event @eventid pair to LPI @lpi,
> + * increasing both @eventid and @lpi to cover the number of requested LPIs.
> + */
> +static int gicv3_its_map_host_events(struct host_its *its,
> +                                     uint32_t devid, uint32_t eventid,
> +                                     uint32_t lpi, uint32_t nr_events)
> +{
> +    uint32_t i;
> +    int ret;
> +
> +    for ( i = 0; i < nr_events; i++ )
> +    {
> +        /* For now we map every host LPI to host CPU 0 */
> +        ret = its_send_cmd_mapti(its, devid, eventid + i, lpi + i, 0);
> +        if ( ret )
> +            return ret;
> +
> +        ret = its_send_cmd_inv(its, devid, eventid + i);
> +        if ( ret )
> +            return ret;
> +    }
> +
> +    /* TODO: Consider using INVALL here. Didn't work on the model, though. */
> +
> +    ret = its_send_cmd_sync(its, 0);
> +    if ( ret )
> +        return ret;
> +
> +    return gicv3_its_wait_commands(its);
> +}
> +
>  /* 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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 9d3df7f..0785701 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -20,14 +20,37 @@
>  
>  #include <xen/lib.h>
>  #include <xen/mm.h>
> +#include <xen/sched.h>
>  #include <xen/sizes.h>
>  #include <xen/warning.h>
> +#include <asm/atomic.h>
> +#include <asm/domain.h>
>  #include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
>  #include <asm/gic_v3_its.h>
>  #include <asm/io.h>
>  #include <asm/page.h>
>  
> +/*
> + * There could be a lot of LPIs on the host side, and they always go to
> + * a guest. So having a struct irq_desc for each of them would be wasteful
> + * and useless.
> + * Instead just store enough information to find the right VCPU to inject
> + * those LPIs into, which just requires the virtual LPI number.
> + * To avoid a global lock on this data structure, this is using a lockless
> + * approach relying on the architectural atomicity of native data types:
> + * We read or write the "data" view of this union atomically, then can
> + * access the broken-down fields in our local copy.
> + */
> +union host_lpi {
> +    uint64_t data;
> +    struct {
> +        uint32_t virt_lpi;
> +        uint16_t dom_id;
> +        uint16_t vcpu_id;
> +    };
> +};
> +
>  #define LPI_PROPTABLE_NEEDS_FLUSHING    (1U << 0)
>  
>  /* Global state */
> @@ -35,12 +58,23 @@ static struct {
>      /* The global LPI property table, shared by all redistributors. */
>      uint8_t *lpi_property;
>      /*
> +     * A two-level table to lookup LPIs firing on the host and look up the
> +     * VCPU and virtual LPI number to inject into.
> +     */
> +    union host_lpi **host_lpis;
> +    /*
>       * Number of physical LPIs the host supports. This is a property of
>       * the GIC hardware. We depart from the habit of naming these things
>       * "physical" in Xen, as the GICv3/4 spec uses the term "physical LPI"
>       * in a different context to differentiate them from "virtual LPIs".
>       */
>      unsigned long long int max_host_lpi_ids;
> +    /*
> +     * Protects allocation and deallocation of host LPIs and next_free_lpi,
> +     * but not the actual data stored in the host_lpi entry.
> +     */
> +    spinlock_t host_lpis_lock;
> +    uint32_t next_free_lpi;
>      unsigned int flags;
>  } lpi_data;
>  
> @@ -53,6 +87,28 @@ struct lpi_redist_data {
>  static DEFINE_PER_CPU(struct lpi_redist_data, lpi_redist);
>  
>  #define MAX_NR_HOST_LPIS   (lpi_data.max_host_lpi_ids - LPI_OFFSET)
> +#define HOST_LPIS_PER_PAGE      (PAGE_SIZE / sizeof(union host_lpi))
> +
> +static union host_lpi *gic_get_host_lpi(uint32_t plpi)
> +{
> +    union host_lpi *block;
> +
> +    if ( !is_lpi(plpi) || plpi >= MAX_NR_HOST_LPIS + LPI_OFFSET )
> +        return NULL;
> +
> +    ASSERT(plpi >= LPI_OFFSET);
> +
> +    plpi -= LPI_OFFSET;
> +
> +    block = lpi_data.host_lpis[plpi / HOST_LPIS_PER_PAGE];
> +    if ( !block )
> +        return NULL;
> +
> +    /* Matches the write barrier in allocation code. */
> +    smp_rmb();
> +
> +    return &block[plpi % HOST_LPIS_PER_PAGE];
> +}
>  
>  /*
>   * An ITS can refer to redistributors in two ways: either by an ID (possibly
> @@ -220,8 +276,18 @@ int gicv3_lpi_init_rdist(void __iomem * rdist_base)
>  static unsigned int max_lpi_bits = 20;
>  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 host_lpi_bits)
>  {
> +    unsigned int nr_lpi_ptrs;
> +
> +    /* We rely on the data structure being atomically accessible. */
> +    BUILD_BUG_ON(sizeof(union host_lpi) > sizeof(unsigned long));
> +
>      /* An implementation needs to support at least 14 bits of LPI IDs. */
>      max_lpi_bits = max(max_lpi_bits, 14U);
>      lpi_data.max_host_lpi_ids = BIT_ULL(min(host_lpi_bits, max_lpi_bits));
> @@ -234,11 +300,172 @@ int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
>      if ( lpi_data.max_host_lpi_ids > BIT(24) )
>          warning_add("Using high number of LPIs, limit memory usage with max_lpi_bits\n");
>  
> +    spin_lock_init(&lpi_data.host_lpis_lock);
> +    lpi_data.next_free_lpi = 0;
> +
> +    nr_lpi_ptrs = MAX_NR_HOST_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 %llu LPIs on the host.\n", MAX_NR_HOST_LPIS);
>  
>      return 0;
>  }
>  
> +static int find_unused_host_lpi(uint32_t start, uint32_t *index)
> +{
> +    unsigned int chunk;
> +    uint32_t i = *index;
> +
> +    ASSERT(spin_is_locked(&lpi_data.host_lpis_lock));
> +
> +    for ( chunk = start;
> +          chunk < MAX_NR_HOST_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 == DOMID_INVALID )
> +            {
> +                *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.
> + */
> +int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi)
> +{
> +    uint32_t lpi, lpi_idx;
> +    int chunk;
> +    int i;
> +
> +    spin_lock(&lpi_data.host_lpis_lock);
> +    lpi_idx = lpi_data.next_free_lpi % HOST_LPIS_PER_PAGE;
> +    chunk = find_unused_host_lpi(lpi_data.next_free_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;
> +
> +        /* TODO: NUMA locality for quicker IRQ path? */
> +        new_chunk = alloc_xenheap_page();
> +        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 = DOMID_INVALID;
> +
> +        /*
> +         * Make sure all slots are really marked empty before publishing the
> +         * new chunk.
> +         */
> +        smp_wmb();
> +
> +        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 = INVALID_LPI;
> +        hlpi.dom_id = d->domain_id;
> +        hlpi.vcpu_id = INVALID_VCPU_ID;
> +        write_u64_atomic(&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;
> +    }
> +
> +    lpi_data.next_free_lpi = lpi + LPI_BLOCK;
> +
> +    /*
> +     * 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);
> +
> +    if ( lpi_data.flags & LPI_PROPTABLE_NEEDS_FLUSHING )
> +        clean_and_invalidate_dcache_va_range(&lpi_data.lpi_property[lpi],
> +                                             LPI_BLOCK);
> +
> +    *first_lpi = lpi + LPI_OFFSET;
> +
> +    return 0;
> +}
> +
> +void gicv3_free_host_lpi_block(uint32_t first_lpi)
> +{
> +    union host_lpi *hlpi, empty_lpi = { .dom_id = DOMID_INVALID };
> +    int i;
> +
> +    hlpi = gic_get_host_lpi(first_lpi);
> +    if ( !hlpi )
> +        return;         /* Nothing to free here. */

We should check that first_lpi is actually the first lpi in a block
before calling gic_get_host_lpi.


> +    spin_lock(&lpi_data.host_lpis_lock);
> +
> +    for ( i = 0; i < LPI_BLOCK; i++ )
> +        write_u64_atomic(&hlpi[i].data, empty_lpi.data);
> +
> +    /*
> +     * Make sure the next allocation can reuse this block, as we do only
> +     * forward scanning when finding an unused block.
> +     */
> +    if ( lpi_data.next_free_lpi > first_lpi )
> +        lpi_data.next_free_lpi = first_lpi;
> +
> +    spin_unlock(&lpi_data.host_lpis_lock);
> +
> +    return;
> +}
> +
>  /*
>   * Local variables:
>   * mode: C
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 7d7ec32..13b3e14 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -108,6 +108,9 @@
>  #define HOST_ITS_FLUSH_CMD_QUEUE        (1U << 0)
>  #define HOST_ITS_USES_PTA               (1U << 1)
>  
> +/* We allocate LPIs on the hosts in chunks of 32 to reduce handling overhead. */
> +#define LPI_BLOCK                       32
> +
>  /* data structure for each hardware ITS */
>  struct host_its {
>      struct list_head entry;
> @@ -146,6 +149,9 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta);
>  /* Map a collection for this host CPU to each host ITS. */
>  int gicv3_its_setup_collection(unsigned int cpu);
>  
> +int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
> +void gicv3_free_host_lpi_block(uint32_t first_lpi);
> +
>  #else
>  
>  static LIST_HEAD(host_its_list);
> diff --git a/xen/include/asm-arm/irq.h b/xen/include/asm-arm/irq.h
> index f940092..7c76626 100644
> --- a/xen/include/asm-arm/irq.h
> +++ b/xen/include/asm-arm/irq.h
> @@ -28,6 +28,9 @@ struct arch_irq_desc {
>  
>  #define LPI_OFFSET      8192
>  
> +/* LPIs are always numbered starting at 8192, so 0 is a good invalid case. */
> +#define INVALID_LPI     0
> +
>  #define nr_irqs NR_IRQS
>  #define nr_static_irqs NR_IRQS
>  #define arch_hwdom_irqs(domid) NR_IRQS
> @@ -41,6 +44,11 @@ struct irq_desc *__irq_to_desc(int irq);
>  
>  void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq);
>  
> +static inline bool is_lpi(unsigned int irq)
> +{
> +    return irq >= LPI_OFFSET;
> +}
> +
>  #define domain_pirq_to_irq(d, pirq) (pirq)
>  
>  bool_t is_assignable_irq(unsigned int irq);
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-05 23:19 ` [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping Andre Przywara
@ 2017-04-05 23:41   ` Stefano Stabellini
  2017-04-06 11:15     ` Julien Grall
  2017-04-06 15:34   ` Julien Grall
  2017-04-07 14:46   ` Julien Grall
  2 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:41 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, 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 an rbtree to
> later be able to iterate over it or to easily teardown guests.
> Because device IDs are per ITS, we need to identify a virtual ITS. We
> use the doorbell address for that purpose, as it is a nice architectural
> MSI property and spares us handling with opaque pointer or break
> the VGIC abstraction.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-v3-its.c        | 263 +++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3-its.c       |   3 +
>  xen/include/asm-arm/domain.h     |   3 +
>  xen/include/asm-arm/gic_v3_its.h |  13 ++
>  4 files changed, 282 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index eb47c9d..45bbfa7 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -21,6 +21,8 @@
>  #include <xen/lib.h>
>  #include <xen/delay.h>
>  #include <xen/mm.h>
> +#include <xen/rbtree.h>
> +#include <xen/sched.h>
>  #include <xen/sizes.h>
>  #include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
> @@ -36,6 +38,26 @@
>   */
>  LIST_HEAD(host_its_list);
>  
> +/*
> + * Describes a device which is using the ITS and is used by a guest.
> + * Since device IDs are per ITS (in contrast to vLPIs, which are per
> + * guest), we have to differentiate between different virtual ITSes.
> + * We use the doorbell address here, since this is a nice architectural
> + * property of MSIs in general and we can easily get to the base address
> + * of the ITS and look that up.
> + */
> +struct its_devices {
> +    struct rb_node rbnode;
> +    struct host_its *hw_its;
> +    void *itt_addr;
> +    paddr_t guest_doorbell;             /* Identifies the virtual ITS */
> +    uint32_t host_devid;
> +    uint32_t guest_devid;
> +    uint32_t eventids;                  /* Number of event IDs (MSIs) */
> +    uint32_t *host_lpi_blocks;          /* Which LPIs are used on the host */
> +    struct pending_irq *pend_irqs;      /* One struct per event */
> +};
> +
>  bool gicv3_its_host_has_its(void)
>  {
>      return !list_empty(&host_its_list);
> @@ -185,6 +207,30 @@ static int its_send_cmd_mapc(struct host_its *its, uint32_t collection_id,
>      return its_send_command(its, cmd);
>  }
>  
> +static int its_send_cmd_mapd(struct host_its *its, uint32_t deviceid,
> +                             uint8_t size_bits, paddr_t itt_addr, bool valid)
> +{
> +    uint64_t cmd[4];
> +
> +    if ( valid )
> +    {
> +        ASSERT(size_bits <= its->evid_bits);
> +        ASSERT(size_bits > 0);
> +        ASSERT(!(itt_addr & ~GENMASK_ULL(51, 8)));
> +
> +        /* The number of events is encoded as "number of bits minus one". */
> +        size_bits--;
> +    }
> +    cmd[0] = GITS_CMD_MAPD | ((uint64_t)deviceid << 32);
> +    cmd[1] = size_bits;
> +    cmd[2] = itt_addr;
> +    if ( valid )
> +        cmd[2] |= GITS_VALID_BIT;
> +    cmd[3] = 0x00;
> +
> +    return its_send_command(its, cmd);
> +}
> +
>  static int its_send_cmd_inv(struct host_its *its,
>                              uint32_t deviceid, uint32_t eventid)
>  {
> @@ -477,6 +523,66 @@ int gicv3_its_init(void)
>      return 0;
>  }
>  
> +static int remove_mapped_guest_device(struct its_devices *dev)
> +{
> +    int ret = 0;
> +    unsigned int i;
> +
> +    if ( dev->hw_its )
> +        /* MAPD also discards all events with this device ID. */
> +        ret = its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
> +
> +    for ( i = 0; i < dev->eventids / LPI_BLOCK; i++ )
> +        gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
> +
> +    /* Make sure the MAPD command above is really executed. */
> +    if ( !ret )
> +        ret = gicv3_its_wait_commands(dev->hw_its);
> +
> +    /* This should never happen, but just in case ... */
> +    if ( ret )
> +        printk(XENLOG_WARNING "Can't unmap host ITS device 0x%x\n",
> +               dev->host_devid);
> +
> +    xfree(dev->itt_addr);
> +    xfree(dev->pend_irqs);
> +    xfree(dev->host_lpi_blocks);
> +    xfree(dev);
> +
> +    return 0;
> +}
> +
> +static struct host_its *gicv3_its_find_by_doorbell(paddr_t doorbell_address)
> +{
> +    struct host_its *hw_its;
> +
> +    list_for_each_entry(hw_its, &host_its_list, entry)
> +    {
> +        if ( hw_its->addr + ITS_DOORBELL_OFFSET == doorbell_address )
> +            return hw_its;
> +    }
> +
> +    return NULL;
> +}
> +
> +static int compare_its_guest_devices(struct its_devices *dev,
> +                                     paddr_t vdoorbell, uint32_t vdevid)
> +{
> +    if ( dev->guest_doorbell < vdoorbell )
> +        return -1;
> +
> +    if ( dev->guest_doorbell > vdoorbell )
> +        return 1;
> +
> +    if ( dev->guest_devid < vdevid )
> +        return -1;
> +
> +    if ( dev->guest_devid > vdevid )
> +        return 1;
> +
> +    return 0;
> +}
> +
>  /*
>   * On the host ITS @its, map @nr_events consecutive LPIs.
>   * The mapping connects a device @devid and event @eventid pair to LPI @lpi,
> @@ -510,6 +616,163 @@ static int gicv3_its_map_host_events(struct host_its *its,
>      return gicv3_its_wait_commands(its);
>  }
>  
> +/*
> + * Map a hardware device, identified by a certain host ITS and its device ID
> + * to domain d, a guest ITS (identified by its doorbell address) and device ID.
> + * Also provide the number of events (MSIs) needed for that device.
> + * This does not check if this particular hardware device is already mapped
> + * at another domain, it is expected that this would be done by the caller.
> + */
> +int gicv3_its_map_guest_device(struct domain *d,
> +                               paddr_t host_doorbell, uint32_t host_devid,
> +                               paddr_t guest_doorbell, uint32_t guest_devid,
> +                               uint32_t nr_events, bool valid)
> +{
> +    void *itt_addr = NULL;
> +    struct host_its *hw_its;
> +    struct its_devices *dev = NULL;
> +    struct rb_node **new = &d->arch.vgic.its_devices.rb_node, *parent = NULL;
> +    unsigned int i;
> +    int ret = -ENOENT;
> +
> +    hw_its = gicv3_its_find_by_doorbell(host_doorbell);
> +    if ( !hw_its )
> +        return ret;
> +
> +    /* Sanitise the provided hardware values against the host ITS. */
> +    if ( host_devid >= BIT(hw_its->devid_bits) )
> +        return -EINVAL;
> +
> +    /* We allocate events and LPIs in chunks of LPI_BLOCK (=32). */
> +    nr_events = ROUNDUP(nr_events, LPI_BLOCK);
> +    if ( nr_events >= BIT(hw_its->evid_bits) )
> +        return -EINVAL;
> +
> +    /* check for already existing mappings */
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    while ( *new )
> +    {
> +        struct its_devices *temp;
> +        int cmp;
> +
> +        temp = rb_entry(*new, struct its_devices, rbnode);
> +
> +        parent = *new;
> +        cmp = compare_its_guest_devices(temp, guest_doorbell, guest_devid);
> +        if ( !cmp )
> +        {
> +            if ( !valid )
> +                rb_erase(&temp->rbnode, &d->arch.vgic.its_devices);
> +
> +            spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +            if ( valid )
> +            {
> +                printk(XENLOG_G_WARNING "d%d tried to remap guest ITS device 0x%x to host device 0x%x\n",
> +                        d->domain_id, guest_devid, host_devid);
> +                return -EBUSY;
> +            }
> +
> +            return remove_mapped_guest_device(temp);
> +        }
> +
> +        if ( cmp > 0 )
> +            new = &((*new)->rb_left);
> +        else
> +            new = &((*new)->rb_right);
> +    }
> +
> +    if ( !valid )
> +        goto out_unlock;
> +
> +    ret = -ENOMEM;
> +
> +    /* An Interrupt Translation Table needs to be 256-byte aligned. */
> +    itt_addr = _xzalloc(nr_events * hw_its->itte_size, 256);
> +    if ( !itt_addr )
> +        goto out_unlock;
> +
> +    dev = xzalloc(struct its_devices);
> +    if ( !dev )
> +        goto out_unlock;
> +
> +    /*
> +     * Allocate the pending_irqs for each virtual LPI. They will be put
> +     * into the domain's radix tree upon the guest's MAPTI command.
> +     */

Please expand the comment with a link to the xen-devel conversation
about allocation strategy and a TODO saying that we shouldn't need to
preallocate all pending_irq struct, but we do it for simplicity at the
moment.


> +    dev->pend_irqs = xzalloc_array(struct pending_irq, nr_events);
> +    if ( !dev->pend_irqs )
> +        goto out_unlock;
> +
> +    dev->host_lpi_blocks = xzalloc_array(uint32_t, nr_events);
> +    if ( !dev->host_lpi_blocks )
> +        goto out_unlock;
> +
> +    ret = its_send_cmd_mapd(hw_its, host_devid, fls(nr_events - 1),
> +                            virt_to_maddr(itt_addr), true);
> +    if ( ret )
> +        goto out_unlock;
> +
> +    dev->itt_addr = itt_addr;
> +    dev->hw_its = hw_its;
> +    dev->guest_doorbell = guest_doorbell;
> +    dev->guest_devid = guest_devid;
> +    dev->host_devid = host_devid;
> +    dev->eventids = nr_events;
> +
> +    rb_link_node(&dev->rbnode, parent, new);
> +    rb_insert_color(&dev->rbnode, &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 < nr_events / LPI_BLOCK; i++ )
> +    {
> +        ret = gicv3_allocate_host_lpi_block(d, &dev->host_lpi_blocks[i]);
> +        if ( ret < 0 )
> +            break;
> +
> +        ret = gicv3_its_map_host_events(hw_its, host_devid, i * LPI_BLOCK,
> +                                        dev->host_lpi_blocks[i], LPI_BLOCK);
> +        if ( ret < 0 )
> +            break;
> +    }
> +
> +    if ( ret )
> +    {
> +        do {
> +            i--;
> +            gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
> +            if ( i == 0 )
> +                break;
> +        } while (1);
> +
> +        /* Unmapping the device will discard all LPIs mapped so far. */
> +        its_send_cmd_mapd(hw_its, host_devid, 1, 0, false);
> +
> +        goto out;
> +    }
> +
> +    return 0;
> +
> +out_unlock:
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +out:
> +    if ( dev )
> +    {
> +        xfree(dev->pend_irqs);
> +        xfree(dev->host_lpi_blocks);
> +    }
> +    xfree(itt_addr);
> +    xfree(dev);
> +
> +    return ret;
> +}
> +
>  /* 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-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 94c3c44..9dfda59 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -69,10 +69,13 @@ struct vits_itte
>  
>  void vgic_v3_its_init_domain(struct domain *d)
>  {
> +    spin_lock_init(&d->arch.vgic.its_devices_lock);
> +    d->arch.vgic.its_devices = RB_ROOT;
>  }
>  
>  void vgic_v3_its_free_domain(struct domain *d)
>  {
> +    ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
>  }
>  
>  /*
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 09fe502..503a3cf 100644
> --- a/xen/include/asm-arm/domain.h
> +++ b/xen/include/asm-arm/domain.h
> @@ -10,6 +10,7 @@
>  #include <asm/gic.h>
>  #include <public/hvm/params.h>
>  #include <xen/serial.h>
> +#include <xen/rbtree.h>
>  
>  struct hvm_domain
>  {
> @@ -108,6 +109,8 @@ struct arch_domain
>          } *rdist_regions;
>          int nr_regions;                     /* Number of rdist regions */
>          uint32_t rdist_stride;              /* Re-Distributor stride */
> +        struct rb_root its_devices;         /* Devices mapped to an ITS */
> +        spinlock_t its_devices_lock;        /* Protects the its_devices tree */
>  #endif
>      } vgic;
>  
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 9b0c0ec..3b7c724 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -103,7 +103,10 @@
>  #define GITS_CMD_MOVALL                 0x0e
>  #define GITS_CMD_DISCARD                0x0f
>  
> +#define ITS_DOORBELL_OFFSET             0x10040
> +
>  #include <xen/device_tree.h>
> +#include <xen/rbtree.h>
>  
>  #define HOST_ITS_FLUSH_CMD_QUEUE        (1U << 0)
>  #define HOST_ITS_USES_PTA               (1U << 1)
> @@ -153,6 +156,16 @@ int gicv3_its_setup_collection(unsigned int cpu);
>  void vgic_v3_its_init_domain(struct domain *d);
>  void vgic_v3_its_free_domain(struct domain *d);
>  
> +/*
> + * Map a device on the host by allocating an ITT on the host (ITS).
> + * "nr_event" specifies how many events (interrupts) this device will need.
> + * Setting "valid" to false deallocates the device.
> + */
> +int gicv3_its_map_guest_device(struct domain *d,
> +                               paddr_t host_doorbell, uint32_t host_devid,
> +                               paddr_t guest_doorbell, uint32_t guest_devid,
> +                               uint32_t nr_events, bool valid);
> +
>  int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
>  void gicv3_free_host_lpi_block(uint32_t first_lpi);
>  
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-05 23:19 ` [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests Andre Przywara
@ 2017-04-05 23:45   ` Stefano Stabellini
  2017-04-06 17:42     ` Andre Przywara
  2017-04-06 18:10   ` Julien Grall
  1 sibling, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:45 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> Upon receiving an LPI on the host, 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.
> Also we enhance struct pending_irq to cache the pending bit and the
> priority information for LPIs, as we can't afford to walk the tables in
> guest memory every time we handle an incoming LPI.
> This introduces a do_LPI() as a hardware gic_ops and a function to
> retrieve the (cached) priority value of an LPI and a vgic_ops.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-v2.c            |  7 +++++
>  xen/arch/arm/gic-v3-lpi.c        | 56 ++++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c            |  1 +
>  xen/arch/arm/gic.c               |  8 +++++-
>  xen/arch/arm/vgic-v2.c           |  7 +++++
>  xen/arch/arm/vgic-v3.c           | 12 +++++++++
>  xen/arch/arm/vgic.c              |  7 ++++-
>  xen/include/asm-arm/gic.h        |  2 ++
>  xen/include/asm-arm/gic_v3_its.h |  8 ++++++
>  xen/include/asm-arm/vgic.h       |  3 +++
>  10 files changed, 109 insertions(+), 2 deletions(-)
> 
> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
> index 270a136..f4d7949 100644
> --- a/xen/arch/arm/gic-v2.c
> +++ b/xen/arch/arm/gic-v2.c
> @@ -1217,6 +1217,12 @@ static int __init gicv2_init(void)
>      return 0;
>  }
>  
> +void gicv2_do_LPI(unsigned int lpi)
> +{
> +    /* No LPIs in a GICv2 */
> +    BUG();
> +}
> +
>  const static struct gic_hw_operations gicv2_ops = {
>      .info                = &gicv2_info,
>      .init                = gicv2_init,
> @@ -1244,6 +1250,7 @@ const static struct gic_hw_operations gicv2_ops = {
>      .make_hwdom_madt     = gicv2_make_hwdom_madt,
>      .map_hwdom_extra_mappings = gicv2_map_hwdown_extra_mappings,
>      .iomem_deny_access   = gicv2_iomem_deny_access,
> +    .do_LPI              = gicv2_do_LPI,
>  };
>  
>  /* Set up the GIC */
> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 0785701..d8baebc 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -136,6 +136,62 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
>          return per_cpu(lpi_redist, cpu).redist_id << 16;
>  }
>  
> +/*
> + * 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.
> + * Please note that LPIs are edge-triggered only, also have no active state,
> + * so spurious interrupts on the host side are no issue (we can just ignore
> + * them).
> + * Also a guest cannot expect that firing interrupts that haven't been
> + * fully configured yet will reach the CPU, so we don't need to care about
> + * this special case.
> + */
> +void gicv3_do_LPI(unsigned int lpi)
> +{
> +    struct domain *d;
> +    union host_lpi *hlpip, hlpi;
> +    struct vcpu *vcpu;
> +
> +    /* EOI the LPI already. */
> +    WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
> +
> +    /* Find out if a guest mapped something to this physical LPI. */
> +    hlpip = gic_get_host_lpi(lpi);
> +    if ( !hlpip )
> +        return;
> +
> +    hlpi.data = read_u64_atomic(&hlpip->data);
> +
> +    /*
> +     * Unmapped events are marked with an invalid LPI ID. We can safely
> +     * ignore them, as they have no further state and no-one can expect
> +     * to see them if they have not been mapped.
> +     */
> +    if ( hlpi.virt_lpi == INVALID_LPI )
> +        return;
> +
> +    d = rcu_lock_domain_by_id(hlpi.dom_id);
> +    if ( !d )
> +        return;
> +
> +    /* Make sure we don't step beyond the vcpu array. */
> +    if ( hlpi.vcpu_id >= d->max_vcpus )
> +    {
> +        rcu_unlock_domain(d);
> +        return;
> +    }
> +
> +    vcpu = d->vcpu[hlpi.vcpu_id];
> +
> +    /* Check if the VCPU is ready to receive LPIs. */
> +    if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
> +        vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
> +
> +    rcu_unlock_domain(d);
> +}
> +
>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>  {
>      uint64_t val;
> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
> index a559e5e..63dbc21 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -1670,6 +1670,7 @@ static const struct gic_hw_operations gicv3_ops = {
>      .make_hwdom_dt_node  = gicv3_make_hwdom_dt_node,
>      .make_hwdom_madt     = gicv3_make_hwdom_madt,
>      .iomem_deny_access   = gicv3_iomem_deny_access,
> +    .do_LPI              = gicv3_do_LPI,
>  };
>  
>  static int __init gicv3_dt_preinit(struct dt_device_node *node, const void *data)
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index 9522c6c..a56be34 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -697,7 +697,13 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
>              do_IRQ(regs, irq, is_fiq);
>              local_irq_disable();
>          }
> -        else if (unlikely(irq < 16))
> +        else if ( is_lpi(irq) )
> +        {
> +            local_irq_enable();
> +            gic_hw_ops->do_LPI(irq);
> +            local_irq_disable();
> +        }
> +        else if ( unlikely(irq < 16) )
>          {
>              do_sgi(regs, irq);
>          }
> diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
> index 3cad68f..5f4c5ad 100644
> --- a/xen/arch/arm/vgic-v2.c
> +++ b/xen/arch/arm/vgic-v2.c
> @@ -710,11 +710,18 @@ static struct pending_irq *vgic_v2_lpi_to_pending(struct domain *d,
>      BUG();
>  }
>  
> +static int vgic_v2_lpi_get_priority(struct domain *d, unsigned int vlpi)
> +{
> +    /* Dummy function, no LPIs on a VGICv2. */
> +    BUG();
> +}
> +
>  static const struct vgic_ops vgic_v2_ops = {
>      .vcpu_init   = vgic_v2_vcpu_init,
>      .domain_init = vgic_v2_domain_init,
>      .domain_free = vgic_v2_domain_free,
>      .lpi_to_pending = vgic_v2_lpi_to_pending,
> +    .lpi_get_priority = vgic_v2_lpi_get_priority,
>      .max_vcpus = 8,
>  };
>  
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 5128f13..2a14305 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -1548,12 +1548,24 @@ struct pending_irq *vgic_v3_lpi_to_pending(struct domain *d, unsigned int lpi)
>      return pirq;
>  }
>  
> +/* Retrieve the priority of an LPI from its struct pending_irq. */
> +int vgic_v3_lpi_get_priority(struct domain *d, uint32_t vlpi)
> +{
> +    struct pending_irq *p = vgic_v3_lpi_to_pending(d, vlpi);
> +
> +    if ( !p )
> +        return GIC_PRI_IRQ;
> +
> +    return p->lpi_priority;
> +}
> +
>  static const struct vgic_ops v3_ops = {
>      .vcpu_init   = vgic_v3_vcpu_init,
>      .domain_init = vgic_v3_domain_init,
>      .domain_free = vgic_v3_domain_free,
>      .emulate_reg  = vgic_v3_emulate_reg,
>      .lpi_to_pending = vgic_v3_lpi_to_pending,
> +    .lpi_get_priority = vgic_v3_lpi_get_priority,
>      /*
>       * We use both AFF1 and AFF0 in (v)MPIDR. Thus, the max number of CPU
>       * that can be supported is up to 4096(==256*16) in theory.
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index d704d7c..cd9a2a5 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -226,10 +226,15 @@ 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;
>  
> +    /* LPIs don't have a rank, also store their priority separately. */
> +    if ( is_lpi(virq) )
> +        return v->domain->arch.vgic.handler->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);
> diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
> index 836a103..42963c0 100644
> --- a/xen/include/asm-arm/gic.h
> +++ b/xen/include/asm-arm/gic.h
> @@ -366,6 +366,8 @@ struct gic_hw_operations {
>      int (*map_hwdom_extra_mappings)(struct domain *d);
>      /* Deny access to GIC regions */
>      int (*iomem_deny_access)(const struct domain *d);
> +    /* Handle LPIs, which require special handling */
> +    void (*do_LPI)(unsigned int lpi);
>  };
>  
>  void register_gic_ops(const struct gic_hw_operations *ops);
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 3b7c724..eb71fbd 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -139,6 +139,8 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
>  
>  bool gicv3_its_host_has_its(void);
>  
> +void gicv3_do_LPI(unsigned int lpi);
> +
>  int gicv3_lpi_init_rdist(void __iomem * rdist_base);
>  
>  /* Initialize the host structures for LPIs and the host ITSes. */
> @@ -182,6 +184,12 @@ static inline bool gicv3_its_host_has_its(void)
>      return false;
>  }
>  
> +static inline void gicv3_do_LPI(unsigned int lpi)
> +{
> +    /* We don't enable LPIs without an ITS. */
> +    BUG();
> +}
> +
>  static inline int gicv3_lpi_init_rdist(void __iomem * rdist_base)
>  {
>      return -ENODEV;
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 7c86f5b..08d6294 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -66,12 +66,14 @@ struct pending_irq
>  #define GIC_IRQ_GUEST_VISIBLE  2
>  #define GIC_IRQ_GUEST_ENABLED  3
>  #define GIC_IRQ_GUEST_MIGRATING   4
> +#define GIC_IRQ_GUEST_LPI_PENDING 5     /* Caches the pending bit of an LPI. */

I don't think we need GIC_IRQ_GUEST_LPI_PENDING, you can reuse
GIC_IRQ_GUEST_QUEUED and list_empty(&n->inflight): if you call
vgic_vcpu_inject_irq passing an irq as argument that is not enabled, it
will get GIC_IRQ_GUEST_QUEUED and added to inflight, but not injected.


>      unsigned long status;
>      struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
>      unsigned int irq;
>  #define GIC_INVALID_LR         (uint8_t)~0
>      uint8_t lr;
>      uint8_t priority;
> +    uint8_t lpi_priority;       /* Caches the priority if this is an LPI. */

The commit message says: "we enhance struct pending_irq to cache the
pending bit and the priority information for LPIs, as we can't afford to
walk the tables in guest memory every time we handle an incoming LPI." I
thought it would be direct access, having the vlpi number in our hands?
Why is it a problem?

If there has been a conversation about this that I am missing, please
provide a link, I'll go back and read it.


>      /* inflight is used to append instances of pending_irq to
>       * vgic.inflight_irqs */
>      struct list_head inflight;
> @@ -136,6 +138,7 @@ struct vgic_ops {
>      bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
>      /* lookup the struct pending_irq for a given LPI interrupt */
>      struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int vlpi);
> +    int (*lpi_get_priority)(struct domain *d, uint32_t vlpi);
>      /* Maximum number of vCPU supported */
>      const unsigned int max_vcpus;
>  };
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables
  2017-04-05 23:19 ` [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
@ 2017-04-05 23:55   ` Stefano Stabellini
  2017-04-06 11:21     ` Julien Grall
  2017-04-06 11:25     ` Andre Przywara
  2017-04-06 20:33   ` Julien Grall
  1 sibling, 2 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:55 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, 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.
> This introduces a function to read and write from and to guest memory,
> to be later able to access the tables located there.
> This vgic_access_guest_memory() function has been written by Vijaya Kumar
> as part of an earlier series.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3.c       | 152 ++++++++++++++++++++++++++++++++++++++-----
>  xen/arch/arm/vgic.c          |  39 +++++++++++
>  xen/include/asm-arm/domain.h |   6 +-
>  xen/include/asm-arm/vgic.h   |   3 +
>  4 files changed, 182 insertions(+), 18 deletions(-)
> 
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 2a14305..0623803 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -19,12 +19,14 @@
>   */
>  
>  #include <xen/bitops.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>
> @@ -228,12 +230,21 @@ 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;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +        *r = vgic_reg64_extract(v->domain->arch.vgic.rdist_propbase, info);
> +        spin_unlock(&v->arch.vgic.lock);
> +        return 1;
>  
>      case VREG64(GICR_PENDBASER):
> -        /* LPI's not implemented */
> -        goto read_as_zero_64;
> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +        *r = vgic_reg64_extract(v->arch.vgic.rdist_pendbase, info);
> +        *r &= ~GICR_PENDBASER_PTZ;       /* WO, reads as 0 */
> +        spin_unlock(&v->arch.vgic.lock);
> +        return 1;
>  
>      case 0x0080:
>          goto read_reserved;
> @@ -301,11 +312,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;
> @@ -330,11 +336,95 @@ 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 &= ~GICR_PROPBASER_RES0_MASK;
> +
> +    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 &= ~GICR_PENDBASER_RES0_MASK;
> +
> +    return reg;
> +}
> +
>  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 )
>      {
> @@ -365,36 +455,64 @@ static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>          goto write_impl_defined;
>  
>      case VREG64(GICR_SETLPIR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>  
>      case VREG64(GICR_CLRLPIR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>  
>      case 0x0050:
>          goto write_reserved;
>  
>      case VREG64(GICR_PROPBASER):
> -        /* LPI is not implemented */
> -        goto write_ignore_64;
> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +
> +        /* Writing PROPBASER with LPIs enabled is UNPREDICTABLE. */
> +        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
> +        {
> +            reg = v->domain->arch.vgic.rdist_propbase;
> +            vgic_reg64_update(&reg, r, info);
> +            reg = sanitize_propbaser(reg);
> +            v->domain->arch.vgic.rdist_propbase = reg;
> +        }
> +
> +        spin_unlock(&v->arch.vgic.lock);
> +
> +        return 1;
>  
>      case VREG64(GICR_PENDBASER):
> -        /* LPI is not implemented */
> -        goto write_ignore_64;
> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +
> +        /* Writing PENDBASER with LPIs enabled is UNPREDICTABLE. */
> +        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
> +        {
> +            reg = v->arch.vgic.rdist_pendbase;
> +            vgic_reg64_update(&reg, r, info);
> +            reg = sanitize_pendbaser(reg);
> +            v->arch.vgic.rdist_pendbase = reg;
> +        }
> +
> +        spin_unlock(&v->arch.vgic.lock);
> +
> +        return 1;
>  
>      case 0x0080:
>          goto write_reserved;
>  
>      case VREG64(GICR_INVLPIR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>  
>      case 0x00A8:
>          goto write_reserved;
>  
>      case VREG64(GICR_INVALLR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>  
>      case 0x00B8:
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index cd9a2a5..9b0dc3d 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -20,6 +20,7 @@
>  #include <xen/bitops.h>
>  #include <xen/lib.h>
>  #include <xen/init.h>
> +#include <xen/domain_page.h>
>  #include <xen/softirq.h>
>  #include <xen/irq.h>
>  #include <xen/sched.h>
> @@ -589,6 +590,44 @@ void vgic_free_virq(struct domain *d, unsigned int virq)
>      clear_bit(virq, d->arch.vgic.allocated_irqs);
>  }
>  
> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
> +                             uint32_t size, bool_t is_write)

Because there are no callers of this function, I think it breaks the
build.


> +{
> +    struct page_info *page;
> +    uint64_t offset;
> +    p2m_type_t p2mt;
> +    void *p;
> +
> +    page = get_page_from_gfn(d, paddr_to_pfn(gpa), &p2mt, P2M_ALLOC);
> +    if ( !page )
> +    {
> +        printk(XENLOG_G_ERR "d%d: vITS: Failed to get table entry\n",
> +               d->domain_id);
> +        return -EINVAL;
> +    }
> +
> +    if ( !p2m_is_ram(p2mt) )
> +    {
> +        put_page(page);
> +        printk(XENLOG_G_ERR "d%d: vITS: with wrong attributes\n", d->domain_id);
> +        return -EINVAL;
> +    }
> +
> +    p = __map_domain_page(page);
> +    /* Offset within the mapped page */
> +    offset = gpa & ~PAGE_MASK;
> +
> +    if ( is_write )
> +        memcpy(p + offset, addr, size);
> +    else
> +        memcpy(addr, p + offset, size);
> +
> +    unmap_domain_page(p);
> +    put_page(page);
> +
> +    return 0;
> +}
> +
>  /*
>   * Local variables:
>   * mode: C
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 6ee7538..f993292 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 */
> +        unsigned long long int nr_lpis;
> +        uint64_t rdist_propbase;
>          struct rb_root its_devices;         /* Devices mapped to an ITS */
>          spinlock_t its_devices_lock;        /* Protects the its_devices tree */
>          struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
> @@ -256,7 +258,9 @@ 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 */
> +        uint64_t rdist_pendbase;
> +#define VGIC_V3_RDIST_LAST      (1 << 0)        /* last vCPU of the rdist */
> +#define VGIC_V3_LPIS_ENABLED    (1 << 1)
>          uint8_t flags;
>      } vgic;
>  
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 08d6294..2371960 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -314,6 +314,9 @@ 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);
>  
> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
> +                             uint32_t size, bool_t is_write);
> +
>  extern int domain_vgic_register(struct domain *d, int *mmio_count);
>  extern int vcpu_vgic_free(struct vcpu *v);
>  extern bool vgic_to_sgi(struct vcpu *v, register_t sgir,
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs
  2017-04-05 23:19 ` [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs Andre Przywara
@ 2017-04-05 23:58   ` Stefano Stabellini
  2017-04-06 19:09     ` Andre Przywara
  2017-04-06 21:20   ` Julien Grall
  1 sibling, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-05 23:58 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, 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. We do this by storing the pending bit in struct
> pending_irq, which is assiociated with mapped LPIs.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-v3-lpi.c | 26 +++++++++++++++++++++++++-
>  1 file changed, 25 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index d8baebc..7d20986 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -136,6 +136,22 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
>          return per_cpu(lpi_redist, cpu).redist_id << 16;
>  }
>  
> +static bool vgic_can_inject_lpi(struct vcpu *vcpu, uint32_t vlpi)
> +{
> +    struct pending_irq *p;
> +
> +    p = vcpu->domain->arch.vgic.handler->lpi_to_pending(vcpu->domain, vlpi);
> +    if ( !p )
> +        return false;
> +
> +    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
> +        return true;
> +
> +    set_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);

See alpine.DEB.2.10.1701051422020.2866@sstabellini-ThinkPad-X260


> +    return false;
> +}
> +
>  /*
>   * Handle incoming LPIs, which are a bit special, because they are potentially
>   * numerous and also only get injected into guests. Treat them specially here,
> @@ -187,7 +203,15 @@ void gicv3_do_LPI(unsigned int lpi)
>  
>      /* Check if the VCPU is ready to receive LPIs. */
>      if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
> -        vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
> +    {
> +        /*
> +         * 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) )
> +            vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
> +    }
>  
>      rcu_unlock_domain(d);
>  }
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-05 23:19 ` [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation Andre Przywara
@ 2017-04-06  0:21   ` Stefano Stabellini
  2017-04-06  8:55     ` Andre Przywara
  2017-04-06 21:43   ` Julien Grall
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  0:21 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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/vgic-v3-its.c        | 416 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3.c            |   9 -
>  xen/include/asm-arm/gic_v3_defs.h |   9 +
>  xen/include/asm-arm/gic_v3_its.h  |   3 +
>  4 files changed, 428 insertions(+), 9 deletions(-)
> 
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 9dfda59..f6bf1ee 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -78,6 +78,422 @@ void vgic_v3_its_free_domain(struct domain *d)
>      ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
>  }
>  
> +/**************************************
> + * Functions that handle ITS commands *
> + **************************************/
> +
> +static uint64_t its_cmd_mask_field(uint64_t *its_cmd, unsigned int word,
> +                                   unsigned int shift, unsigned 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_get_ittaddr(cmd)        (its_cmd_mask_field(cmd, 2, 8, 44) << 8)
> +
> +#define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
> +
> +/*
> + * Requires the vcmd_lock to be held.
> + * TODO: Investigate whether we can be smarter here and don't need to hold
> + * the lock all of the time.
> + */
> +static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
> +{
> +    paddr_t addr = its->cbaser & GENMASK_ULL(51, 12);
> +    uint64_t command[4];
> +
> +    ASSERT(spin_is_locked(&its->vcmd_lock));
> +
> +    if ( its->cwriter >= ITS_CMD_BUFFER_SIZE(its->cbaser) )
> +        return -1;
> +
> +    while ( its->creadr != its->cwriter )
> +    {
> +        int ret;
> +
> +        ret = vgic_access_guest_memory(d, addr + its->creadr,
> +                                       command, sizeof(command), false);
> +        if ( ret )
> +            return ret;
> +
> +        switch ( its_cmd_get_command(command) )
> +        {
> +        case GITS_CMD_SYNC:
> +            /* We handle ITS commands synchronously, so we ignore SYNC. */
> +            break;
> +        default:
> +            gdprintk(XENLOG_WARNING, "ITS: unhandled ITS command %lu\n",
> +                     its_cmd_get_command(command));
> +            break;
> +        }
> +
> +        its->creadr += ITS_CMD_SIZE;
> +        if ( its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser) )
> +            its->creadr = 0;
> +
> +        if ( ret )
> +            gdprintk(XENLOG_WARNING,
> +                     "ITS: ITS command error %d while handling command %lu\n",
> +                     ret, its_cmd_get_command(command));
> +    }
> +
> +    return 0;
> +}
> +
> +/*****************************
> + * ITS registers read access *
> + *****************************/
> +
> +static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
> +                                 register_t *r, void *priv)
> +{
> +    struct virt_its *its = priv;
> +    uint64_t reg;
> +
> +    switch ( info->gpa & 0xffff )
> +    {
> +    case VREG32(GITS_CTLR):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);
> +        spin_lock(&its->its_lock);
> +        if ( its->enabled )
> +            reg = GITS_CTLR_ENABLE;
> +        else
> +            reg = 0;
> +
> +        if ( its->cwriter == its->creadr )
> +            reg |= GITS_CTLR_QUIESCENT;
> +        spin_unlock(&its->its_lock);
> +        spin_unlock(&its->vcmd_lock);
> +
> +        *r = vgic_reg32_extract(reg, 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 ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        reg = GITS_TYPER_PHYSICAL;
> +        reg |= (sizeof(struct vits_itte) - 1) << GITS_TYPER_ITT_SIZE_SHIFT;
> +        reg |= (its->intid_bits - 1) << GITS_TYPER_IDBITS_SHIFT;
> +        reg |= (its->devid_bits - 1) << GITS_TYPER_DEVIDS_SHIFT;
> +        *r = vgic_reg64_extract(reg, info);
> +        break;
> +    case VREG64(GITS_CBASER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->its_lock);
> +        *r = vgic_reg64_extract(its->cbaser, info);

From the comment at the top of this file, it looks like cbaser is
supposed to be protected by the vcmd_lock:

    spinlock_t vcmd_lock;       /* Protects the virtual command buffer, which */
    uint64_t cwriter;           /* consists of CBASER and CWRITER and those   */
    uint64_t creadr;            /* shadow variables cwriter and creadr. */


> +        spin_unlock(&its->its_lock);
> +        break;
> +    case VREG64(GITS_CWRITER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->vcmd_lock);
> +        *r = vgic_reg64_extract(its->cwriter, info);
> +        spin_unlock(&its->vcmd_lock);
> +        break;
> +    case VREG64(GITS_CREADR):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->vcmd_lock);
> +        *r = vgic_reg64_extract(its->creadr, info);
> +        spin_unlock(&its->vcmd_lock);
> +        break;
> +    case VREG64(GITS_BASER0):           /* device table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->its_lock);
> +        *r = vgic_reg64_extract(its->baser_dev, info);
> +        spin_unlock(&its->its_lock);
> +        break;
> +    case VREG64(GITS_BASER1):           /* collection table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->its_lock);
> +        *r = vgic_reg64_extract(its->baser_coll, info);
> +        spin_unlock(&its->its_lock);
> +        break;
> +    case VRANGE64(GITS_BASER2, GITS_BASER7):
> +        goto read_as_zero_64;
> +
> +    case VREG32(GITS_PIDR2):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +        *r = vgic_reg32_extract(GIC_PIDR2_ARCH_GICv3, info);
> +        break;
> +    }
> +
> +    return 1;
> +
> +read_as_zero_64:
> +    if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +    *r = 0;
> +
> +    return 1;
> +
> +bad_width:
> +    printk(XENLOG_G_ERR "vGIIS: bad read width %d r%d offset %#08lx\n",
> +           info->dabt.size, info->dabt.reg, (unsigned long)info->gpa & 0xffff);
> +    domain_crash_synchronous();
> +
> +    return 0;
> +}
> +
> +/******************************
> + * ITS registers write access *
> + ******************************/
> +
> +static unsigned int its_baser_table_size(uint64_t baser)
> +{
> +    unsigned int ret, page_size[4] = {SZ_4K, SZ_16K, SZ_64K, SZ_64K};
> +
> +    ret = page_size[(baser >> GITS_BASER_PAGE_SIZE_SHIFT) & 3];
> +
> +    return ret * ((baser & GITS_BASER_SIZE_MASK) + 1);
> +}
> +
> +static int its_baser_nr_entries(uint64_t baser)
> +{
> +    int entry_size = GITS_BASER_ENTRY_SIZE(baser);
> +
> +    return its_baser_table_size(baser) / entry_size;
> +}
> +
> +/* Must be called with the ITS lock held. */
> +static bool vgic_v3_verify_its_status(struct virt_its *its, bool status)
> +{
> +    ASSERT(spin_is_locked(&its->its_lock));
> +
> +    if ( !status )
> +        return false;
> +
> +    if ( !(its->cbaser & GITS_VALID_BIT) ||
> +         !(its->baser_dev & GITS_VALID_BIT) ||
> +         !(its->baser_coll & GITS_VALID_BIT) )
> +        return false;
> +
> +    return true;
> +}
> +
> +static void sanitize_its_base_reg(uint64_t *reg)
> +{
> +    uint64_t r = *reg;
> +
> +    /* Avoid outer shareable. */
> +    switch ( (r >> GITS_BASER_SHAREABILITY_SHIFT) & 0x03 )
> +    {
> +    case GIC_BASER_OuterShareable:
> +        r = r & ~GITS_BASER_SHAREABILITY_MASK;
> +        r |= GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    /* Avoid any inner non-cacheable mapping. */
> +    switch ( (r >> GITS_BASER_INNER_CACHEABILITY_SHIFT) & 0x07 )
> +    {
> +    case GIC_BASER_CACHE_nCnB:
> +    case GIC_BASER_CACHE_nC:
> +        r = r & ~GITS_BASER_INNER_CACHEABILITY_MASK;
> +        r |= GIC_BASER_CACHE_RaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    /* Only allow non-cacheable or same-as-inner. */
> +    switch ( (r >> GITS_BASER_OUTER_CACHEABILITY_SHIFT) & 0x07 )
> +    {
> +    case GIC_BASER_CACHE_SameAsInner:
> +    case GIC_BASER_CACHE_nC:
> +        break;
> +    default:
> +        r = r & ~GITS_BASER_OUTER_CACHEABILITY_MASK;
> +        r |= GIC_BASER_CACHE_nC << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
> +        break;
> +    }
> +
> +    *reg = r;
> +}
> +
> +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 reg32, ctlr;
> +
> +    switch ( info->gpa & 0xffff )
> +    {
> +    case VREG32(GITS_CTLR):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);
> +        spin_lock(&its->its_lock);
> +        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
> +        reg32 = ctlr;
> +        vgic_reg32_update(&reg32, r, info);
> +
> +        if ( ctlr ^ reg32 )
> +            its->enabled = vgic_v3_verify_its_status(its,
> +                                                     reg32 & GITS_CTLR_ENABLE);
> +        spin_unlock(&its->its_lock);
> +        spin_unlock(&its->vcmd_lock);
> +        return 1;
> +
> +    case VREG32(GITS_IIDR):
> +        goto write_ignore_32;
> +    case VREG32(GITS_TYPER):
> +        goto write_ignore_32;
> +    case VREG64(GITS_CBASER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);
> +        spin_lock(&its->its_lock);
> +        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            spin_unlock(&its->vcmd_lock);
> +            gdprintk(XENLOG_WARNING,
> +                     "ITS: tried to change CBASER with the ITS enabled.\n");
> +            return 1;
> +        }
> +
> +        reg = its->cbaser;
> +        vgic_reg64_update(&reg, r, info);
> +        sanitize_its_base_reg(&reg);
> +
> +        its->cbaser = reg;
> +        its->creadr = 0;
> +        spin_unlock(&its->its_lock);
> +        spin_unlock(&its->vcmd_lock);
> +
> +        return 1;
> +
> +    case VREG64(GITS_CWRITER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);
> +        reg = its->cwriter & 0xfffe0;
> +        vgic_reg64_update(&reg, r, info);
> +        its->cwriter = reg & 0xfffe0;
> +
> +        if ( its->enabled )
> +        {
> +            int ret = vgic_its_handle_cmds(d, its);
> +
> +            if ( ret )
> +                printk(XENLOG_G_WARNING "error handling ITS commands\n");
> +        }
> +        spin_unlock(&its->vcmd_lock);
> +
> +        return 1;
> +
> +    case VREG64(GITS_CREADR):
> +        goto write_ignore_64;
> +    case VREG64(GITS_BASER0):           /* device table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_WARNING, "ITS: tried to change BASER with the ITS enabled.\n");
> +
> +            return 1;
> +        }
> +
> +        reg = its->baser_dev;
> +        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;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_devices = its_baser_nr_entries(reg);
> +        else
> +            its->max_devices = 0;
> +
> +        its->baser_dev = reg;
> +        spin_unlock(&its->its_lock);
> +        return 1;
> +    case VREG64(GITS_BASER1):           /* collection table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_INFO, "ITS: tried to change BASER with the ITS enabled.\n");
> +            return 1;
> +        }
> +
> +        reg = its->baser_coll;
> +        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;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_collections = its_baser_nr_entries(reg);
> +        else
> +            its->max_collections = 0;
> +        its->baser_coll = reg;
> +        spin_unlock(&its->its_lock);
> +        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 "vGITS: bad write width %d r%d offset %#08lx\n",
> +           info->dabt.size, info->dabt.reg, (unsigned long)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
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 0623803..e6a33d0 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -158,15 +158,6 @@ static void vgic_store_irouter(struct domain *d, struct vgic_irq_rank *rank,
>      write_atomic(&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 b7e5a47..e60dea5 100644
> --- a/xen/include/asm-arm/gic_v3_defs.h
> +++ b/xen/include/asm-arm/gic_v3_defs.h
> @@ -198,6 +198,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__ */
>  
>  /*
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index eb71fbd..d3f393f 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -35,6 +35,7 @@
>  #define GITS_BASER5                     0x128
>  #define GITS_BASER6                     0x130
>  #define GITS_BASER7                     0x138
> +#define GITS_PIDR2                      GICR_PIDR2
>  
>  /* Register bits */
>  #define GITS_VALID_BIT                  BIT_ULL(63)
> @@ -57,6 +58,7 @@
>  #define GITS_TYPER_ITT_SIZE_MASK        (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
>  #define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
>                                                   GITS_TYPER_ITT_SIZE_SHIFT) + 1)
> +#define GITS_TYPER_PHYSICAL             (1U << 0)
>  
>  #define GITS_IIDR_VALUE                 0x34c
>  
> @@ -78,6 +80,7 @@
>                          (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
>  #define GITS_BASER_SHAREABILITY_SHIFT   10
>  #define GITS_BASER_PAGE_SIZE_SHIFT      8
> +#define GITS_BASER_SIZE_MASK            0xff
>  #define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
>                                          (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
>                                          GITS_BASER_INDIRECT)
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 19/30] ARM: vITS: handle CLEAR command
  2017-04-05 23:19 ` [PATCH v5 19/30] ARM: vITS: handle CLEAR command Andre Przywara
@ 2017-04-06  0:31   ` Stefano Stabellini
  0 siblings, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  0:31 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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.
> As read_itte() is now eventually used, we add the static keyword.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3-its.c | 49 ++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 47 insertions(+), 2 deletions(-)
> 
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index a145666..71bc08a 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -191,8 +191,8 @@ static bool read_itte_locked(struct virt_its *its, uint32_t devid,
>   * This function takes care of the locking by taking the its_lock itself, so
>   * a caller shall not hold this. Before returning, the lock is dropped again.
>   */
> -bool read_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> -               struct vcpu **vcpu_ptr, uint32_t *vlpi_ptr)
> +static bool read_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> +                      struct vcpu **vcpu_ptr, uint32_t *vlpi_ptr)
>  {
>      bool ret;
>  
> @@ -277,6 +277,48 @@ static uint64_t its_cmd_mask_field(uint64_t *its_cmd, unsigned int word,
>  #define its_cmd_get_validbit(cmd)       its_cmd_mask_field(cmd, 2, 63,  1)
>  #define its_cmd_get_ittaddr(cmd)        (its_cmd_mask_field(cmd, 2, 8, 44) << 8)
>  
> +/*
> + * CLEAR removes the pending state from an LPI. */
> +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 *p;
> +    struct vcpu *vcpu;
> +    uint32_t vlpi;
> +    unsigned long flags;
> +
> +    /* Translate the DevID/EvID pair into a vCPU/vLPI pair. */
> +    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
> +        return -1;
> +
> +    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
> +    if ( !p )
> +        return -1;
> +
> +    spin_lock_irqsave(&vcpu->arch.vgic.lock, flags);
> +
> +    /* We store the pending bit for LPIs in our struct pending_irq. */
> +    clear_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);
> +
> +    /*
> +     * If the LPI is already visible on the guest, it is too late to
> +     * clear the pending state. However this is a benign race that can
> +     * happen on real hardware, too: If the LPI has already been forwarded
> +     * to a CPU interface, a CLEAR request reaching the redistributor has
> +     * no effect on that LPI anymore. Since LPIs are edge triggered and
> +     * have no active state, we don't need to care about this here.
> +     */
> +    if ( !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
> +    {
> +        /* Remove a pending, but not yet injected guest IRQ. */
> +        clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
> +        gic_remove_from_queues(vcpu, vlpi);

I think you also need to remove it from &p->inflight

    list_del_init(&p->inflight);


> +    }
> +
> +    return 0;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>  
>  /*
> @@ -305,6 +347,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>  
>          switch ( its_cmd_get_command(command) )
>          {
> +        case GITS_CMD_CLEAR:
> +            ret = its_handle_clear(its, command);
> +            break;
>          case GITS_CMD_SYNC:
>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>              break;
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 22/30] ARM: vITS: handle MAPD command
  2017-04-05 23:19 ` [PATCH v5 22/30] ARM: vITS: handle MAPD command Andre Przywara
@ 2017-04-06  0:39   ` Stefano Stabellini
  2017-04-07 12:41   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  0:39 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> The MAPD command maps a device by associating a memory region for
> storing ITEs with a certain device ID.
> We store the given guest physical address in the device table, and, if
> this command comes from Dom0, tell the host ITS driver about this new
> mapping, so it can issue the corresponding host MAPD command and create
> the required tables.
> We simply use our existing guest memory access function to find the
> right ITT entry and store the mapping there.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3-its.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 59 insertions(+)
> 
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index bbca5cf..0372ed0 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -42,6 +42,7 @@
>   */
>  struct virt_its {
>      struct domain *d;
> +    paddr_t doorbell_address;
>      unsigned int devid_bits;
>      unsigned int intid_bits;
>      spinlock_t vcmd_lock;       /* Protects the virtual command buffer, which */
> @@ -144,6 +145,20 @@ static struct vcpu *get_vcpu_from_collection(struct virt_its *its,
>  #define DEV_TABLE_ENTRY(addr, bits)                     \
>          (((addr) & GENMASK_ULL(51, 8)) | (((bits) - 1) & GENMASK_ULL(7, 0)))
>  
> +/* Set the address of an ITT for a given device ID. */
> +static int its_set_itt_address(struct virt_its *its, uint32_t devid,
> +                               paddr_t itt_address, uint32_t nr_bits)
> +{
> +    paddr_t addr = get_baser_phys_addr(its->baser_dev);
> +    uint64_t itt_entry = DEV_TABLE_ENTRY(itt_address, nr_bits);
> +
> +    if ( devid >= its->max_devices )
> +        return -ENOENT;
> +
> +    return vgic_access_guest_memory(its->d, addr + devid * sizeof(uint64_t),
> +                                    &itt_entry, sizeof(itt_entry), true);
> +}
> +
>  /*
>   * Lookup the address of the Interrupt Translation Table associated with
>   * a device ID and return the address of the ITTE belonging to the event ID
> @@ -384,6 +399,47 @@ static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
>      return 0;
>  }
>  
> +static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
> +{
> +    /* size and devid get validated by the functions called below. */
> +    uint32_t devid = its_cmd_get_deviceid(cmdptr);
> +    unsigned int size = its_cmd_get_size(cmdptr) + 1;
> +    bool valid = its_cmd_get_validbit(cmdptr);
> +    paddr_t itt_addr = its_cmd_get_ittaddr(cmdptr);
> +    int ret;

The size should be sanitized against the number of event support by the
vITS


> +    /*
> +     * There is no easy and clean way for Xen to know the ITS device ID of a
> +     * particular (PCI) device, so we have to rely on the guest telling
> +     * us about it. For *now* we are just using the device ID *Dom0* uses,
> +     * because the driver there has the actual knowledge.
> +     * Eventually this will be replaced with a dedicated hypercall to
> +     * announce pass-through of devices.
> +     */
> +    if ( is_hardware_domain(its->d) )
> +    {
> +        /*
> +         * Dom0's ITSes are mapped 1:1, so both addresses are the same.
> +         * Also the device IDs are equal.
> +         */
> +        ret = gicv3_its_map_guest_device(its->d, its->doorbell_address, devid,
> +                                         its->doorbell_address, devid,
> +                                         BIT(size), valid);
> +        if ( ret )
> +            return ret;
> +    }
> +
> +    spin_lock(&its->its_lock);
> +    if ( valid )
> +        ret = its_set_itt_address(its, devid, itt_addr, size);
> +    else
> +        ret = its_set_itt_address(its, devid, INVALID_PADDR, 1);
> +
> +    spin_unlock(&its->its_lock);
> +
> +    return ret;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>  
>  /*
> @@ -421,6 +477,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_MAPC:
>              ret = its_handle_mapc(its, command);
>              break;
> +        case GITS_CMD_MAPD:
> +            ret = its_handle_mapd(its, command);
> +            break;
>          case GITS_CMD_SYNC:
>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>              break;
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 23/30] ARM: vITS: handle MAPTI command
  2017-04-05 23:19 ` [PATCH v5 23/30] ARM: vITS: handle MAPTI command Andre Przywara
@ 2017-04-06  0:45   ` Stefano Stabellini
  2017-04-11 16:15     ` Andre Przywara
  2017-04-07 12:53   ` Julien Grall
  2017-04-07 13:07   ` Julien Grall
  2 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  0:45 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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 LPI on the host can be quickly forwarded to a guest.
> Beside entering the VCPU and the virtual LPI number in the respective
> host LPI entry, we also initialize and add the already allocated
> struct pending_irq to our radix tree, so that we can now easily find it
> by its virtual LPI number.
> To be able to later find the targetting VCPU for any given LPI without
> having to walk *all* ITS tables, we store the VCPU ID in the pending_irq
> struct as well.
> This exports the vgic_init_pending_irq() function to be able to
> initialize a new struct pending_irq.
> As write_itte() is now eventually used, we can now add the static tag.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-v3-its.c        | 74 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3-lpi.c        | 18 ++++++++++
>  xen/arch/arm/vgic-v3-its.c       | 76 ++++++++++++++++++++++++++++++++++++++--
>  xen/arch/arm/vgic.c              |  2 +-
>  xen/include/asm-arm/gic_v3_its.h |  6 ++++
>  xen/include/asm-arm/vgic.h       |  2 ++
>  6 files changed, 175 insertions(+), 3 deletions(-)
> 
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index 76b0316..d970119 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -777,6 +777,80 @@ out:
>      return ret;
>  }
>  
> +/* Must be called with the its_device_lock held. */
> +static struct its_devices *get_its_device(struct domain *d, paddr_t vdoorbell,
> +                                          uint32_t vdevid)
> +{
> +    struct rb_node *node = d->arch.vgic.its_devices.rb_node;
> +    struct its_devices *dev;
> +
> +    ASSERT(spin_is_locked(&d->arch.vgic.its_devices_lock));
> +
> +    while (node)
> +    {
> +        int cmp;
> +
> +        dev = rb_entry(node, struct its_devices, rbnode);
> +        cmp = compare_its_guest_devices(dev, vdoorbell, vdevid);
> +
> +        if ( !cmp )
> +            return dev;
> +
> +        if ( cmp > 0 )
> +            node = node->rb_left;
> +        else
> +            node = node->rb_right;
> +    }
> +
> +    return NULL;
> +}
> +
> +static uint32_t get_host_lpi(struct its_devices *dev, uint32_t eventid)
> +{
> +    uint32_t host_lpi = 0;
> +
> +    if ( dev && (eventid < dev->eventids) )
> +        host_lpi = dev->host_lpi_blocks[eventid / LPI_BLOCK] +
> +                                       (eventid % LPI_BLOCK);
> +
> +    return host_lpi;
> +}
> +
> +/*
> + * 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.
> + * Returns a pointer to the already allocated struct pending_irq that is
> + * meant to be used by that event.
> + */
> +struct pending_irq *gicv3_assign_guest_event(struct domain *d,
> +                                             paddr_t vdoorbell_address,
> +                                             uint32_t vdevid, uint32_t veventid,
> +                                             struct vcpu *v, uint32_t virt_lpi)
> +{
> +    struct its_devices *dev;
> +    struct pending_irq *pirq = NULL;
> +    uint32_t host_lpi = 0;
> +
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    dev = get_its_device(d, vdoorbell_address, vdevid);
> +    if ( dev )
> +    {
> +        host_lpi = get_host_lpi(dev, veventid);
> +        pirq = &dev->pend_irqs[veventid];
> +    }
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +    if ( !host_lpi || !pirq )
> +        return NULL;
> +
> +    gicv3_lpi_update_host_entry(host_lpi, d->domain_id,
> +                                v ? v->vcpu_id : INVALID_VCPU_ID, virt_lpi);
> +
> +    return pirq;
> +}
> +
>  /* 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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 7d20986..c997ed5 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -216,6 +216,24 @@ void gicv3_do_LPI(unsigned int lpi)
>      rcu_unlock_domain(d);
>  }
>  
> +void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
> +                                 unsigned int vcpu_id, uint32_t virt_lpi)
> +{
> +    union host_lpi *hlpip, hlpi;
> +
> +    ASSERT(host_lpi >= LPI_OFFSET);
> +
> +    host_lpi -= LPI_OFFSET;
> +
> +    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
> +
> +    hlpi.virt_lpi = virt_lpi;
> +    hlpi.dom_id = domain_id;
> +    hlpi.vcpu_id = vcpu_id;
> +
> +    write_u64_atomic(&hlpip->data, hlpi.data);
> +}
> +
>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>  {
>      uint64_t val;
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 0372ed0..079dd44 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -275,8 +275,8 @@ static bool write_itte_locked(struct virt_its *its, uint32_t devid,
>   * This function takes care of the locking by taking the its_lock itself, so
>   * a caller shall not hold this. Before returning, the lock is dropped again.
>   */
> -bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> -                uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
> +static bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> +                       uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
>  {
>      bool ret;
>  
> @@ -440,6 +440,74 @@ static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
>      return ret;
>  }
>  
> +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), _intid;
> +    uint16_t collid = its_cmd_get_collection(cmdptr);
> +    struct pending_irq *pirq;
> +    struct vcpu *vcpu = NULL;
> +    int ret = 0;
> +
> +    if ( its_cmd_get_command(cmdptr) == GITS_CMD_MAPI )
> +        intid = eventid;
> +
> +    spin_lock(&its->its_lock);
> +    /*
> +     * Check whether there is a valid existing mapping. If yes, behavior is
> +     * unpredictable, we choose to ignore this command here.
> +     * This makes sure we start with a pristine pending_irq below.
> +     */
> +    if ( read_itte_locked(its, devid, eventid, &vcpu, &_intid) &&
> +         _intid != INVALID_LPI )
> +    {
> +        spin_unlock(&its->its_lock);
> +        return -1;
> +    }
> +
> +    /* Enter the mapping in our virtual ITS tables. */
> +    if ( !write_itte_locked(its, devid, eventid, collid, intid, &vcpu) )
> +    {
> +        spin_unlock(&its->its_lock);
> +        return -1;
> +    }
> +
> +    spin_unlock(&its->its_lock);
> +
> +    /*
> +     * Connect this virtual LPI to the corresponding host LPI, which is
> +     * determined by the same device ID and event ID on the host side.
> +     * This returns us the corresponding, still unused pending_irq.
> +     */
> +    pirq = gicv3_assign_guest_event(its->d, its->doorbell_address,
> +                                    devid, eventid, vcpu, intid);
> +    if ( !pirq )
> +        return -1;
> +
> +    vgic_init_pending_irq(pirq, intid);
> +
> +    /*
> +     * Now read the guest's property table to initialize our cached state.
> +     * It can't fire at this time, because it is not known to the host yet.
> +     */
> +    ret = update_lpi_property(its->d, intid, pirq);
> +    if ( ret )
> +        return ret;
> +
> +    pirq->vcpu_id = vcpu->vcpu_id;
> +
> +    /*
> +     * Now insert the pending_irq into the domain's LPI tree, so that
> +     * it becomes live.
> +     */
> +    write_lock(&its->d->arch.vgic.pend_lpi_tree_lock);
> +    radix_tree_insert(&its->d->arch.vgic.pend_lpi_tree, intid, pirq);
> +    write_unlock(&its->d->arch.vgic.pend_lpi_tree_lock);

It looks like the whole allocation, starting from
gicv3_assign_guest_event, needs to be protected by pend_lpi_tree_lock.
Otherwise we risk allocating the same struct twice? Or that is not
possible thanks to the vcmd_lock (because for two struct pending_irq to
clash they need to belong to the same vits)?


> +    return 0;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>  
>  /*
> @@ -480,6 +548,10 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_MAPD:
>              ret = its_handle_mapd(its, command);
>              break;
> +        case GITS_CMD_MAPI:
> +        case GITS_CMD_MAPTI:
> +            ret = its_handle_mapti(its, command);
> +            break;
>          case GITS_CMD_SYNC:
>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>              break;
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index 9b0dc3d..cb1666b 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -61,7 +61,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);
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index d3f393f..30aa1ef 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -174,6 +174,12 @@ int gicv3_its_map_guest_device(struct domain *d,
>  int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
>  void gicv3_free_host_lpi_block(uint32_t first_lpi);
>  
> +struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
> +                                             uint32_t devid, uint32_t eventid,
> +                                             struct vcpu *v, uint32_t virt_lpi);
> +void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
> +                                 unsigned int vcpu_id, uint32_t virt_lpi);
> +
>  #else
>  
>  static LIST_HEAD(host_its_list);
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 2371960..074afe4 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -83,6 +83,7 @@ struct pending_irq
>       * TODO: when implementing irq migration, taking only the current
>       * vgic lock is not going to be enough. */
>      struct list_head lr_queue;
> +    uint16_t vcpu_id;          /* The VCPU for an LPI. */
>  };
>  
>  #define NR_INTERRUPT_PER_RANK   32
> @@ -303,6 +304,7 @@ 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 vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 24/30] ARM: vITS: handle MOVI command
  2017-04-05 23:19 ` [PATCH v5 24/30] ARM: vITS: handle MOVI command Andre Przywara
@ 2017-04-06  0:47   ` Stefano Stabellini
  2017-04-06 10:07     ` Andre Przywara
  2017-04-07 12:55   ` Julien Grall
  1 sibling, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  0:47 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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-v3-its.c        | 24 ++++++++++++++++++++
>  xen/arch/arm/gic-v3-lpi.c        | 15 +++++++++++++
>  xen/arch/arm/vgic-v3-its.c       | 47 ++++++++++++++++++++++++++++++++++++++++
>  xen/include/asm-arm/gic_v3_its.h |  4 ++++
>  4 files changed, 90 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index d970119..a57e63a 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -851,6 +851,30 @@ struct pending_irq *gicv3_assign_guest_event(struct domain *d,
>      return pirq;
>  }
>  
> +/* Changes the target VCPU for a given host LPI assigned to a domain. */
> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t vdoorbell,
> +                          uint32_t vdevid, uint32_t veventid,
> +                          unsigned int vcpu_id)
> +{
> +    uint32_t host_lpi;
> +    struct its_devices *dev;
> +
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    dev = get_its_device(d, vdoorbell, vdevid);
> +    if ( dev )
> +        host_lpi = get_host_lpi(dev, veventid);
> +    else
> +        host_lpi = 0;
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +    if ( !host_lpi )
> +        return -ENOENT;
> +
> +    gicv3_lpi_update_host_vcpuid(host_lpi, vcpu_id);

we need to call vgic_migrate_irq


> +    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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index c997ed5..b9960aa 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -234,6 +234,21 @@ void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>      write_u64_atomic(&hlpip->data, hlpi.data);
>  }
>  
> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id)
> +{
> +    union host_lpi *hlpip;
> +
> +    ASSERT(host_lpi >= LPI_OFFSET);
> +
> +    host_lpi -= LPI_OFFSET;
> +
> +    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
> +
> +    write_u16_atomic(&hlpip->vcpu_id, vcpu_id);
> +
> +    return 0;
> +}
> +
>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>  {
>      uint64_t val;
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 079dd44..6afb915 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -508,6 +508,47 @@ static int its_handle_mapti(struct virt_its *its, uint64_t *cmdptr)
>      return 0;
>  }
>  
> +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 pending_irq *p;
> +    struct vcpu *vcpu;
> +    uint32_t vlpi;
> +    int ret = -1;
> +
> +    spin_lock(&its->its_lock);
> +    /* Check for a mapped LPI and get the LPI number. */
> +    if ( !read_itte_locked(its, devid, eventid, &vcpu, &vlpi) )
> +        goto out_unlock;
> +
> +    /* Check the new collection ID and get the new VCPU pointer */
> +    vcpu = get_vcpu_from_collection(its, collid);
> +    if ( !vcpu )
> +        goto out_unlock;
> +
> +    /* Update our cached vcpu_id in the pending_irq. */
> +    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
> +    p->vcpu_id = vcpu->vcpu_id;
> +
> +    /* Now store the new collection in the translation table. */
> +    if ( !write_itte_locked(its, devid, eventid, collid, vlpi, &vcpu) )
> +        goto out_unlock;
> +
> +    spin_unlock(&its->its_lock);
> +
> +    /* TODO: lookup currently-in-guest virtual IRQs and migrate them? */
> +
> +    return gicv3_lpi_change_vcpu(its->d, its->doorbell_address,
> +                                 devid, eventid, vcpu->vcpu_id);
> +
> +out_unlock:
> +    spin_unlock(&its->its_lock);
> +
> +    return ret;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>  
>  /*
> @@ -552,6 +593,12 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_MAPTI:
>              ret = its_handle_mapti(its, command);
>              break;
> +        case GITS_CMD_MOVALL:
> +            gdprintk(XENLOG_G_INFO, "ITS: ignoring MOVALL command\n");
> +            break;
> +        case GITS_CMD_MOVI:
> +            ret = its_handle_movi(its, command);
> +            break;
>          case GITS_CMD_SYNC:
>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>              break;
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 30aa1ef..daae143 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -177,8 +177,12 @@ void gicv3_free_host_lpi_block(uint32_t first_lpi);
>  struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
>                                               uint32_t devid, uint32_t eventid,
>                                               struct vcpu *v, uint32_t virt_lpi);
> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t doorbell,
> +                          uint32_t devid, uint32_t eventid,
> +                          unsigned int vcpu_id);
>  void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>                                   unsigned int vcpu_id, uint32_t virt_lpi);
> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id);
>  
>  #else
>  
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 25/30] ARM: vITS: handle DISCARD command
  2017-04-05 23:19 ` [PATCH v5 25/30] ARM: vITS: handle DISCARD command Andre Przywara
@ 2017-04-06  0:48   ` Stefano Stabellini
  2017-04-07 13:03   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  0:48 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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-v3-its.c | 32 ++++++++++++++++++++++++++++++++
>  1 file changed, 32 insertions(+)
> 
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 6afb915..47f2884 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -549,6 +549,35 @@ out_unlock:
>      return ret;
>  }
>  
> +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 vcpu *vcpu;
> +    uint32_t vlpi;
> +
> +    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
> +        return -1;
> +
> +    pirq = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
> +    if ( pirq )
> +    {
> +        clear_bit(GIC_IRQ_GUEST_QUEUED, &pirq->status);
> +        gic_remove_from_queues(vcpu, vlpi);

we need to remove it from inflight


> +    }
> +
> +    if ( !write_itte(its, devid, eventid, UNMAPPED_COLLECTION, INVALID_LPI,
> +                     NULL) )
> +        return -1;
> +
> +    if ( !gicv3_assign_guest_event(its->d, its->doorbell_address,
> +                                   devid, eventid, NULL, 0) )
> +        return -1;
> +
> +    return 0;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>  
>  /*
> @@ -580,6 +609,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_CLEAR:
>              ret = its_handle_clear(its, command);
>              break;
> +        case GITS_CMD_DISCARD:
> +            ret = its_handle_discard(its, command);
> +            break;
>          case GITS_CMD_INT:
>              ret = its_handle_int(its, command);
>              break;
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 26/30] ARM: vITS: handle INV command
  2017-04-05 23:19 ` [PATCH v5 26/30] ARM: vITS: handle INV command Andre Przywara
@ 2017-04-06  0:56   ` Stefano Stabellini
  2017-04-07 13:23   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  0:56 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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/vgic-v3-its.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 96 insertions(+)
> 
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 47f2884..0d4b20d 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -376,6 +376,99 @@ static int its_handle_int(struct virt_its *its, uint64_t *cmdptr)
>      return 0;
>  }
>  
> +/*
> + * For a given virtual LPI read the enabled bit and priority from the virtual
> + * property table and update the virtual IRQ's state in the given pending_irq.
> + */
> +static int update_lpi_property(struct domain *d, uint32_t vlpi,
> +                               struct pending_irq *p)
> +{
> +    paddr_t addr;
> +    uint8_t property;
> +    int ret;
> +
> +    addr = d->arch.vgic.rdist_propbase & GENMASK_ULL(51, 12);
> +
> +    ret = vgic_access_guest_memory(d, addr + vlpi - LPI_OFFSET,
> +                                   &property, sizeof(property), false);
> +    if ( ret )
> +        return ret;
> +
> +    p->lpi_priority = property & LPI_PROP_PRIO_MASK;
> +    if ( property & LPI_PROP_ENABLED )
> +        set_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
> +    else
> +        clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
> +
> +    return 0;
> +}
> +
> +/*
> + * For a given virtual LPI read the enabled bit and priority 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.
> + * Also check if this LPI is due to be injected and do it, if needed.
> + */
> +static int update_lpi_enabled_status(struct domain *d,
> +                                     struct vcpu *vcpu, uint32_t vlpi)
> +{
> +    struct pending_irq *p = d->arch.vgic.handler->lpi_to_pending(d, vlpi);
> +    unsigned long flags;
> +    int ret;
> +
> +    if ( !p )
> +        return -EINVAL;
> +
> +    spin_lock_irqsave(&vcpu->arch.vgic.lock, flags);
> +    ret = update_lpi_property(d, vlpi, p);
> +    if ( ret ) {
> +        spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);
> +        return ret;
> +    }
> +
> +    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
> +    {
> +        if ( !list_empty(&p->inflight) &&
> +             !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
> +            gic_raise_guest_irq(vcpu, vlpi, p->lpi_priority);
> +        spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);

Something like this should work for LPIs too.



> +        /* Check whether the LPI has fired while the guest had it disabled. */
> +        if ( test_and_clear_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status) )
> +            vgic_vcpu_inject_irq(vcpu, vlpi);
> +    }
> +    else
> +    {
> +        clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
> +        spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);
> +
> +        gic_remove_from_queues(vcpu, vlpi);
> +    }
> +
> +    return 0;
> +}
> +
> +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 vcpu *vcpu;
> +    uint32_t vlpi;
> +
> +    /* Translate the event into a vCPU/vLPI pair. */
> +    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
> +        return -1;
> +
> +    /*
> +     * Now read the property table and update our cached status. This
> +     * also takes care if this LPI now needs to be injected or removed.
> +     */
> +    if ( update_lpi_enabled_status(its->d, vcpu, vlpi) )
> +        return -1;
> +
> +    return 0;
> +}
> +
>  static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
>  {
>      uint32_t collid = its_cmd_get_collection(cmdptr);
> @@ -615,6 +708,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_INT:
>              ret = its_handle_int(its, command);
>              break;
> +        case GITS_CMD_INV:
> +            ret = its_handle_inv(its, command);
> +            break;
>          case GITS_CMD_MAPC:
>              ret = its_handle_mapc(its, command);
>              break;
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 30/30] ARM: vGIC: advertise LPI support
  2017-04-05 23:19 ` [PATCH v5 30/30] ARM: vGIC: advertise LPI support Andre Przywara
@ 2017-04-06  1:04   ` Stefano Stabellini
  2017-04-06 10:21     ` Andre Przywara
  2017-04-07 13:43   ` Julien Grall
  1 sibling, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06  1:04 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> 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.
> This removes a "TBD" comment, as we now populate the processor number
> in the GICR_TYPE register.
> Advertise 24 bits worth of LPIs to the guest.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 41 insertions(+), 5 deletions(-)
> 
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 3b01247..ba0e79f 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -168,8 +168,12 @@ 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;
> +        spin_lock(&v->arch.vgic.lock);
> +        *r = vgic_reg32_extract(!!(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED),
> +                                info);
> +        spin_unlock(&v->arch.vgic.lock);
> +        return 1;
>  
>      case VREG32(GICR_IIDR):
>          if ( dabt.size != DABT_WORD ) goto bad_width;
> @@ -181,16 +185,20 @@ 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;
> +        /* We use the VCPU ID as the redistributor ID in bits[23:8] */
> +        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;
> @@ -411,6 +419,17 @@ static uint64_t sanitize_pendbaser(uint64_t reg)
>      return reg;
>  }
>  
> +static void vgic_vcpu_enable_lpis(struct vcpu *v)
> +{
> +    uint64_t reg = v->domain->arch.vgic.rdist_propbase;
> +    unsigned int nr_lpis = BIT((reg & 0x1f) + 1) - LPI_OFFSET;
> +
> +    if ( !v->domain->arch.vgic.nr_lpis )
> +        v->domain->arch.vgic.nr_lpis = nr_lpis;

What if nr_lpis was already set and the nr_lpis has changed?


> +    v->arch.vgic.flags |= VGIC_V3_LPIS_ENABLED;
> +}
> +
>  static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>                                            uint32_t gicr_reg,
>                                            register_t r)
> @@ -421,8 +440,20 @@ 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;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +
> +        /* LPIs can only be enabled once, but never disabled again. */
> +        if ( (r & GICR_CTLR_ENABLE_LPIS) &&
> +             !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
> +            vgic_vcpu_enable_lpis(v);
> +
> +        spin_unlock(&v->arch.vgic.lock);
> +
> +        return 1;
>  
>      case VREG32(GICR_IIDR):
>          /* RO */
> @@ -1032,6 +1063,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 = 24;
> +        }
>          typer |= (irq_bits - 1) << GICD_TYPE_ID_BITS_SHIFT;
>  
>          *r = vgic_reg32_extract(typer, info);
> -- 
> 2.8.2
> 

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

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

* Re: [PATCH v5 01/30] bitops: add GENMASK_ULL
  2017-04-05 23:25   ` Stefano Stabellini
@ 2017-04-06  8:45     ` Julien Grall
  2017-04-06  9:15       ` Jan Beulich
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06  8:45 UTC (permalink / raw)
  To: Stefano Stabellini, Andre Przywara
  Cc: Wei Liu, Vijay Kilari, George Dunlap, Andrew Cooper, Ian Jackson,
	Tim Deegan, Jan Beulich, xen-devel, Shanker Donthineni

(CC 'The REST' maintainers)

Hi,

Andre, as I mentioned already a couple of times. You should CC all the 
appropriate maintainers for the code you are modifying.

On 06/04/17 00:25, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> To safely handle 64-bit registers even on 32-bit systems, introduce
>> a GENMASK_ULL variant (lifted from Linux).
>> This adds a BITS_PER_LONG_LONG define as well.
>> Also fix a bug in the comment for the existing GENMASK variant.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

I'd like some input from Andrew here. I suggested a similar patch a year 
ago (see [1]) and the final decision was to drop GENMASK_ULL.

>
>> ---
>>  xen/include/asm-arm/config.h | 2 ++
>>  xen/include/xen/bitops.h     | 5 ++++-
>>  2 files changed, 6 insertions(+), 1 deletion(-)
>>
>> diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
>> index b2edf95..1f730ce 100644
>> --- a/xen/include/asm-arm/config.h
>> +++ b/xen/include/asm-arm/config.h
>> @@ -19,6 +19,8 @@
>>  #define BITS_PER_LONG (BYTES_PER_LONG << 3)
>>  #define POINTER_ALIGN BYTES_PER_LONG
>>
>> +#define BITS_PER_LONG_LONG (sizeof (long long) * BITS_PER_BYTE)
>> +
>>  /* xen_ulong_t is always 64 bits */
>>  #define BITS_PER_XEN_ULONG 64
>>
>> diff --git a/xen/include/xen/bitops.h b/xen/include/xen/bitops.h
>> index bd0883a..9261e06 100644
>> --- a/xen/include/xen/bitops.h
>> +++ b/xen/include/xen/bitops.h
>> @@ -5,11 +5,14 @@
>>  /*
>>   * Create a contiguous bitmask starting at bit position @l and ending at
>>   * position @h. For example
>> - * GENMASK(30, 21) gives us the 32bit vector 0x01fe00000.
>> + * GENMASK(30, 21) gives us the 32bit vector 0x7fe00000.
>>   */
>>  #define GENMASK(h, l) \
>>      (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
>>
>> +#define GENMASK_ULL(h, l) \
>> +    (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
>> +
>>  /*
>>   * ffs: find first bit set. This is defined the same way as
>>   * the libc and compiler builtin ffs routines, therefore
>> --
>> 2.8.2
>>

Cheers,

[1] https://patchwork.kernel.org/patch/8824091/

-- 
Julien Grall

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

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

* Re: [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-06  0:21   ` Stefano Stabellini
@ 2017-04-06  8:55     ` Andre Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-06  8:55 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi Stefano,

On 06/04/17 01:21, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> 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/vgic-v3-its.c        | 416 ++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/vgic-v3.c            |   9 -
>>  xen/include/asm-arm/gic_v3_defs.h |   9 +
>>  xen/include/asm-arm/gic_v3_its.h  |   3 +
>>  4 files changed, 428 insertions(+), 9 deletions(-)
>>
>> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
>> index 9dfda59..f6bf1ee 100644
>> --- a/xen/arch/arm/vgic-v3-its.c
>> +++ b/xen/arch/arm/vgic-v3-its.c
>> @@ -78,6 +78,422 @@ void vgic_v3_its_free_domain(struct domain *d)
>>      ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
>>  }
>>  
>> +/**************************************
>> + * Functions that handle ITS commands *
>> + **************************************/
>> +
>> +static uint64_t its_cmd_mask_field(uint64_t *its_cmd, unsigned int word,
>> +                                   unsigned int shift, unsigned 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_get_ittaddr(cmd)        (its_cmd_mask_field(cmd, 2, 8, 44) << 8)
>> +
>> +#define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>> +
>> +/*
>> + * Requires the vcmd_lock to be held.
>> + * TODO: Investigate whether we can be smarter here and don't need to hold
>> + * the lock all of the time.
>> + */
>> +static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>> +{
>> +    paddr_t addr = its->cbaser & GENMASK_ULL(51, 12);
>> +    uint64_t command[4];
>> +
>> +    ASSERT(spin_is_locked(&its->vcmd_lock));
>> +
>> +    if ( its->cwriter >= ITS_CMD_BUFFER_SIZE(its->cbaser) )
>> +        return -1;
>> +
>> +    while ( its->creadr != its->cwriter )
>> +    {
>> +        int ret;
>> +
>> +        ret = vgic_access_guest_memory(d, addr + its->creadr,
>> +                                       command, sizeof(command), false);
>> +        if ( ret )
>> +            return ret;
>> +
>> +        switch ( its_cmd_get_command(command) )
>> +        {
>> +        case GITS_CMD_SYNC:
>> +            /* We handle ITS commands synchronously, so we ignore SYNC. */
>> +            break;
>> +        default:
>> +            gdprintk(XENLOG_WARNING, "ITS: unhandled ITS command %lu\n",
>> +                     its_cmd_get_command(command));
>> +            break;
>> +        }
>> +
>> +        its->creadr += ITS_CMD_SIZE;
>> +        if ( its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser) )
>> +            its->creadr = 0;
>> +
>> +        if ( ret )
>> +            gdprintk(XENLOG_WARNING,
>> +                     "ITS: ITS command error %d while handling command %lu\n",
>> +                     ret, its_cmd_get_command(command));
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/*****************************
>> + * ITS registers read access *
>> + *****************************/
>> +
>> +static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
>> +                                 register_t *r, void *priv)
>> +{
>> +    struct virt_its *its = priv;
>> +    uint64_t reg;
>> +
>> +    switch ( info->gpa & 0xffff )
>> +    {
>> +    case VREG32(GITS_CTLR):
>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
>> +        spin_lock(&its->its_lock);
>> +        if ( its->enabled )
>> +            reg = GITS_CTLR_ENABLE;
>> +        else
>> +            reg = 0;
>> +
>> +        if ( its->cwriter == its->creadr )
>> +            reg |= GITS_CTLR_QUIESCENT;
>> +        spin_unlock(&its->its_lock);
>> +        spin_unlock(&its->vcmd_lock);
>> +
>> +        *r = vgic_reg32_extract(reg, 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 ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        reg = GITS_TYPER_PHYSICAL;
>> +        reg |= (sizeof(struct vits_itte) - 1) << GITS_TYPER_ITT_SIZE_SHIFT;
>> +        reg |= (its->intid_bits - 1) << GITS_TYPER_IDBITS_SHIFT;
>> +        reg |= (its->devid_bits - 1) << GITS_TYPER_DEVIDS_SHIFT;
>> +        *r = vgic_reg64_extract(reg, info);
>> +        break;
>> +    case VREG64(GITS_CBASER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->its_lock);
>> +        *r = vgic_reg64_extract(its->cbaser, info);
> 
> From the comment at the top of this file, it looks like cbaser is
> supposed to be protected by the vcmd_lock:
> 
>     spinlock_t vcmd_lock;       /* Protects the virtual command buffer, which */
>     uint64_t cwriter;           /* consists of CBASER and CWRITER and those   */
>     uint64_t creadr;            /* shadow variables cwriter and creadr. */

Ah, good spot. I meant CREADR and not CBASER. So the comment is wrong,
CBASER is protected by the ITS lock.

Cheers,
Andre.

>> +        spin_unlock(&its->its_lock);
>> +        break;
>> +    case VREG64(GITS_CWRITER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->vcmd_lock);
>> +        *r = vgic_reg64_extract(its->cwriter, info);
>> +        spin_unlock(&its->vcmd_lock);
>> +        break;
>> +    case VREG64(GITS_CREADR):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->vcmd_lock);
>> +        *r = vgic_reg64_extract(its->creadr, info);
>> +        spin_unlock(&its->vcmd_lock);
>> +        break;
>> +    case VREG64(GITS_BASER0):           /* device table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->its_lock);
>> +        *r = vgic_reg64_extract(its->baser_dev, info);
>> +        spin_unlock(&its->its_lock);
>> +        break;
>> +    case VREG64(GITS_BASER1):           /* collection table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->its_lock);
>> +        *r = vgic_reg64_extract(its->baser_coll, info);
>> +        spin_unlock(&its->its_lock);
>> +        break;
>> +    case VRANGE64(GITS_BASER2, GITS_BASER7):
>> +        goto read_as_zero_64;
>> +
>> +    case VREG32(GITS_PIDR2):
>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>> +        *r = vgic_reg32_extract(GIC_PIDR2_ARCH_GICv3, info);
>> +        break;
>> +    }
>> +
>> +    return 1;
>> +
>> +read_as_zero_64:
>> +    if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +    *r = 0;
>> +
>> +    return 1;
>> +
>> +bad_width:
>> +    printk(XENLOG_G_ERR "vGIIS: bad read width %d r%d offset %#08lx\n",
>> +           info->dabt.size, info->dabt.reg, (unsigned long)info->gpa & 0xffff);
>> +    domain_crash_synchronous();
>> +
>> +    return 0;
>> +}
>> +
>> +/******************************
>> + * ITS registers write access *
>> + ******************************/
>> +
>> +static unsigned int its_baser_table_size(uint64_t baser)
>> +{
>> +    unsigned int ret, page_size[4] = {SZ_4K, SZ_16K, SZ_64K, SZ_64K};
>> +
>> +    ret = page_size[(baser >> GITS_BASER_PAGE_SIZE_SHIFT) & 3];
>> +
>> +    return ret * ((baser & GITS_BASER_SIZE_MASK) + 1);
>> +}
>> +
>> +static int its_baser_nr_entries(uint64_t baser)
>> +{
>> +    int entry_size = GITS_BASER_ENTRY_SIZE(baser);
>> +
>> +    return its_baser_table_size(baser) / entry_size;
>> +}
>> +
>> +/* Must be called with the ITS lock held. */
>> +static bool vgic_v3_verify_its_status(struct virt_its *its, bool status)
>> +{
>> +    ASSERT(spin_is_locked(&its->its_lock));
>> +
>> +    if ( !status )
>> +        return false;
>> +
>> +    if ( !(its->cbaser & GITS_VALID_BIT) ||
>> +         !(its->baser_dev & GITS_VALID_BIT) ||
>> +         !(its->baser_coll & GITS_VALID_BIT) )
>> +        return false;
>> +
>> +    return true;
>> +}
>> +
>> +static void sanitize_its_base_reg(uint64_t *reg)
>> +{
>> +    uint64_t r = *reg;
>> +
>> +    /* Avoid outer shareable. */
>> +    switch ( (r >> GITS_BASER_SHAREABILITY_SHIFT) & 0x03 )
>> +    {
>> +    case GIC_BASER_OuterShareable:
>> +        r = r & ~GITS_BASER_SHAREABILITY_MASK;
>> +        r |= GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +
>> +    /* Avoid any inner non-cacheable mapping. */
>> +    switch ( (r >> GITS_BASER_INNER_CACHEABILITY_SHIFT) & 0x07 )
>> +    {
>> +    case GIC_BASER_CACHE_nCnB:
>> +    case GIC_BASER_CACHE_nC:
>> +        r = r & ~GITS_BASER_INNER_CACHEABILITY_MASK;
>> +        r |= GIC_BASER_CACHE_RaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +
>> +    /* Only allow non-cacheable or same-as-inner. */
>> +    switch ( (r >> GITS_BASER_OUTER_CACHEABILITY_SHIFT) & 0x07 )
>> +    {
>> +    case GIC_BASER_CACHE_SameAsInner:
>> +    case GIC_BASER_CACHE_nC:
>> +        break;
>> +    default:
>> +        r = r & ~GITS_BASER_OUTER_CACHEABILITY_MASK;
>> +        r |= GIC_BASER_CACHE_nC << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
>> +        break;
>> +    }
>> +
>> +    *reg = r;
>> +}
>> +
>> +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 reg32, ctlr;
>> +
>> +    switch ( info->gpa & 0xffff )
>> +    {
>> +    case VREG32(GITS_CTLR):
>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
>> +        spin_lock(&its->its_lock);
>> +        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
>> +        reg32 = ctlr;
>> +        vgic_reg32_update(&reg32, r, info);
>> +
>> +        if ( ctlr ^ reg32 )
>> +            its->enabled = vgic_v3_verify_its_status(its,
>> +                                                     reg32 & GITS_CTLR_ENABLE);
>> +        spin_unlock(&its->its_lock);
>> +        spin_unlock(&its->vcmd_lock);
>> +        return 1;
>> +
>> +    case VREG32(GITS_IIDR):
>> +        goto write_ignore_32;
>> +    case VREG32(GITS_TYPER):
>> +        goto write_ignore_32;
>> +    case VREG64(GITS_CBASER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
>> +        spin_lock(&its->its_lock);
>> +        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
>> +        if ( its->enabled )
>> +        {
>> +            spin_unlock(&its->its_lock);
>> +            spin_unlock(&its->vcmd_lock);
>> +            gdprintk(XENLOG_WARNING,
>> +                     "ITS: tried to change CBASER with the ITS enabled.\n");
>> +            return 1;
>> +        }
>> +
>> +        reg = its->cbaser;
>> +        vgic_reg64_update(&reg, r, info);
>> +        sanitize_its_base_reg(&reg);
>> +
>> +        its->cbaser = reg;
>> +        its->creadr = 0;
>> +        spin_unlock(&its->its_lock);
>> +        spin_unlock(&its->vcmd_lock);
>> +
>> +        return 1;
>> +
>> +    case VREG64(GITS_CWRITER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
>> +        reg = its->cwriter & 0xfffe0;
>> +        vgic_reg64_update(&reg, r, info);
>> +        its->cwriter = reg & 0xfffe0;
>> +
>> +        if ( its->enabled )
>> +        {
>> +            int ret = vgic_its_handle_cmds(d, its);
>> +
>> +            if ( ret )
>> +                printk(XENLOG_G_WARNING "error handling ITS commands\n");
>> +        }
>> +        spin_unlock(&its->vcmd_lock);
>> +
>> +        return 1;
>> +
>> +    case VREG64(GITS_CREADR):
>> +        goto write_ignore_64;
>> +    case VREG64(GITS_BASER0):           /* device table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->its_lock);
>> +
>> +        /*
>> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
>> +         * we choose to ignore it, but warn.
>> +         */
>> +        if ( its->enabled )
>> +        {
>> +            spin_unlock(&its->its_lock);
>> +            gdprintk(XENLOG_WARNING, "ITS: tried to change BASER with the ITS enabled.\n");
>> +
>> +            return 1;
>> +        }
>> +
>> +        reg = its->baser_dev;
>> +        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;
>> +        sanitize_its_base_reg(&reg);
>> +
>> +        if ( reg & GITS_VALID_BIT )
>> +            its->max_devices = its_baser_nr_entries(reg);
>> +        else
>> +            its->max_devices = 0;
>> +
>> +        its->baser_dev = reg;
>> +        spin_unlock(&its->its_lock);
>> +        return 1;
>> +    case VREG64(GITS_BASER1):           /* collection table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->its_lock);
>> +        /*
>> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
>> +         * we choose to ignore it, but warn.
>> +         */
>> +        if ( its->enabled )
>> +        {
>> +            spin_unlock(&its->its_lock);
>> +            gdprintk(XENLOG_INFO, "ITS: tried to change BASER with the ITS enabled.\n");
>> +            return 1;
>> +        }
>> +
>> +        reg = its->baser_coll;
>> +        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;
>> +        sanitize_its_base_reg(&reg);
>> +
>> +        if ( reg & GITS_VALID_BIT )
>> +            its->max_collections = its_baser_nr_entries(reg);
>> +        else
>> +            its->max_collections = 0;
>> +        its->baser_coll = reg;
>> +        spin_unlock(&its->its_lock);
>> +        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 "vGITS: bad write width %d r%d offset %#08lx\n",
>> +           info->dabt.size, info->dabt.reg, (unsigned long)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
>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> index 0623803..e6a33d0 100644
>> --- a/xen/arch/arm/vgic-v3.c
>> +++ b/xen/arch/arm/vgic-v3.c
>> @@ -158,15 +158,6 @@ static void vgic_store_irouter(struct domain *d, struct vgic_irq_rank *rank,
>>      write_atomic(&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 b7e5a47..e60dea5 100644
>> --- a/xen/include/asm-arm/gic_v3_defs.h
>> +++ b/xen/include/asm-arm/gic_v3_defs.h
>> @@ -198,6 +198,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__ */
>>  
>>  /*
>> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
>> index eb71fbd..d3f393f 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -35,6 +35,7 @@
>>  #define GITS_BASER5                     0x128
>>  #define GITS_BASER6                     0x130
>>  #define GITS_BASER7                     0x138
>> +#define GITS_PIDR2                      GICR_PIDR2
>>  
>>  /* Register bits */
>>  #define GITS_VALID_BIT                  BIT_ULL(63)
>> @@ -57,6 +58,7 @@
>>  #define GITS_TYPER_ITT_SIZE_MASK        (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
>>  #define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
>>                                                   GITS_TYPER_ITT_SIZE_SHIFT) + 1)
>> +#define GITS_TYPER_PHYSICAL             (1U << 0)
>>  
>>  #define GITS_IIDR_VALUE                 0x34c
>>  
>> @@ -78,6 +80,7 @@
>>                          (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
>>  #define GITS_BASER_SHAREABILITY_SHIFT   10
>>  #define GITS_BASER_PAGE_SIZE_SHIFT      8
>> +#define GITS_BASER_SIZE_MASK            0xff
>>  #define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
>>                                          (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
>>                                          GITS_BASER_INDIRECT)
>> -- 
>> 2.8.2
>>

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

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

* Re: [PATCH v5 01/30] bitops: add GENMASK_ULL
  2017-04-06  8:45     ` Julien Grall
@ 2017-04-06  9:15       ` Jan Beulich
  2017-04-06  9:55         ` Andre Przywara
  0 siblings, 1 reply; 122+ messages in thread
From: Jan Beulich @ 2017-04-06  9:15 UTC (permalink / raw)
  To: Andre Przywara, Julien Grall
  Cc: sstabellini, Wei Liu, Vijay Kilari, George Dunlap, Andrew Cooper,
	Ian Jackson, Tim Deegan, xen-devel, Shanker Donthineni

>>> On 06.04.17 at 10:45, <julien.grall@arm.com> wrote:
> (CC 'The REST' maintainers)
> 
> Hi,
> 
> Andre, as I mentioned already a couple of times. You should CC all the 
> appropriate maintainers for the code you are modifying.
> 
> On 06/04/17 00:25, Stefano Stabellini wrote:
>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>> To safely handle 64-bit registers even on 32-bit systems, introduce
>>> a GENMASK_ULL variant (lifted from Linux).
>>> This adds a BITS_PER_LONG_LONG define as well.
>>> Also fix a bug in the comment for the existing GENMASK variant.
>>>
>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>
>> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
> 
> I'd like some input from Andrew here. I suggested a similar patch a year 
> ago (see [1]) and the final decision was to drop GENMASK_ULL.

Well, to be honest, rather than asking Andrew (who would likely
just re-state his opinion, as I would re-state mine), it should be
Andre to make clear why things are different now than they
were when the introduction of the macro was first rejected.

Jan


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

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

* Re: [PATCH v5 01/30] bitops: add GENMASK_ULL
  2017-04-06  9:15       ` Jan Beulich
@ 2017-04-06  9:55         ` Andre Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-06  9:55 UTC (permalink / raw)
  To: Jan Beulich, Julien Grall
  Cc: sstabellini, Wei Liu, Vijay Kilari, George Dunlap, Andrew Cooper,
	Ian Jackson, Tim Deegan, xen-devel, Shanker Donthineni

Hi,

On 06/04/17 10:15, Jan Beulich wrote:
>>>> On 06.04.17 at 10:45, <julien.grall@arm.com> wrote:
>> (CC 'The REST' maintainers)
>>
>> Hi,
>>
>> Andre, as I mentioned already a couple of times. You should CC all the 
>> appropriate maintainers for the code you are modifying.
>>
>> On 06/04/17 00:25, Stefano Stabellini wrote:
>>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>>> To safely handle 64-bit registers even on 32-bit systems, introduce
>>>> a GENMASK_ULL variant (lifted from Linux).
>>>> This adds a BITS_PER_LONG_LONG define as well.
>>>> Also fix a bug in the comment for the existing GENMASK variant.
>>>>
>>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>>
>>> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
>>
>> I'd like some input from Andrew here. I suggested a similar patch a year 
>> ago (see [1]) and the final decision was to drop GENMASK_ULL.
> 
> Well, to be honest, rather than asking Andrew (who would likely
> just re-state his opinion, as I would re-state mine), it should be
> Andre to make clear why things are different now than they
> were when the introduction of the macro was first rejected.

So first for the case of GENMASK in general:
ARM64 system registers or ARM IP registers (like in the GIC interrupt
controller) are often 64-bit, and often have several fields encoded at
bit locations *not* nibble aligned, for instance here:
GITS_BASER[0-7]:
Valid: bit[63], Indirect: bit[62], InnerCache: bits[61:59],
Type: bits[58:56], OuterCache: bits[55:53], ...
(from section 8.19.1 of ARM IHI 0069C [1])
So generating bitmasks for those fields is both tedious and error-prone,
also hard to read because of the required 16 nibbles, compare:
0x3800000000000000 vs. GENMASK(61, 59).
I think this might be an ARM specialty, which would explain why nobody
else missed it before.

For this special GENMASK_ULL version: The GIC interrupt controller is
usable from 32-bit ARM (software) as well, so the ~0UL in the normal
GENMASK is not sufficient to cover 64 bits here.

Now although this particular series is for ARM64 only, Julien wanted to
prepare for the case we ever need to port this to ARM32 as well, see:
https://lists.xen.org/archives/html/xen-devel/2016-11/msg00080.html

So long story short: Technically we don't need GENMASK_ULL (and BIT_ULL)
at the moment, but Julien asked for it. So personally I am happy to drop
them, if Julien agrees.
On the other hand this is just a tiny patch, also lifted from Linux, so
I don't see any real danger in including it into Xen (which would also
save me to fix up all the _ULL versions in my patches).

Cheers,
Andre.

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

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

* Re: [PATCH v5 24/30] ARM: vITS: handle MOVI command
  2017-04-06  0:47   ` Stefano Stabellini
@ 2017-04-06 10:07     ` Andre Przywara
  2017-04-06 17:56       ` Stefano Stabellini
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 10:07 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 01:47, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> 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-v3-its.c        | 24 ++++++++++++++++++++
>>  xen/arch/arm/gic-v3-lpi.c        | 15 +++++++++++++
>>  xen/arch/arm/vgic-v3-its.c       | 47 ++++++++++++++++++++++++++++++++++++++++
>>  xen/include/asm-arm/gic_v3_its.h |  4 ++++
>>  4 files changed, 90 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index d970119..a57e63a 100644
>> --- a/xen/arch/arm/gic-v3-its.c
>> +++ b/xen/arch/arm/gic-v3-its.c
>> @@ -851,6 +851,30 @@ struct pending_irq *gicv3_assign_guest_event(struct domain *d,
>>      return pirq;
>>  }
>>  
>> +/* Changes the target VCPU for a given host LPI assigned to a domain. */
>> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t vdoorbell,
>> +                          uint32_t vdevid, uint32_t veventid,
>> +                          unsigned int vcpu_id)
>> +{
>> +    uint32_t host_lpi;
>> +    struct its_devices *dev;
>> +
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    dev = get_its_device(d, vdoorbell, vdevid);
>> +    if ( dev )
>> +        host_lpi = get_host_lpi(dev, veventid);
>> +    else
>> +        host_lpi = 0;
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +    if ( !host_lpi )
>> +        return -ENOENT;
>> +
>> +    gicv3_lpi_update_host_vcpuid(host_lpi, vcpu_id);
> 
> we need to call vgic_migrate_irq

Mmmh, are you sure? The very first statement there reads:

    /* nothing to do for virtual interrupts */
    if ( p->desc == NULL )
        return;

Also my understanding of this command is that it only affects future
MSIs, so ITS translations. If an MSI has already been translated to a
certain redistributor and the ITS signaled it already, this command has
no effect on this particular one.
Also we don't care about benign races, so if this command comes in a tad
to late for a just happening MSI, this is the same problem on real hardware.

Cheers,
Andre.

>> +    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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
>> index c997ed5..b9960aa 100644
>> --- a/xen/arch/arm/gic-v3-lpi.c
>> +++ b/xen/arch/arm/gic-v3-lpi.c
>> @@ -234,6 +234,21 @@ void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>>      write_u64_atomic(&hlpip->data, hlpi.data);
>>  }
>>  
>> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id)
>> +{
>> +    union host_lpi *hlpip;
>> +
>> +    ASSERT(host_lpi >= LPI_OFFSET);
>> +
>> +    host_lpi -= LPI_OFFSET;
>> +
>> +    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
>> +
>> +    write_u16_atomic(&hlpip->vcpu_id, vcpu_id);
>> +
>> +    return 0;
>> +}
>> +
>>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>>  {
>>      uint64_t val;
>> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
>> index 079dd44..6afb915 100644
>> --- a/xen/arch/arm/vgic-v3-its.c
>> +++ b/xen/arch/arm/vgic-v3-its.c
>> @@ -508,6 +508,47 @@ static int its_handle_mapti(struct virt_its *its, uint64_t *cmdptr)
>>      return 0;
>>  }
>>  
>> +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 pending_irq *p;
>> +    struct vcpu *vcpu;
>> +    uint32_t vlpi;
>> +    int ret = -1;
>> +
>> +    spin_lock(&its->its_lock);
>> +    /* Check for a mapped LPI and get the LPI number. */
>> +    if ( !read_itte_locked(its, devid, eventid, &vcpu, &vlpi) )
>> +        goto out_unlock;
>> +
>> +    /* Check the new collection ID and get the new VCPU pointer */
>> +    vcpu = get_vcpu_from_collection(its, collid);
>> +    if ( !vcpu )
>> +        goto out_unlock;
>> +
>> +    /* Update our cached vcpu_id in the pending_irq. */
>> +    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
>> +    p->vcpu_id = vcpu->vcpu_id;
>> +
>> +    /* Now store the new collection in the translation table. */
>> +    if ( !write_itte_locked(its, devid, eventid, collid, vlpi, &vcpu) )
>> +        goto out_unlock;
>> +
>> +    spin_unlock(&its->its_lock);
>> +
>> +    /* TODO: lookup currently-in-guest virtual IRQs and migrate them? */
>> +
>> +    return gicv3_lpi_change_vcpu(its->d, its->doorbell_address,
>> +                                 devid, eventid, vcpu->vcpu_id);
>> +
>> +out_unlock:
>> +    spin_unlock(&its->its_lock);
>> +
>> +    return ret;
>> +}
>> +
>>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>>  
>>  /*
>> @@ -552,6 +593,12 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>>          case GITS_CMD_MAPTI:
>>              ret = its_handle_mapti(its, command);
>>              break;
>> +        case GITS_CMD_MOVALL:
>> +            gdprintk(XENLOG_G_INFO, "ITS: ignoring MOVALL command\n");
>> +            break;
>> +        case GITS_CMD_MOVI:
>> +            ret = its_handle_movi(its, command);
>> +            break;
>>          case GITS_CMD_SYNC:
>>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>>              break;
>> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
>> index 30aa1ef..daae143 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -177,8 +177,12 @@ void gicv3_free_host_lpi_block(uint32_t first_lpi);
>>  struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
>>                                               uint32_t devid, uint32_t eventid,
>>                                               struct vcpu *v, uint32_t virt_lpi);
>> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t doorbell,
>> +                          uint32_t devid, uint32_t eventid,
>> +                          unsigned int vcpu_id);
>>  void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>>                                   unsigned int vcpu_id, uint32_t virt_lpi);
>> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id);
>>  
>>  #else
>>  
>> -- 
>> 2.8.2
>>

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

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

* Re: [PATCH v5 30/30] ARM: vGIC: advertise LPI support
  2017-04-06  1:04   ` Stefano Stabellini
@ 2017-04-06 10:21     ` Andre Przywara
  2017-04-06 11:42       ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 10:21 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 02:04, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> 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.
>> This removes a "TBD" comment, as we now populate the processor number
>> in the GICR_TYPE register.
>> Advertise 24 bits worth of LPIs to the guest.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/vgic-v3.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
>>  1 file changed, 41 insertions(+), 5 deletions(-)
>>
>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> index 3b01247..ba0e79f 100644
>> --- a/xen/arch/arm/vgic-v3.c
>> +++ b/xen/arch/arm/vgic-v3.c
>> @@ -168,8 +168,12 @@ 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;
>> +        spin_lock(&v->arch.vgic.lock);
>> +        *r = vgic_reg32_extract(!!(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED),
>> +                                info);
>> +        spin_unlock(&v->arch.vgic.lock);
>> +        return 1;
>>  
>>      case VREG32(GICR_IIDR):
>>          if ( dabt.size != DABT_WORD ) goto bad_width;
>> @@ -181,16 +185,20 @@ 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;
>> +        /* We use the VCPU ID as the redistributor ID in bits[23:8] */
>> +        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;
>> @@ -411,6 +419,17 @@ static uint64_t sanitize_pendbaser(uint64_t reg)
>>      return reg;
>>  }
>>  
>> +static void vgic_vcpu_enable_lpis(struct vcpu *v)
>> +{
>> +    uint64_t reg = v->domain->arch.vgic.rdist_propbase;
>> +    unsigned int nr_lpis = BIT((reg & 0x1f) + 1) - LPI_OFFSET;
>> +
>> +    if ( !v->domain->arch.vgic.nr_lpis )
>> +        v->domain->arch.vgic.nr_lpis = nr_lpis;
> 
> What if nr_lpis was already set and the nr_lpis has changed?

I think this can never happen:
1) vgic.rdist_propbase is only writeable when the redistributor has not
been enabled yet:
        /* Writing PROPBASER with LPIs enabled is UNPREDICTABLE. */
        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
        {
            reg = v->domain->arch.vgic.rdist_propbase;
            vgic_reg64_update(&reg, r, info);
            reg = sanitize_propbaser(reg);
            v->domain->arch.vgic.rdist_propbase = reg;
        }
	....
2) This function above can only be called once:
	....
        spin_lock(&v->arch.vgic.lock);

        /* LPIs can only be enabled once, but never disabled again. */
        if ( (r & GICR_CTLR_ENABLE_LPIS) &&
             !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
            vgic_vcpu_enable_lpis(v);
	....

Does that sound safe? Sorry if the architecture is a bit awkward in this
regard ;-)

Cheers,
Andre.

>> +    v->arch.vgic.flags |= VGIC_V3_LPIS_ENABLED;
>> +}
>> +
>>  static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>>                                            uint32_t gicr_reg,
>>                                            register_t r)
>> @@ -421,8 +440,20 @@ 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;
>> +
>> +        spin_lock(&v->arch.vgic.lock);
>> +
>> +        /* LPIs can only be enabled once, but never disabled again. */
>> +        if ( (r & GICR_CTLR_ENABLE_LPIS) &&
>> +             !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
>> +            vgic_vcpu_enable_lpis(v);
>> +
>> +        spin_unlock(&v->arch.vgic.lock);
>> +
>> +        return 1;
>>  
>>      case VREG32(GICR_IIDR):
>>          /* RO */
>> @@ -1032,6 +1063,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 = 24;
>> +        }
>>          typer |= (irq_bits - 1) << GICD_TYPE_ID_BITS_SHIFT;
>>  
>>          *r = vgic_reg32_extract(typer, info);
>> -- 
>> 2.8.2
>>

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

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

* Re: [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array
  2017-04-05 23:37   ` Stefano Stabellini
@ 2017-04-06 10:31     ` Andre Przywara
  2017-04-06 11:18       ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 10:31 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 00:37, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> 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.
>> By using the naturally atomic access guarantee the native uint64_t data
>> type gives us, 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-v3-its.c        |  60 +++++++++++
>>  xen/arch/arm/gic-v3-lpi.c        | 227 +++++++++++++++++++++++++++++++++++++++
>>  xen/include/asm-arm/gic_v3_its.h |   6 ++
>>  xen/include/asm-arm/irq.h        |   8 ++
>>  4 files changed, 301 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index 1ecd63b..eb47c9d 100644
>> --- a/xen/arch/arm/gic-v3-its.c
>> +++ b/xen/arch/arm/gic-v3-its.c

[ ... ]

>> +
>> +void gicv3_free_host_lpi_block(uint32_t first_lpi)
>> +{
>> +    union host_lpi *hlpi, empty_lpi = { .dom_id = DOMID_INVALID };
>> +    int i;
>> +
>> +    hlpi = gic_get_host_lpi(first_lpi);
>> +    if ( !hlpi )
>> +        return;         /* Nothing to free here. */
> 
> We should check that first_lpi is actually the first lpi in a block
> before calling gic_get_host_lpi.

Are you thinking about an:
	ASSERT((first_lpi % LPI_BLOCK) == 0);
here? Or even a BUG_ON?

Technically the only two callers of gicv3_free_host_lpi_block() always
take this value from the host_lpi_blocks array, which should be safe.

Cheers,
Andre.

>> +    spin_lock(&lpi_data.host_lpis_lock);
>> +
>> +    for ( i = 0; i < LPI_BLOCK; i++ )
>> +        write_u64_atomic(&hlpi[i].data, empty_lpi.data);
>> +
>> +    /*
>> +     * Make sure the next allocation can reuse this block, as we do only
>> +     * forward scanning when finding an unused block.
>> +     */
>> +    if ( lpi_data.next_free_lpi > first_lpi )
>> +        lpi_data.next_free_lpi = first_lpi;
>> +
>> +    spin_unlock(&lpi_data.host_lpis_lock);
>> +
>> +    return;
>> +}
>> +

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-05 23:41   ` Stefano Stabellini
@ 2017-04-06 11:15     ` Julien Grall
  2017-04-06 16:42       ` Stefano Stabellini
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 11:15 UTC (permalink / raw)
  To: Stefano Stabellini, Andre Przywara
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Stefano,

On 06/04/17 00:41, Stefano Stabellini wrote:
>> +    /* An Interrupt Translation Table needs to be 256-byte aligned. */
>> +    itt_addr = _xzalloc(nr_events * hw_its->itte_size, 256);
>> +    if ( !itt_addr )
>> +        goto out_unlock;
>> +
>> +    dev = xzalloc(struct its_devices);
>> +    if ( !dev )
>> +        goto out_unlock;
>> +
>> +    /*
>> +     * Allocate the pending_irqs for each virtual LPI. They will be put
>> +     * into the domain's radix tree upon the guest's MAPTI command.
>> +     */
>
> Please expand the comment with a link to the xen-devel conversation
> about allocation strategy and a TODO saying that we shouldn't need to
> preallocate all pending_irq struct, but we do it for simplicity at the
> moment.

It would have been useful to provide a link of the conversation you 
mention to allow us answering properly rather than loosing a day for 
confirmation. Anyway, I guess you are speaking about [1].

I agree that we would need to take into account the memory to the guest, 
but this is a work beyond GICv3 ITS support as it is not
supported by Xen today.

Even if you do memory accounting, this would have to be reserved in 
advance to avoid failure afterwards. Which is very similar to memory 
allocation...

So can you explain what you would do here?

Cheers,

[1] 
https://lists.xenproject.org/archives/html/xen-devel/2017-03/msg03758.html

-- 
Julien Grall

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

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

* Re: [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array
  2017-04-06 10:31     ` Andre Przywara
@ 2017-04-06 11:18       ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 11:18 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 11:31, Andre Przywara wrote:
> On 06/04/17 00:37, Stefano Stabellini wrote:
>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>> 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.
>>> By using the naturally atomic access guarantee the native uint64_t data
>>> type gives us, 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-v3-its.c        |  60 +++++++++++
>>>  xen/arch/arm/gic-v3-lpi.c        | 227 +++++++++++++++++++++++++++++++++++++++
>>>  xen/include/asm-arm/gic_v3_its.h |   6 ++
>>>  xen/include/asm-arm/irq.h        |   8 ++
>>>  4 files changed, 301 insertions(+)
>>>
>>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>>> index 1ecd63b..eb47c9d 100644
>>> --- a/xen/arch/arm/gic-v3-its.c
>>> +++ b/xen/arch/arm/gic-v3-its.c
>
> [ ... ]
>
>>> +
>>> +void gicv3_free_host_lpi_block(uint32_t first_lpi)
>>> +{
>>> +    union host_lpi *hlpi, empty_lpi = { .dom_id = DOMID_INVALID };
>>> +    int i;
>>> +
>>> +    hlpi = gic_get_host_lpi(first_lpi);
>>> +    if ( !hlpi )
>>> +        return;         /* Nothing to free here. */
>>
>> We should check that first_lpi is actually the first lpi in a block
>> before calling gic_get_host_lpi.
>
> Are you thinking about an:
> 	ASSERT((first_lpi % LPI_BLOCK) == 0);
> here? Or even a BUG_ON?
>
> Technically the only two callers of gicv3_free_host_lpi_block() always
> take this value from the host_lpi_blocks array, which should be safe.

Well, maybe today. But without documentation someone could misuse this 
function in the future...

The check would be here for safety

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables
  2017-04-05 23:55   ` Stefano Stabellini
@ 2017-04-06 11:21     ` Julien Grall
  2017-04-06 11:25     ` Andre Przywara
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 11:21 UTC (permalink / raw)
  To: Stefano Stabellini, Andre Przywara
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Stefano,

On 06/04/17 00:55, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index cd9a2a5..9b0dc3d 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -20,6 +20,7 @@
>>  #include <xen/bitops.h>
>>  #include <xen/lib.h>
>>  #include <xen/init.h>
>> +#include <xen/domain_page.h>
>>  #include <xen/softirq.h>
>>  #include <xen/irq.h>
>>  #include <xen/sched.h>
>> @@ -589,6 +590,44 @@ void vgic_free_virq(struct domain *d, unsigned int virq)
>>      clear_bit(virq, d->arch.vgic.allocated_irqs);
>>  }
>>
>> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
>> +                             uint32_t size, bool_t is_write)
>
> Because there are no callers of this function, I think it breaks the
> build.

That would have been true if the function was not exported ;)

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables
  2017-04-06 11:25     ` Andre Przywara
@ 2017-04-06 11:24       ` Julien Grall
  2017-04-06 11:28         ` Andre Przywara
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 11:24 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 12:25, Andre Przywara wrote:
> On 06/04/17 00:55, Stefano Stabellini wrote:
>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>>> index cd9a2a5..9b0dc3d 100644
>>> --- a/xen/arch/arm/vgic.c
>>> +++ b/xen/arch/arm/vgic.c
>>> @@ -20,6 +20,7 @@
>>>  #include <xen/bitops.h>
>>>  #include <xen/lib.h>
>>>  #include <xen/init.h>
>>> +#include <xen/domain_page.h>
>>>  #include <xen/softirq.h>
>>>  #include <xen/irq.h>
>>>  #include <xen/sched.h>
>>> @@ -589,6 +590,44 @@ void vgic_free_virq(struct domain *d, unsigned int virq)
>>>      clear_bit(virq, d->arch.vgic.allocated_irqs);
>>>  }
>>>
>>> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
>>> +                             uint32_t size, bool_t is_write)
>>
>> Because there are no callers of this function, I think it breaks the
>> build.
>
> How so? This is a non-static function in a .c file.
> But indeed this function is prematurely introduced, we only need it two
> patches later.
> Fixed that.

I would actually prefer a separate patch for that and you likely need to 
add the Signed-off-by from Vijay as he was the author of the code.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables
  2017-04-05 23:55   ` Stefano Stabellini
  2017-04-06 11:21     ` Julien Grall
@ 2017-04-06 11:25     ` Andre Przywara
  2017-04-06 11:24       ` Julien Grall
  1 sibling, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 11:25 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 00:55, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, 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.
>> This introduces a function to read and write from and to guest memory,
>> to be later able to access the tables located there.
>> This vgic_access_guest_memory() function has been written by Vijaya Kumar
>> as part of an earlier series.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/vgic-v3.c       | 152 ++++++++++++++++++++++++++++++++++++++-----
>>  xen/arch/arm/vgic.c          |  39 +++++++++++
>>  xen/include/asm-arm/domain.h |   6 +-
>>  xen/include/asm-arm/vgic.h   |   3 +
>>  4 files changed, 182 insertions(+), 18 deletions(-)
>>
>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> index 2a14305..0623803 100644
>> --- a/xen/arch/arm/vgic-v3.c
>> +++ b/xen/arch/arm/vgic-v3.c
>> @@ -19,12 +19,14 @@
>>   */
>>  
>>  #include <xen/bitops.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>
>> @@ -228,12 +230,21 @@ 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;
>> +
>> +        spin_lock(&v->arch.vgic.lock);
>> +        *r = vgic_reg64_extract(v->domain->arch.vgic.rdist_propbase, info);
>> +        spin_unlock(&v->arch.vgic.lock);
>> +        return 1;
>>  
>>      case VREG64(GICR_PENDBASER):
>> -        /* LPI's not implemented */
>> -        goto read_as_zero_64;
>> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
>> +
>> +        spin_lock(&v->arch.vgic.lock);
>> +        *r = vgic_reg64_extract(v->arch.vgic.rdist_pendbase, info);
>> +        *r &= ~GICR_PENDBASER_PTZ;       /* WO, reads as 0 */
>> +        spin_unlock(&v->arch.vgic.lock);
>> +        return 1;
>>  
>>      case 0x0080:
>>          goto read_reserved;
>> @@ -301,11 +312,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;
>> @@ -330,11 +336,95 @@ 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 &= ~GICR_PROPBASER_RES0_MASK;
>> +
>> +    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 &= ~GICR_PENDBASER_RES0_MASK;
>> +
>> +    return reg;
>> +}
>> +
>>  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 )
>>      {
>> @@ -365,36 +455,64 @@ static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>>          goto write_impl_defined;
>>  
>>      case VREG64(GICR_SETLPIR):
>> -        /* LPI is not implemented */
>> +        /* LPIs without an ITS are not implemented */
>>          goto write_ignore_64;
>>  
>>      case VREG64(GICR_CLRLPIR):
>> -        /* LPI is not implemented */
>> +        /* LPIs without an ITS are not implemented */
>>          goto write_ignore_64;
>>  
>>      case 0x0050:
>>          goto write_reserved;
>>  
>>      case VREG64(GICR_PROPBASER):
>> -        /* LPI is not implemented */
>> -        goto write_ignore_64;
>> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
>> +
>> +        spin_lock(&v->arch.vgic.lock);
>> +
>> +        /* Writing PROPBASER with LPIs enabled is UNPREDICTABLE. */
>> +        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
>> +        {
>> +            reg = v->domain->arch.vgic.rdist_propbase;
>> +            vgic_reg64_update(&reg, r, info);
>> +            reg = sanitize_propbaser(reg);
>> +            v->domain->arch.vgic.rdist_propbase = reg;
>> +        }
>> +
>> +        spin_unlock(&v->arch.vgic.lock);
>> +
>> +        return 1;
>>  
>>      case VREG64(GICR_PENDBASER):
>> -        /* LPI is not implemented */
>> -        goto write_ignore_64;
>> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
>> +
>> +        spin_lock(&v->arch.vgic.lock);
>> +
>> +        /* Writing PENDBASER with LPIs enabled is UNPREDICTABLE. */
>> +        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
>> +        {
>> +            reg = v->arch.vgic.rdist_pendbase;
>> +            vgic_reg64_update(&reg, r, info);
>> +            reg = sanitize_pendbaser(reg);
>> +            v->arch.vgic.rdist_pendbase = reg;
>> +        }
>> +
>> +        spin_unlock(&v->arch.vgic.lock);
>> +
>> +        return 1;
>>  
>>      case 0x0080:
>>          goto write_reserved;
>>  
>>      case VREG64(GICR_INVLPIR):
>> -        /* LPI is not implemented */
>> +        /* LPIs without an ITS are not implemented */
>>          goto write_ignore_64;
>>  
>>      case 0x00A8:
>>          goto write_reserved;
>>  
>>      case VREG64(GICR_INVALLR):
>> -        /* LPI is not implemented */
>> +        /* LPIs without an ITS are not implemented */
>>          goto write_ignore_64;
>>  
>>      case 0x00B8:
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index cd9a2a5..9b0dc3d 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -20,6 +20,7 @@
>>  #include <xen/bitops.h>
>>  #include <xen/lib.h>
>>  #include <xen/init.h>
>> +#include <xen/domain_page.h>
>>  #include <xen/softirq.h>
>>  #include <xen/irq.h>
>>  #include <xen/sched.h>
>> @@ -589,6 +590,44 @@ void vgic_free_virq(struct domain *d, unsigned int virq)
>>      clear_bit(virq, d->arch.vgic.allocated_irqs);
>>  }
>>  
>> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
>> +                             uint32_t size, bool_t is_write)
> 
> Because there are no callers of this function, I think it breaks the
> build.

How so? This is a non-static function in a .c file.
But indeed this function is prematurely introduced, we only need it two
patches later.
Fixed that.

Cheers,
Andre.

>> +{
>> +    struct page_info *page;
>> +    uint64_t offset;
>> +    p2m_type_t p2mt;
>> +    void *p;
>> +
>> +    page = get_page_from_gfn(d, paddr_to_pfn(gpa), &p2mt, P2M_ALLOC);
>> +    if ( !page )
>> +    {
>> +        printk(XENLOG_G_ERR "d%d: vITS: Failed to get table entry\n",
>> +               d->domain_id);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if ( !p2m_is_ram(p2mt) )
>> +    {
>> +        put_page(page);
>> +        printk(XENLOG_G_ERR "d%d: vITS: with wrong attributes\n", d->domain_id);
>> +        return -EINVAL;
>> +    }
>> +
>> +    p = __map_domain_page(page);
>> +    /* Offset within the mapped page */
>> +    offset = gpa & ~PAGE_MASK;
>> +
>> +    if ( is_write )
>> +        memcpy(p + offset, addr, size);
>> +    else
>> +        memcpy(addr, p + offset, size);
>> +
>> +    unmap_domain_page(p);
>> +    put_page(page);
>> +
>> +    return 0;
>> +}
>> +
>>  /*
>>   * Local variables:
>>   * mode: C
>> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
>> index 6ee7538..f993292 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 */
>> +        unsigned long long int nr_lpis;
>> +        uint64_t rdist_propbase;
>>          struct rb_root its_devices;         /* Devices mapped to an ITS */
>>          spinlock_t its_devices_lock;        /* Protects the its_devices tree */
>>          struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
>> @@ -256,7 +258,9 @@ 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 */
>> +        uint64_t rdist_pendbase;
>> +#define VGIC_V3_RDIST_LAST      (1 << 0)        /* last vCPU of the rdist */
>> +#define VGIC_V3_LPIS_ENABLED    (1 << 1)
>>          uint8_t flags;
>>      } vgic;
>>  
>> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
>> index 08d6294..2371960 100644
>> --- a/xen/include/asm-arm/vgic.h
>> +++ b/xen/include/asm-arm/vgic.h
>> @@ -314,6 +314,9 @@ 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);
>>  
>> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
>> +                             uint32_t size, bool_t is_write);
>> +
>>  extern int domain_vgic_register(struct domain *d, int *mmio_count);
>>  extern int vcpu_vgic_free(struct vcpu *v);
>>  extern bool vgic_to_sgi(struct vcpu *v, register_t sgir,
>> -- 
>> 2.8.2
>>

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

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

* Re: [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables
  2017-04-06 11:24       ` Julien Grall
@ 2017-04-06 11:28         ` Andre Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 11:28 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 12:24, Julien Grall wrote:
> Hi Andre,
> 
> On 06/04/17 12:25, Andre Przywara wrote:
>> On 06/04/17 00:55, Stefano Stabellini wrote:
>>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>>>> index cd9a2a5..9b0dc3d 100644
>>>> --- a/xen/arch/arm/vgic.c
>>>> +++ b/xen/arch/arm/vgic.c
>>>> @@ -20,6 +20,7 @@
>>>>  #include <xen/bitops.h>
>>>>  #include <xen/lib.h>
>>>>  #include <xen/init.h>
>>>> +#include <xen/domain_page.h>
>>>>  #include <xen/softirq.h>
>>>>  #include <xen/irq.h>
>>>>  #include <xen/sched.h>
>>>> @@ -589,6 +590,44 @@ void vgic_free_virq(struct domain *d, unsigned
>>>> int virq)
>>>>      clear_bit(virq, d->arch.vgic.allocated_irqs);
>>>>  }
>>>>
>>>> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void
>>>> *addr,
>>>> +                             uint32_t size, bool_t is_write)
>>>
>>> Because there are no callers of this function, I think it breaks the
>>> build.
>>
>> How so? This is a non-static function in a .c file.
>> But indeed this function is prematurely introduced, we only need it two
>> patches later.
>> Fixed that.
> 
> I would actually prefer a separate patch for that and you likely need to
> add the Signed-off-by from Vijay as he was the author of the code.

Had just the same idea....

Cheers,
Andre.

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

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

* Re: [PATCH v5 30/30] ARM: vGIC: advertise LPI support
  2017-04-06 10:21     ` Andre Przywara
@ 2017-04-06 11:42       ` Julien Grall
  2017-04-06 22:54         ` André Przywara
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 11:42 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 11:21, Andre Przywara wrote:
> Hi,
>
> On 06/04/17 02:04, Stefano Stabellini wrote:
>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>> 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.
>>> This removes a "TBD" comment, as we now populate the processor number
>>> in the GICR_TYPE register.
>>> Advertise 24 bits worth of LPIs to the guest.
>>>
>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>> ---
>>>  xen/arch/arm/vgic-v3.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
>>>  1 file changed, 41 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>>> index 3b01247..ba0e79f 100644
>>> --- a/xen/arch/arm/vgic-v3.c
>>> +++ b/xen/arch/arm/vgic-v3.c
>>> @@ -168,8 +168,12 @@ 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;
>>> +        spin_lock(&v->arch.vgic.lock);
>>> +        *r = vgic_reg32_extract(!!(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED),
>>> +                                info);
>>> +        spin_unlock(&v->arch.vgic.lock);
>>> +        return 1;
>>>
>>>      case VREG32(GICR_IIDR):
>>>          if ( dabt.size != DABT_WORD ) goto bad_width;
>>> @@ -181,16 +185,20 @@ 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;
>>> +        /* We use the VCPU ID as the redistributor ID in bits[23:8] */
>>> +        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;
>>> @@ -411,6 +419,17 @@ static uint64_t sanitize_pendbaser(uint64_t reg)
>>>      return reg;
>>>  }
>>>
>>> +static void vgic_vcpu_enable_lpis(struct vcpu *v)
>>> +{
>>> +    uint64_t reg = v->domain->arch.vgic.rdist_propbase;
>>> +    unsigned int nr_lpis = BIT((reg & 0x1f) + 1) - LPI_OFFSET;
>>> +
>>> +    if ( !v->domain->arch.vgic.nr_lpis )
>>> +        v->domain->arch.vgic.nr_lpis = nr_lpis;
>>
>> What if nr_lpis was already set and the nr_lpis has changed?
>
> I think this can never happen:
> 1) vgic.rdist_propbase is only writeable when the redistributor has not
> been enabled yet:
>         /* Writing PROPBASER with LPIs enabled is UNPREDICTABLE. */
>         if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
>         {
>             reg = v->domain->arch.vgic.rdist_propbase;
>             vgic_reg64_update(&reg, r, info);
>             reg = sanitize_propbaser(reg);
>             v->domain->arch.vgic.rdist_propbase = reg;

This will be called until VGIC_V3_LPIS_ENABLED will be set for the vCPU. 
However rdist_propbase is part of struct domain. So ...

>         }
> 	....
> 2) This function above can only be called once:

... as this function will be called multiple per domain (once per vCPU). 
You could end up having nr_lpis not in sync with propbase.

> 	....
>         spin_lock(&v->arch.vgic.lock);
>
>         /* LPIs can only be enabled once, but never disabled again. */
>         if ( (r & GICR_CTLR_ENABLE_LPIS) &&
>              !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
>             vgic_vcpu_enable_lpis(v);
> 	....
>
> Does that sound safe? Sorry if the architecture is a bit awkward in this
> regard ;-)

I am afraid that this is not safe. Tĥis is fine to have propbase stored 
in the domain because the spec (8.11.9 in ARM IHI 0069C) says:

"Setting different values in different copies of GICR_PROPBASER on 
Redistributors that are required to use a common LPI Configuration table".

But you need to keep Xen internal state consistent.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 00/30] arm64: Dom0 ITS emulation
  2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
                   ` (29 preceding siblings ...)
  2017-04-05 23:19 ` [PATCH v5 30/30] ARM: vGIC: advertise LPI support Andre Przywara
@ 2017-04-06 12:31 ` Julien Grall
  30 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 12:31 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

On 06/04/17 00:18, Andre Przywara wrote:
> Hi,

Hi Andre,

> another round with lots of fixes for the ITS emulation series.
> The access to guest memory has been reworked, we now use a routine to
> copy to and from guest memory to also guarantee atomic access.
> This is courtesy of Vijaya Kumar, from a previous series.
> For a detailed changelog see below.
>
> Open questions:
> - Do we really need the GENMASK_ULL and BIT_ULL version ? This series is
>   really only for ARM64 (which doesn't need those), so we could get rid
>   of this, possibly introduce them later should we *really* need an ARM32
>   version. I think this will be our least problem then.
> - I was thinking about MOVALL and the spec is really confusing here.
>   It needs some discussion whether we need to emulate it or not. However
>   Linux does not use this command, so we might get away with it now.
>   An implementation might be doable along the lines of INVALL.

I would like be ok to skip the implementation for now. However this 
needs to be a TODO.

> - Julien asked for more ITS documentation, which is quite hard to come up
>   with. I will try to explain things in more details tomorrow in commit
>   messages, if there are still things missing.

Yes, writing documentation is quite hard when it is not done at the same 
time as writing the code :). But this is helping the reviewer to 
understand what you wrote. To be clear, I am not asking documenting how 
the hardware ITS ask, although few pointers to the doc is usually 
useful, but having a description in code of the design choice for the vITS.

Anyway, I will try to point where I think the code is not clear and a 
comment is necessary. In the future, I would appreciate if you get use 
to comment your code whilst writing.

Furthermore, I asked to see in the cover letter a list of missing items 
for GICv3 ITS (e.g two-level support, INVALL optimization, MOVALL...). 
But it is still missing. This is a MUST on my side before considering to 
merge ITS into staging.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  2017-04-05 23:26   ` Stefano Stabellini
@ 2017-04-06 12:39     ` Julien Grall
  2017-04-06 14:56       ` Andre Przywara
  2017-04-06 18:55     ` Andre Przywara
  1 sibling, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 12:39 UTC (permalink / raw)
  To: Stefano Stabellini, Andre Przywara
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:26, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
>> new file mode 100644
>> index 0000000..765a655
>> --- /dev/null
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -0,0 +1,67 @@
>> +/*
>> + * ARM GICv3 ITS support
>> + *
>> + * Andre Przywara <andre.przywara@arm.com>
>> + * Copyright (c) 2016,2017 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; under version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __ASM_ARM_ITS_H__
>> +#define __ASM_ARM_ITS_H__
>> +
>> +#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;
>> +};
>> +
>> +
>> +#ifdef CONFIG_HAS_ITS
>> +
>> +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);
>> +
>> +bool gicv3_its_host_has_its(void);
>> +
>> +#else
>> +
>> +static LIST_HEAD(host_its_list);
>
> This is a problem

As far as understand this is here because host_its_list is used 
unconditionally in vgic-v3.c. But this should not be necessary if you 
followed what I asked at least 5 times now and kept ignoring. I.e moving 
the creation of the multiple ITS in a separate function in vgic-v3-its.c.

We are one day before the code freeze, if you want this to get merged. 
Then please address *all* the comments.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS
  2017-04-05 23:18 ` [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS Andre Przywara
  2017-04-05 23:27   ` Stefano Stabellini
@ 2017-04-06 12:41   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 12:41 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:18, Andre Przywara wrote:
> Map the registers frame for each host ITS and populate the host ITS
> structure with some parameters describing the size of certain properties
> like the number of bits for device IDs.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Reviewed-by: Julien Grall <julien.grall@arm.com>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table
  2017-04-05 23:18 ` [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table Andre Przywara
  2017-04-05 23:30   ` Stefano Stabellini
@ 2017-04-06 12:58   ` Julien Grall
  2017-04-06 14:37     ` Andre Przywara
  1 sibling, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 12:58 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:18, Andre Przywara wrote:
> +static unsigned int max_lpi_bits = 20;
> +integer_param("max_lpi_bits", max_lpi_bits);
> +
> +int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
> +{
> +    /* An implementation needs to support at least 14 bits of LPI IDs. */
> +    max_lpi_bits = max(max_lpi_bits, 14U);

I think we should warn the user if we don't use his command line. He 
might think he can disable LPIs using it.

> +    lpi_data.max_host_lpi_ids = BIT_ULL(min(host_lpi_bits, max_lpi_bits));
> +
> +    /*
> +     * Warn if the number of LPIs are quite high, as the user might not want
> +     * to waste megabytes of memory for a mostly empty table.
> +     * It's very unlikely that we need more than 24 bits worth of LPIs.
> +     */
> +    if ( lpi_data.max_host_lpi_ids > BIT(24) )
> +        warning_add("Using high number of LPIs, limit memory usage with max_lpi_bits\n");
> +
> +    printk("GICv3: using at most %llu LPIs on the host.\n", MAX_NR_HOST_LPIS);
> +
> +    return 0;
> +}

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table
  2017-04-06 12:58   ` Julien Grall
@ 2017-04-06 14:37     ` Andre Przywara
  2017-04-06 14:37       ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 14:37 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 13:58, Julien Grall wrote:
> Hi Andre,
> 
> On 06/04/17 00:18, Andre Przywara wrote:
>> +static unsigned int max_lpi_bits = 20;
>> +integer_param("max_lpi_bits", max_lpi_bits);
>> +
>> +int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
>> +{
>> +    /* An implementation needs to support at least 14 bits of LPI
>> IDs. */
>> +    max_lpi_bits = max(max_lpi_bits, 14U);
> 
> I think we should warn the user if we don't use his command line. He
> might think he can disable LPIs using it.

Done.
Shall this be a warning_add() as well or is a printk() sufficient?

Cheers,
Andre.

>> +    lpi_data.max_host_lpi_ids = BIT_ULL(min(host_lpi_bits,
>> max_lpi_bits));
>> +
>> +    /*
>> +     * Warn if the number of LPIs are quite high, as the user might
>> not want
>> +     * to waste megabytes of memory for a mostly empty table.
>> +     * It's very unlikely that we need more than 24 bits worth of LPIs.
>> +     */
>> +    if ( lpi_data.max_host_lpi_ids > BIT(24) )
>> +        warning_add("Using high number of LPIs, limit memory usage
>> with max_lpi_bits\n");
>> +
>> +    printk("GICv3: using at most %llu LPIs on the host.\n",
>> MAX_NR_HOST_LPIS);
>> +
>> +    return 0;
>> +}
> 
> Cheers,
> 

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

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

* Re: [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table
  2017-04-06 14:37     ` Andre Przywara
@ 2017-04-06 14:37       ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 14:37 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 15:37, Andre Przywara wrote:
> On 06/04/17 13:58, Julien Grall wrote:
>> Hi Andre,
>>
>> On 06/04/17 00:18, Andre Przywara wrote:
>>> +static unsigned int max_lpi_bits = 20;
>>> +integer_param("max_lpi_bits", max_lpi_bits);
>>> +
>>> +int gicv3_lpi_init_host_lpis(unsigned int host_lpi_bits)
>>> +{
>>> +    /* An implementation needs to support at least 14 bits of LPI
>>> IDs. */
>>> +    max_lpi_bits = max(max_lpi_bits, 14U);
>>
>> I think we should warn the user if we don't use his command line. He
>> might think he can disable LPIs using it.
>
> Done.
> Shall this be a warning_add() as well or is a printk() sufficient?

I would go on classic printk. I was even thinking a panic as a user 
should not do this. It is up to you.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table
  2017-04-05 23:18 ` [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
@ 2017-04-06 14:44   ` Julien Grall
  2017-04-06 15:49     ` Andre Przywara
  2017-04-06 22:19   ` Julien Grall
  1 sibling, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 14:44 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:18, Andre Przywara wrote:
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 295eb22..6e51b98 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -37,6 +37,11 @@
>  #define GITS_BASER7                     0x138
>
>  /* Register bits */
> +#define GITS_VALID_BIT                  BIT_ULL(63)
> +
> +#define GITS_CTLR_QUIESCENT             BIT(31)
> +#define GITS_CTLR_ENABLE                BIT(0)
> +
>  #define GITS_TYPER_DEVIDS_SHIFT         13
>  #define GITS_TYPER_DEVIDS_MASK          (0x1fUL << GITS_TYPER_DEVIDS_SHIFT)
>  #define GITS_TYPER_DEVICE_ID_BITS(r)    (((r & GITS_TYPER_DEVIDS_MASK) >> \
> @@ -52,6 +57,33 @@
>  #define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
>                                                   GITS_TYPER_ITT_SIZE_SHIFT) + 1)
>
> +#define GITS_IIDR_VALUE                 0x34c

Just spotted this. This value belongs to vgic-its-v3.c as this is a 
"made up value" for the emulation (i.e patch #8).

With that moved:

Reviewed-by: Julien Grall <julien.grall@arm.com>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 07/30] ARM: GICv3 ITS: map ITS command buffer
  2017-04-05 23:18 ` [PATCH v5 07/30] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
@ 2017-04-06 14:49   ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 14:49 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:18, Andre Przywara wrote:
> Instead of directly manipulating the tables in memory, an ITS driver
> sends commands via a ring buffer in normal system memory 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>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

I know I am picky but for once, I added my reviewed-by on v3 and have 
nothing more to say ;). Please retain it;

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling
  2017-04-05 23:19 ` [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
  2017-04-05 23:33   ` Stefano Stabellini
@ 2017-04-06 14:55   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 14:55 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, 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.
> As an ITS might choose between *two* ways of addressing a redistributor,
> we store both the MMIO base address as well as the processor number in
> a per-CPU variable to give each ITS what it wants.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

Thank you for updating the comments!

Reviewed-by: Julien Grall <julien.grall@arm.com>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  2017-04-06 12:39     ` Julien Grall
@ 2017-04-06 14:56       ` Andre Przywara
  2017-04-06 15:02         ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 14:56 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 13:39, Julien Grall wrote:
> Hi Andre,
> 
> On 06/04/17 00:26, Stefano Stabellini wrote:
>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>> diff --git a/xen/include/asm-arm/gic_v3_its.h
>>> b/xen/include/asm-arm/gic_v3_its.h
>>> new file mode 100644
>>> index 0000000..765a655
>>> --- /dev/null
>>> +++ b/xen/include/asm-arm/gic_v3_its.h
>>> @@ -0,0 +1,67 @@
>>> +/*
>>> + * ARM GICv3 ITS support
>>> + *
>>> + * Andre Przywara <andre.przywara@arm.com>
>>> + * Copyright (c) 2016,2017 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; under version 2 of the License.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef __ASM_ARM_ITS_H__
>>> +#define __ASM_ARM_ITS_H__
>>> +
>>> +#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;
>>> +};
>>> +
>>> +
>>> +#ifdef CONFIG_HAS_ITS
>>> +
>>> +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);
>>> +
>>> +bool gicv3_its_host_has_its(void);
>>> +
>>> +#else
>>> +
>>> +static LIST_HEAD(host_its_list);
>>
>> This is a problem
> 
> As far as understand this is here because host_its_list is used
> unconditionally in vgic-v3.c. But this should not be necessary if you
> followed what I asked at least 5 times now and kept ignoring. I.e moving
> the creation of the multiple ITS in a separate function in vgic-v3-its.c.

Alright, I just created vgic_v3_create_mapped_its() in vgic-v3-its.c and
iterate over the hardware ITSes there, which does not need a dummy
host_its_list for non-ITS configs anymore.

Please let me know if you know a better name for that function, I am
blocked on clever naming at the moment.

Cheers,
Andre.

> 
> We are one day before the code freeze, if you want this to get merged.
> Then please address *all* the comments.
> 
> Cheers,
> 

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

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

* Re: [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  2017-04-06 14:56       ` Andre Przywara
@ 2017-04-06 15:02         ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 15:02 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 15:56, Andre Przywara wrote:
> Hi,
>
> On 06/04/17 13:39, Julien Grall wrote:
>> Hi Andre,
>>
>> On 06/04/17 00:26, Stefano Stabellini wrote:
>>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>>> diff --git a/xen/include/asm-arm/gic_v3_its.h
>>>> b/xen/include/asm-arm/gic_v3_its.h
>>>> new file mode 100644
>>>> index 0000000..765a655
>>>> --- /dev/null
>>>> +++ b/xen/include/asm-arm/gic_v3_its.h
>>>> @@ -0,0 +1,67 @@
>>>> +/*
>>>> + * ARM GICv3 ITS support
>>>> + *
>>>> + * Andre Przywara <andre.przywara@arm.com>
>>>> + * Copyright (c) 2016,2017 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; under version 2 of the License.
>>>> + *
>>>> + * This program is distributed in the hope that it will be useful,
>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>> + * GNU General Public License for more details.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License
>>>> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef __ASM_ARM_ITS_H__
>>>> +#define __ASM_ARM_ITS_H__
>>>> +
>>>> +#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;
>>>> +};
>>>> +
>>>> +
>>>> +#ifdef CONFIG_HAS_ITS
>>>> +
>>>> +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);
>>>> +
>>>> +bool gicv3_its_host_has_its(void);
>>>> +
>>>> +#else
>>>> +
>>>> +static LIST_HEAD(host_its_list);
>>>
>>> This is a problem
>>
>> As far as understand this is here because host_its_list is used
>> unconditionally in vgic-v3.c. But this should not be necessary if you
>> followed what I asked at least 5 times now and kept ignoring. I.e moving
>> the creation of the multiple ITS in a separate function in vgic-v3-its.c.
>
> Alright, I just created vgic_v3_create_mapped_its() in vgic-v3-its.c and
> iterate over the hardware ITSes there, which does not need a dummy
> host_its_list for non-ITS configs anymore.
>
> Please let me know if you know a better name for that function, I am
> blocked on clever naming at the moment.

How about moving the iteration in vgic_v3_its_init_domain?

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub
  2017-04-05 23:19 ` [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub Andre Przywara
@ 2017-04-06 15:17   ` Julien Grall
  2017-04-07 14:31   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 15:17 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> +/*
> + * Data structure to describe a virtual ITS.
> + * If both the vcmd_lock and the its_lock are required, the vcmd_lock must
> + * be taken first.
> + */
> +struct virt_its {
> +    struct domain *d;
> +    unsigned int devid_bits;
> +    unsigned int intid_bits;
> +    spinlock_t vcmd_lock;       /* Protects the virtual command buffer, which */
> +    uint64_t cwriter;           /* consists of CBASER and CWRITER and those   */

s/CBASER/CREADER/

> +    uint64_t creadr;            /* shadow variables cwriter and creadr. */
> +    /* Protects the rest of this structure, including the ITS tables. */
> +    spinlock_t its_lock;
> +    uint64_t cbaser;
> +    uint64_t baser_dev, baser_coll;     /* BASER0 and BASER1 for the guest */
> +    unsigned int max_collections;
> +    unsigned int max_devices;
> +    bool enabled;
> +};

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-05 23:19 ` [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping Andre Przywara
  2017-04-05 23:41   ` Stefano Stabellini
@ 2017-04-06 15:34   ` Julien Grall
  2017-04-06 16:10     ` Andre Przywara
  2017-04-07 14:46   ` Julien Grall
  2 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 15:34 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, 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 an rbtree to
> later be able to iterate over it or to easily teardown guests.
> Because device IDs are per ITS, we need to identify a virtual ITS. We
> use the doorbell address for that purpose, as it is a nice architectural
> MSI property and spares us handling with opaque pointer or break
> the VGIC abstraction.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-v3-its.c        | 263 +++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3-its.c       |   3 +
>  xen/include/asm-arm/domain.h     |   3 +
>  xen/include/asm-arm/gic_v3_its.h |  13 ++
>  4 files changed, 282 insertions(+)
>
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index eb47c9d..45bbfa7 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -21,6 +21,8 @@
>  #include <xen/lib.h>
>  #include <xen/delay.h>
>  #include <xen/mm.h>
> +#include <xen/rbtree.h>
> +#include <xen/sched.h>
>  #include <xen/sizes.h>
>  #include <asm/gic.h>
>  #include <asm/gic_v3_defs.h>
> @@ -36,6 +38,26 @@
>   */
>  LIST_HEAD(host_its_list);
>
> +/*
> + * Describes a device which is using the ITS and is used by a guest.
> + * Since device IDs are per ITS (in contrast to vLPIs, which are per
> + * guest), we have to differentiate between different virtual ITSes.
> + * We use the doorbell address here, since this is a nice architectural
> + * property of MSIs in general and we can easily get to the base address
> + * of the ITS and look that up.
> + */
> +struct its_devices {

Again, why its_devices? You only store the information for one device.

> +    struct rb_node rbnode;
> +    struct host_its *hw_its;
> +    void *itt_addr;
> +    paddr_t guest_doorbell;             /* Identifies the virtual ITS */
> +    uint32_t host_devid;
> +    uint32_t guest_devid;
> +    uint32_t eventids;                  /* Number of event IDs (MSIs) */
> +    uint32_t *host_lpi_blocks;          /* Which LPIs are used on the host */
> +    struct pending_irq *pend_irqs;      /* One struct per event */
> +};

[...]

> +static int remove_mapped_guest_device(struct its_devices *dev)
> +{
> +    int ret = 0;
> +    unsigned int i;
> +
> +    if ( dev->hw_its )
> +        /* MAPD also discards all events with this device ID. */
> +        ret = its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
> +
> +    for ( i = 0; i < dev->eventids / LPI_BLOCK; i++ )
> +        gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
> +
> +    /* Make sure the MAPD command above is really executed. */
> +    if ( !ret )
> +        ret = gicv3_its_wait_commands(dev->hw_its);
> +
> +    /* This should never happen, but just in case ... */
> +    if ( ret )
> +        printk(XENLOG_WARNING "Can't unmap host ITS device 0x%x\n",
> +               dev->host_devid);

I think we want to ratelimit this.

> +
> +    xfree(dev->itt_addr);
> +    xfree(dev->pend_irqs);
> +    xfree(dev->host_lpi_blocks);
> +    xfree(dev);
> +
> +    return 0;
> +}

[...]

> +/*
> + * Map a hardware device, identified by a certain host ITS and its device ID
> + * to domain d, a guest ITS (identified by its doorbell address) and device ID.
> + * Also provide the number of events (MSIs) needed for that device.
> + * This does not check if this particular hardware device is already mapped
> + * at another domain, it is expected that this would be done by the caller.
> + */
> +int gicv3_its_map_guest_device(struct domain *d,
> +                               paddr_t host_doorbell, uint32_t host_devid,
> +                               paddr_t guest_doorbell, uint32_t guest_devid,
> +                               uint32_t nr_events, bool valid)
> +{
> +    void *itt_addr = NULL;
> +    struct host_its *hw_its;
> +    struct its_devices *dev = NULL;
> +    struct rb_node **new = &d->arch.vgic.its_devices.rb_node, *parent = NULL;
> +    unsigned int i;
> +    int ret = -ENOENT;
> +
> +    hw_its = gicv3_its_find_by_doorbell(host_doorbell);
> +    if ( !hw_its )
> +        return ret;
> +
> +    /* Sanitise the provided hardware values against the host ITS. */
> +    if ( host_devid >= BIT(hw_its->devid_bits) )
> +        return -EINVAL;
> +
> +    /* We allocate events and LPIs in chunks of LPI_BLOCK (=32). */
> +    nr_events = ROUNDUP(nr_events, LPI_BLOCK);

I think it is still not enough. The MAPD command will use the number of 
bits, so you want to make sure you cover all the events in the table.

For instance, if nr_events = 66, you will round up to 96. My 
understanding, is you will call mapd with 7 bits rather 8 bits.

So you have to round up to the next power of two. Whether you want to 
allocate a power of two of LPIs is another question. But the ITT should 
be configured correctly.

[...]

> +    if ( ret )
> +    {
> +        do {
> +            i--;
> +            gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
> +            if ( i == 0 )
> +                break;
> +        } while (1);

This could be  } while ( i > 0 ) saving 3 lines.

> +
> +        /* Unmapping the device will discard all LPIs mapped so far. */
> +        its_send_cmd_mapd(hw_its, host_devid, 1, 0, false);

You likely need to check the return of its_send_mapd...

Also why 1 for the 3rd argument?

> +
> +        goto out;
> +    }
> +
> +    return 0;
> +
> +out_unlock:
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +out:
> +    if ( dev )
> +    {
> +        xfree(dev->pend_irqs);
> +        xfree(dev->host_lpi_blocks);
> +    }
> +    xfree(itt_addr);
> +    xfree(dev);
> +
> +    return ret;
> +}
> +

Cheers,


-- 
Julien Grall

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

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

* Re: [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table
  2017-04-06 14:44   ` Julien Grall
@ 2017-04-06 15:49     ` Andre Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 15:49 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 15:44, Julien Grall wrote:
> Hi Andre,
> 
> On 06/04/17 00:18, Andre Przywara wrote:
>> diff --git a/xen/include/asm-arm/gic_v3_its.h
>> b/xen/include/asm-arm/gic_v3_its.h
>> index 295eb22..6e51b98 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -37,6 +37,11 @@
>>  #define GITS_BASER7                     0x138
>>
>>  /* Register bits */
>> +#define GITS_VALID_BIT                  BIT_ULL(63)
>> +
>> +#define GITS_CTLR_QUIESCENT             BIT(31)
>> +#define GITS_CTLR_ENABLE                BIT(0)
>> +
>>  #define GITS_TYPER_DEVIDS_SHIFT         13
>>  #define GITS_TYPER_DEVIDS_MASK          (0x1fUL <<
>> GITS_TYPER_DEVIDS_SHIFT)
>>  #define GITS_TYPER_DEVICE_ID_BITS(r)    (((r &
>> GITS_TYPER_DEVIDS_MASK) >> \
>> @@ -52,6 +57,33 @@
>>  #define GITS_TYPER_ITT_SIZE(r)          ((((r) &
>> GITS_TYPER_ITT_SIZE_MASK) >> \
>>                                                  
>> GITS_TYPER_ITT_SIZE_SHIFT) + 1)
>>
>> +#define GITS_IIDR_VALUE                 0x34c
> 
> Just spotted this. This value belongs to vgic-its-v3.c as this is a
> "made up value" for the emulation (i.e patch #8).

Ah, good spot. While it's easy to move this, I wonder if we should have
something pseudo-unique for Xen.
In the KVM emulation we use this implementer value (which means "ARM"),
plus an ASCII "K" in the upper 8 bits. This allows the Linux ITS driver
to apply specific errata workarounds, if needed.
Shall I use a similar approach with "X" instead?
This would allow us to later push Xen-emulation specific fixes into
Linux shall we need it.

> With that moved:
> 
> Reviewed-by: Julien Grall <julien.grall@arm.com>

Thanks!

Andre.

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

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

* Re: [PATCH v5 12/30] ARM: GICv3: introduce separate pending_irq structs for LPIs
  2017-04-05 23:19 ` [PATCH v5 12/30] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
@ 2017-04-06 15:59   ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 15:59 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 142eb64..5128f13 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -1451,6 +1451,9 @@ static int vgic_v3_domain_init(struct domain *d)
>
>      vgic_v3_its_init_domain(d);
>
> +    rwlock_init(&d->arch.vgic.pend_lpi_tree_lock);
> +    radix_tree_init(&d->arch.vgic.pend_lpi_tree);
> +
>      /*
>       * Domain 0 gets the hardware address.
>       * Guests get the virtual platform layout.
> @@ -1524,14 +1527,33 @@ static int vgic_v3_domain_init(struct domain *d)
>  static void vgic_v3_domain_free(struct domain *d)
>  {
>      vgic_v3_its_free_domain(d);
> +    radix_tree_destroy(&d->arch.vgic.pend_lpi_tree, NULL);
>      xfree(d->arch.vgic.rdist_regions);
>  }
>
> +/*
> + * Looks up a virtual LPI number in our tree of mapped LPIs. This will return
> + * the corresponding struct pending_irq, which we also use to store the
> + * enabled and pending bit plus the priority.
> + * Returns NULL if an LPI cannot be found (or no LPIs are supported).

I asked few questions on the previous version about when lpi_to_pending 
returns NULL and the fact that none of the vGIC code is able to handle 
that. I was hoping a bit of documentation/ASSERT in the code rather than 
hiding the problem as you seem to do in this version.

Looking at the whole code, I see that you insert the pending_irq in the 
radix when on MAPTI. But never remove it. However the command DISCARD or 
MAPD (V=0) will remove the mapping from the ITT.

Which means it is never possible to re-use the same vLPI again. However 
if you remove from the radix tree you may end up having the LPI still in 
LR but lpi_to_pending return NULL and make Xen segfault.

So what will protect Xen against segfault? What is the plan?


> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 503a3cf..6ee7538 100644
> --- a/xen/include/asm-arm/domain.h
> +++ b/xen/include/asm-arm/domain.h
> @@ -111,6 +111,8 @@ struct arch_domain
>          uint32_t rdist_stride;              /* Re-Distributor stride */
>          struct rb_root its_devices;         /* Devices mapped to an ITS */
>          spinlock_t its_devices_lock;        /* Protects the its_devices tree */
> +        struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
> +        rwlock_t pend_lpi_tree_lock;        /* Protects the pend_lpi_tree */
>  #endif
>      } vgic;
>
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 894c3f1..7c86f5b 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -134,6 +134,8 @@ struct vgic_ops {
>      void (*domain_free)(struct domain *d);
>      /* vGIC sysreg/cpregs emulate */
>      bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
> +    /* lookup the struct pending_irq for a given LPI interrupt */
> +    struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int vlpi);
>      /* Maximum number of vCPU supported */
>      const unsigned int max_vcpus;
>  };
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-06 15:34   ` Julien Grall
@ 2017-04-06 16:10     ` Andre Przywara
  2017-04-06 18:00       ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 16:10 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 16:34, Julien Grall wrote:
> Hi Andre,
> 
> On 06/04/17 00:19, 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 an rbtree to
>> later be able to iterate over it or to easily teardown guests.
>> Because device IDs are per ITS, we need to identify a virtual ITS. We
>> use the doorbell address for that purpose, as it is a nice architectural
>> MSI property and spares us handling with opaque pointer or break
>> the VGIC abstraction.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-v3-its.c        | 263
>> +++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/vgic-v3-its.c       |   3 +
>>  xen/include/asm-arm/domain.h     |   3 +
>>  xen/include/asm-arm/gic_v3_its.h |  13 ++
>>  4 files changed, 282 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index eb47c9d..45bbfa7 100644
>> --- a/xen/arch/arm/gic-v3-its.c
>> +++ b/xen/arch/arm/gic-v3-its.c
>> @@ -21,6 +21,8 @@
>>  #include <xen/lib.h>
>>  #include <xen/delay.h>
>>  #include <xen/mm.h>
>> +#include <xen/rbtree.h>
>> +#include <xen/sched.h>
>>  #include <xen/sizes.h>
>>  #include <asm/gic.h>
>>  #include <asm/gic_v3_defs.h>
>> @@ -36,6 +38,26 @@
>>   */
>>  LIST_HEAD(host_its_list);
>>
>> +/*
>> + * Describes a device which is using the ITS and is used by a guest.
>> + * Since device IDs are per ITS (in contrast to vLPIs, which are per
>> + * guest), we have to differentiate between different virtual ITSes.
>> + * We use the doorbell address here, since this is a nice architectural
>> + * property of MSIs in general and we can easily get to the base address
>> + * of the ITS and look that up.
>> + */
>> +struct its_devices {
> 
> Again, why its_devices? You only store the information for one device.

You are right, and actually I fixed that already an hour ago.

>> +    struct rb_node rbnode;
>> +    struct host_its *hw_its;
>> +    void *itt_addr;
>> +    paddr_t guest_doorbell;             /* Identifies the virtual ITS */
>> +    uint32_t host_devid;
>> +    uint32_t guest_devid;
>> +    uint32_t eventids;                  /* Number of event IDs (MSIs) */
>> +    uint32_t *host_lpi_blocks;          /* Which LPIs are used on the
>> host */
>> +    struct pending_irq *pend_irqs;      /* One struct per event */
>> +};
> 
> [...]
> 
>> +static int remove_mapped_guest_device(struct its_devices *dev)
>> +{
>> +    int ret = 0;
>> +    unsigned int i;
>> +
>> +    if ( dev->hw_its )
>> +        /* MAPD also discards all events with this device ID. */
>> +        ret = its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0,
>> false);
>> +
>> +    for ( i = 0; i < dev->eventids / LPI_BLOCK; i++ )
>> +        gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
>> +
>> +    /* Make sure the MAPD command above is really executed. */
>> +    if ( !ret )
>> +        ret = gicv3_its_wait_commands(dev->hw_its);
>> +
>> +    /* This should never happen, but just in case ... */
>> +    if ( ret )
>> +        printk(XENLOG_WARNING "Can't unmap host ITS device 0x%x\n",
>> +               dev->host_devid);
> 
> I think we want to ratelimit this.

OK.

>> +
>> +    xfree(dev->itt_addr);
>> +    xfree(dev->pend_irqs);
>> +    xfree(dev->host_lpi_blocks);
>> +    xfree(dev);
>> +
>> +    return 0;
>> +}
> 
> [...]
> 
>> +/*
>> + * Map a hardware device, identified by a certain host ITS and its
>> device ID
>> + * to domain d, a guest ITS (identified by its doorbell address) and
>> device ID.
>> + * Also provide the number of events (MSIs) needed for that device.
>> + * This does not check if this particular hardware device is already
>> mapped
>> + * at another domain, it is expected that this would be done by the
>> caller.
>> + */
>> +int gicv3_its_map_guest_device(struct domain *d,
>> +                               paddr_t host_doorbell, uint32_t
>> host_devid,
>> +                               paddr_t guest_doorbell, uint32_t
>> guest_devid,
>> +                               uint32_t nr_events, bool valid)
>> +{
>> +    void *itt_addr = NULL;
>> +    struct host_its *hw_its;
>> +    struct its_devices *dev = NULL;
>> +    struct rb_node **new = &d->arch.vgic.its_devices.rb_node, *parent
>> = NULL;
>> +    unsigned int i;
>> +    int ret = -ENOENT;
>> +
>> +    hw_its = gicv3_its_find_by_doorbell(host_doorbell);
>> +    if ( !hw_its )
>> +        return ret;
>> +
>> +    /* Sanitise the provided hardware values against the host ITS. */
>> +    if ( host_devid >= BIT(hw_its->devid_bits) )
>> +        return -EINVAL;
>> +
>> +    /* We allocate events and LPIs in chunks of LPI_BLOCK (=32). */
>> +    nr_events = ROUNDUP(nr_events, LPI_BLOCK);
> 
> I think it is still not enough. The MAPD command will use the number of
> bits, so you want to make sure you cover all the events in the table.
> 
> For instance, if nr_events = 66, you will round up to 96. My
> understanding, is you will call mapd with 7 bits rather 8 bits.
> 
> So you have to round up to the next power of two. Whether you want to
> allocate a power of two of LPIs is another question. But the ITT should
> be configured correctly.

Again good catch.

> 
> [...]
> 
>> +    if ( ret )
>> +    {
>> +        do {
>> +            i--;
>> +            gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
>> +            if ( i == 0 )
>> +                break;
>> +        } while (1);
> 
> This could be  } while ( i > 0 ) saving 3 lines.

Argh, I rewrote this sucker three times because "i" is unsigned and I
apparently missed the easiest solution ;-)
And still there is a bug in there (if the first allocation fails already
above). Do you mind if I revert "i" back to a signed int? That would
allow me to do write "while ( i >= 0 )" here or a for loop.

>> +
>> +        /* Unmapping the device will discard all LPIs mapped so far. */
>> +        its_send_cmd_mapd(hw_its, host_devid, 1, 0, false);
> 
> You likely need to check the return of its_send_mapd...
> 
> Also why 1 for the 3rd argument?

Good catch, I thought I changed this to 0 after I applied your changes
to the wrapper function ...

>> +
>> +        goto out;
>> +    }
>> +
>> +    return 0;
>> +
>> +out_unlock:
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +out:
>> +    if ( dev )
>> +    {
>> +        xfree(dev->pend_irqs);
>> +        xfree(dev->host_lpi_blocks);
>> +    }
>> +    xfree(itt_addr);
>> +    xfree(dev);
>> +
>> +    return ret;
>> +}
>> +
> 
> Cheers,
> 
> 

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-06 11:15     ` Julien Grall
@ 2017-04-06 16:42       ` Stefano Stabellini
  2017-04-06 17:12         ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06 16:42 UTC (permalink / raw)
  To: Julien Grall
  Cc: Andre Przywara, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari, xen-devel

On Thu, 6 Apr 2017, Julien Grall wrote:
> Hi Stefano,
> 
> On 06/04/17 00:41, Stefano Stabellini wrote:
> > > +    /* An Interrupt Translation Table needs to be 256-byte aligned. */
> > > +    itt_addr = _xzalloc(nr_events * hw_its->itte_size, 256);
> > > +    if ( !itt_addr )
> > > +        goto out_unlock;
> > > +
> > > +    dev = xzalloc(struct its_devices);
> > > +    if ( !dev )
> > > +        goto out_unlock;
> > > +
> > > +    /*
> > > +     * Allocate the pending_irqs for each virtual LPI. They will be put
> > > +     * into the domain's radix tree upon the guest's MAPTI command.
> > > +     */
> > 
> > Please expand the comment with a link to the xen-devel conversation
> > about allocation strategy and a TODO saying that we shouldn't need to
> > preallocate all pending_irq struct, but we do it for simplicity at the
> > moment.
> 
> It would have been useful to provide a link of the conversation you mention to
> allow us answering properly rather than loosing a day for confirmation.
> Anyway, I guess you are speaking about [1].
> [1] https://lists.xenproject.org/archives/html/xen-devel/2017-03/msg03758.html

Yes, that's right.


> I agree that we would need to take into account the memory to the guest, but
> this is a work beyond GICv3 ITS support as it is not
> supported by Xen today.
> 
> Even if you do memory accounting, this would have to be reserved in advance to
> avoid failure afterwards. Which is very similar to memory allocation...
> 
> So can you explain what you would do here?

I don't think we need a full plan written down in a comment, but I think
it is fair to say that there is room for improvement, especially given
that we are already introducing other data structures such as the host
LPI array. I would write the following:

  Allocate the pending_irqs for each virtual LPI. They will be put into
  the domain's radix tree upon the guest's MAPTI command.

  TODO. For now, allocate all pending_irqs immediately.  However,
  delayed allocation strategies are possible, see this discussion:
  https://lists.xenproject.org/archives/html/xen-devel/2017-03/msg03758.html

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-06 16:42       ` Stefano Stabellini
@ 2017-04-06 17:12         ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 17:12 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: Andre Przywara, Shanker Donthineni, Vijay Kilari, xen-devel



On 06/04/17 17:42, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Julien Grall wrote:
>> Hi Stefano,
>>
>> On 06/04/17 00:41, Stefano Stabellini wrote:
>>>> +    /* An Interrupt Translation Table needs to be 256-byte aligned. */
>>>> +    itt_addr = _xzalloc(nr_events * hw_its->itte_size, 256);
>>>> +    if ( !itt_addr )
>>>> +        goto out_unlock;
>>>> +
>>>> +    dev = xzalloc(struct its_devices);
>>>> +    if ( !dev )
>>>> +        goto out_unlock;
>>>> +
>>>> +    /*
>>>> +     * Allocate the pending_irqs for each virtual LPI. They will be put
>>>> +     * into the domain's radix tree upon the guest's MAPTI command.
>>>> +     */
>>>
>>> Please expand the comment with a link to the xen-devel conversation
>>> about allocation strategy and a TODO saying that we shouldn't need to
>>> preallocate all pending_irq struct, but we do it for simplicity at the
>>> moment.
>>
>> It would have been useful to provide a link of the conversation you mention to
>> allow us answering properly rather than loosing a day for confirmation.
>> Anyway, I guess you are speaking about [1].
>> [1] https://lists.xenproject.org/archives/html/xen-devel/2017-03/msg03758.html
>
> Yes, that's right.
>
>
>> I agree that we would need to take into account the memory to the guest, but
>> this is a work beyond GICv3 ITS support as it is not
>> supported by Xen today.
>>
>> Even if you do memory accounting, this would have to be reserved in advance to
>> avoid failure afterwards. Which is very similar to memory allocation...
>>
>> So can you explain what you would do here?
>
> I don't think we need a full plan written down in a comment, but I think
> it is fair to say that there is room for improvement, especially given
> that we are already introducing other data structures such as the host
> LPI array. I would write the following:
>
>   Allocate the pending_irqs for each virtual LPI. They will be put into
>   the domain's radix tree upon the guest's MAPTI command.
>
>   TODO. For now, allocate all pending_irqs immediately.  However,
>   delayed allocation strategies are possible, see this discussion:
>   https://lists.xenproject.org/archives/html/xen-devel/2017-03/msg03758.html

Sounds good to me. We can discuss about the strategies later on.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-05 23:45   ` Stefano Stabellini
@ 2017-04-06 17:42     ` Andre Przywara
  2017-04-06 18:47       ` Stefano Stabellini
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 17:42 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi Stefano,

thanks for spending your brain cells on this nasty code.

...

On 06/04/17 00:45, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> Upon receiving an LPI on the host, 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.
>> Also we enhance struct pending_irq to cache the pending bit and the
>> priority information for LPIs, as we can't afford to walk the tables in
>> guest memory every time we handle an incoming LPI.
>> This introduces a do_LPI() as a hardware gic_ops and a function to
>> retrieve the (cached) priority value of an LPI and a vgic_ops.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-v2.c            |  7 +++++
>>  xen/arch/arm/gic-v3-lpi.c        | 56 ++++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic-v3.c            |  1 +
>>  xen/arch/arm/gic.c               |  8 +++++-
>>  xen/arch/arm/vgic-v2.c           |  7 +++++
>>  xen/arch/arm/vgic-v3.c           | 12 +++++++++
>>  xen/arch/arm/vgic.c              |  7 ++++-
>>  xen/include/asm-arm/gic.h        |  2 ++
>>  xen/include/asm-arm/gic_v3_its.h |  8 ++++++
>>  xen/include/asm-arm/vgic.h       |  3 +++
>>  10 files changed, 109 insertions(+), 2 deletions(-)
>>
>> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
>> index 270a136..f4d7949 100644
>> --- a/xen/arch/arm/gic-v2.c
>> +++ b/xen/arch/arm/gic-v2.c
>> @@ -1217,6 +1217,12 @@ static int __init gicv2_init(void)
>>      return 0;
>>  }
>>  
>> +void gicv2_do_LPI(unsigned int lpi)
>> +{
>> +    /* No LPIs in a GICv2 */
>> +    BUG();
>> +}
>> +
>>  const static struct gic_hw_operations gicv2_ops = {
>>      .info                = &gicv2_info,
>>      .init                = gicv2_init,
>> @@ -1244,6 +1250,7 @@ const static struct gic_hw_operations gicv2_ops = {
>>      .make_hwdom_madt     = gicv2_make_hwdom_madt,
>>      .map_hwdom_extra_mappings = gicv2_map_hwdown_extra_mappings,
>>      .iomem_deny_access   = gicv2_iomem_deny_access,
>> +    .do_LPI              = gicv2_do_LPI,
>>  };
>>  
>>  /* Set up the GIC */
>> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
>> index 0785701..d8baebc 100644
>> --- a/xen/arch/arm/gic-v3-lpi.c
>> +++ b/xen/arch/arm/gic-v3-lpi.c
>> @@ -136,6 +136,62 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
>>          return per_cpu(lpi_redist, cpu).redist_id << 16;
>>  }
>>  
>> +/*
>> + * 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.
>> + * Please note that LPIs are edge-triggered only, also have no active state,
>> + * so spurious interrupts on the host side are no issue (we can just ignore
>> + * them).
>> + * Also a guest cannot expect that firing interrupts that haven't been
>> + * fully configured yet will reach the CPU, so we don't need to care about
>> + * this special case.
>> + */
>> +void gicv3_do_LPI(unsigned int lpi)
>> +{
>> +    struct domain *d;
>> +    union host_lpi *hlpip, hlpi;
>> +    struct vcpu *vcpu;
>> +
>> +    /* EOI the LPI already. */
>> +    WRITE_SYSREG32(lpi, ICC_EOIR1_EL1);
>> +
>> +    /* Find out if a guest mapped something to this physical LPI. */
>> +    hlpip = gic_get_host_lpi(lpi);
>> +    if ( !hlpip )
>> +        return;
>> +
>> +    hlpi.data = read_u64_atomic(&hlpip->data);
>> +
>> +    /*
>> +     * Unmapped events are marked with an invalid LPI ID. We can safely
>> +     * ignore them, as they have no further state and no-one can expect
>> +     * to see them if they have not been mapped.
>> +     */
>> +    if ( hlpi.virt_lpi == INVALID_LPI )
>> +        return;
>> +
>> +    d = rcu_lock_domain_by_id(hlpi.dom_id);
>> +    if ( !d )
>> +        return;
>> +
>> +    /* Make sure we don't step beyond the vcpu array. */
>> +    if ( hlpi.vcpu_id >= d->max_vcpus )
>> +    {
>> +        rcu_unlock_domain(d);
>> +        return;
>> +    }
>> +
>> +    vcpu = d->vcpu[hlpi.vcpu_id];
>> +
>> +    /* Check if the VCPU is ready to receive LPIs. */
>> +    if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
>> +        vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
>> +
>> +    rcu_unlock_domain(d);
>> +}
>> +
>>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>>  {
>>      uint64_t val;
>> diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
>> index a559e5e..63dbc21 100644
>> --- a/xen/arch/arm/gic-v3.c
>> +++ b/xen/arch/arm/gic-v3.c
>> @@ -1670,6 +1670,7 @@ static const struct gic_hw_operations gicv3_ops = {
>>      .make_hwdom_dt_node  = gicv3_make_hwdom_dt_node,
>>      .make_hwdom_madt     = gicv3_make_hwdom_madt,
>>      .iomem_deny_access   = gicv3_iomem_deny_access,
>> +    .do_LPI              = gicv3_do_LPI,
>>  };
>>  
>>  static int __init gicv3_dt_preinit(struct dt_device_node *node, const void *data)
>> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
>> index 9522c6c..a56be34 100644
>> --- a/xen/arch/arm/gic.c
>> +++ b/xen/arch/arm/gic.c
>> @@ -697,7 +697,13 @@ void gic_interrupt(struct cpu_user_regs *regs, int is_fiq)
>>              do_IRQ(regs, irq, is_fiq);
>>              local_irq_disable();
>>          }
>> -        else if (unlikely(irq < 16))
>> +        else if ( is_lpi(irq) )
>> +        {
>> +            local_irq_enable();
>> +            gic_hw_ops->do_LPI(irq);
>> +            local_irq_disable();
>> +        }
>> +        else if ( unlikely(irq < 16) )
>>          {
>>              do_sgi(regs, irq);
>>          }
>> diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
>> index 3cad68f..5f4c5ad 100644
>> --- a/xen/arch/arm/vgic-v2.c
>> +++ b/xen/arch/arm/vgic-v2.c
>> @@ -710,11 +710,18 @@ static struct pending_irq *vgic_v2_lpi_to_pending(struct domain *d,
>>      BUG();
>>  }
>>  
>> +static int vgic_v2_lpi_get_priority(struct domain *d, unsigned int vlpi)
>> +{
>> +    /* Dummy function, no LPIs on a VGICv2. */
>> +    BUG();
>> +}
>> +
>>  static const struct vgic_ops vgic_v2_ops = {
>>      .vcpu_init   = vgic_v2_vcpu_init,
>>      .domain_init = vgic_v2_domain_init,
>>      .domain_free = vgic_v2_domain_free,
>>      .lpi_to_pending = vgic_v2_lpi_to_pending,
>> +    .lpi_get_priority = vgic_v2_lpi_get_priority,
>>      .max_vcpus = 8,
>>  };
>>  
>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> index 5128f13..2a14305 100644
>> --- a/xen/arch/arm/vgic-v3.c
>> +++ b/xen/arch/arm/vgic-v3.c
>> @@ -1548,12 +1548,24 @@ struct pending_irq *vgic_v3_lpi_to_pending(struct domain *d, unsigned int lpi)
>>      return pirq;
>>  }
>>  
>> +/* Retrieve the priority of an LPI from its struct pending_irq. */
>> +int vgic_v3_lpi_get_priority(struct domain *d, uint32_t vlpi)
>> +{
>> +    struct pending_irq *p = vgic_v3_lpi_to_pending(d, vlpi);
>> +
>> +    if ( !p )
>> +        return GIC_PRI_IRQ;
>> +
>> +    return p->lpi_priority;
>> +}
>> +
>>  static const struct vgic_ops v3_ops = {
>>      .vcpu_init   = vgic_v3_vcpu_init,
>>      .domain_init = vgic_v3_domain_init,
>>      .domain_free = vgic_v3_domain_free,
>>      .emulate_reg  = vgic_v3_emulate_reg,
>>      .lpi_to_pending = vgic_v3_lpi_to_pending,
>> +    .lpi_get_priority = vgic_v3_lpi_get_priority,
>>      /*
>>       * We use both AFF1 and AFF0 in (v)MPIDR. Thus, the max number of CPU
>>       * that can be supported is up to 4096(==256*16) in theory.
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index d704d7c..cd9a2a5 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -226,10 +226,15 @@ 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;
>>  
>> +    /* LPIs don't have a rank, also store their priority separately. */
>> +    if ( is_lpi(virq) )
>> +        return v->domain->arch.vgic.handler->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);
>> diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
>> index 836a103..42963c0 100644
>> --- a/xen/include/asm-arm/gic.h
>> +++ b/xen/include/asm-arm/gic.h
>> @@ -366,6 +366,8 @@ struct gic_hw_operations {
>>      int (*map_hwdom_extra_mappings)(struct domain *d);
>>      /* Deny access to GIC regions */
>>      int (*iomem_deny_access)(const struct domain *d);
>> +    /* Handle LPIs, which require special handling */
>> +    void (*do_LPI)(unsigned int lpi);
>>  };
>>  
>>  void register_gic_ops(const struct gic_hw_operations *ops);
>> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
>> index 3b7c724..eb71fbd 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -139,6 +139,8 @@ void gicv3_its_dt_init(const struct dt_device_node *node);
>>  
>>  bool gicv3_its_host_has_its(void);
>>  
>> +void gicv3_do_LPI(unsigned int lpi);
>> +
>>  int gicv3_lpi_init_rdist(void __iomem * rdist_base);
>>  
>>  /* Initialize the host structures for LPIs and the host ITSes. */
>> @@ -182,6 +184,12 @@ static inline bool gicv3_its_host_has_its(void)
>>      return false;
>>  }
>>  
>> +static inline void gicv3_do_LPI(unsigned int lpi)
>> +{
>> +    /* We don't enable LPIs without an ITS. */
>> +    BUG();
>> +}
>> +
>>  static inline int gicv3_lpi_init_rdist(void __iomem * rdist_base)
>>  {
>>      return -ENODEV;
>> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
>> index 7c86f5b..08d6294 100644
>> --- a/xen/include/asm-arm/vgic.h
>> +++ b/xen/include/asm-arm/vgic.h
>> @@ -66,12 +66,14 @@ struct pending_irq
>>  #define GIC_IRQ_GUEST_VISIBLE  2
>>  #define GIC_IRQ_GUEST_ENABLED  3
>>  #define GIC_IRQ_GUEST_MIGRATING   4
>> +#define GIC_IRQ_GUEST_LPI_PENDING 5     /* Caches the pending bit of an LPI. */
> 
> I don't think we need GIC_IRQ_GUEST_LPI_PENDING, you can reuse
> GIC_IRQ_GUEST_QUEUED and list_empty(&n->inflight): if you call
> vgic_vcpu_inject_irq passing an irq as argument that is not enabled, it
> will get GIC_IRQ_GUEST_QUEUED and added to inflight, but not injected.

Mmh, that's an interesting suggestion.
The neat thing about piggy-backing on those status bits is that we can
use the atomic set_bit/clear_bit to set and clear the pending state,
independently from any code accessing the structure, even without taking
any lock. Especially since the existing VGIC code does not care about
this bit.
However looking at all users of GUEST_LPI_PENDING, I see that we either
already hold the VGIC lock and we are able to take it, so we might be
able to use the combination you suggested.

I tried to make some sense of what "inflight" *really* means, can you
shed some light on this?
My understanding is that it's about IRQs that should be injected, but
are not ready (because they are disabled). Are there other cases IRQs
are in the "inflight" list? Is that also the spillover in case all LRs
are used (in this case they would GIC_IRQ_GUEST_ENABLED)?

>>      unsigned long status;
>>      struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
>>      unsigned int irq;
>>  #define GIC_INVALID_LR         (uint8_t)~0
>>      uint8_t lr;
>>      uint8_t priority;
>> +    uint8_t lpi_priority;       /* Caches the priority if this is an LPI. */
> 
> The commit message says: "we enhance struct pending_irq to cache the
> pending bit and the priority information for LPIs, as we can't afford to
> walk the tables in guest memory every time we handle an incoming LPI." I
> thought it would be direct access, having the vlpi number in our hands?
> Why is it a problem?
> 
> If there has been a conversation about this that I am missing, please
> provide a link, I'll go back and read it.

Well, the property table is in guest memory as the other ITS tables and
we now access this in a new way (vgic_access_guest_memory()), which is
quite costly: we need to do the p2m lookup, map the page, access the
data, unmap the page and put the page back. Everything on itself is not
really too demanding (on arm64), but doing this in the interrupt
handling path to learn the priority value sounds a bit over the top.
For the *ITS* emulation (command handling) we can cope with this
overhead (because this is only needing during the virtual command
handling), but I wanted to avoid this in the generic GIC code, which is
also a hot path, as I understand.
Since the GICv3 architecture explicitly allows caching this information,
I wanted to use this opportunity.

Does that make sense?

Cheers,
Andre.

> 
>>      /* inflight is used to append instances of pending_irq to
>>       * vgic.inflight_irqs */
>>      struct list_head inflight;
>> @@ -136,6 +138,7 @@ struct vgic_ops {
>>      bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
>>      /* lookup the struct pending_irq for a given LPI interrupt */
>>      struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int vlpi);
>> +    int (*lpi_get_priority)(struct domain *d, uint32_t vlpi);
>>      /* Maximum number of vCPU supported */
>>      const unsigned int max_vcpus;
>>  };
>> -- 
>> 2.8.2
>>

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

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

* Re: [PATCH v5 24/30] ARM: vITS: handle MOVI command
  2017-04-06 10:07     ` Andre Przywara
@ 2017-04-06 17:56       ` Stefano Stabellini
  0 siblings, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06 17:56 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> On 06/04/17 01:47, Stefano Stabellini wrote:
> > On Thu, 6 Apr 2017, Andre Przywara wrote:
> >> 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-v3-its.c        | 24 ++++++++++++++++++++
> >>  xen/arch/arm/gic-v3-lpi.c        | 15 +++++++++++++
> >>  xen/arch/arm/vgic-v3-its.c       | 47 ++++++++++++++++++++++++++++++++++++++++
> >>  xen/include/asm-arm/gic_v3_its.h |  4 ++++
> >>  4 files changed, 90 insertions(+)
> >>
> >> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> >> index d970119..a57e63a 100644
> >> --- a/xen/arch/arm/gic-v3-its.c
> >> +++ b/xen/arch/arm/gic-v3-its.c
> >> @@ -851,6 +851,30 @@ struct pending_irq *gicv3_assign_guest_event(struct domain *d,
> >>      return pirq;
> >>  }
> >>  
> >> +/* Changes the target VCPU for a given host LPI assigned to a domain. */
> >> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t vdoorbell,
> >> +                          uint32_t vdevid, uint32_t veventid,
> >> +                          unsigned int vcpu_id)
> >> +{
> >> +    uint32_t host_lpi;
> >> +    struct its_devices *dev;
> >> +
> >> +    spin_lock(&d->arch.vgic.its_devices_lock);
> >> +    dev = get_its_device(d, vdoorbell, vdevid);
> >> +    if ( dev )
> >> +        host_lpi = get_host_lpi(dev, veventid);
> >> +    else
> >> +        host_lpi = 0;
> >> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> >> +
> >> +    if ( !host_lpi )
> >> +        return -ENOENT;
> >> +
> >> +    gicv3_lpi_update_host_vcpuid(host_lpi, vcpu_id);
> > 
> > we need to call vgic_migrate_irq
> 
> Mmmh, are you sure? The very first statement there reads:
> 
>     /* nothing to do for virtual interrupts */
>     if ( p->desc == NULL )
>         return;
> 
> Also my understanding of this command is that it only affects future
> MSIs, so ITS translations. If an MSI has already been translated to a
> certain redistributor and the ITS signaled it already, this command has
> no effect on this particular one.
> Also we don't care about benign races, so if this command comes in a tad
> to late for a just happening MSI, this is the same problem on real hardware.

It is more complicated than I thought. I would add a in-code comment
here like this:

  TODO: we do not change physical irq affinity, in response to a virtual
  movi command. In other words, the physical LPI will still be delivered
  to the same pcpu.

In addition to the comment, I wouldn't mind if also a warning was
printed (with gdprintk).


> >> +    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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> >> index c997ed5..b9960aa 100644
> >> --- a/xen/arch/arm/gic-v3-lpi.c
> >> +++ b/xen/arch/arm/gic-v3-lpi.c
> >> @@ -234,6 +234,21 @@ void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
> >>      write_u64_atomic(&hlpip->data, hlpi.data);
> >>  }
> >>  
> >> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id)
> >> +{
> >> +    union host_lpi *hlpip;
> >> +
> >> +    ASSERT(host_lpi >= LPI_OFFSET);
> >> +
> >> +    host_lpi -= LPI_OFFSET;
> >> +
> >> +    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
> >> +
> >> +    write_u16_atomic(&hlpip->vcpu_id, vcpu_id);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
> >>  {
> >>      uint64_t val;
> >> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> >> index 079dd44..6afb915 100644
> >> --- a/xen/arch/arm/vgic-v3-its.c
> >> +++ b/xen/arch/arm/vgic-v3-its.c
> >> @@ -508,6 +508,47 @@ static int its_handle_mapti(struct virt_its *its, uint64_t *cmdptr)
> >>      return 0;
> >>  }
> >>  
> >> +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 pending_irq *p;
> >> +    struct vcpu *vcpu;
> >> +    uint32_t vlpi;
> >> +    int ret = -1;
> >> +
> >> +    spin_lock(&its->its_lock);
> >> +    /* Check for a mapped LPI and get the LPI number. */
> >> +    if ( !read_itte_locked(its, devid, eventid, &vcpu, &vlpi) )
> >> +        goto out_unlock;
> >> +
> >> +    /* Check the new collection ID and get the new VCPU pointer */
> >> +    vcpu = get_vcpu_from_collection(its, collid);
> >> +    if ( !vcpu )
> >> +        goto out_unlock;
> >> +
> >> +    /* Update our cached vcpu_id in the pending_irq. */
> >> +    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
> >> +    p->vcpu_id = vcpu->vcpu_id;
> >> +
> >> +    /* Now store the new collection in the translation table. */
> >> +    if ( !write_itte_locked(its, devid, eventid, collid, vlpi, &vcpu) )
> >> +        goto out_unlock;
> >> +
> >> +    spin_unlock(&its->its_lock);
> >> +
> >> +    /* TODO: lookup currently-in-guest virtual IRQs and migrate them? */
> >> +
> >> +    return gicv3_lpi_change_vcpu(its->d, its->doorbell_address,
> >> +                                 devid, eventid, vcpu->vcpu_id);
> >> +
> >> +out_unlock:
> >> +    spin_unlock(&its->its_lock);
> >> +
> >> +    return ret;
> >> +}
> >> +
> >>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
> >>  
> >>  /*
> >> @@ -552,6 +593,12 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
> >>          case GITS_CMD_MAPTI:
> >>              ret = its_handle_mapti(its, command);
> >>              break;
> >> +        case GITS_CMD_MOVALL:
> >> +            gdprintk(XENLOG_G_INFO, "ITS: ignoring MOVALL command\n");
> >> +            break;
> >> +        case GITS_CMD_MOVI:
> >> +            ret = its_handle_movi(its, command);
> >> +            break;
> >>          case GITS_CMD_SYNC:
> >>              /* We handle ITS commands synchronously, so we ignore SYNC. */
> >>              break;
> >> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> >> index 30aa1ef..daae143 100644
> >> --- a/xen/include/asm-arm/gic_v3_its.h
> >> +++ b/xen/include/asm-arm/gic_v3_its.h
> >> @@ -177,8 +177,12 @@ void gicv3_free_host_lpi_block(uint32_t first_lpi);
> >>  struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
> >>                                               uint32_t devid, uint32_t eventid,
> >>                                               struct vcpu *v, uint32_t virt_lpi);
> >> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t doorbell,
> >> +                          uint32_t devid, uint32_t eventid,
> >> +                          unsigned int vcpu_id);
> >>  void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
> >>                                   unsigned int vcpu_id, uint32_t virt_lpi);
> >> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id);
> >>  
> >>  #else
> >>  
> >> -- 
> >> 2.8.2
> >>
> 

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-06 16:10     ` Andre Przywara
@ 2017-04-06 18:00       ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 18:00 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 17:10, Andre Przywara wrote:
> On 06/04/17 16:34, Julien Grall wrote:
>>> +    if ( ret )
>>> +    {
>>> +        do {
>>> +            i--;
>>> +            gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
>>> +            if ( i == 0 )
>>> +                break;
>>> +        } while (1);
>>
>> This could be  } while ( i > 0 ) saving 3 lines.
>
> Argh, I rewrote this sucker three times because "i" is unsigned and I
> apparently missed the easiest solution ;-)
> And still there is a bug in there (if the first allocation fails already
> above). Do you mind if I revert "i" back to a signed int? That would
> allow me to do write "while ( i >= 0 )" here or a for loop.

I was about to say that you can have up 2^32 events (it is silly I 
know), but i represent the block. So I would be ok.

May I ask to add comment? So someone (to not say me) does not go over 
the code and switched to unsigned in the future :).

>>> +
>>> +        goto out;
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +out_unlock:
>>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>>> +
>>> +out:
>>> +    if ( dev )
>>> +    {
>>> +        xfree(dev->pend_irqs);
>>> +        xfree(dev->host_lpi_blocks);
>>> +    }
>>> +    xfree(itt_addr);
>>> +    xfree(dev);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>
>> Cheers,
>>
>>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-05 23:19 ` [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests Andre Przywara
  2017-04-05 23:45   ` Stefano Stabellini
@ 2017-04-06 18:10   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 18:10 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
> index 270a136..f4d7949 100644
> --- a/xen/arch/arm/gic-v2.c
> +++ b/xen/arch/arm/gic-v2.c
> @@ -1217,6 +1217,12 @@ static int __init gicv2_init(void)
>      return 0;
>  }
>
> +void gicv2_do_LPI(unsigned int lpi)

This should be static.

> +{
> +    /* No LPIs in a GICv2 */
> +    BUG();
> +}
> +

[...]

> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 0785701..d8baebc 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -136,6 +136,62 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
>          return per_cpu(lpi_redist, cpu).redist_id << 16;
>  }
>
> +/*
> + * 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.
> + * Please note that LPIs are edge-triggered only, also have no active state,
> + * so spurious interrupts on the host side are no issue (we can just ignore
> + * them).
> + * Also a guest cannot expect that firing interrupts that haven't been
> + * fully configured yet will reach the CPU, so we don't need to care about
> + * this special case.
> + */
> +void gicv3_do_LPI(unsigned int lpi)

Ditto.

> +{
> +    struct domain *d;
> +    union host_lpi *hlpip, hlpi;
> +    struct vcpu *vcpu;
> +

As mentioned on the previous version, you will need irq_enter and 
irq_exit in the return path. So the common code know you are in an 
interrupt handler (see in_irq()).

[...]

> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 5128f13..2a14305 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -1548,12 +1548,24 @@ struct pending_irq *vgic_v3_lpi_to_pending(struct domain *d, unsigned int lpi)
>      return pirq;
>  }
>
> +/* Retrieve the priority of an LPI from its struct pending_irq. */
> +int vgic_v3_lpi_get_priority(struct domain *d, uint32_t vlpi)

This should be static.

> +{
> +    struct pending_irq *p = vgic_v3_lpi_to_pending(d, vlpi);
> +
> +    if ( !p )
> +        return GIC_PRI_IRQ;
> +
> +    return p->lpi_priority;
> +}
> +
>  static const struct vgic_ops v3_ops = {
>      .vcpu_init   = vgic_v3_vcpu_init,
>      .domain_init = vgic_v3_domain_init,
>      .domain_free = vgic_v3_domain_free,
>      .emulate_reg  = vgic_v3_emulate_reg,
>      .lpi_to_pending = vgic_v3_lpi_to_pending,
> +    .lpi_get_priority = vgic_v3_lpi_get_priority,
>      /*
>       * We use both AFF1 and AFF0 in (v)MPIDR. Thus, the max number of CPU
>       * that can be supported is up to 4096(==256*16) in theory.

[...]

> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 7c86f5b..08d6294 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -66,12 +66,14 @@ struct pending_irq
>  #define GIC_IRQ_GUEST_VISIBLE  2
>  #define GIC_IRQ_GUEST_ENABLED  3
>  #define GIC_IRQ_GUEST_MIGRATING   4
> +#define GIC_IRQ_GUEST_LPI_PENDING 5     /* Caches the pending bit of an LPI. */

Please use the big comment above to describe GUEST_LPI_PENDING.

>      unsigned long status;
>      struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
>      unsigned int irq;
>  #define GIC_INVALID_LR         (uint8_t)~0
>      uint8_t lr;
>      uint8_t priority;
> +    uint8_t lpi_priority;       /* Caches the priority if this is an LPI. */
>      /* inflight is used to append instances of pending_irq to
>       * vgic.inflight_irqs */
>      struct list_head inflight;
> @@ -136,6 +138,7 @@ struct vgic_ops {
>      bool (*emulate_reg)(struct cpu_user_regs *regs, union hsr hsr);
>      /* lookup the struct pending_irq for a given LPI interrupt */
>      struct pending_irq *(*lpi_to_pending)(struct domain *d, unsigned int vlpi);
> +    int (*lpi_get_priority)(struct domain *d, uint32_t vlpi);
>      /* Maximum number of vCPU supported */
>      const unsigned int max_vcpus;
>  };
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-06 17:42     ` Andre Przywara
@ 2017-04-06 18:47       ` Stefano Stabellini
  2017-04-06 19:13         ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06 18:47 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> Hi Stefano,
> 
> thanks for spending your brain cells on this nasty code.
> 
> ...
> 
> On 06/04/17 00:45, Stefano Stabellini wrote:
> > On Thu, 6 Apr 2017, Andre Przywara wrote:
> >> Upon receiving an LPI on the host, 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.
> >> Also we enhance struct pending_irq to cache the pending bit and the
> >> priority information for LPIs, as we can't afford to walk the tables in
> >> guest memory every time we handle an incoming LPI.
> >> This introduces a do_LPI() as a hardware gic_ops and a function to
> >> retrieve the (cached) priority value of an LPI and a vgic_ops.
> >>
> >> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> >>
> >> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> >> index 7c86f5b..08d6294 100644
> >> --- a/xen/include/asm-arm/vgic.h
> >> +++ b/xen/include/asm-arm/vgic.h
> >> @@ -66,12 +66,14 @@ struct pending_irq
> >>  #define GIC_IRQ_GUEST_VISIBLE  2
> >>  #define GIC_IRQ_GUEST_ENABLED  3
> >>  #define GIC_IRQ_GUEST_MIGRATING   4
> >> +#define GIC_IRQ_GUEST_LPI_PENDING 5     /* Caches the pending bit of an LPI. */
> > 
> > I don't think we need GIC_IRQ_GUEST_LPI_PENDING, you can reuse
> > GIC_IRQ_GUEST_QUEUED and list_empty(&n->inflight): if you call
> > vgic_vcpu_inject_irq passing an irq as argument that is not enabled, it
> > will get GIC_IRQ_GUEST_QUEUED and added to inflight, but not injected.
> 
> Mmh, that's an interesting suggestion.
> The neat thing about piggy-backing on those status bits is that we can
> use the atomic set_bit/clear_bit to set and clear the pending state,
> independently from any code accessing the structure, even without taking
> any lock. Especially since the existing VGIC code does not care about
> this bit.
> However looking at all users of GUEST_LPI_PENDING, I see that we either
> already hold the VGIC lock and we are able to take it, so we might be
> able to use the combination you suggested.
> 
> I tried to make some sense of what "inflight" *really* means, can you
> shed some light on this?

See the comment in xen/include/asm-arm/vgic.h.

vgic_vcpu_inject_irq(irq) is called:
- GIC_IRQ_GUEST_QUEUED is set
- pending_irq is added to the inflight list

At this stage the irq is tracked as being inflight, but not really
injected into the guest yet.

Once/If the irq is enabled, we try to inject it into the guest.
gic_raise_guest_irq(irq) is called:
- if there is a free LR
  - clear GIC_IRQ_GUEST_QUEUED
  - set GIC_IRQ_GUEST_VISIBLE
- otherwise
  - add pending_irq to lr_pending
    - pending_irq is still on the inflight list too
    - pending_irq will be removed from lr_pending, and the irq added to an LR when
      available

When the guest finishes with the irq and EOIes it:
gic_update_one_lr is called:
- clear LR
- clear GIC_IRQ_GUEST_VISIBLE
- pending_irq is removed from inflight


Thus, you can use list_empty(&p->inflight) and QUEUED to check if the
LPI has been set to PENDING but it hasn't been injected yet, because the
corresponding pending_irq remains on the inflight list until the LPI has
been EOIed. but QUEUED is cleared as soon as the irq is added to an LR.

It is possible to set an interrupt as QUEUED, even if pending_irq is
already inflight and the irq is VISIBLE. That just means that the
physical irq has been reasserted after the virq becomes ACTIVE and
before the EOI.


> My understanding is that it's about IRQs that should be injected, but
> are not ready (because they are disabled). Are there other cases IRQs
> are in the "inflight" list? Is that also the spillover in case all LRs
> are used (in this case they would GIC_IRQ_GUEST_ENABLED)?

No. If all LRs are used, pending_irq is added to lr_pending. However,
pending_irq *also* remains in the inflight list until EOI.

If (ENABLED && QUEUED && inflight) is the condition to try to inject the
irq into the guest. If list_empty(&v->arch.vgic.lr_pending) is the
condition to actually be able to add the irq to an lr.


> >>      unsigned long status;
> >>      struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
> >>      unsigned int irq;
> >>  #define GIC_INVALID_LR         (uint8_t)~0
> >>      uint8_t lr;
> >>      uint8_t priority;
> >> +    uint8_t lpi_priority;       /* Caches the priority if this is an LPI. */
> > 
> > The commit message says: "we enhance struct pending_irq to cache the
> > pending bit and the priority information for LPIs, as we can't afford to
> > walk the tables in guest memory every time we handle an incoming LPI." I
> > thought it would be direct access, having the vlpi number in our hands?
> > Why is it a problem?
> > 
> > If there has been a conversation about this that I am missing, please
> > provide a link, I'll go back and read it.
> 
> Well, the property table is in guest memory as the other ITS tables and
> we now access this in a new way (vgic_access_guest_memory()), which is
> quite costly: we need to do the p2m lookup, map the page, access the
> data, unmap the page and put the page back. 

map, unmap and put are (almost) nop. The only operation is the
p2m_lookup that could be easily avoided by storing the struct page_info*
or mfn corresponding to the guest-provided data structures. We should be
able to do this in a very small number of steps, right?


> Everything on itself is not
> really too demanding (on arm64), but doing this in the interrupt
> handling path to learn the priority value sounds a bit over the top.
> For the *ITS* emulation (command handling) we can cope with this
> overhead (because this is only needing during the virtual command
> handling), but I wanted to avoid this in the generic GIC code, which is
> also a hot path, as I understand.
> Since the GICv3 architecture explicitly allows caching this information,
> I wanted to use this opportunity.
> 
> Does that make sense?

Yes, I see your reasoning and you are right that it is very important to
keep the hot path very fast. However, I think we should be able to do
that without duplicating information and adding another field to
pending_irq.

If you think that what I suggested is feasible but it's too complex to
do for now, then I would still drop the new lpi_priority field from
pending_irq, implement the slow version based on
vgic_access_guest_memory for now, and add another TODO comment in the
code.

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

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

* Re: [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT
  2017-04-05 23:26   ` Stefano Stabellini
  2017-04-06 12:39     ` Julien Grall
@ 2017-04-06 18:55     ` Andre Przywara
  1 sibling, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 18:55 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 00:26, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> Parse the GIC subnodes in the device tree 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 (as an EXPERT option), use
>> XEN_CONFIG_EXPERT=y on the make command line to see and use the option.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/Kconfig             |  5 +++
>>  xen/arch/arm/Makefile            |  1 +
>>  xen/arch/arm/gic-v3-its.c        | 77 ++++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic-v3.c            | 10 +++---
>>  xen/include/asm-arm/gic_v3_its.h | 67 ++++++++++++++++++++++++++++++++++
>>  5 files changed, 156 insertions(+), 4 deletions(-)
>>  create mode 100644 xen/arch/arm/gic-v3-its.c
>>  create mode 100644 xen/include/asm-arm/gic_v3_its.h
>>
>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>> index 43123e6..d46b98c 100644
>> --- a/xen/arch/arm/Kconfig
>> +++ b/xen/arch/arm/Kconfig
>> @@ -45,6 +45,11 @@ config ACPI
>>  config HAS_GICV3
>>  	bool
>>  
>> +config HAS_ITS
>> +        bool
>> +        prompt "GICv3 ITS MSI controller support" if EXPERT = "y"
>> +        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 0ce94a8..39c0a03 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-v3-its.o
>>  obj-y += guestcopy.o
>>  obj-y += hvm.o
>>  obj-y += io.o
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> new file mode 100644
>> index 0000000..6b02349
>> --- /dev/null
>> +++ b/xen/arch/arm/gic-v3-its.c
>> @@ -0,0 +1,77 @@
>> +/*
>> + * xen/arch/arm/gic-v3-its.c
>> + *
>> + * ARM GICv3 Interrupt Translation Service (ITS) support
>> + *
>> + * Copyright (C) 2016,2017 - 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; under version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <xen/lib.h>
>> +#include <asm/gic_v3_defs.h>
>> +#include <asm/gic_v3_its.h>
>> +
>> +/*
>> + * No lock here, as this list gets only populated upon boot while scanning
>> + * firmware tables for all host ITSes, and only gets iterated afterwards.
>> + */
>> +LIST_HEAD(host_its_list);
>> +
>> +bool gicv3_its_host_has_its(void)
>> +{
>> +    return !list_empty(&host_its_list);
>> +}
>> +
>> +/* 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)
>> +    {
>> +        uint64_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 695f01f..b626298 100644
>> --- a/xen/arch/arm/gic-v3.c
>> +++ b/xen/arch/arm/gic-v3.c
>> @@ -42,6 +42,7 @@
>>  #include <asm/device.h>
>>  #include <asm/gic.h>
>>  #include <asm/gic_v3_defs.h>
>> +#include <asm/gic_v3_its.h>
>>  #include <asm/cpufeature.h>
>>  #include <asm/acpi.h>
>>  
>> @@ -1227,11 +1228,12 @@ static void __init gicv3_dt_init(void)
>>       */
>>      res = dt_device_get_address(node, 1 + gicv3.rdist_count,
>>                                  &cbase, &csize);
>> -    if ( res )
>> -        return;
>> +    if ( !res )
>> +        dt_device_get_address(node, 1 + gicv3.rdist_count + 2,
>> +                              &vbase, &vsize);
>>  
>> -    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_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
>> new file mode 100644
>> index 0000000..765a655
>> --- /dev/null
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -0,0 +1,67 @@
>> +/*
>> + * ARM GICv3 ITS support
>> + *
>> + * Andre Przywara <andre.przywara@arm.com>
>> + * Copyright (c) 2016,2017 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; under version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __ASM_ARM_ITS_H__
>> +#define __ASM_ARM_ITS_H__
>> +
>> +#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;
>> +};
>> +
>> +
>> +#ifdef CONFIG_HAS_ITS
>> +
>> +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);
>> +
>> +bool gicv3_its_host_has_its(void);
>> +
>> +#else
>> +
>> +static LIST_HEAD(host_its_list);
> 
> This is a problem

So I got rid of this by doing the host_its_list iteration in
vgic-v3-its.c, as Julien suggested. This allows this host list to vanish
from a non-ITS config completely.

Thanks for pointing this out!

Cheers,
Andre.

> 
>> +static inline void gicv3_its_dt_init(const struct dt_device_node *node)
>> +{
>> +}
>> +
>> +static inline bool gicv3_its_host_has_its(void)
>> +{
>> +    return false;
>> +}
>> +
>> +#endif /* CONFIG_HAS_ITS */
>> +
>> +#endif
>> +
>> +/*
>> + * Local variables:
>> + * mode: C
>> + * c-file-style: "BSD"
>> + * c-basic-offset: 4
>> + * indent-tabs-mode: nil
>> + * End:
>> + */
>> -- 
>> 2.8.2
>>

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

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

* Re: [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs
  2017-04-05 23:58   ` Stefano Stabellini
@ 2017-04-06 19:09     ` Andre Przywara
  2017-04-06 20:43       ` Stefano Stabellini
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-06 19:09 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 00:58, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, 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. We do this by storing the pending bit in struct
>> pending_irq, which is assiociated with mapped LPIs.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-v3-lpi.c | 26 +++++++++++++++++++++++++-
>>  1 file changed, 25 insertions(+), 1 deletion(-)
>>
>> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
>> index d8baebc..7d20986 100644
>> --- a/xen/arch/arm/gic-v3-lpi.c
>> +++ b/xen/arch/arm/gic-v3-lpi.c
>> @@ -136,6 +136,22 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
>>          return per_cpu(lpi_redist, cpu).redist_id << 16;
>>  }
>>  
>> +static bool vgic_can_inject_lpi(struct vcpu *vcpu, uint32_t vlpi)
>> +{
>> +    struct pending_irq *p;
>> +
>> +    p = vcpu->domain->arch.vgic.handler->lpi_to_pending(vcpu->domain, vlpi);
>> +    if ( !p )
>> +        return false;
>> +
>> +    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
>> +        return true;
>> +
>> +    set_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);
> 
> See alpine.DEB.2.10.1701051422020.2866@sstabellini-ThinkPad-X260

(from this email:)
> 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).

So I can surely change the function to return that information, but I
think we don't have a good way of handling the storm easily.
First sending the required INV command to let the host know about our
change to the property table might take some time (we have a timeout in
there), also takes a spinlock. Which doesn't sound too good in the
interrupt injection path.
But secondly re-enabling the interrupt is not easily possible currently.
Ideally one would expect this to happen when the guest enables the
corresponding virtual LPI, but that would again require to send an INV
command to the host ITS, which is something that we avoid when triggered
by a guest (the MAPD exception is only for Dom0 and will hopefully go
away later).
So a guest could send virtual INVs (disabling and enabling the virtual
LPI) and trying to flood the host command queue.

So shall I change the function anyway and add a TODO comment, so that we
can later revisit this scenario?

Cheers,
Andre.

> 
>> +    return false;
>> +}
>> +
>>  /*
>>   * Handle incoming LPIs, which are a bit special, because they are potentially
>>   * numerous and also only get injected into guests. Treat them specially here,
>> @@ -187,7 +203,15 @@ void gicv3_do_LPI(unsigned int lpi)
>>  
>>      /* Check if the VCPU is ready to receive LPIs. */
>>      if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
>> -        vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
>> +    {
>> +        /*
>> +         * 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) )
>> +            vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
>> +    }
>>  
>>      rcu_unlock_domain(d);
>>  }
>> -- 
>> 2.8.2
>>

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-06 18:47       ` Stefano Stabellini
@ 2017-04-06 19:13         ` Julien Grall
  2017-04-06 19:47           ` Stefano Stabellini
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 19:13 UTC (permalink / raw)
  To: Stefano Stabellini, Andre Przywara
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Stefano,

On 04/06/2017 07:47 PM, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>>>      unsigned long status;
>>>>      struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */
>>>>      unsigned int irq;
>>>>  #define GIC_INVALID_LR         (uint8_t)~0
>>>>      uint8_t lr;
>>>>      uint8_t priority;
>>>> +    uint8_t lpi_priority;       /* Caches the priority if this is an LPI. */
>>>
>>> The commit message says: "we enhance struct pending_irq to cache the
>>> pending bit and the priority information for LPIs, as we can't afford to
>>> walk the tables in guest memory every time we handle an incoming LPI." I
>>> thought it would be direct access, having the vlpi number in our hands?
>>> Why is it a problem?
>>>
>>> If there has been a conversation about this that I am missing, please
>>> provide a link, I'll go back and read it.
>>
>> Well, the property table is in guest memory as the other ITS tables and
>> we now access this in a new way (vgic_access_guest_memory()), which is
>> quite costly: we need to do the p2m lookup, map the page, access the
>> data, unmap the page and put the page back.
>
> map, unmap and put are (almost) nop. The only operation is the
> p2m_lookup that could be easily avoided by storing the struct page_info*
> or mfn corresponding to the guest-provided data structures. We should be
> able to do this in a very small number of steps, right?

The property table could be really big (up to the number of LPIs 
supported by the guest). The memory is contiguous from a domain point of 
view but not necessarily on the host. So you would have to store a big 
number of MFN. IIRC the property table can be up to 4GB, so we would 
need an array of 8MB.

Also reading from the guest would require some safety which is not 
necessary here.

Furthermore, we have space in pending_irq because of padding.

So why bothering doing that?

>
>
>> Everything on itself is not
>> really too demanding (on arm64), but doing this in the interrupt
>> handling path to learn the priority value sounds a bit over the top.
>> For the *ITS* emulation (command handling) we can cope with this
>> overhead (because this is only needing during the virtual command
>> handling), but I wanted to avoid this in the generic GIC code, which is
>> also a hot path, as I understand.
>> Since the GICv3 architecture explicitly allows caching this information,
>> I wanted to use this opportunity.
>>
>> Does that make sense?
>
> Yes, I see your reasoning and you are right that it is very important to
> keep the hot path very fast. However, I think we should be able to do
> that without duplicating information and adding another field to
> pending_irq.

Why? We should not trust information living in the guest memory. This is 
a call to attack Xen. So this means more safety.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-06 19:13         ` Julien Grall
@ 2017-04-06 19:47           ` Stefano Stabellini
  2017-04-06 19:54             ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06 19:47 UTC (permalink / raw)
  To: Julien Grall
  Cc: Andre Przywara, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari, xen-devel

On Thu, 6 Apr 2017, Julien Grall wrote:
> On 04/06/2017 07:47 PM, Stefano Stabellini wrote:
> > On Thu, 6 Apr 2017, Andre Przywara wrote:
> > > > >      unsigned long status;
> > > > >      struct irq_desc *desc; /* only set it the irq corresponds to a
> > > > > physical irq */
> > > > >      unsigned int irq;
> > > > >  #define GIC_INVALID_LR         (uint8_t)~0
> > > > >      uint8_t lr;
> > > > >      uint8_t priority;
> > > > > +    uint8_t lpi_priority;       /* Caches the priority if this is an
> > > > > LPI. */
> > > > 
> > > > The commit message says: "we enhance struct pending_irq to cache the
> > > > pending bit and the priority information for LPIs, as we can't afford to
> > > > walk the tables in guest memory every time we handle an incoming LPI." I
> > > > thought it would be direct access, having the vlpi number in our hands?
> > > > Why is it a problem?
> > > > 
> > > > If there has been a conversation about this that I am missing, please
> > > > provide a link, I'll go back and read it.
> > > 
> > > Well, the property table is in guest memory as the other ITS tables and
> > > we now access this in a new way (vgic_access_guest_memory()), which is
> > > quite costly: we need to do the p2m lookup, map the page, access the
> > > data, unmap the page and put the page back.
> > 
> > map, unmap and put are (almost) nop. The only operation is the
> > p2m_lookup that could be easily avoided by storing the struct page_info*
> > or mfn corresponding to the guest-provided data structures. We should be
> > able to do this in a very small number of steps, right?
> 
> The property table could be really big (up to the number of LPIs supported by
> the guest). The memory is contiguous from a domain point of view but not
> necessarily on the host. So you would have to store a big number of MFN. IIRC
> the property table can be up to 4GB, so we would need an array of 8MB.

Yes, but in that scenario, with so many LPIs, the new lpi_priority field
will use overall 4GB. In comparison 8MB sounds awesome. But I didn't
notice that it was using the padding space.


> Also reading from the guest would require some safety which is not necessary
> here.
> 
> Furthermore, we have space in pending_irq because of padding.
> 
> So why bothering doing that?

I didn't notice that it was taking over some of the padding. That is
much better. I can live with it then.

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-06 19:47           ` Stefano Stabellini
@ 2017-04-06 19:54             ` Julien Grall
  2017-04-06 20:29               ` Stefano Stabellini
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 19:54 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: Andre Przywara, Shanker Donthineni, Vijay Kilari, xen-devel

Hi Stefano,

On 04/06/2017 08:47 PM, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Julien Grall wrote:
>> On 04/06/2017 07:47 PM, Stefano Stabellini wrote:
>>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>>>>>      unsigned long status;
>>>>>>      struct irq_desc *desc; /* only set it the irq corresponds to a
>>>>>> physical irq */
>>>>>>      unsigned int irq;
>>>>>>  #define GIC_INVALID_LR         (uint8_t)~0
>>>>>>      uint8_t lr;
>>>>>>      uint8_t priority;
>>>>>> +    uint8_t lpi_priority;       /* Caches the priority if this is an
>>>>>> LPI. */
>>>>>
>>>>> The commit message says: "we enhance struct pending_irq to cache the
>>>>> pending bit and the priority information for LPIs, as we can't afford to
>>>>> walk the tables in guest memory every time we handle an incoming LPI." I
>>>>> thought it would be direct access, having the vlpi number in our hands?
>>>>> Why is it a problem?
>>>>>
>>>>> If there has been a conversation about this that I am missing, please
>>>>> provide a link, I'll go back and read it.
>>>>
>>>> Well, the property table is in guest memory as the other ITS tables and
>>>> we now access this in a new way (vgic_access_guest_memory()), which is
>>>> quite costly: we need to do the p2m lookup, map the page, access the
>>>> data, unmap the page and put the page back.
>>>
>>> map, unmap and put are (almost) nop. The only operation is the
>>> p2m_lookup that could be easily avoided by storing the struct page_info*
>>> or mfn corresponding to the guest-provided data structures. We should be
>>> able to do this in a very small number of steps, right?
>>
>> The property table could be really big (up to the number of LPIs supported by
>> the guest). The memory is contiguous from a domain point of view but not
>> necessarily on the host. So you would have to store a big number of MFN. IIRC
>> the property table can be up to 4GB, so we would need an array of 8MB.
>
> Yes, but in that scenario, with so many LPIs, the new lpi_priority field
> will use overall 4GB. In comparison 8MB sounds awesome. But I didn't
> notice that it was using the padding space.
>
>
>> Also reading from the guest would require some safety which is not necessary
>> here.
>>
>> Furthermore, we have space in pending_irq because of padding.
>>
>> So why bothering doing that?
>
> I didn't notice that it was taking over some of the padding. That is
> much better. I can live with it then.

Would a comment /* This field is only used for caching LPI priority. 
This could be removed and read from the guest memory if we need space. 
*/ be useful?

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests
  2017-04-06 19:54             ` Julien Grall
@ 2017-04-06 20:29               ` Stefano Stabellini
  0 siblings, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06 20:29 UTC (permalink / raw)
  To: Julien Grall
  Cc: Andre Przywara, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari, xen-devel

On Thu, 6 Apr 2017, Julien Grall wrote:
> Hi Stefano,
> 
> On 04/06/2017 08:47 PM, Stefano Stabellini wrote:
> > On Thu, 6 Apr 2017, Julien Grall wrote:
> > > On 04/06/2017 07:47 PM, Stefano Stabellini wrote:
> > > > On Thu, 6 Apr 2017, Andre Przywara wrote:
> > > > > > >      unsigned long status;
> > > > > > >      struct irq_desc *desc; /* only set it the irq corresponds to
> > > > > > > a
> > > > > > > physical irq */
> > > > > > >      unsigned int irq;
> > > > > > >  #define GIC_INVALID_LR         (uint8_t)~0
> > > > > > >      uint8_t lr;
> > > > > > >      uint8_t priority;
> > > > > > > +    uint8_t lpi_priority;       /* Caches the priority if this is
> > > > > > > an
> > > > > > > LPI. */
> > > > > > 
> > > > > > The commit message says: "we enhance struct pending_irq to cache the
> > > > > > pending bit and the priority information for LPIs, as we can't
> > > > > > afford to
> > > > > > walk the tables in guest memory every time we handle an incoming
> > > > > > LPI." I
> > > > > > thought it would be direct access, having the vlpi number in our
> > > > > > hands?
> > > > > > Why is it a problem?
> > > > > > 
> > > > > > If there has been a conversation about this that I am missing,
> > > > > > please
> > > > > > provide a link, I'll go back and read it.
> > > > > 
> > > > > Well, the property table is in guest memory as the other ITS tables
> > > > > and
> > > > > we now access this in a new way (vgic_access_guest_memory()), which is
> > > > > quite costly: we need to do the p2m lookup, map the page, access the
> > > > > data, unmap the page and put the page back.
> > > > 
> > > > map, unmap and put are (almost) nop. The only operation is the
> > > > p2m_lookup that could be easily avoided by storing the struct page_info*
> > > > or mfn corresponding to the guest-provided data structures. We should be
> > > > able to do this in a very small number of steps, right?
> > > 
> > > The property table could be really big (up to the number of LPIs supported
> > > by
> > > the guest). The memory is contiguous from a domain point of view but not
> > > necessarily on the host. So you would have to store a big number of MFN.
> > > IIRC
> > > the property table can be up to 4GB, so we would need an array of 8MB.
> > 
> > Yes, but in that scenario, with so many LPIs, the new lpi_priority field
> > will use overall 4GB. In comparison 8MB sounds awesome. But I didn't
> > notice that it was using the padding space.
> > 
> > 
> > > Also reading from the guest would require some safety which is not
> > > necessary
> > > here.
> > > 
> > > Furthermore, we have space in pending_irq because of padding.
> > > 
> > > So why bothering doing that?
> > 
> > I didn't notice that it was taking over some of the padding. That is
> > much better. I can live with it then.
> 
> Would a comment /* This field is only used for caching LPI priority. This
> could be removed and read from the guest memory if we need space. */ be
> useful?

No need for a comment but the following sentence in the commit message
is a bit misleading:

"as we can't afford to walk the tables in guest memory every time we
handle an incoming LPI."

I would just rewrite it to say that it's faster than accessing guest
tables, and it doesn't require any more memory as we are exploiting some
of the bytes used for padding.

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

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

* Re: [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables
  2017-04-05 23:19 ` [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
  2017-04-05 23:55   ` Stefano Stabellini
@ 2017-04-06 20:33   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 20:33 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 12:19 AM, 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.
> This introduces a function to read and write from and to guest memory,
> to be later able to access the tables located there.
> This vgic_access_guest_memory() function has been written by Vijaya Kumar
> as part of an earlier series.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3.c       | 152 ++++++++++++++++++++++++++++++++++++++-----
>  xen/arch/arm/vgic.c          |  39 +++++++++++
>  xen/include/asm-arm/domain.h |   6 +-
>  xen/include/asm-arm/vgic.h   |   3 +
>  4 files changed, 182 insertions(+), 18 deletions(-)
>
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 2a14305..0623803 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -19,12 +19,14 @@
>   */
>
>  #include <xen/bitops.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>
> @@ -228,12 +230,21 @@ 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;

As discussed f2f, I would like to see this code gated with "if has_its" 
for now.

By that I mean:

if ( !has_its )
   goto read_as_zero_64;

> +
> +        spin_lock(&v->arch.vgic.lock);

The locking looks wrong to me. rdist_probase is per domain but you take 
the vCPU vgic lock.

You likely want to take the domain vgic lock. e.g:

vgic_lock(v);

> +        *r = vgic_reg64_extract(v->domain->arch.vgic.rdist_propbase, info);

NIT: It would simplify the code if you introduce a temporary variable d 
to store v->domain.

> +        spin_unlock(&v->arch.vgic.lock);
> +        return 1;
>
>      case VREG64(GICR_PENDBASER):
> -        /* LPI's not implemented */
> -        goto read_as_zero_64;

Same here.

> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +        *r = vgic_reg64_extract(v->arch.vgic.rdist_pendbase, info);
> +        *r &= ~GICR_PENDBASER_PTZ;       /* WO, reads as 0 */
> +        spin_unlock(&v->arch.vgic.lock);
> +        return 1;
>
>      case 0x0080:
>          goto read_reserved;
> @@ -301,11 +312,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;
> @@ -330,11 +336,95 @@ 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 &= ~GICR_PROPBASER_RES0_MASK;
> +
> +    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 &= ~GICR_PENDBASER_RES0_MASK;
> +
> +    return reg;
> +}
> +
>  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 )
>      {
> @@ -365,36 +455,64 @@ static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>          goto write_impl_defined;
>
>      case VREG64(GICR_SETLPIR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>
>      case VREG64(GICR_CLRLPIR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>
>      case 0x0050:
>          goto write_reserved;
>
>      case VREG64(GICR_PROPBASER):
> -        /* LPI is not implemented */
> -        goto write_ignore_64;
> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +
> +        spin_lock(&v->arch.vgic.lock);

Please look to the discussion on patch #30 about the locking issue...

> +
> +        /* Writing PROPBASER with LPIs enabled is UNPREDICTABLE. */
> +        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
> +        {
> +            reg = v->domain->arch.vgic.rdist_propbase;
> +            vgic_reg64_update(&reg, r, info);
> +            reg = sanitize_propbaser(reg);
> +            v->domain->arch.vgic.rdist_propbase = reg;

... and the fact that rdist_probase is per domain and not per vCPU.

> +        }
> +
> +        spin_unlock(&v->arch.vgic.lock);
> +
> +        return 1;
>
>      case VREG64(GICR_PENDBASER):
> -        /* LPI is not implemented */
> -        goto write_ignore_64;
> +        if ( !vgic_reg64_check_access(dabt) ) goto bad_width;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +
> +        /* Writing PENDBASER with LPIs enabled is UNPREDICTABLE. */
> +        if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
> +        {
> +            reg = v->arch.vgic.rdist_pendbase;
> +            vgic_reg64_update(&reg, r, info);
> +            reg = sanitize_pendbaser(reg);
> +            v->arch.vgic.rdist_pendbase = reg;
> +        }
> +
> +        spin_unlock(&v->arch.vgic.lock);
> +
> +        return 1;
>
>      case 0x0080:
>          goto write_reserved;
>
>      case VREG64(GICR_INVLPIR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>
>      case 0x00A8:
>          goto write_reserved;
>
>      case VREG64(GICR_INVALLR):
> -        /* LPI is not implemented */
> +        /* LPIs without an ITS are not implemented */
>          goto write_ignore_64;
>
>      case 0x00B8:
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index cd9a2a5..9b0dc3d 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -20,6 +20,7 @@
>  #include <xen/bitops.h>
>  #include <xen/lib.h>
>  #include <xen/init.h>
> +#include <xen/domain_page.h>
>  #include <xen/softirq.h>
>  #include <xen/irq.h>
>  #include <xen/sched.h>
> @@ -589,6 +590,44 @@ void vgic_free_virq(struct domain *d, unsigned int virq)
>      clear_bit(virq, d->arch.vgic.allocated_irqs);
>  }
>
> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,

I think, it would make sense to rename addr to buff.

> +                             uint32_t size, bool_t is_write)
> +{
> +    struct page_info *page;
> +    uint64_t offset;
> +    p2m_type_t p2mt;
> +    void *p;
> +
> +    page = get_page_from_gfn(d, paddr_to_pfn(gpa), &p2mt, P2M_ALLOC);
> +    if ( !page )
> +    {
> +        printk(XENLOG_G_ERR "d%d: vITS: Failed to get table entry\n",
> +               d->domain_id);
> +        return -EINVAL;
> +    }
> +
> +    if ( !p2m_is_ram(p2mt) )
> +    {
> +        put_page(page);
> +        printk(XENLOG_G_ERR "d%d: vITS: with wrong attributes\n", d->domain_id);

The message is quite difficult to parse: "with wrong attributes". It 
seems there are some part missing.

I would say: "vITS: memory used by the ITS should be RAM."

> +        return -EINVAL;
> +    }
> +
> +    p = __map_domain_page(page);
> +    /* Offset within the mapped page */
> +    offset = gpa & ~PAGE_MASK;
> +
> +    if ( is_write )
> +        memcpy(p + offset, addr, size);
> +    else
> +        memcpy(addr, p + offset, size);

What if we are crossing a page boundary? Even if it doesn't happen 
today, there are no documentation on how to use the function. So a user 
would expect you to handle this case.

I don't mind if it is not handle, but we need at least a check to 
prevent mis-usage and a security issue (copying too much data back to 
the guest).

> +
> +    unmap_domain_page(p);
> +    put_page(page);
> +
> +    return 0;
> +}
> +
>  /*
>   * Local variables:
>   * mode: C
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index 6ee7538..f993292 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 */
> +        unsigned long long int nr_lpis;
> +        uint64_t rdist_propbase;
>          struct rb_root its_devices;         /* Devices mapped to an ITS */
>          spinlock_t its_devices_lock;        /* Protects the its_devices tree */
>          struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
> @@ -256,7 +258,9 @@ 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 */
> +        uint64_t rdist_pendbase;
> +#define VGIC_V3_RDIST_LAST      (1 << 0)        /* last vCPU of the rdist */
> +#define VGIC_V3_LPIS_ENABLED    (1 << 1)
>          uint8_t flags;
>      } vgic;
>
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 08d6294..2371960 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -314,6 +314,9 @@ 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);
>
> +int vgic_access_guest_memory(struct domain *d, paddr_t gpa, void *addr,
> +                             uint32_t size, bool_t is_write);
> +
>  extern int domain_vgic_register(struct domain *d, int *mmio_count);
>  extern int vcpu_vgic_free(struct vcpu *v);
>  extern bool vgic_to_sgi(struct vcpu *v, register_t sgir,
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs
  2017-04-06 19:09     ` Andre Przywara
@ 2017-04-06 20:43       ` Stefano Stabellini
  0 siblings, 0 replies; 122+ messages in thread
From: Stefano Stabellini @ 2017-04-06 20:43 UTC (permalink / raw)
  To: Andre Przywara
  Cc: xen-devel, Julien Grall, Stefano Stabellini, Shanker Donthineni,
	Vijay Kilari

On Thu, 6 Apr 2017, Andre Przywara wrote:
> On 06/04/17 00:58, Stefano Stabellini wrote:
> > On Thu, 6 Apr 2017, 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. We do this by storing the pending bit in struct
> >> pending_irq, which is assiociated with mapped LPIs.
> >>
> >> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> >> ---
> >>  xen/arch/arm/gic-v3-lpi.c | 26 +++++++++++++++++++++++++-
> >>  1 file changed, 25 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/xen/arch/arm/gic-v3-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> >> index d8baebc..7d20986 100644
> >> --- a/xen/arch/arm/gic-v3-lpi.c
> >> +++ b/xen/arch/arm/gic-v3-lpi.c
> >> @@ -136,6 +136,22 @@ uint64_t gicv3_get_redist_address(unsigned int cpu, bool use_pta)
> >>          return per_cpu(lpi_redist, cpu).redist_id << 16;
> >>  }
> >>  
> >> +static bool vgic_can_inject_lpi(struct vcpu *vcpu, uint32_t vlpi)
> >> +{
> >> +    struct pending_irq *p;
> >> +
> >> +    p = vcpu->domain->arch.vgic.handler->lpi_to_pending(vcpu->domain, vlpi);
> >> +    if ( !p )
> >> +        return false;
> >> +
> >> +    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
> >> +        return true;
> >> +
> >> +    set_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);
> > 
> > See alpine.DEB.2.10.1701051422020.2866@sstabellini-ThinkPad-X260
> 
> (from this email:)
> > 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).
> 
> So I can surely change the function to return that information, but I
> think we don't have a good way of handling the storm easily.
> First sending the required INV command to let the host know about our
> change to the property table might take some time (we have a timeout in
> there), also takes a spinlock. Which doesn't sound too good in the
> interrupt injection path.
> But secondly re-enabling the interrupt is not easily possible currently.
> Ideally one would expect this to happen when the guest enables the
> corresponding virtual LPI, but that would again require to send an INV
> command to the host ITS, which is something that we avoid when triggered
> by a guest (the MAPD exception is only for Dom0 and will hopefully go
> away later).
> So a guest could send virtual INVs (disabling and enabling the virtual
> LPI) and trying to flood the host command queue.

I was thinking of clearing the enable bit in the LPI configuration table
for the physical LPI, and clearing GIC_IRQ_GUEST_ENABLED. We need to set
a new flag to say "this LPI has been forcefully disabled." Re- enabling
the LPI is not as important, but it could be done on EOI
(gic_update_one_lr).


> So shall I change the function anyway and add a TODO comment, so that we
> can later revisit this scenario?

That would be the minimum requirement for me, adding a reference to
http://marc.info/?l=xen-devel&m=148055519432739.

 
> >> +    return false;
> >> +}
> >> +
> >>  /*
> >>   * Handle incoming LPIs, which are a bit special, because they are potentially
> >>   * numerous and also only get injected into guests. Treat them specially here,
> >> @@ -187,7 +203,15 @@ void gicv3_do_LPI(unsigned int lpi)
> >>  
> >>      /* Check if the VCPU is ready to receive LPIs. */
> >>      if ( vcpu->arch.vgic.flags & VGIC_V3_LPIS_ENABLED )
> >> -        vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
> >> +    {
> >> +        /*
> >> +         * 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) )
> >> +            vgic_vcpu_inject_irq(vcpu, hlpi.virt_lpi);
> >> +    }
> >>  
> >>      rcu_unlock_domain(d);
> >>  }
> >> -- 
> >> 2.8.2
> >>
> 

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

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

* Re: [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs
  2017-04-05 23:19 ` [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs Andre Przywara
  2017-04-05 23:58   ` Stefano Stabellini
@ 2017-04-06 21:20   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 21:20 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 12:19 AM, 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. We do this by storing the pending bit in struct
> pending_irq, which is assiociated with mapped LPIs.

s/assiociated/associated/

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-05 23:19 ` [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation Andre Przywara
  2017-04-06  0:21   ` Stefano Stabellini
@ 2017-04-06 21:43   ` Julien Grall
  2017-04-06 22:22     ` André Przywara
  2017-04-06 22:25   ` Julien Grall
  2017-04-06 22:39   ` Julien Grall
  3 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 21:43 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 12:19 AM, Andre Przywara wrote:
> 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/vgic-v3-its.c        | 416 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3.c            |   9 -
>  xen/include/asm-arm/gic_v3_defs.h |   9 +
>  xen/include/asm-arm/gic_v3_its.h  |   3 +
>  4 files changed, 428 insertions(+), 9 deletions(-)
>
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 9dfda59..f6bf1ee 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -78,6 +78,422 @@ void vgic_v3_its_free_domain(struct domain *d)
>      ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
>  }
>
> +/**************************************
> + * Functions that handle ITS commands *
> + **************************************/
> +
> +static uint64_t its_cmd_mask_field(uint64_t *its_cmd, unsigned int word,
> +                                   unsigned int shift, unsigned 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_get_ittaddr(cmd)        (its_cmd_mask_field(cmd, 2, 8, 44) << 8)
> +
> +#define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
> +
> +/*
> + * Requires the vcmd_lock to be held.
> + * TODO: Investigate whether we can be smarter here and don't need to hold
> + * the lock all of the time.
> + */
> +static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
> +{
> +    paddr_t addr = its->cbaser & GENMASK_ULL(51, 12);
> +    uint64_t command[4];
> +
> +    ASSERT(spin_is_locked(&its->vcmd_lock));
> +
> +    if ( its->cwriter >= ITS_CMD_BUFFER_SIZE(its->cbaser) )
> +        return -1;
> +
> +    while ( its->creadr != its->cwriter )
> +    {
> +        int ret;
> +
> +        ret = vgic_access_guest_memory(d, addr + its->creadr,
> +                                       command, sizeof(command), false);
> +        if ( ret )
> +            return ret;
> +
> +        switch ( its_cmd_get_command(command) )
> +        {
> +        case GITS_CMD_SYNC:
> +            /* We handle ITS commands synchronously, so we ignore SYNC. */
> +            break;
> +        default:
> +            gdprintk(XENLOG_WARNING, "ITS: unhandled ITS command %lu\n",
> +                     its_cmd_get_command(command));
> +            break;
> +        }
> +
> +        its->creadr += ITS_CMD_SIZE;
> +        if ( its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser) )
> +            its->creadr = 0;
> +
> +        if ( ret )
> +            gdprintk(XENLOG_WARNING,
> +                     "ITS: ITS command error %d while handling command %lu\n",
> +                     ret, its_cmd_get_command(command));
> +    }
> +
> +    return 0;
> +}
> +
> +/*****************************
> + * ITS registers read access *
> + *****************************/
> +
> +static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
> +                                 register_t *r, void *priv)
> +{
> +    struct virt_its *its = priv;
> +    uint64_t reg;
> +
> +    switch ( info->gpa & 0xffff )
> +    {
> +    case VREG32(GITS_CTLR):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);
> +        spin_lock(&its->its_lock);
> +        if ( its->enabled )
> +            reg = GITS_CTLR_ENABLE;
> +        else
> +            reg = 0;
> +
> +        if ( its->cwriter == its->creadr )

I think it would be better if you can read atomically cwriter and 
creadr. This would avoid to take the vcmd_lock here, as this would 
prevent a guest vCPU to access this register while you are processing 
the command queue.

> +            reg |= GITS_CTLR_QUIESCENT;
> +        spin_unlock(&its->its_lock);
> +        spin_unlock(&its->vcmd_lock);
> +
> +        *r = vgic_reg32_extract(reg, 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 ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        reg = GITS_TYPER_PHYSICAL;
> +        reg |= (sizeof(struct vits_itte) - 1) << GITS_TYPER_ITT_SIZE_SHIFT;
> +        reg |= (its->intid_bits - 1) << GITS_TYPER_IDBITS_SHIFT;
> +        reg |= (its->devid_bits - 1) << GITS_TYPER_DEVIDS_SHIFT;
> +        *r = vgic_reg64_extract(reg, info);
> +        break;
> +    case VREG64(GITS_CBASER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->its_lock);
> +        *r = vgic_reg64_extract(its->cbaser, info);
> +        spin_unlock(&its->its_lock);
> +        break;
> +    case VREG64(GITS_CWRITER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->vcmd_lock);

Same remark here.


> +        *r = vgic_reg64_extract(its->cwriter, info);
> +        spin_unlock(&its->vcmd_lock);
> +        break;
> +    case VREG64(GITS_CREADR):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->vcmd_lock);

Ditto.

> +        *r = vgic_reg64_extract(its->creadr, info);
> +        spin_unlock(&its->vcmd_lock);
> +        break;
> +    case VREG64(GITS_BASER0):           /* device table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->its_lock);
> +        *r = vgic_reg64_extract(its->baser_dev, info);
> +        spin_unlock(&its->its_lock);
> +        break;
> +    case VREG64(GITS_BASER1):           /* collection table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +        spin_lock(&its->its_lock);
> +        *r = vgic_reg64_extract(its->baser_coll, info);
> +        spin_unlock(&its->its_lock);
> +        break;
> +    case VRANGE64(GITS_BASER2, GITS_BASER7):
> +        goto read_as_zero_64;
> +
> +    case VREG32(GITS_PIDR2):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +        *r = vgic_reg32_extract(GIC_PIDR2_ARCH_GICv3, info);
> +        break;
> +    }
> +
> +    return 1;
> +
> +read_as_zero_64:
> +    if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +    *r = 0;
> +
> +    return 1;
> +
> +bad_width:
> +    printk(XENLOG_G_ERR "vGIIS: bad read width %d r%d offset %#08lx\n",
> +           info->dabt.size, info->dabt.reg, (unsigned long)info->gpa & 0xffff);
> +    domain_crash_synchronous();
> +
> +    return 0;
> +}
> +
> +/******************************
> + * ITS registers write access *
> + ******************************/
> +
> +static unsigned int its_baser_table_size(uint64_t baser)
> +{
> +    unsigned int ret, page_size[4] = {SZ_4K, SZ_16K, SZ_64K, SZ_64K};
> +
> +    ret = page_size[(baser >> GITS_BASER_PAGE_SIZE_SHIFT) & 3];
> +
> +    return ret * ((baser & GITS_BASER_SIZE_MASK) + 1);
> +}
> +
> +static int its_baser_nr_entries(uint64_t baser)

As said on v4, this should return unsigned int.

> +{
> +    int entry_size = GITS_BASER_ENTRY_SIZE(baser);

Ditto.

> +
> +    return its_baser_table_size(baser) / entry_size;
> +}
> +
> +/* Must be called with the ITS lock held. */
> +static bool vgic_v3_verify_its_status(struct virt_its *its, bool status)
> +{
> +    ASSERT(spin_is_locked(&its->its_lock));
> +
> +    if ( !status )
> +        return false;
> +
> +    if ( !(its->cbaser & GITS_VALID_BIT) ||
> +         !(its->baser_dev & GITS_VALID_BIT) ||
> +         !(its->baser_coll & GITS_VALID_BIT) )
> +        return false;
> +
> +    return true;
> +}
> +
> +static void sanitize_its_base_reg(uint64_t *reg)
> +{
> +    uint64_t r = *reg;
> +
> +    /* Avoid outer shareable. */
> +    switch ( (r >> GITS_BASER_SHAREABILITY_SHIFT) & 0x03 )
> +    {
> +    case GIC_BASER_OuterShareable:
> +        r = r & ~GITS_BASER_SHAREABILITY_MASK;
> +        r |= GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    /* Avoid any inner non-cacheable mapping. */
> +    switch ( (r >> GITS_BASER_INNER_CACHEABILITY_SHIFT) & 0x07 )
> +    {
> +    case GIC_BASER_CACHE_nCnB:
> +    case GIC_BASER_CACHE_nC:
> +        r = r & ~GITS_BASER_INNER_CACHEABILITY_MASK;
> +        r |= GIC_BASER_CACHE_RaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    /* Only allow non-cacheable or same-as-inner. */
> +    switch ( (r >> GITS_BASER_OUTER_CACHEABILITY_SHIFT) & 0x07 )
> +    {
> +    case GIC_BASER_CACHE_SameAsInner:
> +    case GIC_BASER_CACHE_nC:
> +        break;
> +    default:
> +        r = r & ~GITS_BASER_OUTER_CACHEABILITY_MASK;
> +        r |= GIC_BASER_CACHE_nC << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
> +        break;
> +    }
> +
> +    *reg = r;
> +}
> +
> +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 reg32, ctlr;
> +
> +    switch ( info->gpa & 0xffff )
> +    {
> +    case VREG32(GITS_CTLR):
> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);

I am not sure to understand why taking the vcmd_lock here.

> +        spin_lock(&its->its_lock);
> +        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
> +        reg32 = ctlr;
> +        vgic_reg32_update(&reg32, r, info);
> +
> +        if ( ctlr ^ reg32 )
> +            its->enabled = vgic_v3_verify_its_status(its,
> +                                                     reg32 & GITS_CTLR_ENABLE);

Should not you print a warning to help a user debugging if you don't 
enable ITS as expected?

Also, how do you disable it?

> +        spin_unlock(&its->its_lock);
> +        spin_unlock(&its->vcmd_lock);
> +        return 1;
> +
> +    case VREG32(GITS_IIDR):
> +        goto write_ignore_32;
> +    case VREG32(GITS_TYPER):
> +        goto write_ignore_32;
> +    case VREG64(GITS_CBASER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);
> +        spin_lock(&its->its_lock);
> +        /* Changing base registers with the ITS enabled is UNPREDICTABLE. */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            spin_unlock(&its->vcmd_lock);
> +            gdprintk(XENLOG_WARNING,
> +                     "ITS: tried to change CBASER with the ITS enabled.\n");
> +            return 1;
> +        }
> +
> +        reg = its->cbaser;
> +        vgic_reg64_update(&reg, r, info);
> +        sanitize_its_base_reg(&reg);
> +
> +        its->cbaser = reg;
> +        its->creadr = 0;
> +        spin_unlock(&its->its_lock);
> +        spin_unlock(&its->vcmd_lock);
> +
> +        return 1;
> +
> +    case VREG64(GITS_CWRITER):
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->vcmd_lock);
> +        reg = its->cwriter & 0xfffe0;
> +        vgic_reg64_update(&reg, r, info);
> +        its->cwriter = reg & 0xfffe0;
> +
> +        if ( its->enabled )
> +        {
> +            int ret = vgic_its_handle_cmds(d, its);
> +
> +            if ( ret )
> +                printk(XENLOG_G_WARNING "error handling ITS commands\n");

You likely want to print the domain id here. So I would turn to gdprintk.

> +        }
> +        spin_unlock(&its->vcmd_lock);
> +
> +        return 1;
> +
> +    case VREG64(GITS_CREADR):
> +        goto write_ignore_64;
> +    case VREG64(GITS_BASER0):           /* device table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_WARNING, "ITS: tried to change BASER with the ITS enabled.\n");
> +
> +            return 1;
> +        }
> +
> +        reg = its->baser_dev;
> +        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;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_devices = its_baser_nr_entries(reg);
> +        else
> +            its->max_devices = 0;
> +
> +        its->baser_dev = reg;
> +        spin_unlock(&its->its_lock);
> +        return 1;
> +    case VREG64(GITS_BASER1):           /* collection table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_INFO, "ITS: tried to change BASER with the ITS enabled.\n");
> +            return 1;
> +        }
> +
> +        reg = its->baser_coll;
> +        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;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_collections = its_baser_nr_entries(reg);
> +        else
> +            its->max_collections = 0;
> +        its->baser_coll = reg;
> +        spin_unlock(&its->its_lock);
> +        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 "vGITS: bad write width %d r%d offset %#08lx\n",
> +           info->dabt.size, info->dabt.reg, (unsigned long)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
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 0623803..e6a33d0 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -158,15 +158,6 @@ static void vgic_store_irouter(struct domain *d, struct vgic_irq_rank *rank,
>      write_atomic(&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 b7e5a47..e60dea5 100644
> --- a/xen/include/asm-arm/gic_v3_defs.h
> +++ b/xen/include/asm-arm/gic_v3_defs.h
> @@ -198,6 +198,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 );
> +}

Again, this should be in vgic-emul.h and not gic_v3_defs.h

> +
>  #endif /* __ASM_ARM_GIC_V3_DEFS_H__ */
>
>  /*
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index eb71fbd..d3f393f 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -35,6 +35,7 @@
>  #define GITS_BASER5                     0x128
>  #define GITS_BASER6                     0x130
>  #define GITS_BASER7                     0x138
> +#define GITS_PIDR2                      GICR_PIDR2
>
>  /* Register bits */
>  #define GITS_VALID_BIT                  BIT_ULL(63)
> @@ -57,6 +58,7 @@
>  #define GITS_TYPER_ITT_SIZE_MASK        (0xfUL << GITS_TYPER_ITT_SIZE_SHIFT)
>  #define GITS_TYPER_ITT_SIZE(r)          ((((r) & GITS_TYPER_ITT_SIZE_MASK) >> \
>                                                   GITS_TYPER_ITT_SIZE_SHIFT) + 1)
> +#define GITS_TYPER_PHYSICAL             (1U << 0)
>
>  #define GITS_IIDR_VALUE                 0x34c
>
> @@ -78,6 +80,7 @@
>                          (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
>  #define GITS_BASER_SHAREABILITY_SHIFT   10
>  #define GITS_BASER_PAGE_SIZE_SHIFT      8
> +#define GITS_BASER_SIZE_MASK            0xff
>  #define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
>                                          (31UL << GITS_BASER_ENTRY_SIZE_SHIFT) |\
>                                          GITS_BASER_INDIRECT)
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table
  2017-04-05 23:18 ` [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
  2017-04-06 14:44   ` Julien Grall
@ 2017-04-06 22:19   ` Julien Grall
  2017-04-06 22:25     ` André Przywara
  1 sibling, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-06 22:19 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 12:18 AM, Andre Przywara wrote:
> Each ITS maps a pair of a DeviceID (for instance derived from a 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 over to the ITS.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
> ---
>  xen/arch/arm/gic-v3-its.c        | 132 +++++++++++++++++++++++++++++++++++++++
>  xen/include/asm-arm/gic_v3_its.h |  32 ++++++++++
>  2 files changed, 164 insertions(+)
>
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index 0298866..eef2933 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -35,9 +35,109 @@ bool gicv3_its_host_has_its(void)
>      return !list_empty(&host_its_list);
>  }
>
> +#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_ULL(58, 56) | GENMASK_ULL(52, 48))

It is a bit odd that you have defined BASER_RO_MASK and ...

[...]

> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 295eb22..6e51b98 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h

[...]

> +#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_ENTRY_SIZE(reg)                                       \
> +                        (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
> +#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)

... GITS_BASER_RO_MASK which from my understand covers the same 
registers but for different purpose.

Looking more closely, this last definition should belong to vgic-v3-its.c

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-06 21:43   ` Julien Grall
@ 2017-04-06 22:22     ` André Przywara
  2017-04-06 22:34       ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: André Przywara @ 2017-04-06 22:22 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

On 06/04/17 22:43, Julien Grall wrote:

Salut Julien,

> On 04/06/2017 12:19 AM, Andre Przywara wrote:
>> 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/vgic-v3-its.c        | 416
>> ++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/vgic-v3.c            |   9 -
>>  xen/include/asm-arm/gic_v3_defs.h |   9 +
>>  xen/include/asm-arm/gic_v3_its.h  |   3 +
>>  4 files changed, 428 insertions(+), 9 deletions(-)
>>
>> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
>> index 9dfda59..f6bf1ee 100644
>> --- a/xen/arch/arm/vgic-v3-its.c
>> +++ b/xen/arch/arm/vgic-v3-its.c
>> @@ -78,6 +78,422 @@ void vgic_v3_its_free_domain(struct domain *d)
>>      ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
>>  }
>>
>> +/**************************************
>> + * Functions that handle ITS commands *
>> + **************************************/
>> +
>> +static uint64_t its_cmd_mask_field(uint64_t *its_cmd, unsigned int word,
>> +                                   unsigned int shift, unsigned 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_get_ittaddr(cmd)        (its_cmd_mask_field(cmd, 2,
>> 8, 44) << 8)
>> +
>> +#define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>> +
>> +/*
>> + * Requires the vcmd_lock to be held.
>> + * TODO: Investigate whether we can be smarter here and don't need to
>> hold
>> + * the lock all of the time.
>> + */
>> +static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>> +{
>> +    paddr_t addr = its->cbaser & GENMASK_ULL(51, 12);
>> +    uint64_t command[4];
>> +
>> +    ASSERT(spin_is_locked(&its->vcmd_lock));
>> +
>> +    if ( its->cwriter >= ITS_CMD_BUFFER_SIZE(its->cbaser) )
>> +        return -1;
>> +
>> +    while ( its->creadr != its->cwriter )
>> +    {
>> +        int ret;
>> +
>> +        ret = vgic_access_guest_memory(d, addr + its->creadr,
>> +                                       command, sizeof(command), false);
>> +        if ( ret )
>> +            return ret;
>> +
>> +        switch ( its_cmd_get_command(command) )
>> +        {
>> +        case GITS_CMD_SYNC:
>> +            /* We handle ITS commands synchronously, so we ignore
>> SYNC. */
>> +            break;
>> +        default:
>> +            gdprintk(XENLOG_WARNING, "ITS: unhandled ITS command %lu\n",
>> +                     its_cmd_get_command(command));
>> +            break;
>> +        }
>> +
>> +        its->creadr += ITS_CMD_SIZE;
>> +        if ( its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser) )
>> +            its->creadr = 0;
>> +
>> +        if ( ret )
>> +            gdprintk(XENLOG_WARNING,
>> +                     "ITS: ITS command error %d while handling
>> command %lu\n",
>> +                     ret, its_cmd_get_command(command));
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/*****************************
>> + * ITS registers read access *
>> + *****************************/
>> +
>> +static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
>> +                                 register_t *r, void *priv)
>> +{
>> +    struct virt_its *its = priv;
>> +    uint64_t reg;
>> +
>> +    switch ( info->gpa & 0xffff )
>> +    {
>> +    case VREG32(GITS_CTLR):
>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
>> +        spin_lock(&its->its_lock);
>> +        if ( its->enabled )
>> +            reg = GITS_CTLR_ENABLE;
>> +        else
>> +            reg = 0;
>> +
>> +        if ( its->cwriter == its->creadr )
> 
> I think it would be better if you can read atomically cwriter and
> creadr.

What do you mean by "atomically" here? For reading and comparing *both*
registers I have to hold a lock, isn't it?

> This would avoid to take the vcmd_lock here, as this would
> prevent a guest vCPU to access this register while you are processing
> the command queue.

That's a noble goal, but I don't know if this is easily feasible.
I wonder if one can use spin_trylock(&its->vcmd_lock) here, and assume
not quiescent if that fails? My understanding is that an OS would poll
CTLR until it becomes quiescent (Linux does so), so it would try again,
returning here until the lock becomes actually free.
Would that work?
But looking at the Linux code again I see that this is only done once at
probe time (before the ITS is enabled), so at least from that side it's
not really worth optimizing.

>> +            reg |= GITS_CTLR_QUIESCENT;
>> +        spin_unlock(&its->its_lock);
>> +        spin_unlock(&its->vcmd_lock);
>> +
>> +        *r = vgic_reg32_extract(reg, 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 ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        reg = GITS_TYPER_PHYSICAL;
>> +        reg |= (sizeof(struct vits_itte) - 1) <<
>> GITS_TYPER_ITT_SIZE_SHIFT;
>> +        reg |= (its->intid_bits - 1) << GITS_TYPER_IDBITS_SHIFT;
>> +        reg |= (its->devid_bits - 1) << GITS_TYPER_DEVIDS_SHIFT;
>> +        *r = vgic_reg64_extract(reg, info);
>> +        break;
>> +    case VREG64(GITS_CBASER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->its_lock);
>> +        *r = vgic_reg64_extract(its->cbaser, info);
>> +        spin_unlock(&its->its_lock);
>> +        break;
>> +    case VREG64(GITS_CWRITER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->vcmd_lock);
> 
> Same remark here.

Indeed it looks like I don't need the lock here.
That other open source hypervisor certainly doesn't ;-)

>> +        *r = vgic_reg64_extract(its->cwriter, info);
>> +        spin_unlock(&its->vcmd_lock);
>> +        break;
>> +    case VREG64(GITS_CREADR):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->vcmd_lock);
> 
> Ditto.

Currently this does not work because it could read it when creadr
reaches the end of the buffer (before the code above resets it to 0).
But by rewriting that code using a local variable this should be doable.

>> +        *r = vgic_reg64_extract(its->creadr, info);
>> +        spin_unlock(&its->vcmd_lock);
>> +        break;
>> +    case VREG64(GITS_BASER0):           /* device table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->its_lock);
>> +        *r = vgic_reg64_extract(its->baser_dev, info);
>> +        spin_unlock(&its->its_lock);
>> +        break;
>> +    case VREG64(GITS_BASER1):           /* collection table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +        spin_lock(&its->its_lock);
>> +        *r = vgic_reg64_extract(its->baser_coll, info);
>> +        spin_unlock(&its->its_lock);
>> +        break;
>> +    case VRANGE64(GITS_BASER2, GITS_BASER7):
>> +        goto read_as_zero_64;
>> +
>> +    case VREG32(GITS_PIDR2):
>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>> +        *r = vgic_reg32_extract(GIC_PIDR2_ARCH_GICv3, info);
>> +        break;
>> +    }
>> +
>> +    return 1;
>> +
>> +read_as_zero_64:
>> +    if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +    *r = 0;
>> +
>> +    return 1;
>> +
>> +bad_width:
>> +    printk(XENLOG_G_ERR "vGIIS: bad read width %d r%d offset %#08lx\n",
>> +           info->dabt.size, info->dabt.reg, (unsigned long)info->gpa
>> & 0xffff);
>> +    domain_crash_synchronous();
>> +
>> +    return 0;
>> +}
>> +
>> +/******************************
>> + * ITS registers write access *
>> + ******************************/
>> +
>> +static unsigned int its_baser_table_size(uint64_t baser)
>> +{
>> +    unsigned int ret, page_size[4] = {SZ_4K, SZ_16K, SZ_64K, SZ_64K};
>> +
>> +    ret = page_size[(baser >> GITS_BASER_PAGE_SIZE_SHIFT) & 3];
>> +
>> +    return ret * ((baser & GITS_BASER_SIZE_MASK) + 1);
>> +}
>> +
>> +static int its_baser_nr_entries(uint64_t baser)
> 
> As said on v4, this should return unsigned int.
> 
>> +{
>> +    int entry_size = GITS_BASER_ENTRY_SIZE(baser);
> 
> Ditto.

I found this myself today and fixed that already.
Sorry for missing that.

> 
>> +
>> +    return its_baser_table_size(baser) / entry_size;
>> +}
>> +
>> +/* Must be called with the ITS lock held. */
>> +static bool vgic_v3_verify_its_status(struct virt_its *its, bool status)
>> +{
>> +    ASSERT(spin_is_locked(&its->its_lock));
>> +
>> +    if ( !status )
>> +        return false;
>> +
>> +    if ( !(its->cbaser & GITS_VALID_BIT) ||
>> +         !(its->baser_dev & GITS_VALID_BIT) ||
>> +         !(its->baser_coll & GITS_VALID_BIT) )
>> +        return false;
>> +
>> +    return true;
>> +}
>> +
>> +static void sanitize_its_base_reg(uint64_t *reg)
>> +{
>> +    uint64_t r = *reg;
>> +
>> +    /* Avoid outer shareable. */
>> +    switch ( (r >> GITS_BASER_SHAREABILITY_SHIFT) & 0x03 )
>> +    {
>> +    case GIC_BASER_OuterShareable:
>> +        r = r & ~GITS_BASER_SHAREABILITY_MASK;
>> +        r |= GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +
>> +    /* Avoid any inner non-cacheable mapping. */
>> +    switch ( (r >> GITS_BASER_INNER_CACHEABILITY_SHIFT) & 0x07 )
>> +    {
>> +    case GIC_BASER_CACHE_nCnB:
>> +    case GIC_BASER_CACHE_nC:
>> +        r = r & ~GITS_BASER_INNER_CACHEABILITY_MASK;
>> +        r |= GIC_BASER_CACHE_RaWb <<
>> GITS_BASER_INNER_CACHEABILITY_SHIFT;
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +
>> +    /* Only allow non-cacheable or same-as-inner. */
>> +    switch ( (r >> GITS_BASER_OUTER_CACHEABILITY_SHIFT) & 0x07 )
>> +    {
>> +    case GIC_BASER_CACHE_SameAsInner:
>> +    case GIC_BASER_CACHE_nC:
>> +        break;
>> +    default:
>> +        r = r & ~GITS_BASER_OUTER_CACHEABILITY_MASK;
>> +        r |= GIC_BASER_CACHE_nC << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
>> +        break;
>> +    }
>> +
>> +    *reg = r;
>> +}
>> +
>> +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 reg32, ctlr;
>> +
>> +    switch ( info->gpa & 0xffff )
>> +    {
>> +    case VREG32(GITS_CTLR):
>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
> 
> I am not sure to understand why taking the vcmd_lock here.

To prevent a nasty guest from turning off the ITS while commands are
still handled.

>> +        spin_lock(&its->its_lock);
>> +        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
>> +        reg32 = ctlr;
>> +        vgic_reg32_update(&reg32, r, info);
>> +
>> +        if ( ctlr ^ reg32 )
>> +            its->enabled = vgic_v3_verify_its_status(its,
>> +                                                     reg32 &
>> GITS_CTLR_ENABLE);
> 
> Should not you print a warning to help a user debugging if you don't
> enable ITS as expected?

Good idea.

> Also, how do you disable it?

Not sure what you mean? vgic_v3_verify_its_status() returns false if the
ENABLE_BIT has been cleared, so its->enabled becomes false.
Or what do I miss?

>> +        spin_unlock(&its->its_lock);
>> +        spin_unlock(&its->vcmd_lock);
>> +        return 1;
>> +
>> +    case VREG32(GITS_IIDR):
>> +        goto write_ignore_32;
>> +    case VREG32(GITS_TYPER):
>> +        goto write_ignore_32;
>> +    case VREG64(GITS_CBASER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
>> +        spin_lock(&its->its_lock);
>> +        /* Changing base registers with the ITS enabled is
>> UNPREDICTABLE. */
>> +        if ( its->enabled )
>> +        {
>> +            spin_unlock(&its->its_lock);
>> +            spin_unlock(&its->vcmd_lock);
>> +            gdprintk(XENLOG_WARNING,
>> +                     "ITS: tried to change CBASER with the ITS
>> enabled.\n");
>> +            return 1;
>> +        }
>> +
>> +        reg = its->cbaser;
>> +        vgic_reg64_update(&reg, r, info);
>> +        sanitize_its_base_reg(&reg);
>> +
>> +        its->cbaser = reg;
>> +        its->creadr = 0;
>> +        spin_unlock(&its->its_lock);
>> +        spin_unlock(&its->vcmd_lock);
>> +
>> +        return 1;
>> +
>> +    case VREG64(GITS_CWRITER):
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->vcmd_lock);
>> +        reg = its->cwriter & 0xfffe0;
>> +        vgic_reg64_update(&reg, r, info);
>> +        its->cwriter = reg & 0xfffe0;
>> +
>> +        if ( its->enabled )
>> +        {
>> +            int ret = vgic_its_handle_cmds(d, its);
>> +
>> +            if ( ret )
>> +                printk(XENLOG_G_WARNING "error handling ITS
>> commands\n");
> 
> You likely want to print the domain id here. So I would turn to gdprintk.

Indeed.

>> +        }
>> +        spin_unlock(&its->vcmd_lock);
>> +
>> +        return 1;
>> +
>> +    case VREG64(GITS_CREADR):
>> +        goto write_ignore_64;
>> +    case VREG64(GITS_BASER0):           /* device table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->its_lock);
>> +
>> +        /*
>> +         * Changing base registers with the ITS enabled is
>> UNPREDICTABLE,
>> +         * we choose to ignore it, but warn.
>> +         */
>> +        if ( its->enabled )
>> +        {
>> +            spin_unlock(&its->its_lock);
>> +            gdprintk(XENLOG_WARNING, "ITS: tried to change BASER with
>> the ITS enabled.\n");
>> +
>> +            return 1;
>> +        }
>> +
>> +        reg = its->baser_dev;
>> +        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;
>> +        sanitize_its_base_reg(&reg);
>> +
>> +        if ( reg & GITS_VALID_BIT )
>> +            its->max_devices = its_baser_nr_entries(reg);
>> +        else
>> +            its->max_devices = 0;
>> +
>> +        its->baser_dev = reg;
>> +        spin_unlock(&its->its_lock);
>> +        return 1;
>> +    case VREG64(GITS_BASER1):           /* collection table */
>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>> +
>> +        spin_lock(&its->its_lock);
>> +        /*
>> +         * Changing base registers with the ITS enabled is
>> UNPREDICTABLE,
>> +         * we choose to ignore it, but warn.
>> +         */
>> +        if ( its->enabled )
>> +        {
>> +            spin_unlock(&its->its_lock);
>> +            gdprintk(XENLOG_INFO, "ITS: tried to change BASER with
>> the ITS enabled.\n");
>> +            return 1;
>> +        }
>> +
>> +        reg = its->baser_coll;
>> +        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;
>> +        sanitize_its_base_reg(&reg);
>> +
>> +        if ( reg & GITS_VALID_BIT )
>> +            its->max_collections = its_baser_nr_entries(reg);
>> +        else
>> +            its->max_collections = 0;
>> +        its->baser_coll = reg;
>> +        spin_unlock(&its->its_lock);
>> +        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 "vGITS: bad write width %d r%d offset %#08lx\n",
>> +           info->dabt.size, info->dabt.reg, (unsigned long)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
>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>> index 0623803..e6a33d0 100644
>> --- a/xen/arch/arm/vgic-v3.c
>> +++ b/xen/arch/arm/vgic-v3.c
>> @@ -158,15 +158,6 @@ static void vgic_store_irouter(struct domain *d,
>> struct vgic_irq_rank *rank,
>>      write_atomic(&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 b7e5a47..e60dea5 100644
>> --- a/xen/include/asm-arm/gic_v3_defs.h
>> +++ b/xen/include/asm-arm/gic_v3_defs.h
>> @@ -198,6 +198,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 );
>> +}
> 
> Again, this should be in vgic-emul.h and not gic_v3_defs.h

Fixed in my tree already. Sorry for missing that again.

Cheers,
Andre.

>> +
>>  #endif /* __ASM_ARM_GIC_V3_DEFS_H__ */
>>
>>  /*
>> diff --git a/xen/include/asm-arm/gic_v3_its.h
>> b/xen/include/asm-arm/gic_v3_its.h
>> index eb71fbd..d3f393f 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -35,6 +35,7 @@
>>  #define GITS_BASER5                     0x128
>>  #define GITS_BASER6                     0x130
>>  #define GITS_BASER7                     0x138
>> +#define GITS_PIDR2                      GICR_PIDR2
>>
>>  /* Register bits */
>>  #define GITS_VALID_BIT                  BIT_ULL(63)
>> @@ -57,6 +58,7 @@
>>  #define GITS_TYPER_ITT_SIZE_MASK        (0xfUL <<
>> GITS_TYPER_ITT_SIZE_SHIFT)
>>  #define GITS_TYPER_ITT_SIZE(r)          ((((r) &
>> GITS_TYPER_ITT_SIZE_MASK) >> \
>>                                                  
>> GITS_TYPER_ITT_SIZE_SHIFT) + 1)
>> +#define GITS_TYPER_PHYSICAL             (1U << 0)
>>
>>  #define GITS_IIDR_VALUE                 0x34c
>>
>> @@ -78,6 +80,7 @@
>>                          (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) &
>> 0x1f) + 1)
>>  #define GITS_BASER_SHAREABILITY_SHIFT   10
>>  #define GITS_BASER_PAGE_SIZE_SHIFT      8
>> +#define GITS_BASER_SIZE_MASK            0xff
>>  #define GITS_BASER_RO_MASK              (GITS_BASER_TYPE_MASK | \
>>                                          (31UL <<
>> GITS_BASER_ENTRY_SIZE_SHIFT) |\
>>                                          GITS_BASER_INDIRECT)
>>
> 
> Cheers,
> 


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

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

* Re: [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-05 23:19 ` [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation Andre Przywara
  2017-04-06  0:21   ` Stefano Stabellini
  2017-04-06 21:43   ` Julien Grall
@ 2017-04-06 22:25   ` Julien Grall
  2017-04-06 22:39   ` Julien Grall
  3 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 22:25 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 12:19 AM, Andre Przywara wrote:
> +    case VREG64(GITS_BASER0):           /* device table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_WARNING, "ITS: tried to change BASER with the ITS enabled.\n");
> +
> +            return 1;
> +        }
> +
> +        reg = its->baser_dev;
> +        vgic_reg64_update(&reg, r, info);
> +
> +        reg &= ~GITS_BASER_RO_MASK;

It took me a bit of time to understand how you ensure the guest will not 
hand over two-level table. But this is hidden in the GITS_BASER_RO_MASK 
as you always mask the indirect bit.

I would prefer if you make clear that two-level table are not currently 
supported with a comment.


> +        reg |= (sizeof(uint64_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
> +        reg |= GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_devices = its_baser_nr_entries(reg);
> +        else
> +            its->max_devices = 0;
> +
> +        its->baser_dev = reg;
> +        spin_unlock(&its->its_lock);
> +        return 1;
> +    case VREG64(GITS_BASER1):           /* collection table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_INFO, "ITS: tried to change BASER with the ITS enabled.\n");
> +            return 1;
> +        }
> +
> +        reg = its->baser_coll;
> +        vgic_reg64_update(&reg, r, info);
> +        reg &= ~GITS_BASER_RO_MASK;

Ditto.

> +        reg |= (sizeof(uint16_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
> +        reg |= GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_collections = its_baser_nr_entries(reg);
> +        else
> +            its->max_collections = 0;
> +        its->baser_coll = reg;
> +        spin_unlock(&its->its_lock);
> +        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;
> +    }


Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table
  2017-04-06 22:19   ` Julien Grall
@ 2017-04-06 22:25     ` André Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: André Przywara @ 2017-04-06 22:25 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

On 06/04/17 23:19, Julien Grall wrote:
> Hi Andre,
> 
> On 04/06/2017 12:18 AM, Andre Przywara wrote:
>> Each ITS maps a pair of a DeviceID (for instance derived from a 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 over to the ITS.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
>> ---
>>  xen/arch/arm/gic-v3-its.c        | 132
>> +++++++++++++++++++++++++++++++++++++++
>>  xen/include/asm-arm/gic_v3_its.h |  32 ++++++++++
>>  2 files changed, 164 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index 0298866..eef2933 100644
>> --- a/xen/arch/arm/gic-v3-its.c
>> +++ b/xen/arch/arm/gic-v3-its.c
>> @@ -35,9 +35,109 @@ bool gicv3_its_host_has_its(void)
>>      return !list_empty(&host_its_list);
>>  }
>>
>> +#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_ULL(58, 56) | GENMASK_ULL(52, 48))
> 
> It is a bit odd that you have defined BASER_RO_MASK and ...
> 
> [...]
> 
>> diff --git a/xen/include/asm-arm/gic_v3_its.h
>> b/xen/include/asm-arm/gic_v3_its.h
>> index 295eb22..6e51b98 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
> 
> [...]
> 
>> +#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_ENTRY_SIZE(reg)                                       \
>> +                        (((reg >> GITS_BASER_ENTRY_SIZE_SHIFT) &
>> 0x1f) + 1)
>> +#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)
> 
> ... GITS_BASER_RO_MASK which from my understand covers the same
> registers but for different purpose.
> 
> Looking more closely, this last definition should belong to vgic-v3-its.c

Doh, good catch.

Cheers,
Andre


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

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

* Re: [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-06 22:22     ` André Przywara
@ 2017-04-06 22:34       ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 22:34 UTC (permalink / raw)
  To: André Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 11:22 PM, André Przywara wrote:
> On 06/04/17 22:43, Julien Grall wrote:
>> On 04/06/2017 12:19 AM, Andre Przywara wrote:
>>> +static int vgic_v3_its_mmio_read(struct vcpu *v, mmio_info_t *info,
>>> +                                 register_t *r, void *priv)
>>> +{
>>> +    struct virt_its *its = priv;
>>> +    uint64_t reg;
>>> +
>>> +    switch ( info->gpa & 0xffff )
>>> +    {
>>> +    case VREG32(GITS_CTLR):
>>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>>> +
>>> +        spin_lock(&its->vcmd_lock);
>>> +        spin_lock(&its->its_lock);
>>> +        if ( its->enabled )
>>> +            reg = GITS_CTLR_ENABLE;
>>> +        else
>>> +            reg = 0;
>>> +
>>> +        if ( its->cwriter == its->creadr )
>>
>> I think it would be better if you can read atomically cwriter and
>> creadr.
>
> What do you mean by "atomically" here? For reading and comparing *both*
> registers I have to hold a lock, isn't it?
>
>> This would avoid to take the vcmd_lock here, as this would
>> prevent a guest vCPU to access this register while you are processing
>> the command queue.
>
> That's a noble goal, but I don't know if this is easily feasible.
> I wonder if one can use spin_trylock(&its->vcmd_lock) here, and assume
> not quiescent if that fails? My understanding is that an OS would poll
> CTLR until it becomes quiescent (Linux does so), so it would try again,
> returning here until the lock becomes actually free.
> Would that work?
> But looking at the Linux code again I see that this is only done once at
> probe time (before the ITS is enabled), so at least from that side it's
> not really worth optimizing.

As you may know, I don't buy the reason: "Linux is only doing it once".

In this case my worry is not about optimizing because a guest could call 
it often but the fact the you might end up in all the vCPUs but one 
reading GITS_CTLR and one handling the command queue. So formers will 
wait on the lock which will monopolize the pCPUs.

The command queue handling is not ideal (it can take a bit of time), but 
this is going to be worst. And I really don't want to see that.

>>> +        *r = vgic_reg64_extract(its->cwriter, info);
>>> +        spin_unlock(&its->vcmd_lock);
>>> +        break;
>>> +    case VREG64(GITS_CREADR):
>>> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
>>> +        spin_lock(&its->vcmd_lock);
>>
>> Ditto.
>
> Currently this does not work because it could read it when creadr
> reaches the end of the buffer (before the code above resets it to 0).
> But by rewriting that code using a local variable this should be doable.

Please do it.

>>> +
>>> +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 reg32, ctlr;
>>> +
>>> +    switch ( info->gpa & 0xffff )
>>> +    {
>>> +    case VREG32(GITS_CTLR):
>>> +        if ( info->dabt.size != DABT_WORD ) goto bad_width;
>>> +
>>> +        spin_lock(&its->vcmd_lock);
>>
>> I am not sure to understand why taking the vcmd_lock here.
>
> To prevent a nasty guest from turning off the ITS while commands are
> still handled.

Please document it.

>
>>> +        spin_lock(&its->its_lock);
>>> +        ctlr = its->enabled ? GITS_CTLR_ENABLE : 0;
>>> +        reg32 = ctlr;
>>> +        vgic_reg32_update(&reg32, r, info);
>>> +
>>> +        if ( ctlr ^ reg32 )
>>> +            its->enabled = vgic_v3_verify_its_status(its,
>>> +                                                     reg32 &
>>> GITS_CTLR_ENABLE);
>>
>> Should not you print a warning to help a user debugging if you don't
>> enable ITS as expected?
>
> Good idea.
>
>> Also, how do you disable it?
>
> Not sure what you mean? vgic_v3_verify_its_status() returns false if the
> ENABLE_BIT has been cleared, so its->enabled becomes false.
> Or what do I miss?

I think I wrote the question and answered myself before sending the 
e-mail. Though I forgot to drop it.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation
  2017-04-05 23:19 ` [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation Andre Przywara
                     ` (2 preceding siblings ...)
  2017-04-06 22:25   ` Julien Grall
@ 2017-04-06 22:39   ` Julien Grall
  3 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 22:39 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

Another thing I missed.

On 04/06/2017 12:19 AM, Andre Przywara wrote:
> +    case VREG64(GITS_BASER0):           /* device table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_WARNING, "ITS: tried to change BASER with the ITS enabled.\n");
> +
> +            return 1;
> +        }
> +
> +        reg = its->baser_dev;
> +        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;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_devices = its_baser_nr_entries(reg);

Should not you validate this against the maximum number of device 
supported by the ITS (i.e devid)?

> +        else
> +            its->max_devices = 0;
> +
> +        its->baser_dev = reg;
> +        spin_unlock(&its->its_lock);
> +        return 1;
> +    case VREG64(GITS_BASER1):           /* collection table */
> +        if ( !vgic_reg64_check_access(info->dabt) ) goto bad_width;
> +
> +        spin_lock(&its->its_lock);
> +        /*
> +         * Changing base registers with the ITS enabled is UNPREDICTABLE,
> +         * we choose to ignore it, but warn.
> +         */
> +        if ( its->enabled )
> +        {
> +            spin_unlock(&its->its_lock);
> +            gdprintk(XENLOG_INFO, "ITS: tried to change BASER with the ITS enabled.\n");
> +            return 1;
> +        }
> +
> +        reg = its->baser_coll;
> +        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;
> +        sanitize_its_base_reg(&reg);
> +
> +        if ( reg & GITS_VALID_BIT )
> +            its->max_collections = its_baser_nr_entries(reg);

Similar question for the collection. Although, I am not sure against what.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 18/30] ARM: vITS: introduce translation table walks
  2017-04-05 23:19 ` [PATCH v5 18/30] ARM: vITS: introduce translation table walks Andre Przywara
@ 2017-04-06 22:41   ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-06 22:41 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 12:19 AM, Andre Przywara wrote:
> The ITS stores the target (v)CPU and the (virtual) LPI number in tables.
> Introduce functions to walk those tables and translate an device ID -
> event ID pair into a pair of virtual LPI and vCPU.
> We map those tables on demand - which is cheap on arm64. Also we take
> care of the locking on the way, since we can't easily protect those ITTs
> from being altered by the guest.
>
> To allow compiling without warnings, we declare two functions as
> non-static for the moment, which two later patches will fix.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic.c         |   2 +
>  xen/arch/arm/vgic-v3-its.c | 179 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 181 insertions(+)
>
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index a56be34..5000b0d 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -491,6 +491,8 @@ static void gic_update_one_lr(struct vcpu *v, int i)
>      {
>          gic_hw_ops->clear_lr(i);
>          clear_bit(i, &this_cpu(lr_mask));
> +        if ( is_lpi(irq) )
> +            clear_bit(GIC_IRQ_GUEST_LPI_PENDING, &p->status);

I am struggling to understand why this change is introduced in this 
patch. This does not look related to the translation table.

>
>          if ( p->desc != NULL )
>              clear_bit(_IRQ_INPROGRESS, &p->desc->status);
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index f6bf1ee..a145666 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -67,6 +67,8 @@ struct vits_itte
>      uint16_t pad;
>  };
>
> +#define UNMAPPED_COLLECTION      ((uint16_t)~0)
> +
>  void vgic_v3_its_init_domain(struct domain *d)
>  {
>      spin_lock_init(&d->arch.vgic.its_devices_lock);
> @@ -78,6 +80,183 @@ void vgic_v3_its_free_domain(struct domain *d)
>      ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
>  }
>
> +/*
> + * 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_ULL(47, 16)) |
> +                ((reg & GENMASK_ULL(15, 12)) << 36);
> +    else
> +        return reg & GENMASK_ULL(47, 12);
> +}
> +
> +/* Must be called with the ITS lock held. */
> +static struct vcpu *get_vcpu_from_collection(struct virt_its *its,
> +                                             uint16_t collid)
> +{
> +    paddr_t addr = get_baser_phys_addr(its->baser_coll);
> +    uint16_t vcpu_id;
> +    int ret;
> +
> +    ASSERT(spin_is_locked(&its->its_lock));
> +
> +    if ( collid >= its->max_collections )
> +        return NULL;
> +
> +    ret = vgic_access_guest_memory(its->d, addr + collid * sizeof(uint16_t),
> +                                   &vcpu_id, sizeof(vcpu_id), false);
> +    if ( ret )
> +        return NULL;
> +
> +    if ( vcpu_id == UNMAPPED_COLLECTION || vcpu_id >= its->d->max_vcpus )
> +        return NULL;
> +
> +    return its->d->vcpu[vcpu_id];
> +}
> +
> +/*
> + * Our device table encodings:
> + * Contains the guest physical address of the Interrupt Translation Table in
> + * bits [51:8], and the size of it is encoded as the number of bits minus one
> + * in the lowest 8 bits of the word.
> + */
> +#define DEV_TABLE_ITT_ADDR(x) ((x) & GENMASK_ULL(51, 8))
> +#define DEV_TABLE_ITT_SIZE(x) (BIT(((x) & GENMASK_ULL(7, 0)) + 1))
> +#define DEV_TABLE_ENTRY(addr, bits)                     \
> +        (((addr) & GENMASK_ULL(51, 8)) | (((bits) - 1) & GENMASK_ULL(7, 0)))
> +
> +/*
> + * Lookup the address of the Interrupt Translation Table associated with
> + * a device ID and return the address of the ITTE belonging to the event ID
> + * (which is an index into that table).

You likely want a TODO support two-level table here.

> + */
> +static paddr_t its_get_itte_address(struct virt_its *its,
> +                                    uint32_t devid, uint32_t evid)
> +{
> +    paddr_t addr = get_baser_phys_addr(its->baser_dev);
> +    uint64_t itt;
> +
> +    if ( devid >= its->max_devices )
> +        return INVALID_PADDR;
> +
> +    if ( vgic_access_guest_memory(its->d, addr + devid * sizeof(uint64_t),
> +                                  &itt, sizeof(itt), false) )
> +        return INVALID_PADDR;
> +
> +    if ( evid >= DEV_TABLE_ITT_SIZE(itt) ||
> +         DEV_TABLE_ITT_ADDR(itt) == INVALID_PADDR )
> +        return INVALID_PADDR;
> +
> +    return DEV_TABLE_ITT_ADDR(itt) + evid * sizeof(struct vits_itte);
> +}
> +
> +/*
> + * Queries the collection and device tables to get the vCPU and virtual
> + * LPI number for a given guest event. This first accesses the guest memory
> + * to resolve the address of the ITTE, then reads the ITTE entry at this
> + * address and puts the result in vcpu_ptr and vlpi_ptr.
> + * Requires the ITS lock to be held.
> + */
> +static bool read_itte_locked(struct virt_its *its, uint32_t devid,
> +                             uint32_t evid, struct vcpu **vcpu_ptr,
> +                             uint32_t *vlpi_ptr)
> +{
> +    paddr_t addr;
> +    struct vits_itte itte;
> +    struct vcpu *vcpu;
> +
> +    ASSERT(spin_is_locked(&its->its_lock));
> +
> +    addr = its_get_itte_address(its, devid, evid);
> +    if ( addr == INVALID_PADDR )
> +        return false;
> +
> +    if ( vgic_access_guest_memory(its->d, addr, &itte, sizeof(itte), false) )
> +        return false;
> +
> +    vcpu = get_vcpu_from_collection(its, itte.collection);
> +    if ( !vcpu )
> +        return false;
> +
> +    *vcpu_ptr = vcpu;
> +    *vlpi_ptr = itte.vlpi;
> +    return true;
> +}
> +
> +/*
> + * This function takes care of the locking by taking the its_lock itself, so
> + * a caller shall not hold this. Before returning, the lock is dropped again.
> + */
> +bool read_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> +               struct vcpu **vcpu_ptr, uint32_t *vlpi_ptr)
> +{
> +    bool ret;
> +
> +    spin_lock(&its->its_lock);
> +    ret = read_itte_locked(its, devid, evid, vcpu_ptr, vlpi_ptr);
> +    spin_unlock(&its->its_lock);
> +
> +    return ret;
> +}
> +
> +/*
> + * Queries the collection and device tables to translate the device ID and
> + * event ID and find the appropriate ITTE. The given collection ID and the
> + * virtual LPI number are then stored into that entry.
> + * If vcpu_ptr is provided, returns the VCPU belonging to that collection.
> + * Requires the ITS lock to be held.
> + */
> +static bool write_itte_locked(struct virt_its *its, uint32_t devid,
> +                              uint32_t evid, uint32_t collid, uint32_t vlpi,
> +                              struct vcpu **vcpu_ptr)
> +{
> +    paddr_t addr;
> +    struct vits_itte itte;
> +
> +    ASSERT(spin_is_locked(&its->its_lock));
> +
> +    if ( collid >= its->max_collections )
> +        return false;
> +
> +    if ( vlpi >= its->d->arch.vgic.nr_lpis )
> +        return false;
> +
> +    addr = its_get_itte_address(its, devid, evid);
> +    if ( addr == INVALID_PADDR )
> +        return false;
> +
> +    itte.collection = collid;
> +    itte.vlpi = vlpi;
> +
> +    if ( vgic_access_guest_memory(its->d, addr, &itte, sizeof(itte), true) )
> +        return false;
> +
> +    if ( vcpu_ptr )
> +        *vcpu_ptr = get_vcpu_from_collection(its, collid);
> +
> +    return true;
> +}
> +
> +/*
> + * This function takes care of the locking by taking the its_lock itself, so
> + * a caller shall not hold this. Before returning, the lock is dropped again.
> + */
> +bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> +                uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
> +{
> +    bool ret;
> +
> +    spin_lock(&its->its_lock);
> +    ret = write_itte_locked(its, devid, evid, collid, vlpi, vcpu_ptr);
> +    spin_unlock(&its->its_lock);
> +
> +    return ret;
> +}
> +
>  /**************************************
>   * Functions that handle ITS commands *
>   **************************************/
>

-- 
Julien Grall

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

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

* Re: [PATCH v5 30/30] ARM: vGIC: advertise LPI support
  2017-04-06 11:42       ` Julien Grall
@ 2017-04-06 22:54         ` André Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: André Przywara @ 2017-04-06 22:54 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

On 06/04/17 12:42, Julien Grall wrote:
> Hi,
> 
> On 06/04/17 11:21, Andre Przywara wrote:
>> Hi,
>>
>> On 06/04/17 02:04, Stefano Stabellini wrote:
>>> On Thu, 6 Apr 2017, Andre Przywara wrote:
>>>> 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.
>>>> This removes a "TBD" comment, as we now populate the processor number
>>>> in the GICR_TYPE register.
>>>> Advertise 24 bits worth of LPIs to the guest.
>>>>
>>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>>> ---
>>>>  xen/arch/arm/vgic-v3.c | 46
>>>> +++++++++++++++++++++++++++++++++++++++++-----
>>>>  1 file changed, 41 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
>>>> index 3b01247..ba0e79f 100644
>>>> --- a/xen/arch/arm/vgic-v3.c
>>>> +++ b/xen/arch/arm/vgic-v3.c
>>>> @@ -168,8 +168,12 @@ 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;
>>>> +        spin_lock(&v->arch.vgic.lock);
>>>> +        *r = vgic_reg32_extract(!!(v->arch.vgic.flags &
>>>> VGIC_V3_LPIS_ENABLED),
>>>> +                                info);
>>>> +        spin_unlock(&v->arch.vgic.lock);
>>>> +        return 1;
>>>>
>>>>      case VREG32(GICR_IIDR):
>>>>          if ( dabt.size != DABT_WORD ) goto bad_width;
>>>> @@ -181,16 +185,20 @@ 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;
>>>> +        /* We use the VCPU ID as the redistributor ID in bits[23:8] */
>>>> +        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;
>>>> @@ -411,6 +419,17 @@ static uint64_t sanitize_pendbaser(uint64_t reg)
>>>>      return reg;
>>>>  }
>>>>
>>>> +static void vgic_vcpu_enable_lpis(struct vcpu *v)
>>>> +{
>>>> +    uint64_t reg = v->domain->arch.vgic.rdist_propbase;
>>>> +    unsigned int nr_lpis = BIT((reg & 0x1f) + 1) - LPI_OFFSET;
>>>> +
>>>> +    if ( !v->domain->arch.vgic.nr_lpis )
>>>> +        v->domain->arch.vgic.nr_lpis = nr_lpis;
>>>
>>> What if nr_lpis was already set and the nr_lpis has changed?
>>
>> I think this can never happen:
>> 1) vgic.rdist_propbase is only writeable when the redistributor has not
>> been enabled yet:
>>         /* Writing PROPBASER with LPIs enabled is UNPREDICTABLE. */
>>         if ( !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
>>         {
>>             reg = v->domain->arch.vgic.rdist_propbase;
>>             vgic_reg64_update(&reg, r, info);
>>             reg = sanitize_propbaser(reg);
>>             v->domain->arch.vgic.rdist_propbase = reg;
> 
> This will be called until VGIC_V3_LPIS_ENABLED will be set for the vCPU.
> However rdist_propbase is part of struct domain. So ...
> 
>>         }
>>     ....
>> 2) This function above can only be called once:
> 
> ... as this function will be called multiple per domain (once per vCPU).
> You could end up having nr_lpis not in sync with propbase.
> 
>>     ....
>>         spin_lock(&v->arch.vgic.lock);
>>
>>         /* LPIs can only be enabled once, but never disabled again. */
>>         if ( (r & GICR_CTLR_ENABLE_LPIS) &&
>>              !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
>>             vgic_vcpu_enable_lpis(v);
>>     ....
>>
>> Does that sound safe? Sorry if the architecture is a bit awkward in this
>> regard ;-)
> 
> I am afraid that this is not safe. Tĥis is fine to have propbase stored
> in the domain because the spec (8.11.9 in ARM IHI 0069C) says:
> 
> "Setting different values in different copies of GICR_PROPBASER on
> Redistributors that are required to use a common LPI Configuration table".
> 
> But you need to keep Xen internal state consistent.

Ah, the lovely GIC architecture. So technically GICR_PROPBASER is per
redistributor (so per VCPU in our case), that's why I was mechanically
using the VCPU lock. But as you rightly mention, the spec goes into
great details why it actually isn't ;-) So indeed I need to use a lock
per domain, also prevent changing propbaser once at least *one* VCPU has
enabled its redistributor. Looks like I need a counter.

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] 122+ messages in thread

* Re: [PATCH v5 22/30] ARM: vITS: handle MAPD command
  2017-04-05 23:19 ` [PATCH v5 22/30] ARM: vITS: handle MAPD command Andre Przywara
  2017-04-06  0:39   ` Stefano Stabellini
@ 2017-04-07 12:41   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 12:41 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> The MAPD command maps a device by associating a memory region for
> storing ITEs with a certain device ID.
> We store the given guest physical address in the device table, and, if
> this command comes from Dom0, tell the host ITS driver about this new
> mapping, so it can issue the corresponding host MAPD command and create
> the required tables.
> We simply use our existing guest memory access function to find the
> right ITT entry and store the mapping there.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3-its.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 59 insertions(+)
>
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index bbca5cf..0372ed0 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -42,6 +42,7 @@
>   */
>  struct virt_its {
>      struct domain *d;
> +    paddr_t doorbell_address;
>      unsigned int devid_bits;
>      unsigned int intid_bits;
>      spinlock_t vcmd_lock;       /* Protects the virtual command buffer, which */
> @@ -144,6 +145,20 @@ static struct vcpu *get_vcpu_from_collection(struct virt_its *its,
>  #define DEV_TABLE_ENTRY(addr, bits)                     \
>          (((addr) & GENMASK_ULL(51, 8)) | (((bits) - 1) & GENMASK_ULL(7, 0)))
>
> +/* Set the address of an ITT for a given device ID. */
> +static int its_set_itt_address(struct virt_its *its, uint32_t devid,
> +                               paddr_t itt_address, uint32_t nr_bits)
> +{
> +    paddr_t addr = get_baser_phys_addr(its->baser_dev);
> +    uint64_t itt_entry = DEV_TABLE_ENTRY(itt_address, nr_bits);
> +
> +    if ( devid >= its->max_devices )
> +        return -ENOENT;
> +
> +    return vgic_access_guest_memory(its->d, addr + devid * sizeof(uint64_t),
> +                                    &itt_entry, sizeof(itt_entry), true);
> +}
> +
>  /*
>   * Lookup the address of the Interrupt Translation Table associated with
>   * a device ID and return the address of the ITTE belonging to the event ID
> @@ -384,6 +399,47 @@ static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
>      return 0;
>  }
>
> +static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
> +{
> +    /* size and devid get validated by the functions called below. */
> +    uint32_t devid = its_cmd_get_deviceid(cmdptr);
> +    unsigned int size = its_cmd_get_size(cmdptr) + 1;
> +    bool valid = its_cmd_get_validbit(cmdptr);
> +    paddr_t itt_addr = its_cmd_get_ittaddr(cmdptr);
> +    int ret;
> +
> +    /*
> +     * There is no easy and clean way for Xen to know the ITS device ID of a
> +     * particular (PCI) device, so we have to rely on the guest telling
> +     * us about it. For *now* we are just using the device ID *Dom0* uses,
> +     * because the driver there has the actual knowledge.
> +     * Eventually this will be replaced with a dedicated hypercall to
> +     * announce pass-through of devices.
> +     */
> +    if ( is_hardware_domain(its->d) )
> +    {
> +        /*
> +         * Dom0's ITSes are mapped 1:1, so both addresses are the same.
> +         * Also the device IDs are equal.
> +         */
> +        ret = gicv3_its_map_guest_device(its->d, its->doorbell_address, devid,
> +                                         its->doorbell_address, devid,
> +                                         BIT(size), valid);
> +        if ( ret )
> +            return ret;
> +    }
> +
> +    spin_lock(&its->its_lock);
> +    if ( valid )
> +        ret = its_set_itt_address(its, devid, itt_addr, size);
> +    else
> +        ret = its_set_itt_address(its, devid, INVALID_PADDR, 1);
> +

MAPD (V=0) command will also remove mapping between vLPI <-> (DeviceID, 
EventID). This means that you need to remove the irq_pending from the 
radix tree and also removing the mapping pLPI <-> vLPI.

> +    spin_unlock(&its->its_lock);
> +
> +    return ret;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>
>  /*
> @@ -421,6 +477,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_MAPC:
>              ret = its_handle_mapc(its, command);
>              break;
> +        case GITS_CMD_MAPD:
> +            ret = its_handle_mapd(its, command);
> +            break;
>          case GITS_CMD_SYNC:
>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>              break;
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 23/30] ARM: vITS: handle MAPTI command
  2017-04-05 23:19 ` [PATCH v5 23/30] ARM: vITS: handle MAPTI command Andre Przywara
  2017-04-06  0:45   ` Stefano Stabellini
@ 2017-04-07 12:53   ` Julien Grall
  2017-04-07 13:07   ` Julien Grall
  2 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 12:53 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> 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 LPI on the host can be quickly forwarded to a guest.
> Beside entering the VCPU and the virtual LPI number in the respective
> host LPI entry, we also initialize and add the already allocated
> struct pending_irq to our radix tree, so that we can now easily find it
> by its virtual LPI number.
> To be able to later find the targetting VCPU for any given LPI without
> having to walk *all* ITS tables, we store the VCPU ID in the pending_irq
> struct as well.
> This exports the vgic_init_pending_irq() function to be able to
> initialize a new struct pending_irq.
> As write_itte() is now eventually used, we can now add the static tag.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/gic-v3-its.c        | 74 ++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3-lpi.c        | 18 ++++++++++
>  xen/arch/arm/vgic-v3-its.c       | 76 ++++++++++++++++++++++++++++++++++++++--
>  xen/arch/arm/vgic.c              |  2 +-
>  xen/include/asm-arm/gic_v3_its.h |  6 ++++
>  xen/include/asm-arm/vgic.h       |  2 ++
>  6 files changed, 175 insertions(+), 3 deletions(-)
>
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index 76b0316..d970119 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -777,6 +777,80 @@ out:
>      return ret;
>  }
>
> +/* Must be called with the its_device_lock held. */
> +static struct its_devices *get_its_device(struct domain *d, paddr_t vdoorbell,
> +                                          uint32_t vdevid)
> +{
> +    struct rb_node *node = d->arch.vgic.its_devices.rb_node;
> +    struct its_devices *dev;
> +
> +    ASSERT(spin_is_locked(&d->arch.vgic.its_devices_lock));
> +
> +    while (node)
> +    {
> +        int cmp;
> +
> +        dev = rb_entry(node, struct its_devices, rbnode);
> +        cmp = compare_its_guest_devices(dev, vdoorbell, vdevid);
> +
> +        if ( !cmp )
> +            return dev;
> +
> +        if ( cmp > 0 )
> +            node = node->rb_left;
> +        else
> +            node = node->rb_right;
> +    }
> +
> +    return NULL;
> +}
> +
> +static uint32_t get_host_lpi(struct its_devices *dev, uint32_t eventid)
> +{
> +    uint32_t host_lpi = 0;
> +
> +    if ( dev && (eventid < dev->eventids) )
> +        host_lpi = dev->host_lpi_blocks[eventid / LPI_BLOCK] +
> +                                       (eventid % LPI_BLOCK);
> +
> +    return host_lpi;
> +}
> +
> +/*
> + * 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.
> + * Returns a pointer to the already allocated struct pending_irq that is
> + * meant to be used by that event.
> + */
> +struct pending_irq *gicv3_assign_guest_event(struct domain *d,
> +                                             paddr_t vdoorbell_address,
> +                                             uint32_t vdevid, uint32_t veventid,
> +                                             struct vcpu *v, uint32_t virt_lpi)
> +{
> +    struct its_devices *dev;
> +    struct pending_irq *pirq = NULL;
> +    uint32_t host_lpi = 0;
> +
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    dev = get_its_device(d, vdoorbell_address, vdevid);
> +    if ( dev )
> +    {
> +        host_lpi = get_host_lpi(dev, veventid);
> +        pirq = &dev->pend_irqs[veventid];
> +    }
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +    if ( !host_lpi || !pirq )

How pirq could be NULL if host_lpi is set?

> +        return NULL;
> +
> +    gicv3_lpi_update_host_entry(host_lpi, d->domain_id,
> +                                v ? v->vcpu_id : INVALID_VCPU_ID, virt_lpi);
> +
> +    return pirq;
> +}
> +
>  /* 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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index 7d20986..c997ed5 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -216,6 +216,24 @@ void gicv3_do_LPI(unsigned int lpi)
>      rcu_unlock_domain(d);
>  }
>
> +void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
> +                                 unsigned int vcpu_id, uint32_t virt_lpi)
> +{
> +    union host_lpi *hlpip, hlpi;
> +
> +    ASSERT(host_lpi >= LPI_OFFSET);
> +
> +    host_lpi -= LPI_OFFSET;
> +
> +    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
> +
> +    hlpi.virt_lpi = virt_lpi;
> +    hlpi.dom_id = domain_id;
> +    hlpi.vcpu_id = vcpu_id;
> +
> +    write_u64_atomic(&hlpip->data, hlpi.data);
> +}
> +
>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>  {
>      uint64_t val;
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 0372ed0..079dd44 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -275,8 +275,8 @@ static bool write_itte_locked(struct virt_its *its, uint32_t devid,
>   * This function takes care of the locking by taking the its_lock itself, so
>   * a caller shall not hold this. Before returning, the lock is dropped again.
>   */
> -bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> -                uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
> +static bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
> +                       uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
>  {
>      bool ret;
>
> @@ -440,6 +440,74 @@ static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
>      return ret;
>  }
>
> +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), _intid;
> +    uint16_t collid = its_cmd_get_collection(cmdptr);
> +    struct pending_irq *pirq;
> +    struct vcpu *vcpu = NULL;
> +    int ret = 0;
> +
> +    if ( its_cmd_get_command(cmdptr) == GITS_CMD_MAPI )
> +        intid = eventid;
> +
> +    spin_lock(&its->its_lock);
> +    /*
> +     * Check whether there is a valid existing mapping. If yes, behavior is
> +     * unpredictable, we choose to ignore this command here.
> +     * This makes sure we start with a pristine pending_irq below.
> +     */
> +    if ( read_itte_locked(its, devid, eventid, &vcpu, &_intid) &&
> +         _intid != INVALID_LPI )
> +    {
> +        spin_unlock(&its->its_lock);
> +        return -1;
> +    }
> +
> +    /* Enter the mapping in our virtual ITS tables. */
> +    if ( !write_itte_locked(its, devid, eventid, collid, intid, &vcpu) )
> +    {
> +        spin_unlock(&its->its_lock);
> +        return -1;
> +    }
> +
> +    spin_unlock(&its->its_lock);
> +
> +    /*
> +     * Connect this virtual LPI to the corresponding host LPI, which is
> +     * determined by the same device ID and event ID on the host side.
> +     * This returns us the corresponding, still unused pending_irq.
> +     */
> +    pirq = gicv3_assign_guest_event(its->d, its->doorbell_address,
> +                                    devid, eventid, vcpu, intid);
> +    if ( !pirq )
> +        return -1;

I was expected the ITT change to be reverted if gicv3_assign_guest_event 
has failed.

> +
> +    vgic_init_pending_irq(pirq, intid);
> +
> +    /*
> +     * Now read the guest's property table to initialize our cached state.
> +     * It can't fire at this time, because it is not known to the host yet.
> +     */
> +    ret = update_lpi_property(its->d, intid, pirq);
> +    if ( ret )
> +        return ret;

Same remark here.

> +
> +    pirq->vcpu_id = vcpu->vcpu_id;
> +
> +    /*
> +     * Now insert the pending_irq into the domain's LPI tree, so that
> +     * it becomes live.
> +     */
> +    write_lock(&its->d->arch.vgic.pend_lpi_tree_lock);
> +    radix_tree_insert(&its->d->arch.vgic.pend_lpi_tree, intid, pirq);

radix_tree_insert could fail, for instance before memory allocation 
failure or because the vLPI is already mapped to another (DevID, 
EventID). How do you plan to handle that?

I am ok to defer the memory allocation problem, but the latter have to 
be addressed properly.

> +    write_unlock(&its->d->arch.vgic.pend_lpi_tree_lock);
> +
> +    return 0;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>
>  /*
> @@ -480,6 +548,10 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_MAPD:
>              ret = its_handle_mapd(its, command);
>              break;
> +        case GITS_CMD_MAPI:
> +        case GITS_CMD_MAPTI:
> +            ret = its_handle_mapti(its, command);
> +            break;
>          case GITS_CMD_SYNC:
>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>              break;
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index 9b0dc3d..cb1666b 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -61,7 +61,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);
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index d3f393f..30aa1ef 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -174,6 +174,12 @@ int gicv3_its_map_guest_device(struct domain *d,
>  int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
>  void gicv3_free_host_lpi_block(uint32_t first_lpi);
>
> +struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
> +                                             uint32_t devid, uint32_t eventid,
> +                                             struct vcpu *v, uint32_t virt_lpi);
> +void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
> +                                 unsigned int vcpu_id, uint32_t virt_lpi);
> +
>  #else
>
>  static LIST_HEAD(host_its_list);
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index 2371960..074afe4 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -83,6 +83,7 @@ struct pending_irq
>       * TODO: when implementing irq migration, taking only the current
>       * vgic lock is not going to be enough. */
>      struct list_head lr_queue;
> +    uint16_t vcpu_id;          /* The VCPU for an LPI. */
>  };
>
>  #define NR_INTERRUPT_PER_RANK   32
> @@ -303,6 +304,7 @@ 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 vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 24/30] ARM: vITS: handle MOVI command
  2017-04-05 23:19 ` [PATCH v5 24/30] ARM: vITS: handle MOVI command Andre Przywara
  2017-04-06  0:47   ` Stefano Stabellini
@ 2017-04-07 12:55   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 12:55 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> 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-v3-its.c        | 24 ++++++++++++++++++++
>  xen/arch/arm/gic-v3-lpi.c        | 15 +++++++++++++
>  xen/arch/arm/vgic-v3-its.c       | 47 ++++++++++++++++++++++++++++++++++++++++
>  xen/include/asm-arm/gic_v3_its.h |  4 ++++
>  4 files changed, 90 insertions(+)
>
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index d970119..a57e63a 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -851,6 +851,30 @@ struct pending_irq *gicv3_assign_guest_event(struct domain *d,
>      return pirq;
>  }
>
> +/* Changes the target VCPU for a given host LPI assigned to a domain. */
> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t vdoorbell,
> +                          uint32_t vdevid, uint32_t veventid,
> +                          unsigned int vcpu_id)
> +{
> +    uint32_t host_lpi;
> +    struct its_devices *dev;
> +
> +    spin_lock(&d->arch.vgic.its_devices_lock);
> +    dev = get_its_device(d, vdoorbell, vdevid);
> +    if ( dev )
> +        host_lpi = get_host_lpi(dev, veventid);
> +    else
> +        host_lpi = 0;
> +    spin_unlock(&d->arch.vgic.its_devices_lock);
> +
> +    if ( !host_lpi )
> +        return -ENOENT;
> +
> +    gicv3_lpi_update_host_vcpuid(host_lpi, vcpu_id);
> +
> +    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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
> index c997ed5..b9960aa 100644
> --- a/xen/arch/arm/gic-v3-lpi.c
> +++ b/xen/arch/arm/gic-v3-lpi.c
> @@ -234,6 +234,21 @@ void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>      write_u64_atomic(&hlpip->data, hlpi.data);
>  }
>
> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id)
> +{
> +    union host_lpi *hlpip;
> +
> +    ASSERT(host_lpi >= LPI_OFFSET);
> +
> +    host_lpi -= LPI_OFFSET;
> +
> +    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
> +
> +    write_u16_atomic(&hlpip->vcpu_id, vcpu_id);
> +
> +    return 0;
> +}
> +
>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>  {
>      uint64_t val;
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 079dd44..6afb915 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -508,6 +508,47 @@ static int its_handle_mapti(struct virt_its *its, uint64_t *cmdptr)
>      return 0;
>  }
>
> +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 pending_irq *p;
> +    struct vcpu *vcpu;
> +    uint32_t vlpi;
> +    int ret = -1;
> +
> +    spin_lock(&its->its_lock);
> +    /* Check for a mapped LPI and get the LPI number. */
> +    if ( !read_itte_locked(its, devid, eventid, &vcpu, &vlpi) )
> +        goto out_unlock;
> +
> +    /* Check the new collection ID and get the new VCPU pointer */
> +    vcpu = get_vcpu_from_collection(its, collid);
> +    if ( !vcpu )
> +        goto out_unlock;
> +
> +    /* Update our cached vcpu_id in the pending_irq. */
> +    p = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
> +    p->vcpu_id = vcpu->vcpu_id;
> +
> +    /* Now store the new collection in the translation table. */
> +    if ( !write_itte_locked(its, devid, eventid, collid, vlpi, &vcpu) )
> +        goto out_unlock;
> +
> +    spin_unlock(&its->its_lock);
> +
> +    /* TODO: lookup currently-in-guest virtual IRQs and migrate them? */
> +
> +    return gicv3_lpi_change_vcpu(its->d, its->doorbell_address,
> +                                 devid, eventid, vcpu->vcpu_id);
> +
> +out_unlock:
> +    spin_unlock(&its->its_lock);
> +
> +    return ret;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>
>  /*
> @@ -552,6 +593,12 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_MAPTI:
>              ret = its_handle_mapti(its, command);
>              break;
> +        case GITS_CMD_MOVALL:
> +            gdprintk(XENLOG_G_INFO, "ITS: ignoring MOVALL command\n");

Again, I'd like some explanation in the commit message why MOVALL is not 
implemented and a TODO in the code.

> +            break;
> +        case GITS_CMD_MOVI:
> +            ret = its_handle_movi(its, command);
> +            break;
>          case GITS_CMD_SYNC:
>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>              break;
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 30aa1ef..daae143 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -177,8 +177,12 @@ void gicv3_free_host_lpi_block(uint32_t first_lpi);
>  struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
>                                               uint32_t devid, uint32_t eventid,
>                                               struct vcpu *v, uint32_t virt_lpi);
> +int gicv3_lpi_change_vcpu(struct domain *d, paddr_t doorbell,
> +                          uint32_t devid, uint32_t eventid,
> +                          unsigned int vcpu_id);
>  void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>                                   unsigned int vcpu_id, uint32_t virt_lpi);
> +int gicv3_lpi_update_host_vcpuid(uint32_t host_lpi, unsigned int vcpu_id);
>
>  #else
>
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 25/30] ARM: vITS: handle DISCARD command
  2017-04-05 23:19 ` [PATCH v5 25/30] ARM: vITS: handle DISCARD command Andre Przywara
  2017-04-06  0:48   ` Stefano Stabellini
@ 2017-04-07 13:03   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:03 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> 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-v3-its.c | 32 ++++++++++++++++++++++++++++++++
>  1 file changed, 32 insertions(+)
>
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 6afb915..47f2884 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -549,6 +549,35 @@ out_unlock:
>      return ret;
>  }
>
> +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 vcpu *vcpu;
> +    uint32_t vlpi;
> +
> +    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
> +        return -1;
> +
> +    pirq = its->d->arch.vgic.handler->lpi_to_pending(its->d, vlpi);
> +    if ( pirq )
> +    {
> +        clear_bit(GIC_IRQ_GUEST_QUEUED, &pirq->status);
> +        gic_remove_from_queues(vcpu, vlpi);
> +    }

Do we expect to have pirq returning NULL? If so, in which case?

Also, why not directly using

pirq = gicv3_assign_guest(....);

This would prevent the issue below where it would not be possible to 
write back in the guest memory, and leave the mapping with the host LPI.

> +
> +    if ( !write_itte(its, devid, eventid, UNMAPPED_COLLECTION, INVALID_LPI,
> +                     NULL) )
> +        return -1;
> +
> +    if ( !gicv3_assign_guest_event(its->d, its->doorbell_address,
> +                                   devid, eventid, NULL, 0) )

In which case gicv3_assign_guest_event can fail? And what would happen 
if it fails. By that I mean, would we need to carry some clean-up for 
host lpi during domain destruction?

> +        return -1;
> +
> +    return 0;
> +}
> +
>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>
>  /*
> @@ -580,6 +609,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_CLEAR:
>              ret = its_handle_clear(its, command);
>              break;
> +        case GITS_CMD_DISCARD:
> +            ret = its_handle_discard(its, command);
> +            break;
>          case GITS_CMD_INT:
>              ret = its_handle_int(its, command);
>              break;
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 23/30] ARM: vITS: handle MAPTI command
  2017-04-05 23:19 ` [PATCH v5 23/30] ARM: vITS: handle MAPTI command Andre Przywara
  2017-04-06  0:45   ` Stefano Stabellini
  2017-04-07 12:53   ` Julien Grall
@ 2017-04-07 13:07   ` Julien Grall
  2017-04-07 13:12     ` Andre Przywara
  2 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:07 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari



On 06/04/17 00:19, Andre Przywara wrote:
> +    vgic_init_pending_irq(pirq, intid);
> +
> +    /*
> +     * Now read the guest's property table to initialize our cached state.
> +     * It can't fire at this time, because it is not known to the host yet.
> +     */
> +    ret = update_lpi_property(its->d, intid, pirq);

This function is introduced in a latter patch (see #26). Please re-order 
the patches to avoid that.

> +    if ( ret )
> +        return ret;
> +
> +    pirq->vcpu_id = vcpu->vcpu_id;

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 23/30] ARM: vITS: handle MAPTI command
  2017-04-07 13:07   ` Julien Grall
@ 2017-04-07 13:12     ` Andre Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-07 13:12 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 07/04/17 14:07, Julien Grall wrote:
> 
> 
> On 06/04/17 00:19, Andre Przywara wrote:
>> +    vgic_init_pending_irq(pirq, intid);
>> +
>> +    /*
>> +     * Now read the guest's property table to initialize our cached
>> state.
>> +     * It can't fire at this time, because it is not known to the
>> host yet.
>> +     */
>> +    ret = update_lpi_property(its->d, intid, pirq);
> 
> This function is introduced in a latter patch (see #26). Please re-order
> the patches to avoid that.

Thanks for the heads up, I found this myself already and just fixed it.

Cheers,
Andre.

>> +    if ( ret )
>> +        return ret;
>> +
>> +    pirq->vcpu_id = vcpu->vcpu_id;
> 
> Cheers,
> 

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

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

* Re: [PATCH v5 26/30] ARM: vITS: handle INV command
  2017-04-05 23:19 ` [PATCH v5 26/30] ARM: vITS: handle INV command Andre Przywara
  2017-04-06  0:56   ` Stefano Stabellini
@ 2017-04-07 13:23   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:23 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> 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/vgic-v3-its.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 96 insertions(+)
>
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 47f2884..0d4b20d 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -376,6 +376,99 @@ static int its_handle_int(struct virt_its *its, uint64_t *cmdptr)
>      return 0;
>  }
>
> +/*
> + * For a given virtual LPI read the enabled bit and priority from the virtual
> + * property table and update the virtual IRQ's state in the given pending_irq.
> + */
> +static int update_lpi_property(struct domain *d, uint32_t vlpi,
> +                               struct pending_irq *p)

What is the locking expectation for this function?

> +{
> +    paddr_t addr;
> +    uint8_t property;
> +    int ret;
> +
> +    addr = d->arch.vgic.rdist_propbase & GENMASK_ULL(51, 12);
> +
> +    ret = vgic_access_guest_memory(d, addr + vlpi - LPI_OFFSET,
> +                                   &property, sizeof(property), false);
> +    if ( ret )
> +        return ret;
> +
> +    p->lpi_priority = property & LPI_PROP_PRIO_MASK;

I don't think this change will be atomic. So what is preventing the 
lpi_priority to be read incorrectly?

> +    if ( property & LPI_PROP_ENABLED )
> +        set_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
> +    else
> +        clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
> +
> +    return 0;
> +}
> +
> +/*
> + * For a given virtual LPI read the enabled bit and priority 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.
> + * Also check if this LPI is due to be injected and do it, if needed.
> + */
> +static int update_lpi_enabled_status(struct domain *d,
> +                                     struct vcpu *vcpu, uint32_t vlpi)

The use of this function is a bit weird. You are already looking the 
radix when handling the command INVALL and you can easily get the 
pending_irq from the struct its_device for INV command.

So you could avoid the lookup lpi_to_pending and the check ( !p ) which 
I think is confusing.

> +{
> +    struct pending_irq *p = d->arch.vgic.handler->lpi_to_pending(d, vlpi);
> +    unsigned long flags;
> +    int ret;
> +
> +    if ( !p )
> +        return -EINVAL;
> +
> +    spin_lock_irqsave(&vcpu->arch.vgic.lock, flags);
> +    ret = update_lpi_property(d, vlpi, p);
> +    if ( ret ) {

Coding style:

if ( ... )
{

> +        spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);
> +        return ret;
> +    }
> +
> +    if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) )
> +    {
> +        if ( !list_empty(&p->inflight) &&
> +             !test_bit(GIC_IRQ_GUEST_VISIBLE, &p->status) )
> +            gic_raise_guest_irq(vcpu, vlpi, p->lpi_priority);
> +        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(GIC_IRQ_GUEST_LPI_PENDING, &p->status) )
> +            vgic_vcpu_inject_irq(vcpu, vlpi);
> +    }
> +    else
> +    {
> +        clear_bit(GIC_IRQ_GUEST_ENABLED, &p->status);
> +        spin_unlock_irqrestore(&vcpu->arch.vgic.lock, flags);
> +
> +        gic_remove_from_queues(vcpu, vlpi);
> +    }
> +
> +    return 0;
> +}
> +
> +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 vcpu *vcpu;
> +    uint32_t vlpi;
> +
> +    /* Translate the event into a vCPU/vLPI pair. */
> +    if ( !read_itte(its, devid, eventid, &vcpu, &vlpi) )
> +        return -1;
> +
> +    /*
> +     * Now read the property table and update our cached status. This
> +     * also takes care if this LPI now needs to be injected or removed.
> +     */
> +    if ( update_lpi_enabled_status(its->d, vcpu, vlpi) )
> +        return -1;
> +
> +    return 0;
> +}
> +
>  static int its_handle_mapc(struct virt_its *its, uint64_t *cmdptr)
>  {
>      uint32_t collid = its_cmd_get_collection(cmdptr);
> @@ -615,6 +708,9 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>          case GITS_CMD_INT:
>              ret = its_handle_int(its, command);
>              break;
> +        case GITS_CMD_INV:
> +            ret = its_handle_inv(its, command);
> +            break;
>          case GITS_CMD_MAPC:
>              ret = its_handle_mapc(its, command);
>              break;
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 28/30] ARM: vITS: create and initialize virtual ITSes for Dom0
  2017-04-05 23:19 ` [PATCH v5 28/30] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
@ 2017-04-07 13:38   ` Julien Grall
  0 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:38 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> For each hardware ITS create and initialize a virtual ITS for Dom0.
> We use the same memory mapped address to keep the doorbell working.
> This introduces a function to initialize a virtual ITS.
> We maintain a list of virtual ITSes, at the moment for the only
> purpose of later being able to free them again.
> We advertise 24 bits worth of LPIs on the guest side, using the full
> 32 bits seems to trigger a Linux bug (to be investigated).

No, we don't emulate a specific number just because an OS is buggy 
without a minimum of investigation.

But as I said, the vITS emulation for DOM0 should follow the host vITS 
in term of number. If the hardware expose N bits, then it should be N 
bits for the vITS.

If there is a Linux bug with 32 bits on Xen, either this bug is present 
on the hardware too or it is something else.

>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3-its.c       | 46 ++++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/vgic-v3.c           | 17 +++++++++++++++
>  xen/include/asm-arm/domain.h     |  2 ++
>  xen/include/asm-arm/gic_v3_its.h | 12 +++++++++++
>  4 files changed, 77 insertions(+)
>
> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
> index 9684b3a..4e66cad 100644
> --- a/xen/arch/arm/vgic-v3-its.c
> +++ b/xen/arch/arm/vgic-v3-its.c
> @@ -42,6 +42,7 @@
>   */
>  struct virt_its {
>      struct domain *d;
> +    struct list_head vits_list;
>      paddr_t doorbell_address;
>      unsigned int devid_bits;
>      unsigned int intid_bits;
> @@ -72,12 +73,20 @@ struct vits_itte
>
>  void vgic_v3_its_init_domain(struct domain *d)
>  {
> +    INIT_LIST_HEAD(&d->arch.vgic.vits_list);
>      spin_lock_init(&d->arch.vgic.its_devices_lock);
>      d->arch.vgic.its_devices = RB_ROOT;
>  }
>
>  void vgic_v3_its_free_domain(struct domain *d)
>  {
> +    struct virt_its *pos, *temp;
> +
> +    list_for_each_entry_safe( pos, temp, &d->arch.vgic.vits_list, vits_list )
> +    {
> +        list_del(&pos->vits_list);
> +        xfree(pos);
> +    }

NIT: newline here.

>      ASSERT(RB_EMPTY_ROOT(&d->arch.vgic.its_devices));
>  }
>
> @@ -1157,6 +1166,43 @@ 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, paddr_t guest_addr,
> +                             unsigned int devid_bits, unsigned int intid_bits)
> +{
> +    struct virt_its *its;
> +    uint64_t base_attr;
> +
> +    its = xzalloc(struct virt_its);
> +    if ( !its )
> +        return -ENOMEM;
> +
> +    base_attr  = GIC_BASER_InnerShareable << GITS_BASER_SHAREABILITY_SHIFT;
> +    base_attr |= GIC_BASER_CACHE_SameAsInner << GITS_BASER_OUTER_CACHEABILITY_SHIFT;
> +    base_attr |= GIC_BASER_CACHE_RaWaWb << GITS_BASER_INNER_CACHEABILITY_SHIFT;
> +
> +    its->cbaser  = base_attr;
> +    base_attr |= 0ULL << GITS_BASER_PAGE_SIZE_SHIFT;    /* 4K pages */
> +    its->baser_dev = GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT;
> +    its->baser_dev |= (sizeof(uint64_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;

Why uint64_t? I know this is the size of the ITT, but my concern is you 
spread this size of a bit everywhere in the code which will make very 
difficult to update it.

If you have introduced a struct it would have been easier to update the 
size layout.

So please find a way to minimize the number of uint64_t over the code.

> +    its->baser_dev |= base_attr;
> +    its->baser_coll  = GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT;
> +    its->baser_coll |= (sizeof(uint16_t) - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;

Same remark here.

> +    its->baser_coll |= base_attr;
> +    its->d = d;
> +    its->doorbell_address = guest_addr + ITS_DOORBELL_OFFSET;
> +    its->devid_bits = devid_bits;
> +    its->intid_bits = intid_bits;
> +    spin_lock_init(&its->vcmd_lock);
> +    spin_lock_init(&its->its_lock);

I was expecting to some mapping for the doorbell in the IOMMU page 
table. What is the plan to support SMMU and GICv3 ITS at the same time?

> +
> +    register_mmio_handler(d, &vgic_its_mmio_handler, guest_addr, SZ_64K, its);

You likely need to update mmio_count (see vgic_v3_init) when using ITS 
so we can provide enough space the MMIO handler array to register all 
the ITS.

> +
> +    /* Register the virtual ITSes to be able to clean them up later. */
> +    list_add_tail(&its->vits_list, &d->arch.vgic.vits_list);
> +
> +    return 0;
> +}
> +
>  /*
>   * Local variables:
>   * mode: C
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index e6a33d0..3b01247 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -30,6 +30,7 @@
>  #include <asm/current.h>
>  #include <asm/mmio.h>
>  #include <asm/gic_v3_defs.h>
> +#include <asm/gic_v3_its.h>
>  #include <asm/vgic.h>
>  #include <asm/vgic-emul.h>
>  #include <asm/vreg.h>
> @@ -1569,6 +1570,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;
> @@ -1594,6 +1596,21 @@ 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;

Again, why this change? It is the 5 times I am saying this... It is 
starting to be very annoying.

> +
> +        list_for_each_entry(hw_its, &host_its_list, entry)

This should go in vgic-v3-its.c (see discussion on patch #3).

> +        {
> +            /*
> +             * For each host ITS create a virtual ITS using the same
> +             * base and thus doorbell address.
> +             * Use the same number of device ID bits as the host, and
> +             * allow 24 bits for the interrupt ID.

See my comment above.

> +             */
> +            vgic_v3_its_init_virtual(d, hw_its->addr, hw_its->devid_bits, 24);
> +
> +            d->arch.vgic.has_its = true;
> +        }
> +
>      }
>      else
>      {
> diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
> index f993292..cbbfb99 100644
> --- a/xen/include/asm-arm/domain.h
> +++ b/xen/include/asm-arm/domain.h
> @@ -115,6 +115,8 @@ struct arch_domain
>          spinlock_t its_devices_lock;        /* Protects the its_devices tree */
>          struct radix_tree_root pend_lpi_tree; /* Stores struct pending_irq's */
>          rwlock_t pend_lpi_tree_lock;        /* Protects the pend_lpi_tree */
> +        struct list_head vits_list;         /* List of virtual ITSes */
> +        bool has_its;
>  #endif
>      } vgic;
>
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index daae143..1b8e47c 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -161,6 +161,10 @@ int gicv3_its_setup_collection(unsigned int cpu);
>  void vgic_v3_its_init_domain(struct domain *d);
>  void vgic_v3_its_free_domain(struct domain *d);
>
> +/* Create and register a virtual ITS at the given guest address. */
> +int vgic_v3_its_init_virtual(struct domain *d, paddr_t guest_addr,
> +			     unsigned int devid_bits, unsigned int intid_bits);
> +
>  /*
>   * Map a device on the host by allocating an ITT on the host (ITS).
>   * "nr_event" specifies how many events (interrupts) this device will need.
> @@ -237,6 +241,14 @@ static inline void vgic_v3_its_free_domain(struct domain *d)
>  {
>  }
>
> +static inline int vgic_v3_its_init_virtual(struct domain *d,
> +                                           paddr_t guest_addr,
> +                                           unsigned int devid_bits,
> +                                           unsigned int intid_bits)
> +{
> +    return 0;
> +}
> +
>  #endif /* CONFIG_HAS_ITS */
>
>  #endif
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT
  2017-04-05 23:19 ` [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
@ 2017-04-07 13:41   ` Julien Grall
  2017-04-07 13:46     ` Andre Przywara
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:41 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> 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-v3-its.c        | 78 ++++++++++++++++++++++++++++++++++++++++
>  xen/arch/arm/gic-v3.c            |  4 ++-
>  xen/include/asm-arm/gic_v3_its.h | 13 +++++++
>  3 files changed, 94 insertions(+), 1 deletion(-)
>
> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
> index a57e63a..a167471 100644
> --- a/xen/arch/arm/gic-v3-its.c
> +++ b/xen/arch/arm/gic-v3-its.c
> @@ -20,6 +20,7 @@
>
>  #include <xen/lib.h>
>  #include <xen/delay.h>
> +#include <xen/libfdt/libfdt.h>
>  #include <xen/mm.h>
>  #include <xen/rbtree.h>
>  #include <xen/sched.h>
> @@ -875,6 +876,83 @@ int gicv3_lpi_change_vcpu(struct domain *d, paddr_t vdoorbell,
>      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.
> + * Giving NULL for the its_list will make it use the list of host ITSes.
> + */
> +int gicv3_its_make_dt_nodes(struct list_head *its_list,

Why do you have this parameter its_list that you always set to NULL?

> +                            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 ( !its_list )
> +        its_list = &host_its_list;
> +
> +    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 54fbb19..2fbcf52 100644
> --- a/xen/arch/arm/gic-v3.c
> +++ b/xen/arch/arm/gic-v3.c
> @@ -1172,8 +1172,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(NULL, d, gic, fdt);
>  }
>
>  static const hw_irq_controller gicv3_host_irq_type = {
> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
> index 1b8e47c..6538916 100644
> --- a/xen/include/asm-arm/gic_v3_its.h
> +++ b/xen/include/asm-arm/gic_v3_its.h
> @@ -165,6 +165,12 @@ void vgic_v3_its_free_domain(struct domain *d);
>  int vgic_v3_its_init_virtual(struct domain *d, paddr_t guest_addr,
>  			     unsigned int devid_bits, unsigned int intid_bits);
>
> +/* 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).
>   * "nr_event" specifies how many events (interrupts) this device will need.
> @@ -248,6 +254,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 */
>
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 30/30] ARM: vGIC: advertise LPI support
  2017-04-05 23:19 ` [PATCH v5 30/30] ARM: vGIC: advertise LPI support Andre Przywara
  2017-04-06  1:04   ` Stefano Stabellini
@ 2017-04-07 13:43   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:43 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 06/04/17 00:19, Andre Przywara wrote:
> 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.
> This removes a "TBD" comment, as we now populate the processor number
> in the GICR_TYPE register.
> Advertise 24 bits worth of LPIs to the guest.

Why 24 bits? This should be the number of LPIs supported by the host.

And likely this number should be set from vgic_v3_domain_init(....) and 
not hardcoded in the emulation.

>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v3.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 41 insertions(+), 5 deletions(-)
>
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index 3b01247..ba0e79f 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -168,8 +168,12 @@ 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;
> +        spin_lock(&v->arch.vgic.lock);
> +        *r = vgic_reg32_extract(!!(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED),
> +                                info);
> +        spin_unlock(&v->arch.vgic.lock);
> +        return 1;
>
>      case VREG32(GICR_IIDR):
>          if ( dabt.size != DABT_WORD ) goto bad_width;
> @@ -181,16 +185,20 @@ 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;
> +        /* We use the VCPU ID as the redistributor ID in bits[23:8] */
> +        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;
> @@ -411,6 +419,17 @@ static uint64_t sanitize_pendbaser(uint64_t reg)
>      return reg;
>  }
>
> +static void vgic_vcpu_enable_lpis(struct vcpu *v)
> +{
> +    uint64_t reg = v->domain->arch.vgic.rdist_propbase;
> +    unsigned int nr_lpis = BIT((reg & 0x1f) + 1) - LPI_OFFSET;
> +
> +    if ( !v->domain->arch.vgic.nr_lpis )
> +        v->domain->arch.vgic.nr_lpis = nr_lpis;
> +
> +    v->arch.vgic.flags |= VGIC_V3_LPIS_ENABLED;
> +}
> +
>  static int __vgic_v3_rdistr_rd_mmio_write(struct vcpu *v, mmio_info_t *info,
>                                            uint32_t gicr_reg,
>                                            register_t r)
> @@ -421,8 +440,20 @@ 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;
> +
> +        spin_lock(&v->arch.vgic.lock);
> +
> +        /* LPIs can only be enabled once, but never disabled again. */
> +        if ( (r & GICR_CTLR_ENABLE_LPIS) &&
> +             !(v->arch.vgic.flags & VGIC_V3_LPIS_ENABLED) )
> +            vgic_vcpu_enable_lpis(v);
> +
> +        spin_unlock(&v->arch.vgic.lock);
> +
> +        return 1;
>
>      case VREG32(GICR_IIDR):
>          /* RO */
> @@ -1032,6 +1063,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 = 24;

See my comment above.

> +        }
>          typer |= (irq_bits - 1) << GICD_TYPE_ID_BITS_SHIFT;
>
>          *r = vgic_reg32_extract(typer, info);
>

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT
  2017-04-07 13:46     ` Andre Przywara
@ 2017-04-07 13:45       ` Julien Grall
  2017-04-07 13:47         ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:45 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari



On 07/04/17 14:46, Andre Przywara wrote:
> Hi,
>
> On 07/04/17 14:41, Julien Grall wrote:
>> Hi Andre,
>>
>> On 06/04/17 00:19, Andre Przywara wrote:
>>> 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-v3-its.c        | 78
>>> ++++++++++++++++++++++++++++++++++++++++
>>>  xen/arch/arm/gic-v3.c            |  4 ++-
>>>  xen/include/asm-arm/gic_v3_its.h | 13 +++++++
>>>  3 files changed, 94 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>>> index a57e63a..a167471 100644
>>> --- a/xen/arch/arm/gic-v3-its.c
>>> +++ b/xen/arch/arm/gic-v3-its.c
>>> @@ -20,6 +20,7 @@
>>>
>>>  #include <xen/lib.h>
>>>  #include <xen/delay.h>
>>> +#include <xen/libfdt/libfdt.h>
>>>  #include <xen/mm.h>
>>>  #include <xen/rbtree.h>
>>>  #include <xen/sched.h>
>>> @@ -875,6 +876,83 @@ int gicv3_lpi_change_vcpu(struct domain *d,
>>> paddr_t vdoorbell,
>>>      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.
>>> + * Giving NULL for the its_list will make it use the list of host ITSes.
>>> + */
>>> +int gicv3_its_make_dt_nodes(struct list_head *its_list,
>>
>> Why do you have this parameter its_list that you always set to NULL?
>
> The idea is to later allow only a subset of ITSes to be mapped into a
> guest. In this case this function would be called with a specific
> version of the list.
> So this feature is basically just in for future DomU support.

DomU DTS are created by the toolstack and not Xen. So please drop this 
pointless variable.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT
  2017-04-07 13:41   ` Julien Grall
@ 2017-04-07 13:46     ` Andre Przywara
  2017-04-07 13:45       ` Julien Grall
  0 siblings, 1 reply; 122+ messages in thread
From: Andre Przywara @ 2017-04-07 13:46 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 07/04/17 14:41, Julien Grall wrote:
> Hi Andre,
> 
> On 06/04/17 00:19, Andre Przywara wrote:
>> 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-v3-its.c        | 78
>> ++++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic-v3.c            |  4 ++-
>>  xen/include/asm-arm/gic_v3_its.h | 13 +++++++
>>  3 files changed, 94 insertions(+), 1 deletion(-)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index a57e63a..a167471 100644
>> --- a/xen/arch/arm/gic-v3-its.c
>> +++ b/xen/arch/arm/gic-v3-its.c
>> @@ -20,6 +20,7 @@
>>
>>  #include <xen/lib.h>
>>  #include <xen/delay.h>
>> +#include <xen/libfdt/libfdt.h>
>>  #include <xen/mm.h>
>>  #include <xen/rbtree.h>
>>  #include <xen/sched.h>
>> @@ -875,6 +876,83 @@ int gicv3_lpi_change_vcpu(struct domain *d,
>> paddr_t vdoorbell,
>>      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.
>> + * Giving NULL for the its_list will make it use the list of host ITSes.
>> + */
>> +int gicv3_its_make_dt_nodes(struct list_head *its_list,
> 
> Why do you have this parameter its_list that you always set to NULL?

The idea is to later allow only a subset of ITSes to be mapped into a
guest. In this case this function would be called with a specific
version of the list.
So this feature is basically just in for future DomU support.

Cheers,
Andre.

> 
>> +                            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 ( !its_list )
>> +        its_list = &host_its_list;
>> +
>> +    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 54fbb19..2fbcf52 100644
>> --- a/xen/arch/arm/gic-v3.c
>> +++ b/xen/arch/arm/gic-v3.c
>> @@ -1172,8 +1172,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(NULL, d, gic, fdt);
>>  }
>>
>>  static const hw_irq_controller gicv3_host_irq_type = {
>> diff --git a/xen/include/asm-arm/gic_v3_its.h
>> b/xen/include/asm-arm/gic_v3_its.h
>> index 1b8e47c..6538916 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -165,6 +165,12 @@ void vgic_v3_its_free_domain(struct domain *d);
>>  int vgic_v3_its_init_virtual(struct domain *d, paddr_t guest_addr,
>>                   unsigned int devid_bits, unsigned int intid_bits);
>>
>> +/* 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).
>>   * "nr_event" specifies how many events (interrupts) this device will
>> need.
>> @@ -248,6 +254,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 */
>>
>>
> 
> Cheers,
> 

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

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

* Re: [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT
  2017-04-07 13:45       ` Julien Grall
@ 2017-04-07 13:47         ` Julien Grall
  2017-04-07 13:49           ` Andre Przywara
  0 siblings, 1 reply; 122+ messages in thread
From: Julien Grall @ 2017-04-07 13:47 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari



On 07/04/17 14:45, Julien Grall wrote:
>
>
> On 07/04/17 14:46, Andre Przywara wrote:
>> Hi,
>>
>> On 07/04/17 14:41, Julien Grall wrote:
>>> Hi Andre,
>>>
>>> On 06/04/17 00:19, Andre Przywara wrote:
>>>> 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-v3-its.c        | 78
>>>> ++++++++++++++++++++++++++++++++++++++++
>>>>  xen/arch/arm/gic-v3.c            |  4 ++-
>>>>  xen/include/asm-arm/gic_v3_its.h | 13 +++++++
>>>>  3 files changed, 94 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>>>> index a57e63a..a167471 100644
>>>> --- a/xen/arch/arm/gic-v3-its.c
>>>> +++ b/xen/arch/arm/gic-v3-its.c
>>>> @@ -20,6 +20,7 @@
>>>>
>>>>  #include <xen/lib.h>
>>>>  #include <xen/delay.h>
>>>> +#include <xen/libfdt/libfdt.h>
>>>>  #include <xen/mm.h>
>>>>  #include <xen/rbtree.h>
>>>>  #include <xen/sched.h>
>>>> @@ -875,6 +876,83 @@ int gicv3_lpi_change_vcpu(struct domain *d,
>>>> paddr_t vdoorbell,
>>>>      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.
>>>> + * Giving NULL for the its_list will make it use the list of host
>>>> ITSes.
>>>> + */
>>>> +int gicv3_its_make_dt_nodes(struct list_head *its_list,
>>>
>>> Why do you have this parameter its_list that you always set to NULL?
>>
>> The idea is to later allow only a subset of ITSes to be mapped into a
>> guest. In this case this function would be called with a specific
>> version of the list.
>> So this feature is basically just in for future DomU support.
>
> DomU DTS are created by the toolstack and not Xen. So please drop this
> pointless variable.

To complete what I just said, DOM0 will always have a virtual ITS per 
host ITS not matter whether they will be used. So there is really no 
point of that.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT
  2017-04-07 13:47         ` Julien Grall
@ 2017-04-07 13:49           ` Andre Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-07 13:49 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi,

On 07/04/17 14:47, Julien Grall wrote:
> 
> 
> On 07/04/17 14:45, Julien Grall wrote:
>>
>>
>> On 07/04/17 14:46, Andre Przywara wrote:
>>> Hi,
>>>
>>> On 07/04/17 14:41, Julien Grall wrote:
>>>> Hi Andre,
>>>>
>>>> On 06/04/17 00:19, Andre Przywara wrote:
>>>>> 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-v3-its.c        | 78
>>>>> ++++++++++++++++++++++++++++++++++++++++
>>>>>  xen/arch/arm/gic-v3.c            |  4 ++-
>>>>>  xen/include/asm-arm/gic_v3_its.h | 13 +++++++
>>>>>  3 files changed, 94 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>>>>> index a57e63a..a167471 100644
>>>>> --- a/xen/arch/arm/gic-v3-its.c
>>>>> +++ b/xen/arch/arm/gic-v3-its.c
>>>>> @@ -20,6 +20,7 @@
>>>>>
>>>>>  #include <xen/lib.h>
>>>>>  #include <xen/delay.h>
>>>>> +#include <xen/libfdt/libfdt.h>
>>>>>  #include <xen/mm.h>
>>>>>  #include <xen/rbtree.h>
>>>>>  #include <xen/sched.h>
>>>>> @@ -875,6 +876,83 @@ int gicv3_lpi_change_vcpu(struct domain *d,
>>>>> paddr_t vdoorbell,
>>>>>      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.
>>>>> + * Giving NULL for the its_list will make it use the list of host
>>>>> ITSes.
>>>>> + */
>>>>> +int gicv3_its_make_dt_nodes(struct list_head *its_list,
>>>>
>>>> Why do you have this parameter its_list that you always set to NULL?
>>>
>>> The idea is to later allow only a subset of ITSes to be mapped into a
>>> guest. In this case this function would be called with a specific
>>> version of the list.
>>> So this feature is basically just in for future DomU support.
>>
>> DomU DTS are created by the toolstack and not Xen. So please drop this
>> pointless variable.
> 
> To complete what I just said, DOM0 will always have a virtual ITS per
> host ITS not matter whether they will be used. So there is really no
> point of that.

OK, fair enough.

Thanks!
Andre.

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

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

* Re: [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub
  2017-04-05 23:19 ` [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub Andre Przywara
  2017-04-06 15:17   ` Julien Grall
@ 2017-04-07 14:31   ` Julien Grall
  1 sibling, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 14:31 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

On 04/06/2017 12:19 AM, Andre Przywara wrote:
> +/*
> + * 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;
> +    uint16_t pad;
> +};
> +
> +void vgic_v3_its_init_domain(struct domain *d)

likely vgic_v3_its_init_domain will return an error if it failed to 
allocate something.

So modify the return type.

> +{
> +}
> +
> +void vgic_v3_its_free_domain(struct domain *d)
> +{
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping
  2017-04-05 23:19 ` [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping Andre Przywara
  2017-04-05 23:41   ` Stefano Stabellini
  2017-04-06 15:34   ` Julien Grall
@ 2017-04-07 14:46   ` Julien Grall
  2 siblings, 0 replies; 122+ messages in thread
From: Julien Grall @ 2017-04-07 14:46 UTC (permalink / raw)
  To: Andre Przywara, Stefano Stabellini
  Cc: xen-devel, Shanker Donthineni, Vijay Kilari

Hi Andre,

A couple of more comments.

On 04/06/2017 12:19 AM, Andre Przywara wrote:
> +static int remove_mapped_guest_device(struct its_devices *dev)
> +{
> +    int ret = 0;
> +    unsigned int i;
> +

I would add a TODO here to think about the interaction with the guest 
vITS when removing a device whilst it is still in-use. Such as when the 
vLPI is still inflight or the ITT has not been unmapped.

It still possible to do a MAPD (V=0) before whilst vLPI associated to 
this device are inflight. This would lead to issue when freeing 
pending_irq as they would become NULL.

Maybe for this version we can think to never remove a device from DOM0 
(i.e MAPD(V=0) will not call gicv3_its_map_guest_device).

This would prevent all the issue possible with pending_irq. But this 
would need to be fixed before guest support (*hint* this should be added 
in the TODO in the cover letter ;)).

> +    if ( dev->hw_its )
> +        /* MAPD also discards all events with this device ID. */
> +        ret = its_send_cmd_mapd(dev->hw_its, dev->host_devid, 0, 0, false);
> +
> +    for ( i = 0; i < dev->eventids / LPI_BLOCK; i++ )
> +        gicv3_free_host_lpi_block(dev->host_lpi_blocks[i]);
> +
> +    /* Make sure the MAPD command above is really executed. */
> +    if ( !ret )
> +        ret = gicv3_its_wait_commands(dev->hw_its);
> +
> +    /* This should never happen, but just in case ... */
> +    if ( ret )
> +        printk(XENLOG_WARNING "Can't unmap host ITS device 0x%x\n",
> +               dev->host_devid);
> +
> +    xfree(dev->itt_addr);
> +    xfree(dev->pend_irqs);
> +    xfree(dev->host_lpi_blocks);
> +    xfree(dev);
> +
> +    return 0;

[...]

>  /*
>   * On the host ITS @its, map @nr_events consecutive LPIs.
>   * The mapping connects a device @devid and event @eventid pair to LPI @lpi,
> @@ -510,6 +616,163 @@ static int gicv3_its_map_host_events(struct host_its *its,
>      return gicv3_its_wait_commands(its);
>  }
>
> +/*
> + * Map a hardware device, identified by a certain host ITS and its device ID
> + * to domain d, a guest ITS (identified by its doorbell address) and device ID.
> + * Also provide the number of events (MSIs) needed for that device.
> + * This does not check if this particular hardware device is already mapped
> + * at another domain, it is expected that this would be done by the caller.
> + */
> +int gicv3_its_map_guest_device(struct domain *d,
> +                               paddr_t host_doorbell, uint32_t host_devid,
> +                               paddr_t guest_doorbell, uint32_t guest_devid,
> +                               uint32_t nr_events, bool valid)

My understanding if the number of event cannot be 0. You would start 
from 1 and it is possible to have up to 2^32 events which will overflow.

So I think we should use uint64_t for nr_events.

Cheers,

-- 
Julien Grall

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

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

* Re: [PATCH v5 23/30] ARM: vITS: handle MAPTI command
  2017-04-06  0:45   ` Stefano Stabellini
@ 2017-04-11 16:15     ` Andre Przywara
  0 siblings, 0 replies; 122+ messages in thread
From: Andre Przywara @ 2017-04-11 16:15 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: xen-devel, Julien Grall, Shanker Donthineni, Vijay Kilari

Hi,

On 06/04/17 01:45, Stefano Stabellini wrote:
> On Thu, 6 Apr 2017, Andre Przywara wrote:
>> 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 LPI on the host can be quickly forwarded to a guest.
>> Beside entering the VCPU and the virtual LPI number in the respective
>> host LPI entry, we also initialize and add the already allocated
>> struct pending_irq to our radix tree, so that we can now easily find it
>> by its virtual LPI number.
>> To be able to later find the targetting VCPU for any given LPI without
>> having to walk *all* ITS tables, we store the VCPU ID in the pending_irq
>> struct as well.
>> This exports the vgic_init_pending_irq() function to be able to
>> initialize a new struct pending_irq.
>> As write_itte() is now eventually used, we can now add the static tag.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  xen/arch/arm/gic-v3-its.c        | 74 ++++++++++++++++++++++++++++++++++++++
>>  xen/arch/arm/gic-v3-lpi.c        | 18 ++++++++++
>>  xen/arch/arm/vgic-v3-its.c       | 76 ++++++++++++++++++++++++++++++++++++++--
>>  xen/arch/arm/vgic.c              |  2 +-
>>  xen/include/asm-arm/gic_v3_its.h |  6 ++++
>>  xen/include/asm-arm/vgic.h       |  2 ++
>>  6 files changed, 175 insertions(+), 3 deletions(-)
>>
>> diff --git a/xen/arch/arm/gic-v3-its.c b/xen/arch/arm/gic-v3-its.c
>> index 76b0316..d970119 100644
>> --- a/xen/arch/arm/gic-v3-its.c
>> +++ b/xen/arch/arm/gic-v3-its.c
>> @@ -777,6 +777,80 @@ out:
>>      return ret;
>>  }
>>  
>> +/* Must be called with the its_device_lock held. */
>> +static struct its_devices *get_its_device(struct domain *d, paddr_t vdoorbell,
>> +                                          uint32_t vdevid)
>> +{
>> +    struct rb_node *node = d->arch.vgic.its_devices.rb_node;
>> +    struct its_devices *dev;
>> +
>> +    ASSERT(spin_is_locked(&d->arch.vgic.its_devices_lock));
>> +
>> +    while (node)
>> +    {
>> +        int cmp;
>> +
>> +        dev = rb_entry(node, struct its_devices, rbnode);
>> +        cmp = compare_its_guest_devices(dev, vdoorbell, vdevid);
>> +
>> +        if ( !cmp )
>> +            return dev;
>> +
>> +        if ( cmp > 0 )
>> +            node = node->rb_left;
>> +        else
>> +            node = node->rb_right;
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +static uint32_t get_host_lpi(struct its_devices *dev, uint32_t eventid)
>> +{
>> +    uint32_t host_lpi = 0;
>> +
>> +    if ( dev && (eventid < dev->eventids) )
>> +        host_lpi = dev->host_lpi_blocks[eventid / LPI_BLOCK] +
>> +                                       (eventid % LPI_BLOCK);
>> +
>> +    return host_lpi;
>> +}
>> +
>> +/*
>> + * 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.
>> + * Returns a pointer to the already allocated struct pending_irq that is
>> + * meant to be used by that event.
>> + */
>> +struct pending_irq *gicv3_assign_guest_event(struct domain *d,
>> +                                             paddr_t vdoorbell_address,
>> +                                             uint32_t vdevid, uint32_t veventid,
>> +                                             struct vcpu *v, uint32_t virt_lpi)
>> +{
>> +    struct its_devices *dev;
>> +    struct pending_irq *pirq = NULL;
>> +    uint32_t host_lpi = 0;
>> +
>> +    spin_lock(&d->arch.vgic.its_devices_lock);
>> +    dev = get_its_device(d, vdoorbell_address, vdevid);
>> +    if ( dev )
>> +    {
>> +        host_lpi = get_host_lpi(dev, veventid);
>> +        pirq = &dev->pend_irqs[veventid];
>> +    }
>> +    spin_unlock(&d->arch.vgic.its_devices_lock);
>> +
>> +    if ( !host_lpi || !pirq )
>> +        return NULL;
>> +
>> +    gicv3_lpi_update_host_entry(host_lpi, d->domain_id,
>> +                                v ? v->vcpu_id : INVALID_VCPU_ID, virt_lpi);
>> +
>> +    return pirq;
>> +}
>> +
>>  /* 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-lpi.c b/xen/arch/arm/gic-v3-lpi.c
>> index 7d20986..c997ed5 100644
>> --- a/xen/arch/arm/gic-v3-lpi.c
>> +++ b/xen/arch/arm/gic-v3-lpi.c
>> @@ -216,6 +216,24 @@ void gicv3_do_LPI(unsigned int lpi)
>>      rcu_unlock_domain(d);
>>  }
>>  
>> +void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>> +                                 unsigned int vcpu_id, uint32_t virt_lpi)
>> +{
>> +    union host_lpi *hlpip, hlpi;
>> +
>> +    ASSERT(host_lpi >= LPI_OFFSET);
>> +
>> +    host_lpi -= LPI_OFFSET;
>> +
>> +    hlpip = &lpi_data.host_lpis[host_lpi / HOST_LPIS_PER_PAGE][host_lpi % HOST_LPIS_PER_PAGE];
>> +
>> +    hlpi.virt_lpi = virt_lpi;
>> +    hlpi.dom_id = domain_id;
>> +    hlpi.vcpu_id = vcpu_id;
>> +
>> +    write_u64_atomic(&hlpip->data, hlpi.data);
>> +}
>> +
>>  static int gicv3_lpi_allocate_pendtable(uint64_t *reg)
>>  {
>>      uint64_t val;
>> diff --git a/xen/arch/arm/vgic-v3-its.c b/xen/arch/arm/vgic-v3-its.c
>> index 0372ed0..079dd44 100644
>> --- a/xen/arch/arm/vgic-v3-its.c
>> +++ b/xen/arch/arm/vgic-v3-its.c
>> @@ -275,8 +275,8 @@ static bool write_itte_locked(struct virt_its *its, uint32_t devid,
>>   * This function takes care of the locking by taking the its_lock itself, so
>>   * a caller shall not hold this. Before returning, the lock is dropped again.
>>   */
>> -bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
>> -                uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
>> +static bool write_itte(struct virt_its *its, uint32_t devid, uint32_t evid,
>> +                       uint32_t collid, uint32_t vlpi, struct vcpu **vcpu_ptr)
>>  {
>>      bool ret;
>>  
>> @@ -440,6 +440,74 @@ static int its_handle_mapd(struct virt_its *its, uint64_t *cmdptr)
>>      return ret;
>>  }
>>  
>> +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), _intid;
>> +    uint16_t collid = its_cmd_get_collection(cmdptr);
>> +    struct pending_irq *pirq;
>> +    struct vcpu *vcpu = NULL;
>> +    int ret = 0;
>> +
>> +    if ( its_cmd_get_command(cmdptr) == GITS_CMD_MAPI )
>> +        intid = eventid;
>> +
>> +    spin_lock(&its->its_lock);
>> +    /*
>> +     * Check whether there is a valid existing mapping. If yes, behavior is
>> +     * unpredictable, we choose to ignore this command here.
>> +     * This makes sure we start with a pristine pending_irq below.
>> +     */
>> +    if ( read_itte_locked(its, devid, eventid, &vcpu, &_intid) &&
>> +         _intid != INVALID_LPI )
>> +    {
>> +        spin_unlock(&its->its_lock);
>> +        return -1;
>> +    }
>> +
>> +    /* Enter the mapping in our virtual ITS tables. */
>> +    if ( !write_itte_locked(its, devid, eventid, collid, intid, &vcpu) )
>> +    {
>> +        spin_unlock(&its->its_lock);
>> +        return -1;
>> +    }
>> +
>> +    spin_unlock(&its->its_lock);
>> +
>> +    /*
>> +     * Connect this virtual LPI to the corresponding host LPI, which is
>> +     * determined by the same device ID and event ID on the host side.
>> +     * This returns us the corresponding, still unused pending_irq.
>> +     */
>> +    pirq = gicv3_assign_guest_event(its->d, its->doorbell_address,
>> +                                    devid, eventid, vcpu, intid);
>> +    if ( !pirq )
>> +        return -1;
>> +
>> +    vgic_init_pending_irq(pirq, intid);
>> +
>> +    /*
>> +     * Now read the guest's property table to initialize our cached state.
>> +     * It can't fire at this time, because it is not known to the host yet.
>> +     */
>> +    ret = update_lpi_property(its->d, intid, pirq);
>> +    if ( ret )
>> +        return ret;
>> +
>> +    pirq->vcpu_id = vcpu->vcpu_id;
>> +
>> +    /*
>> +     * Now insert the pending_irq into the domain's LPI tree, so that
>> +     * it becomes live.
>> +     */
>> +    write_lock(&its->d->arch.vgic.pend_lpi_tree_lock);
>> +    radix_tree_insert(&its->d->arch.vgic.pend_lpi_tree, intid, pirq);
>> +    write_unlock(&its->d->arch.vgic.pend_lpi_tree_lock);
> 
> It looks like the whole allocation, starting from
> gicv3_assign_guest_event, needs to be protected by pend_lpi_tree_lock.
> Otherwise we risk allocating the same struct twice? Or that is not
> possible thanks to the vcmd_lock (because for two struct pending_irq to
> clash they need to belong to the same vits)?

Yes, the pending_irq's are allocated on MAPD, which uses an ITS and a
device ID, so this is unique. We hold the vcmd_lock, so there can't be
another command handler on this ITS.

Nevertheless I added some lines to check the return value of
radix_tree_insert, so we deal with any error here and rollback the above
actions if that fails because of ENOMEM or EEXISTS.

Cheers,
Andre.

>> +    return 0;
>> +}
>> +
>>  #define ITS_CMD_BUFFER_SIZE(baser)      ((((baser) & 0xff) + 1) << 12)
>>  
>>  /*
>> @@ -480,6 +548,10 @@ static int vgic_its_handle_cmds(struct domain *d, struct virt_its *its)
>>          case GITS_CMD_MAPD:
>>              ret = its_handle_mapd(its, command);
>>              break;
>> +        case GITS_CMD_MAPI:
>> +        case GITS_CMD_MAPTI:
>> +            ret = its_handle_mapti(its, command);
>> +            break;
>>          case GITS_CMD_SYNC:
>>              /* We handle ITS commands synchronously, so we ignore SYNC. */
>>              break;
>> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
>> index 9b0dc3d..cb1666b 100644
>> --- a/xen/arch/arm/vgic.c
>> +++ b/xen/arch/arm/vgic.c
>> @@ -61,7 +61,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);
>> diff --git a/xen/include/asm-arm/gic_v3_its.h b/xen/include/asm-arm/gic_v3_its.h
>> index d3f393f..30aa1ef 100644
>> --- a/xen/include/asm-arm/gic_v3_its.h
>> +++ b/xen/include/asm-arm/gic_v3_its.h
>> @@ -174,6 +174,12 @@ int gicv3_its_map_guest_device(struct domain *d,
>>  int gicv3_allocate_host_lpi_block(struct domain *d, uint32_t *first_lpi);
>>  void gicv3_free_host_lpi_block(uint32_t first_lpi);
>>  
>> +struct pending_irq *gicv3_assign_guest_event(struct domain *d, paddr_t doorbell,
>> +                                             uint32_t devid, uint32_t eventid,
>> +                                             struct vcpu *v, uint32_t virt_lpi);
>> +void gicv3_lpi_update_host_entry(uint32_t host_lpi, int domain_id,
>> +                                 unsigned int vcpu_id, uint32_t virt_lpi);
>> +
>>  #else
>>  
>>  static LIST_HEAD(host_its_list);
>> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
>> index 2371960..074afe4 100644
>> --- a/xen/include/asm-arm/vgic.h
>> +++ b/xen/include/asm-arm/vgic.h
>> @@ -83,6 +83,7 @@ struct pending_irq
>>       * TODO: when implementing irq migration, taking only the current
>>       * vgic lock is not going to be enough. */
>>      struct list_head lr_queue;
>> +    uint16_t vcpu_id;          /* The VCPU for an LPI. */
>>  };
>>  
>>  #define NR_INTERRUPT_PER_RANK   32
>> @@ -303,6 +304,7 @@ 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 vgic_irq_rank *vgic_rank_offset(struct vcpu *v, int b, int n, int s);
>> -- 
>> 2.8.2
>>

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

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

end of thread, other threads:[~2017-04-11 16:13 UTC | newest]

Thread overview: 122+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-05 23:18 [PATCH v5 00/30] arm64: Dom0 ITS emulation Andre Przywara
2017-04-05 23:18 ` [PATCH v5 01/30] bitops: add GENMASK_ULL Andre Przywara
2017-04-05 23:25   ` Stefano Stabellini
2017-04-06  8:45     ` Julien Grall
2017-04-06  9:15       ` Jan Beulich
2017-04-06  9:55         ` Andre Przywara
2017-04-05 23:18 ` [PATCH v5 02/30] bitops: add BIT_ULL variant Andre Przywara
2017-04-05 23:26   ` Stefano Stabellini
2017-04-05 23:18 ` [PATCH v5 03/30] ARM: GICv3 ITS: parse and store ITS subnodes from hardware DT Andre Przywara
2017-04-05 23:26   ` Stefano Stabellini
2017-04-06 12:39     ` Julien Grall
2017-04-06 14:56       ` Andre Przywara
2017-04-06 15:02         ` Julien Grall
2017-04-06 18:55     ` Andre Przywara
2017-04-05 23:18 ` [PATCH v5 04/30] ARM: GICv3 ITS: initialize host ITS Andre Przywara
2017-04-05 23:27   ` Stefano Stabellini
2017-04-06 12:41   ` Julien Grall
2017-04-05 23:18 ` [PATCH v5 05/30] ARM: GICv3: allocate LPI pending and property table Andre Przywara
2017-04-05 23:30   ` Stefano Stabellini
2017-04-06 12:58   ` Julien Grall
2017-04-06 14:37     ` Andre Przywara
2017-04-06 14:37       ` Julien Grall
2017-04-05 23:18 ` [PATCH v5 06/30] ARM: GICv3 ITS: allocate device and collection table Andre Przywara
2017-04-06 14:44   ` Julien Grall
2017-04-06 15:49     ` Andre Przywara
2017-04-06 22:19   ` Julien Grall
2017-04-06 22:25     ` André Przywara
2017-04-05 23:18 ` [PATCH v5 07/30] ARM: GICv3 ITS: map ITS command buffer Andre Przywara
2017-04-06 14:49   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 08/30] ARM: GICv3 ITS: introduce ITS command handling Andre Przywara
2017-04-05 23:33   ` Stefano Stabellini
2017-04-06 14:55   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 09/30] ARM: GICv3 ITS: introduce host LPI array Andre Przywara
2017-04-05 23:37   ` Stefano Stabellini
2017-04-06 10:31     ` Andre Przywara
2017-04-06 11:18       ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 10/30] ARM: vGICv3: introduce ITS emulation stub Andre Przywara
2017-04-06 15:17   ` Julien Grall
2017-04-07 14:31   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 11/30] ARM: GICv3 ITS: introduce device mapping Andre Przywara
2017-04-05 23:41   ` Stefano Stabellini
2017-04-06 11:15     ` Julien Grall
2017-04-06 16:42       ` Stefano Stabellini
2017-04-06 17:12         ` Julien Grall
2017-04-06 15:34   ` Julien Grall
2017-04-06 16:10     ` Andre Przywara
2017-04-06 18:00       ` Julien Grall
2017-04-07 14:46   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 12/30] ARM: GICv3: introduce separate pending_irq structs for LPIs Andre Przywara
2017-04-06 15:59   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 13/30] ARM: GICv3: forward pending LPIs to guests Andre Przywara
2017-04-05 23:45   ` Stefano Stabellini
2017-04-06 17:42     ` Andre Przywara
2017-04-06 18:47       ` Stefano Stabellini
2017-04-06 19:13         ` Julien Grall
2017-04-06 19:47           ` Stefano Stabellini
2017-04-06 19:54             ` Julien Grall
2017-04-06 20:29               ` Stefano Stabellini
2017-04-06 18:10   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 14/30] ARM: GICv3: enable ITS and LPIs on the host Andre Przywara
2017-04-05 23:19 ` [PATCH v5 15/30] ARM: vGICv3: handle virtual LPI pending and property tables Andre Przywara
2017-04-05 23:55   ` Stefano Stabellini
2017-04-06 11:21     ` Julien Grall
2017-04-06 11:25     ` Andre Przywara
2017-04-06 11:24       ` Julien Grall
2017-04-06 11:28         ` Andre Przywara
2017-04-06 20:33   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 16/30] ARM: vGICv3: handle disabled LPIs Andre Przywara
2017-04-05 23:58   ` Stefano Stabellini
2017-04-06 19:09     ` Andre Przywara
2017-04-06 20:43       ` Stefano Stabellini
2017-04-06 21:20   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 17/30] ARM: vITS: add command handling stub and MMIO emulation Andre Przywara
2017-04-06  0:21   ` Stefano Stabellini
2017-04-06  8:55     ` Andre Przywara
2017-04-06 21:43   ` Julien Grall
2017-04-06 22:22     ` André Przywara
2017-04-06 22:34       ` Julien Grall
2017-04-06 22:25   ` Julien Grall
2017-04-06 22:39   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 18/30] ARM: vITS: introduce translation table walks Andre Przywara
2017-04-06 22:41   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 19/30] ARM: vITS: handle CLEAR command Andre Przywara
2017-04-06  0:31   ` Stefano Stabellini
2017-04-05 23:19 ` [PATCH v5 20/30] ARM: vITS: handle INT command Andre Przywara
2017-04-05 23:19 ` [PATCH v5 21/30] ARM: vITS: handle MAPC command Andre Przywara
2017-04-05 23:19 ` [PATCH v5 22/30] ARM: vITS: handle MAPD command Andre Przywara
2017-04-06  0:39   ` Stefano Stabellini
2017-04-07 12:41   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 23/30] ARM: vITS: handle MAPTI command Andre Przywara
2017-04-06  0:45   ` Stefano Stabellini
2017-04-11 16:15     ` Andre Przywara
2017-04-07 12:53   ` Julien Grall
2017-04-07 13:07   ` Julien Grall
2017-04-07 13:12     ` Andre Przywara
2017-04-05 23:19 ` [PATCH v5 24/30] ARM: vITS: handle MOVI command Andre Przywara
2017-04-06  0:47   ` Stefano Stabellini
2017-04-06 10:07     ` Andre Przywara
2017-04-06 17:56       ` Stefano Stabellini
2017-04-07 12:55   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 25/30] ARM: vITS: handle DISCARD command Andre Przywara
2017-04-06  0:48   ` Stefano Stabellini
2017-04-07 13:03   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 26/30] ARM: vITS: handle INV command Andre Przywara
2017-04-06  0:56   ` Stefano Stabellini
2017-04-07 13:23   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 27/30] ARM: vITS: handle INVALL command Andre Przywara
2017-04-05 23:19 ` [PATCH v5 28/30] ARM: vITS: create and initialize virtual ITSes for Dom0 Andre Przywara
2017-04-07 13:38   ` Julien Grall
2017-04-05 23:19 ` [PATCH v5 29/30] ARM: vITS: create ITS subnodes for Dom0 DT Andre Przywara
2017-04-07 13:41   ` Julien Grall
2017-04-07 13:46     ` Andre Przywara
2017-04-07 13:45       ` Julien Grall
2017-04-07 13:47         ` Julien Grall
2017-04-07 13:49           ` Andre Przywara
2017-04-05 23:19 ` [PATCH v5 30/30] ARM: vGIC: advertise LPI support Andre Przywara
2017-04-06  1:04   ` Stefano Stabellini
2017-04-06 10:21     ` Andre Przywara
2017-04-06 11:42       ` Julien Grall
2017-04-06 22:54         ` André Przywara
2017-04-07 13:43   ` Julien Grall
2017-04-06 12:31 ` [PATCH v5 00/30] arm64: Dom0 ITS emulation Julien Grall

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.