[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v4 6/8] xen/iommu: smmu: Add Xen specific code to be able to use the driver




On Thursday 26 February 2015 12:22 AM, Julien Grall wrote:
The main goal is to modify as little the Linux code to be able to port
easily new feature added in Linux repo for the driver.

To achieve that we:
     - Add helpers to Linux function not implemented on Xen
     - Add callbacks used by Xen to do our own stuff and call Linux ones
     - Only modify when required the code which comes from Linux. If so a
     comment has been added with /* Xen: ... */ explaining why it's
     necessary.

The support for PCI has been commented because it's not yet supported by
Xen ARM and therefore won't compile.

Signed-off-by: Julien Grall <julien.grall@xxxxxxxxxx>

---
     Changes in v4:
         - Re-shuffle the code to rationalize the number of lines changed
         in the Linux SMMU code
         - Use #if 0 /* Xen: ... */ rather than using 2 lines
         - Add a bunch of definitions to avoid some #if 0
         - Remove the "hack" for the Midway SMMU

     Changes in v2:
         - Add the ACCESS_ONCE definition in the drivers. The patch to
         introduce the one in common code has been dropped.
         - The include xen/device.h has been dropped in favor of
         asm/device.h
---
  xen/drivers/passthrough/arm/Makefile |   1 +
  xen/drivers/passthrough/arm/smmu.c   | 661 ++++++++++++++++++++++++++++++++---
  2 files changed, 617 insertions(+), 45 deletions(-)
Hi Julien,
Few comments (first level)
a) What is the difference between assign / attach / add. There are different apis and I get lost reading the code between them.
Either provide a description or use a better nomenclature.

b) In which case a xen domain be having multiple contexts. Even though it is technically possible but there is no defined method to create a different context in non-pci or pci-passthrough.
Could you please add some detail.

-mj

diff --git a/xen/drivers/passthrough/arm/Makefile 
b/xen/drivers/passthrough/arm/Makefile
index 0484b79..f4cd26e 100644
--- a/xen/drivers/passthrough/arm/Makefile
+++ b/xen/drivers/passthrough/arm/Makefile
@@ -1 +1,2 @@
  obj-y += iommu.o
+obj-y += smmu.o
diff --git a/xen/drivers/passthrough/arm/smmu.c 
b/xen/drivers/passthrough/arm/smmu.c
index 6cd47b7..d01a26a 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -18,6 +18,13 @@
   *
   * Author: Will Deacon <will.deacon@xxxxxxx>
   *
+ * Based on Linux drivers/iommu/arm-smmu.c
+ *     => commit e6b5be2be4e30037eb551e0ed09dd97bd00d85d3
+ *
+ * Xen modification:
+ * Julien Grall <julien.grall@xxxxxxxxxx>
+ * Copyright (C) 2014 Linaro Limited.
+ *
   * This driver currently supports:
   *    - SMMUv1 and v2 implementations
   *    - Stream-matching and stream-indexing
@@ -28,25 +35,287 @@
   *    - Context fault reporting
   */
-#define pr_fmt(fmt) "arm-smmu: " fmt -#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/iommu.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
+#include <xen/config.h>
+#include <xen/delay.h>
+#include <xen/errno.h>
+#include <xen/err.h>
+#include <xen/irq.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/mm.h>
+#include <xen/vmap.h>
+#include <xen/rbtree.h>
+#include <xen/sched.h>
+#include <xen/sizes.h>
+#include <asm/atomic.h>
+#include <asm/device.h>
+#include <asm/io.h>
+#include <asm/platform.h>
+
+/* Xen: The below defines are redefined within the file. Undef it */
+#undef SCTLR_AFE
+#undef SCTLR_TRE
+#undef SCTLR_M
+#undef TTBCR_EAE
+
+/* Alias to Xen device tree helpers */
+#define device_node dt_device_node
+#define of_phandle_args dt_phandle_args
+#define of_device_id dt_device_match
+#define of_match_node dt_match_node
+#define of_property_read_u32(np, pname, out) (!dt_property_read_u32(np, pname, 
out))
+#define of_property_read_bool dt_property_read_bool
+#define of_parse_phandle_with_args dt_parse_phandle_with_args
+
+/* Xen: Helpers to get device MMIO and IRQs */
+struct resource
+{
+       u64 addr;
+       u64 size;
+       unsigned int type;
+};
+
+#define resource_size(res) (res)->size;
+
+#define platform_device dt_device_node
+
+#define IORESOURCE_MEM 0
+#define IORESOURCE_IRQ 1
+
+static struct resource *platform_get_resource(struct platform_device *pdev,
+                                             unsigned int type,
+                                             unsigned int num)
+{
+       /*
+        * The resource is only used between 2 calls of platform_get_resource.
+        * It's quite ugly but it's avoid to add too much code in the part
+        * imported from Linux
+        */
+       static struct resource res;
+       int ret = 0;
+
+       res.type = type;
+
+       switch (type) {
+       case IORESOURCE_MEM:
+               ret = dt_device_get_address(pdev, num, &res.addr, &res.size);
+
+               return ((ret) ? NULL : &res);
+
+       case IORESOURCE_IRQ:
+               ret = platform_get_irq(pdev, num);
+               if (ret < 0)
+                       return NULL;
+
+               res.addr = ret;
+               res.size = 1;
+
+               return &res;
+
+       default:
+               return NULL;
+       }
+}
+
+/* Xen: Helpers for IRQ functions */
+#define request_irq(irq, func, flags, name, dev) request_irq(irq, flags, func, 
name, dev)
+#define free_irq release_irq
+
+enum irqreturn {
+       IRQ_NONE        = (0 << 0),
+       IRQ_HANDLED     = (1 << 0),
+};
+
+typedef enum irqreturn irqreturn_t;
+
+/* Device logger functions
+ * TODO: Handle PCI
+ */
+#define dev_print(dev, lvl, fmt, ...)                                          
\
+        printk(lvl "smmu: %s: " fmt, dt_node_full_name(dev_to_dt(dev)), ## 
__VA_ARGS__)
+
+#define dev_dbg(dev, fmt, ...) dev_print(dev, XENLOG_DEBUG, fmt, ## 
__VA_ARGS__)
+#define dev_notice(dev, fmt, ...) dev_print(dev, XENLOG_INFO, fmt, ## 
__VA_ARGS__)
+#define dev_warn(dev, fmt, ...) dev_print(dev, XENLOG_WARNING, fmt, ## 
__VA_ARGS__)
+#define dev_err(dev, fmt, ...) dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_err_ratelimited(dev, fmt, ...)                                     
\
+        dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)           _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)           _xzalloc(size, sizeof(void *))
+#define devm_kzalloc(dev, size, flags) _xzalloc(size, sizeof(void *))
+#define kmalloc_array(size, n, flags)  _xmalloc_array(size, sizeof(void *), n)
+
+static void __iomem *devm_ioremap_resource(struct device *dev,
+                                          struct resource *res)
+{
+       void __iomem *ptr;
+
+       if (!res || res->type != IORESOURCE_MEM) {
+               dev_err(dev, "Invalid resource\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       ptr = ioremap_nocache(res->addr, res->size);
+       if (!ptr) {
+               dev_err(dev,
+                       "ioremap failed (addr 0x%"PRIx64" size 0x%"PRIx64")\n",
+                       res->addr, res->size);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       return ptr;
+}
+
+/* Xen doesn't handle IOMMU fault */
+#define report_iommu_fault(...)        1
+
+#define IOMMU_FAULT_READ       0
+#define IOMMU_FAULT_WRITE      1
+
+/*
+ * Xen: PCI functions
+ * TODO: It should be implemented when PCI will be supported
+ */
+#define to_pci_dev(dev)        (NULL)
+static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
+                                        int (*fn) (struct pci_dev *pdev,
+                                                   u16 alias, void *data),
+                                        void *data)
+{
+       BUG();
+       return 0;
+}
+
+/* Xen: misc */
+#define PHYS_MASK_SHIFT                PADDR_BITS
+typedef paddr_t phys_addr_t;
+
+#ifdef CONFIG_ARM_64
+# define CONFIG_64BIT
+#endif
+
+#define VA_BITS                0       /* Only used for configuring stage-1 
input size */
+
+/* The macro ACCESS_ONCE start to be replaced in Linux in favor of
+ * {READ, WRITE}_ONCE. Rather than introducing in the common code, keep a
+ * version here. We will have to drop it when the SMMU code in Linux will
+ * switch to {READ, WRITE}_ONCE.
+ */
+#define __ACCESS_ONCE(x) ({ \
+        __maybe_unused typeof(x) __var = 0; \
+       (volatile typeof(x) *)&(x); })
+#define ACCESS_ONCE(x) (*__ACCESS_ONCE(x))
+
+#define MODULE_DEVICE_TABLE(type, name)
+#define module_param_named(name, value, type, perm)
+#define MODULE_PARM_DESC(_parm, desc)
+
+/* Xen: Dummy iommu_domain */
+struct iommu_domain
+{
+       /* Runtime SMMU configuration for this iommu_domain */
+       struct arm_smmu_domain          *priv;
+
+       /* Used to link iommu_domain contexts for a same domain.
+        * There is at least one per-SMMU to used by the domain.
+        * */
+       struct list_head                list;
+};
+
+/* Xen: Describes informations required for a Xen domain */
+struct arm_smmu_xen_domain {
+       spinlock_t                      lock;
+       /* List of context (i.e iommu_domain) associated to this domain */
+       struct list_head                contexts;
+};
+
+/*
+ * Xen: Information about each device stored in dev->archdata.iommu
+ *
+ * Initially dev->archdata.iommu only stores the iommu_domain (runtime
+ * configuration of the SMMU) but, on Xen, we also have to store the
+ * iommu_group (list of streamIDs associated to the device).
+ *
+ * This is because Linux has a field iommu_group in the struct device. On Xen,
+ * that would require to move so hackery (dummy iommu_group) in a more generic
+ * place.
+ * */
+struct arm_smmu_xen_device {
+       struct iommu_domain *domain;
+       struct iommu_group *group;
+};
+
+#define dev_archdata(dev) ((struct arm_smmu_xen_device *)dev->archdata.iommu)
+#define dev_iommu_domain(dev) (dev_archdata(dev)->domain)
+#define dev_iommu_group(dev) (dev_archdata(dev)->group)
+
+/* Xen: Dummy iommu_group */
+struct iommu_group
+{
+       /* Streamids of the device */
+       struct arm_smmu_master_cfg *cfg;
+
+       atomic_t ref;
+};
+
+static struct iommu_group *iommu_group_alloc(void)
+{
+       struct iommu_group *group = xzalloc(struct iommu_group);
+
+       if (!group)
+               return ERR_PTR(-ENOMEM);
-#include <linux/amba/bus.h>
+       atomic_set(&group->ref, 1);
-#include <asm/pgalloc.h>
+       return group;
+}
+
+static void iommu_group_put(struct iommu_group *group)
+{
+       if (atomic_dec_and_test(&group->ref))
+               xfree(group);
+}
+
+static void iommu_group_set_iommudata(struct iommu_group *group,
+                                     struct arm_smmu_master_cfg *cfg,
+                                     void (*releasefn)(void *))
+{
+       /* TODO: Store the releasefn for the PCI */
+       ASSERT(releasefn == NULL);
+
+       group->cfg = cfg;
+}
+
+static int iommu_group_add_device(struct iommu_group *group,
+                                 struct device *dev)
+{
+       dev_iommu_group(dev) = group;
+
+       atomic_inc(&group->ref);
+
+       return 0;
+}
+
+static struct iommu_group *iommu_group_get(struct device *dev)
+{
+       struct iommu_group *group = dev_iommu_group(dev);
+
+       if (group)
+               atomic_inc(&group->ref);
+
+       return group;
+}
+
+#define iommu_group_get_iommudata(group) (group)->cfg
+
+/***** Start of Linux SMMU code *****/
/* Maximum number of stream IDs assigned to a single device */
  #define MAX_MASTER_STREAMIDS          MAX_PHANDLE_ARGS
@@ -397,7 +666,9 @@ struct arm_smmu_cfg {
        u8                              cbndx;
        u8                              irptndx;
        u32                             cbar;
-       pgd_t                           *pgd;
+
+       /* Xen: Domain associated to this configuration */
+       struct domain                   *domain;
  };
  #define INVALID_IRPTNDX                       0xff
@@ -446,6 +717,7 @@ static void parse_driver_options(struct arm_smmu_device *smmu) static struct device_node *dev_get_dev_node(struct device *dev)
  {
+#if 0 /* Xen: TODO: Add support for PCI */
        if (dev_is_pci(dev)) {
                struct pci_bus *bus = to_pci_dev(dev)->bus;
@@ -453,6 +725,7 @@ static struct device_node *dev_get_dev_node(struct device *dev)
                        bus = bus->parent;
                return bus->bridge->parent->of_node;
        }
+#endif
return dev->of_node;
  }
@@ -546,6 +819,9 @@ static int register_smmu_master(struct arm_smmu_device 
*smmu,
        master->of_node                      = masterspec->np;
        master->cfg.num_streamids    = masterspec->args_count;
+ /* Xen: Let Xen knows that the device is protected by an SMMU */
+       dt_device_set_protected(masterspec->np);
+
        for (i = 0; i < master->cfg.num_streamids; ++i) {
                u16 streamid = masterspec->args[i];
@@ -712,6 +988,24 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
        return IRQ_HANDLED;
  }
+/* Xen: Interrupt handlers wrapper */
+static void arm_smmu_context_fault_xen(int irq, void *dev,
+                                      struct cpu_user_regs *regs)
+{
+       arm_smmu_context_fault(irq, dev);
+}
+
+#define arm_smmu_context_fault arm_smmu_context_fault_xen
+
+static void arm_smmu_global_fault_xen(int irq, void *dev,
+                                     struct cpu_user_regs *regs)
+{
+       arm_smmu_global_fault(irq, dev);
+}
+
+#define arm_smmu_global_fault arm_smmu_global_fault_xen
+
+#if 0 /* Xen: Page tables are shared with the processor */
  static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
                                   size_t size)
  {
@@ -733,6 +1027,7 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_device 
*smmu, void *addr,
                                DMA_TO_DEVICE);
        }
  }
+#endif
static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
  {
@@ -741,6 +1036,7 @@ static void arm_smmu_init_context_bank(struct 
arm_smmu_domain *smmu_domain)
        struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
        struct arm_smmu_device *smmu = smmu_domain->smmu;
        void __iomem *cb_base, *gr0_base, *gr1_base;
+       paddr_t p2maddr;
gr0_base = ARM_SMMU_GR0(smmu);
        gr1_base = ARM_SMMU_GR1(smmu);
@@ -824,11 +1120,16 @@ static void arm_smmu_init_context_bank(struct 
arm_smmu_domain *smmu_domain)
        }
/* TTBR0 */
-       arm_smmu_flush_pgtable(smmu, cfg->pgd,
-                              PTRS_PER_PGD * sizeof(pgd_t));
-       reg = __pa(cfg->pgd);
+       /* Xen: The page table is shared with the P2M code */
+       ASSERT(smmu_domain->cfg.domain != NULL);
+       p2maddr = page_to_maddr(smmu_domain->cfg.domain->arch.p2m.root);
+
+       dev_notice(smmu->dev, "d%u: p2maddr 0x%"PRIpaddr"\n",
+                  smmu_domain->cfg.domain->domain_id, p2maddr);
+
+       reg = (p2maddr & ((1ULL << 32) - 1));
        writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
-       reg = (phys_addr_t)__pa(cfg->pgd) >> 32;
+       reg = (p2maddr >> 32);
        if (stage1)
                reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
        writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
@@ -873,6 +1174,9 @@ static void arm_smmu_init_context_bank(struct 
arm_smmu_domain *smmu_domain)
                reg = 0;
        }
+ /* Xen: The attributes to walk the page table should be the same as
+        * VTCR_EL2. Currently doesn't differ from Linux ones.
+        */
        reg |= TTBCR_EAE |
              (TTBCR_SH_IS << TTBCR_SH0_SHIFT) |
              (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) |
@@ -1015,7 +1319,6 @@ static void arm_smmu_destroy_domain_context(struct 
iommu_domain *domain)
  static int arm_smmu_domain_init(struct iommu_domain *domain)
  {
        struct arm_smmu_domain *smmu_domain;
-       pgd_t *pgd;
/*
         * Allocate the domain and initialise some of its data structures.
@@ -1026,20 +1329,12 @@ static int arm_smmu_domain_init(struct iommu_domain 
*domain)
        if (!smmu_domain)
                return -ENOMEM;
- pgd = kcalloc(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL);
-       if (!pgd)
-               goto out_free_domain;
-       smmu_domain->cfg.pgd = pgd;
-
        spin_lock_init(&smmu_domain->lock);
        domain->priv = smmu_domain;
        return 0;
-
-out_free_domain:
-       kfree(smmu_domain);
-       return -ENOMEM;
  }
+#if 0 /* Xen: Page tables are shared with the processor */
  static void arm_smmu_free_ptes(pmd_t *pmd)
  {
        pgtable_t table = pmd_pgtable(*pmd);
@@ -1102,6 +1397,7 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain 
*smmu_domain)
kfree(pgd_base);
  }
+#endif
static void arm_smmu_domain_destroy(struct iommu_domain *domain)
  {
@@ -1112,7 +1408,6 @@ static void arm_smmu_domain_destroy(struct iommu_domain 
*domain)
         * already been detached.
         */
        arm_smmu_destroy_domain_context(domain);
-       arm_smmu_free_pgtables(smmu_domain);
        kfree(smmu_domain);
  }
@@ -1229,11 +1524,12 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
        /*
         * We *must* clear the S2CR first, because freeing the SMR means
         * that it can be re-allocated immediately.
+        * Xen: Unlike Linux, any access to non-configured stream will fault.
         */
        for (i = 0; i < cfg->num_streamids; ++i) {
                u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
- writel_relaxed(S2CR_TYPE_BYPASS,
+               writel_relaxed(S2CR_TYPE_FAULT,
                               gr0_base + ARM_SMMU_GR0_S2CR(idx));
        }
@@ -1253,7 +1549,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
                return -ENXIO;
        }
- if (dev->archdata.iommu) {
+       if (dev_iommu_domain(dev)) {
                dev_err(dev, "already attached to IOMMU domain\n");
                return -EEXIST;
        }
@@ -1285,8 +1581,9 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
                return -ENODEV;
ret = arm_smmu_domain_add_master(smmu_domain, cfg);
+
        if (!ret)
-               dev->archdata.iommu = domain;
+               dev_iommu_domain(dev) = domain;
        return ret;
  }
@@ -1299,10 +1596,14 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
        if (!cfg)
                return;
- dev->archdata.iommu = NULL;
+       dev_iommu_domain(dev) = NULL;
        arm_smmu_domain_remove_master(smmu_domain, cfg);
  }
+#if 0 /*
+       * Xen: The page table is shared with the processor, therefore
+       * helpers to implement separate is not necessary.
+       */
  static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
                                             unsigned long end)
  {
@@ -1591,7 +1892,9 @@ static phys_addr_t arm_smmu_iova_to_phys(struct 
iommu_domain *domain,
return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK);
  }
+#endif
+#if 0 /* Xen: arm_smmu_capable is not used at the moment */
  static bool arm_smmu_capable(enum iommu_cap cap)
  {
        switch (cap) {
@@ -1609,6 +1912,7 @@ static bool arm_smmu_capable(enum iommu_cap cap)
                return false;
        }
  }
+#endif
static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
  {
@@ -1676,6 +1980,7 @@ out_put_group:
        return ret;
  }
+#if 0 /* Xen: We don't support remove device for now. Will be useful for PCI */
  static void arm_smmu_remove_device(struct device *dev)
  {
        iommu_group_remove_device(dev);
@@ -1733,6 +2038,7 @@ static const struct iommu_ops arm_smmu_ops = {
                                   ARM_SMMU_PTE_CONT_SIZE |
                                   PAGE_SIZE),
  };
+#endif
static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
  {
@@ -1748,7 +2054,11 @@ static void arm_smmu_device_reset(struct arm_smmu_device 
*smmu)
        /* Mark all SMRn as invalid and all S2CRn as bypass */
        for (i = 0; i < smmu->num_mapping_groups; ++i) {
                writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
-               writel_relaxed(S2CR_TYPE_BYPASS,
+               /*
+                * Xen: Unlike Linux, any access to a non-configure stream
+                * will fault by default.
+                */
+               writel_relaxed(S2CR_TYPE_FAULT,
                        gr0_base + ARM_SMMU_GR0_S2CR(i));
        }
@@ -1774,6 +2084,8 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) /* Enable client access, but bypass when no mapping is found */
        reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
+       /* Xen: Unlike Linux, generate a fault when no mapping is found */
+       reg |= sCR0_USFCFG;
/* Disable forced broadcasting */
        reg &= ~sCR0_FB;
@@ -1882,7 +2194,7 @@ static int arm_smmu_device_cfg_probe(struct 
arm_smmu_device *smmu)
                }
dev_notice(smmu->dev,
-                          "\tstream matching with %u register groups, mask 
0x%x",
+                          "\tstream matching with %u register groups, mask 
0x%x\n",
                           smmu->num_mapping_groups, mask);
        } else {
                smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
@@ -1917,12 +2229,16 @@ static int arm_smmu_device_cfg_probe(struct 
arm_smmu_device *smmu)
        size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK);
        smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size);
+ /* Xen: Stage-2 input size is not restricted */
+       smmu->s2_input_size = size;
+#if 0
        /* Stage-2 input size limited due to pgd allocation (PTRS_PER_PGD) */
  #ifdef CONFIG_64BIT
        smmu->s2_input_size = min_t(unsigned long, VA_BITS, size);
  #else
        smmu->s2_input_size = min(32UL, size);
  #endif
+#endif
/* The stage-2 output mask is also applied for bypass */
        size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
@@ -1969,6 +2285,10 @@ static const struct of_device_id arm_smmu_of_match[] = {
  };
  MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
+/*
+ * Xen: We don't have refcount for allocated memory so manually free memory
+ * when an error occured.
+ */
  static int arm_smmu_device_dt_probe(struct platform_device *pdev)
  {
        const struct of_device_id *of_id;
@@ -1991,14 +2311,17 @@ static int arm_smmu_device_dt_probe(struct 
platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        smmu->base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(smmu->base))
-               return PTR_ERR(smmu->base);
+       if (IS_ERR(smmu->base)) {
+               err = PTR_ERR(smmu->base);
+               goto out_free;
+       }
        smmu->size = resource_size(res);
if (of_property_read_u32(dev->of_node, "#global-interrupts",
                                 &smmu->num_global_irqs)) {
                dev_err(dev, "missing #global-interrupts property\n");
-               return -ENODEV;
+               err = -ENODEV;
+               goto out_free;
        }
num_irqs = 0;
@@ -2011,14 +2334,16 @@ static int arm_smmu_device_dt_probe(struct 
platform_device *pdev)
        if (!smmu->num_context_irqs) {
                dev_err(dev, "found %d interrupts but expected at least %d\n",
                        num_irqs, smmu->num_global_irqs + 1);
-               return -ENODEV;
+               err = -ENODEV;
+               goto out_free;
        }
smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs,
                                  GFP_KERNEL);
        if (!smmu->irqs) {
                dev_err(dev, "failed to allocate %d irqs\n", num_irqs);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto out_free;
        }
for (i = 0; i < num_irqs; ++i) {
@@ -2026,7 +2351,8 @@ static int arm_smmu_device_dt_probe(struct 
platform_device *pdev)
if (irq < 0) {
                        dev_err(dev, "failed to get irq index %d\n", i);
-                       return -ENODEV;
+                       err = -ENODEV;
+                       goto out_free;
                }
                smmu->irqs[i] = irq;
        }
@@ -2091,12 +2417,19 @@ out_put_masters:
        for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
                struct arm_smmu_master *master
                        = container_of(node, struct arm_smmu_master, node);
-               of_node_put(master->of_node);
+               kfree(master);
        }
+out_free:
+       kfree(smmu->irqs);
+       if (!IS_ERR(smmu->base))
+               iounmap(smmu->base);
+       kfree(smmu);
+
        return err;
  }
+#if 0 /* Xen: We never remove SMMU */
  static int arm_smmu_device_remove(struct platform_device *pdev)
  {
        int i;
@@ -2191,3 +2524,241 @@ module_exit(arm_smmu_exit);
  MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
  MODULE_AUTHOR("Will Deacon <will.deacon@xxxxxxx>");
  MODULE_LICENSE("GPL v2");
+#endif
+
+/***** Start of Xen specific code *****/
+
+/* Xen only supports stage-2 translation, so force the value to 2. */
+static int force_stage = 2;
+
+static void arm_smmu_iotlb_flush_all(struct domain *d)
+{
+       struct arm_smmu_xen_domain *smmu_domain = 
domain_hvm_iommu(d)->arch.priv;
+       struct iommu_domain *cfg;
+
+       spin_lock(&smmu_domain->lock);
+       list_for_each_entry(cfg, &smmu_domain->contexts, list) {
+               /*
+                * Only invalidate the context when SMMU is present.
+                * This is because the context initialization is delayed
+                * until a master has been added.
+                */
+               if (unlikely(!ACCESS_ONCE(cfg->priv->smmu)))
+                       continue;
+               arm_smmu_tlb_inv_context(cfg->priv);
+       }
+       spin_unlock(&smmu_domain->lock);
+}
+
+static void arm_smmu_iotlb_flush(struct domain *d, unsigned long gfn,
+                                 unsigned int page_count)
+{
+    /* ARM SMMU v1 doesn't have flush by VMA and VMID */
+    arm_smmu_iotlb_flush_all(d);
+}
+
+static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
+                              struct device *dev)
+{
+       struct iommu_domain *domain;
+       struct arm_smmu_xen_domain *xen_domain;
+       int ret;
+
+       xen_domain = domain_hvm_iommu(d)->arch.priv;
+
+       if (!dev->archdata.iommu) {
+               dev->archdata.iommu = xzalloc(struct arm_smmu_xen_device);
+               if (!dev->archdata.iommu)
+                       return -ENOMEM;
+       }
+
+       if (!dev_iommu_group(dev)) {
+               ret = arm_smmu_add_device(dev);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * TODO: Share the context bank (i.e iommu_domain) when the device is
+        * under the same SMMU as another device assigned to this domain.
+        * Would it useful for PCI
+        */
+       domain = xzalloc(struct iommu_domain);
+       if (!domain)
+               return -ENOMEM;
+
+       ret = arm_smmu_domain_init(domain);
+       if (ret)
+               goto err_dom_init;
+
+       domain->priv->cfg.domain = d;
+
+       ret = arm_smmu_attach_dev(domain, dev);
+       if (ret)
+               goto err_attach_dev;
+
+       spin_lock(&xen_domain->lock);
+       /* Chain the new context to the domain */
+       list_add(&domain->list, &xen_domain->contexts);
+       spin_unlock(&xen_domain->lock);
+
+       return 0;
+
+err_attach_dev:
+       arm_smmu_domain_destroy(domain);
+err_dom_init:
+       xfree(domain);
+
+       return ret;
+}
+
+static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
+{
+       struct iommu_domain *domain = dev_iommu_domain(dev);
+       struct arm_smmu_xen_domain *xen_domain;
+
+       xen_domain = domain_hvm_iommu(d)->arch.priv;
+
+       if (!domain || domain->priv->cfg.domain != d) {
+               dev_err(dev, " not attached to domain %d\n", d->domain_id);
+               return -ESRCH;
+       }
+
+       arm_smmu_detach_dev(domain, dev);
+
+       spin_lock(&xen_domain->lock);
+       list_del(&domain->list);
+       spin_unlock(&xen_domain->lock);
+
+       arm_smmu_domain_destroy(domain);
+       xfree(domain);
+
+       return 0;
+}
+
+static int arm_smmu_reassign_dev(struct domain *s, struct domain *t,
+                                u8 devfn,  struct device *dev)
+{
+       int ret = 0;
+
+       /* Don't allow remapping on other domain than hwdom */
+       if (t != hardware_domain)
+               return -EPERM;
+
+       if (t == s)
+               return 0;
+
+       ret = arm_smmu_deassign_dev(s, dev);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int arm_smmu_iommu_domain_init(struct domain *d)
+{
+       struct arm_smmu_xen_domain *xen_domain;
+
+       xen_domain = xzalloc(struct arm_smmu_xen_domain);
+       if ( !xen_domain )
+               return -ENOMEM;
+
+       spin_lock_init(&xen_domain->lock);
+       INIT_LIST_HEAD(&xen_domain->contexts);
+
+       domain_hvm_iommu(d)->arch.priv = xen_domain;
+
+       return 0;
+}
+
+static void __hwdom_init arm_smmu_iommu_hwdom_init(struct domain *d)
+{
+}
+
+static void arm_smmu_iommu_domain_teardown(struct domain *d)
+{
+       struct arm_smmu_xen_domain *xen_domain = domain_hvm_iommu(d)->arch.priv;
+
+       ASSERT(list_empty(&xen_domain->contexts));
+       xfree(xen_domain);
+}
+
+static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
+                            unsigned long mfn, unsigned int flags)
+{
+       p2m_type_t t;
+
+       /*
+        * Grant mappings can be used for DMA requests. The dev_bus_addr
+        * returned by the hypercall is the MFN (not the IPA). For device
+        * protected by an IOMMU, Xen needs to add a 1:1 mapping in the domain
+        * p2m to allow DMA request to work.
+        * This is only valid when the domain is directed mapped. Hence this
+        * function should only be used by gnttab code with gfn == mfn.
+        */
+       BUG_ON(!is_domain_direct_mapped(d));
+       BUG_ON(mfn != gfn);
+
+       /* We only support readable and writable flags */
+       if (!(flags & (IOMMUF_readable | IOMMUF_writable)))
+               return -EINVAL;
+
+       t = (flags & IOMMUF_writable) ? p2m_iommu_map_rw : p2m_iommu_map_ro;
+
+       /*
+        * The function guest_physmap_add_entry replaces the current mapping
+        * if there is already one...
+        */
+       return guest_physmap_add_entry(d, gfn, mfn, 0, t);
+}
+
+static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
+{
+       /*
+        * This function should only be used by gnttab code when the domain
+        * is direct mapped
+        */
+       if ( !is_domain_direct_mapped(d) )
+               return -EINVAL;
+
+       guest_physmap_remove_page(d, gfn, gfn, 0);
+
+       return 0;
+}
+
+static const struct iommu_ops arm_smmu_iommu_ops = {
+    .init = arm_smmu_iommu_domain_init,
+    .hwdom_init = arm_smmu_iommu_hwdom_init,
+    .teardown = arm_smmu_iommu_domain_teardown,
+    .iotlb_flush = arm_smmu_iotlb_flush,
+    .iotlb_flush_all = arm_smmu_iotlb_flush_all,
+    .assign_device = arm_smmu_assign_dev,
+    .reassign_device = arm_smmu_reassign_dev,
+    .map_page = arm_smmu_map_page,
+    .unmap_page = arm_smmu_unmap_page,
+};
+
+static __init int arm_smmu_dt_init(struct dt_device_node *dev,
+                                  const void *data)
+{
+       int rc;
+
+       /*
+        * Even if the device can't be initialized, we don't want to
+        * give the SMMU device to dom0.
+        */
+       dt_device_set_used_by(dev, DOMID_XEN);
+
+       rc = arm_smmu_device_dt_probe(dev);
+       if (rc)
+               return rc;
+
+       iommu_set_ops(&arm_smmu_iommu_ops);
+
+       return 0;
+}
+
+DT_DEVICE_START(smmu, "ARM SMMU", DEVICE_IOMMU)
+       .dt_match = arm_smmu_of_match,
+       .init = arm_smmu_dt_init,
+DT_DEVICE_END


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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.