Re: dma-coherent: fix dma_declare_coherent_memory() logic error

2017-09-14 Thread Roy Pledge
On 9/5/2017 4:10 AM, Arnd Bergmann wrote:
> A recent change interprets the return code of dma_init_coherent_memory
> as an error value, but it is instead a boolean, where 'true' indicates
> success. This leads causes the caller to always do the wrong thing,
> and also triggers a compile-time warning about it:
> 
> drivers/base/dma-coherent.c: In function 'dma_declare_coherent_memory':
> drivers/base/dma-coherent.c:99:15: error: 'mem' may be used uninitialized in 
> this function [-Werror=maybe-uninitialized]
> 
> I ended up changing the code a little more, to give use the usual
> error handling, as this seemed the best way to fix up the warning
> and make the code look reasonable at the same time.
> 
> Fixes: 2436bdcda53f ("dma-coherent: remove the DMA_MEMORY_MAP and 
> DMA_MEMORY_IO flags")
> Signed-off-by: Arnd Bergmann 
> ---
>   drivers/base/dma-coherent.c | 38 +-
>   1 file changed, 25 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c
> index f82a504583d4..a39b2166b145 100644
> --- a/drivers/base/dma-coherent.c
> +++ b/drivers/base/dma-coherent.c
> @@ -37,7 +37,7 @@ static inline dma_addr_t dma_get_device_base(struct device 
> *dev,
>   return mem->device_base;
>   }
>   
> -static bool dma_init_coherent_memory(
> +static int dma_init_coherent_memory(
>   phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags,
>   struct dma_coherent_mem **mem)
>   {
> @@ -45,20 +45,28 @@ static bool dma_init_coherent_memory(
>   void __iomem *mem_base = NULL;
>   int pages = size >> PAGE_SHIFT;
>   int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
> + int ret;
>   
> - if (!size)
> + if (!size) {
> + ret = -EINVAL;
>   goto out;
> + }
>   
>   mem_base = memremap(phys_addr, size, MEMREMAP_WC);
> - if (!mem_base)
> + if (!mem_base) {
> + ret = -EINVAL;
>   goto out;
> -
> + }
>   dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
> - if (!dma_mem)
> + if (!dma_mem) {
> + ret = -ENOMEM;
>   goto out;
> + }
>   dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
> - if (!dma_mem->bitmap)
> + if (!dma_mem->bitmap) {
> + ret = -ENOMEM;
>   goto out;
> + }
>   
>   dma_mem->virt_base = mem_base;
>   dma_mem->device_base = device_addr;
> @@ -68,13 +76,13 @@ static bool dma_init_coherent_memory(
>   spin_lock_init(_mem->spinlock);
>   
>   *mem = dma_mem;
> - return true;
> + return 0;
>   
>   out:
>   kfree(dma_mem);
>   if (mem_base)
>   memunmap(mem_base);
> - return false;
> + return ret;
>   }
>   
>   static void dma_release_coherent_memory(struct dma_coherent_mem *mem)
> @@ -338,14 +346,18 @@ static struct reserved_mem *dma_reserved_default_memory 
> __initdata;
>   static int rmem_dma_device_init(struct reserved_mem *rmem, struct device 
> *dev)
>   {
>   struct dma_coherent_mem *mem = rmem->priv;
> + int ret;
> +
> + if (!mem)
> + return -ENODEV;

When I picked up this change my use of of_reserved_mem_device_init() 
broke. The only place rmem->priv is set is in this function (bottom of 
the patch) so the !mem check above will always fail. Am I missing 
something? It seems to me the intent here was to only call 
dma_init_coherent_memory() once and use rmem->priv as a cache for future 
calls but I'm just looking at the implemation of this for the first time.

>   
> - if (!mem &&
> - !dma_init_coherent_memory(rmem->base, rmem->base, rmem->size,
> -   DMA_MEMORY_EXCLUSIVE,
> -   )) {
> + ret = dma_init_coherent_memory(rmem->base, rmem->base, rmem->size,
> +DMA_MEMORY_EXCLUSIVE, );
> +
> + if (ret) {
>   pr_err("Reserved memory: failed to init DMA memory pool at %pa, 
> size %ld MiB\n",
>   >base, (unsigned long)rmem->size / SZ_1M);
> - return -ENODEV;
> + return ret;
>   }
>   mem->use_dev_dma_pfn_offset = true;
>   rmem->priv = mem;
> 

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[RFC] iommu: arm-smmu: stall support

2017-09-14 Thread Rob Clark
Adds a new domain property for iommu clients to opt-in to stalling
with asynchronous resume, and for the client to determine if the
iommu supports this.

Current motivation is that:

a) On 8x96/a530, if we don't enable CFCFG (or HUPCF) then non-
   faulting translations which are happening concurrently with
   one that faults, fail (or return garbage), which triggers all
   sorts of fun GPU crashes, which generally have no relation
   to the root fault.  (The CP can be far ahead in the cmdstream
   from the other parts of the GPU...)

b) I am working on a debugfs feature to dump submits/batches
   that cause GPU hangs, and I would like to also use this for
   faults.  But it needs to run in non-atomic context, so I
   need to toss things off to a workqueue, and then resume
   the iommu after it finishes.

c) (and ofc at some point in the future for SVM we'd like to
   be able to pin unpinned pages and things like that, in
   response to faults.)

TODO
 - For RFC I thought it would be easier to review the idea
   as a single patch, but it should be split into separate
   core and arm-smmu parts

 - I vaguely remember someone (Will?) mentioning that there
   could be cases with multiple masters sharing a single
   context bank, and somehow stalling might not work in that
   case?  (How does that even happen, arm-smmu assignes the
   context banks?  Maybe I'm mis-remembering the details.)
   I think that this probably shouldn't effect the API parts
   of this RFC, the iommu driver should already know about
   all the devices that might attach because of ->attach_dev()
   so it could fail in _set_attr()?

Signed-off-by: Rob Clark 
---
 drivers/iommu/arm-smmu.c | 36 
 drivers/iommu/iommu.c| 21 +
 include/linux/iommu.h| 14 ++
 3 files changed, 67 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index fe8e7fd61282..50131985a1e7 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -239,6 +239,7 @@ struct arm_smmu_domain {
struct io_pgtable_ops   *pgtbl_ops;
struct arm_smmu_cfg cfg;
enum arm_smmu_domain_stage  stage;
+   boolstall;
struct mutexinit_mutex; /* Protects smmu pointer */
spinlock_t  cb_lock; /* Serialises ATS1* ops */
struct iommu_domain domain;
@@ -544,6 +545,24 @@ static const struct iommu_gather_ops 
arm_smmu_s2_tlb_ops_v1 = {
.tlb_sync   = arm_smmu_tlb_sync_vmid,
 };
 
+static void arm_smmu_domain_resume(struct iommu_domain *domain, bool terminate)
+{
+   struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+   struct arm_smmu_cfg *cfg = _domain->cfg;
+   struct arm_smmu_device *smmu = smmu_domain->smmu;
+   void __iomem *cb_base;
+   unsigned val;
+
+   cb_base = ARM_SMMU_CB(smmu, cfg->cbndx);
+
+   if (terminate)
+   val = RESUME_TERMINATE;
+   else
+   val = RESUME_RETRY;
+
+   writel_relaxed(val, cb_base + ARM_SMMU_CB_RESUME);
+}
+
 static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
 {
u32 fsr, fsynr;
@@ -563,11 +582,14 @@ static irqreturn_t arm_smmu_context_fault(int irq, void 
*dev)
fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0);
iova = readq_relaxed(cb_base + ARM_SMMU_CB_FAR);
 
-   dev_err_ratelimited(smmu->dev,
-   "Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, cb=%d\n",
-   fsr, iova, fsynr, cfg->cbndx);
-
writel(fsr, cb_base + ARM_SMMU_CB_FSR);
+
+   if (!report_iommu_fault(domain, smmu->dev, iova, 0)) {
+   dev_err_ratelimited(smmu->dev,
+   "Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, 
cb=%d\n",
+   fsr, iova, fsynr, cfg->cbndx);
+   }
+
return IRQ_HANDLED;
 }
 
@@ -698,6 +720,8 @@ static void arm_smmu_init_context_bank(struct 
arm_smmu_domain *smmu_domain,
 
/* SCTLR */
reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE | SCTLR_M;
+   if (smmu_domain->stall)
+   reg |= SCTLR_CFCFG;/* stall on fault */
if (stage1)
reg |= SCTLR_S1_ASIDPNE;
 #ifdef __BIG_ENDIAN
@@ -1524,6 +1548,9 @@ static int arm_smmu_domain_set_attr(struct iommu_domain 
*domain,
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
 
break;
+   case DOMAIN_ATTR_STALL:
+   smmu_domain->stall = *(bool *)data;
+   break;
default:
ret = -ENODEV;
}
@@ -1587,6 +1614,7 @@ static struct iommu_ops arm_smmu_ops = {
.device_group   = arm_smmu_device_group,
.domain_get_attr= arm_smmu_domain_get_attr,
.domain_set_attr= arm_smmu_domain_set_attr,
+   

[PATCH v7 4/5] iommu/dma: Add a helper function to reserve HW MSI address regions for IOMMU drivers

2017-09-14 Thread Shameer Kolothum
IOMMU drivers can use this to implement their .get_resv_regions callback
for HW MSI specific reservations(e.g. ARM GICv3 ITS MSI region).

Signed-off-by: Shameer Kolothum 
[John: added DT support]
Signed-off-by: John Garry 
---
 drivers/iommu/dma-iommu.c | 19 +++
 include/linux/dma-iommu.h |  7 +++
 2 files changed, 26 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 9d1cebe..f8709a2 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -19,6 +19,7 @@
  * along with this program.  If not, see .
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -27,6 +28,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -198,6 +200,23 @@ void iommu_dma_get_resv_regions(struct device *dev, struct 
list_head *list)
 }
 EXPORT_SYMBOL(iommu_dma_get_resv_regions);
 
+/**
+ * iommu_dma_get_msi_resv_regions - Reserved region driver helper
+ * @dev: Device from iommu_get_resv_regions()
+ * @list: Reserved region list from iommu_get_resv_regions()
+ *
+ * IOMMU drivers can use this to implement their .get_resv_regions
+ * callback for HW MSI specific reservations.
+ */
+int iommu_dma_get_msi_resv_regions(struct device *dev, struct list_head *list)
+{
+   if (is_of_node(dev->iommu_fwspec->iommu_fwnode))
+   return of_iommu_msi_get_resv_regions(dev, list);
+
+   return iort_iommu_msi_get_resv_regions(dev, list);
+}
+EXPORT_SYMBOL(iommu_dma_get_msi_resv_regions);
+
 static int cookie_init_hw_msi_region(struct iommu_dma_cookie *cookie,
phys_addr_t start, phys_addr_t end)
 {
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 92f2083..6062ef0 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -74,6 +74,8 @@ void iommu_dma_unmap_resource(struct device *dev, dma_addr_t 
handle,
 void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
 void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
 
+int iommu_dma_get_msi_resv_regions(struct device *dev, struct list_head *list);
+
 #else
 
 struct iommu_domain;
@@ -107,6 +109,11 @@ static inline void iommu_dma_get_resv_regions(struct 
device *dev, struct list_he
 {
 }
 
+static inline int iommu_dma_get_msi_resv_regions(struct device *dev, struct 
list_head *list)
+{
+   return -ENODEV;
+}
+
 #endif /* CONFIG_IOMMU_DMA */
 #endif /* __KERNEL__ */
 #endif /* __DMA_IOMMU_H */
-- 
1.9.1


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v7 0/5] iommu/smmu-v3: Workaround for hisilicon 161010801 erratum(reserve HW MSI)

2017-09-14 Thread Shameer Kolothum
On certain HiSilicon platforms (hip06/hip07) the GIC ITS and PCIe RC
deviates from the standard implementation and this breaks PCIe MSI
functionality when SMMU is enabled.

The HiSilicon erratum 161010801 describes this limitation of certain
HiSilicon platforms to support the SMMU mappings for MSI transactions.
On these platforms GICv3 ITS translator is presented with the deviceID
by extending the MSI payload data to 64 bits to include the deviceID.
Hence, the PCIe controller on this platforms has to differentiate the MSI
payload against other DMA payload and has to modify the MSI payload.
This basically makes it difficult for this platforms to have a SMMU
translation for MSI.

This patch implements an ACPI and DT based quirk to reserve the hw msi
regions in the smmu-v3 driver which means these address regions will
not be translated and will be excluded from iova allocations.

To implement this quirk, the following changes are incorporated:
1. Added a generic helper function to IORT code to retrieve the
   associated ITS base address from a device IORT node.
2. Added a generic helper function to of iommu code to retrieve the
   associated msi controller base address from for a PCI RC
   msi-mapping and also platform device msi-parent.
3. Added quirk to SMMUv3 to retrieve the HW ITS address and replace
   the default SW MSI reserve address based on the IORT SMMU model
   or DT bindings.

Changelog:

v6 --> v7
Addressed request from Will to add DT support for the erratum:
 - added bt binding
 - add of_iommu_msi_get_resv_regions()
New arm64 silicon errata entry
Rename iort_iommu_{its->msi}_get_resv_regions

v5 --> v6
Addressed comments from Robin and Lorenzo:
-No change to patch#1 .
-Reverted v5 patch#2 as this might break the platforms where this quirk
  is not applicable. Provided a generic function in iommu code and added
  back the quirk implementation in SMMU v3 driver(patch#3)
 
v4 --> v5
Addressed comments from Robin and Lorenzo:
-Added a comment to make it clear that, for now, only straightforward 
  HW topologies are handled while reserving ITS regions(patch #1).

v3 --> v4
Rebased on 4.13-rc1.
Addressed comments from Robin, Will and Lorenzo:
-As suggested by Robin, moved the ITS msi reservation into 
  iommu_dma_get_resv_regions().
-Added its_count != resv region failure case(patch #1).

v2 --> v3
Addressed comments from Lorenzo and Robin:
-Removed dev_is_pci() check in smmuV3 driver.
-Don't treat device not having an ITS mapping as an error in
  iort helper function.

v1 --> v2
-patch 2/2: Invoke iort helper fn based on fwnode type(acpi).

RFCv2 -->PATCH
-Incorporated Lorenzo's review comments.

RFC v1 --> RFC v2
Based on Robin's review comments,
-Removed  the generic erratum framework.
-Using IORT/MADT tables to retrieve the ITS base addr instead  of vendor 
specific CSRT table.

John Garry (2):
  Doc: iommu/arm-smmu-v3: Add workaround for HiSilicon erratum 161010801
  iommu/of: Add msi address regions reservation helper

Shameer Kolothum (3):
  ACPI/IORT: Add msi address regions reservation helper
  iommu/dma: Add a helper function to reserve HW MSI address regions for
IOMMU drivers
  iommu/arm-smmu-v3:Enable ACPI based HiSilicon erratum 161010801

 Documentation/arm64/silicon-errata.txt |   1 +
 .../devicetree/bindings/iommu/arm,smmu-v3.txt  |   3 +
 drivers/acpi/arm64/iort.c  |  96 -
 drivers/iommu/arm-smmu-v3.c|  28 -
 drivers/iommu/dma-iommu.c  |  19 
 drivers/iommu/of_iommu.c   | 117 +
 drivers/irqchip/irq-gic-v3-its.c   |   3 +-
 include/linux/acpi_iort.h  |   7 +-
 include/linux/dma-iommu.h  |   7 ++
 include/linux/of_iommu.h   |  10 ++
 10 files changed, 281 insertions(+), 10 deletions(-)

-- 
1.9.1


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v7 2/5] ACPI/IORT: Add msi address regions reservation helper

2017-09-14 Thread Shameer Kolothum
On some platforms msi parent address regions have to be excluded from
normal IOVA allocation in that they are detected and decoded in a HW
specific way by system components and so they cannot be considered normal
IOVA address space.

Add a helper function that retrieves ITS address regions - the msi
parent - through IORT device <-> ITS mappings and reserves it so that
these regions will not be translated by IOMMU and will be excluded from
IOVA allocations.

Signed-off-by: Shameer Kolothum 
[lorenzo.pieral...@arm.com: updated commit log/added comments]
Signed-off-by: Lorenzo Pieralisi 
---
 drivers/acpi/arm64/iort.c| 96 ++--
 drivers/irqchip/irq-gic-v3-its.c |  3 +-
 include/linux/acpi_iort.h|  7 ++-
 3 files changed, 101 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index a3215ee..b2e9150 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -39,6 +39,7 @@
 struct iort_its_msi_chip {
struct list_headlist;
struct fwnode_handle*fw_node;
+   phys_addr_t base_addr;
u32 translation_id;
 };
 
@@ -136,14 +137,16 @@ typedef acpi_status (*iort_find_node_callback)
 static DEFINE_SPINLOCK(iort_msi_chip_lock);
 
 /**
- * iort_register_domain_token() - register domain token and related ITS ID
- * to the list from where we can get it back later on.
+ * iort_register_domain_token() - register domain token along with related
+ * ITS ID and base address to the list from where we can get it back later on.
  * @trans_id: ITS ID.
+ * @base: ITS base address.
  * @fw_node: Domain token.
  *
  * Returns: 0 on success, -ENOMEM if no memory when allocating list element
  */
-int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
+int iort_register_domain_token(int trans_id, phys_addr_t base,
+  struct fwnode_handle *fw_node)
 {
struct iort_its_msi_chip *its_msi_chip;
 
@@ -153,6 +156,7 @@ int iort_register_domain_token(int trans_id, struct 
fwnode_handle *fw_node)
 
its_msi_chip->fw_node = fw_node;
its_msi_chip->translation_id = trans_id;
+   its_msi_chip->base_addr = base;
 
spin_lock(_msi_chip_lock);
list_add(_msi_chip->list, _msi_chip_list);
@@ -481,6 +485,24 @@ int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
return -ENODEV;
 }
 
+static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+{
+   struct iort_its_msi_chip *its_msi_chip;
+   bool match = false;
+
+   spin_lock(_msi_chip_lock);
+   list_for_each_entry(its_msi_chip, _msi_chip_list, list) {
+   if (its_msi_chip->translation_id == its_id) {
+   *base = its_msi_chip->base_addr;
+   match = true;
+   break;
+   }
+   }
+   spin_unlock(_msi_chip_lock);
+
+   return match ? 0 : -ENODEV;
+}
+
 /**
  * iort_dev_find_its_id() - Find the ITS identifier for a device
  * @dev: The device.
@@ -639,6 +661,72 @@ int iort_add_device_replay(const struct iommu_ops *ops, 
struct device *dev)
 
return err;
 }
+
+/**
+ * iort_iommu_msi_get_resv_regions - Reserved region driver helper
+ * @dev: Device from iommu_get_resv_regions()
+ * @list: Reserved region list from iommu_get_resv_regions()
+ *
+ * Returns: Number of reserved regions on success (0 if no associated msi
+ *  regions), appropriate error value otherwise. The ITS regions
+ *  associated with the device are the msi reserved regions.
+ */
+int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
+{
+   struct acpi_iort_its_group *its;
+   struct acpi_iort_node *node, *its_node = NULL;
+   int i, resv = 0;
+
+   node = iort_find_dev_node(dev);
+   if (!node)
+   return -ENODEV;
+
+   /*
+* Current logic to reserve ITS regions relies on HW topologies
+* where a given PCI or named component maps its IDs to only one
+* ITS group; if a PCI or named component can map its IDs to
+* different ITS groups through IORT mappings this function has
+* to be reworked to ensure we reserve regions for all ITS groups
+* a given PCI or named component may map IDs to.
+*/
+   if (dev_is_pci(dev)) {
+   u32 rid;
+
+   pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid, );
+   its_node = iort_node_map_id(node, rid, NULL, IORT_MSI_TYPE);
+   } else {
+   for (i = 0; i < node->mapping_count; i++) {
+   its_node = iort_node_map_platform_id(node, NULL,
+IORT_MSI_TYPE, i);
+   if (its_node)
+   break;
+   }
+   }
+
+   if 

[PATCH v7 3/5] iommu/of: Add msi address regions reservation helper

2017-09-14 Thread Shameer Kolothum
From: John Garry 

On some platforms msi-controller address regions have to be excluded
from normal IOVA allocation in that they are detected and decoded in
a HW specific way by system components and so they cannot be considered
normal IOVA address space.

Add a helper function that retrieves msi address regions through device
tree msi mapping, so that these regions will not be translated by IOMMU
and will be excluded from IOVA allocations.

Signed-off-by: John Garry 
Signed-off-by: Shameer Kolothum 
---
 drivers/iommu/of_iommu.c | 117 +++
 include/linux/of_iommu.h |  10 
 2 files changed, 127 insertions(+)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 8cb6082..f2d1a76 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -246,6 +247,122 @@ const struct iommu_ops *of_iommu_configure(struct device 
*dev,
return ops;
 }
 
+/**
+ * of_iommu_msi_get_resv_regions - Reserved region driver helper
+ * @dev: Device from iommu_get_resv_regions()
+ * @list: Reserved region list from iommu_get_resv_regions()
+ *
+ * Returns: Number of reserved regions on success (0 if no associated
+ *  msi parent), appropriate error value otherwise.
+ */
+int of_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
+{
+   int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+   struct iommu_resv_region *region;
+   struct device_node *np;
+   struct resource res;
+   int i, resv = 0, mappings = 0;
+
+   if (dev_is_pci(dev)) {
+   struct device *dma_dev, *bridge;
+   struct of_phandle_args iommu_spec;
+   struct pci_dev *pdev = to_pci_dev(dev);
+   int err, count;
+   u32 rid, map_mask;
+   const __be32 *msi_map;
+
+   bridge = pci_get_host_bridge_device(pdev);
+   dma_dev = bridge->parent;
+   pci_put_host_bridge_device(bridge);
+
+   if (!dma_dev->of_node)
+   return -ENODEV;
+
+   iommu_spec.args_count = 1;
+   np = iommu_spec.np = dma_dev->of_node;
+   pci_for_each_dma_alias(pdev, __get_pci_rid, _spec);
+
+   rid = iommu_spec.args[0];
+   if (!of_property_read_u32(np, "msi-map-mask", _mask))
+   rid &= map_mask;
+
+   msi_map = of_get_property(np, "msi-map", NULL);
+   if (!msi_map)
+   return -ENODEV;
+
+   mappings = of_count_phandle_with_args(np, "msi-map", NULL) / 4;
+
+   for (i = 0, count = mappings; i < count; i++, msi_map += 4) {
+   struct device_node *msi_node;
+   u32 rid_base, rid_len, phandle;
+
+   rid_base = be32_to_cpup(msi_map + 0);
+   phandle = be32_to_cpup(msi_map + 1);
+   rid_len = be32_to_cpup(msi_map + 3);
+
+   /* check rid is within range */
+   if (rid < rid_base || rid >= rid_base + rid_len) {
+   mappings--;
+   continue;
+   }
+
+   msi_node = of_find_node_by_phandle(phandle);
+   if (!msi_node)
+   return -ENODEV;
+
+   err = of_address_to_resource(msi_node, 0, );
+   of_node_put(msi_node);
+   if (err)
+   return err;
+
+   region = iommu_alloc_resv_region(res.start,
+resource_size(),
+prot, IOMMU_RESV_MSI);
+   if (region) {
+   list_add_tail(>list, head);
+   resv++;
+   }
+   }
+   } else if (dev->of_node) {
+   struct device_node *msi_np;
+   int index = 0;
+   int tuples;
+
+   np = dev->of_node;
+
+   tuples = of_count_phandle_with_args(np, "msi-parent", NULL);
+
+   while (index < tuples) {
+   int msi_cells = 0;
+   int err;
+
+   msi_np = of_parse_phandle(np, "msi-parent", index);
+   if (!msi_np)
+   return -ENODEV;
+
+   of_property_read_u32(msi_np, "#msi-cells", _cells);
+
+   err = of_address_to_resource(msi_np, 0, );
+   of_node_put(msi_np);
+   if (err)
+   return err;
+
+   

[PATCH v7 5/5] iommu/arm-smmu-v3:Enable ACPI based HiSilicon erratum 161010801

2017-09-14 Thread Shameer Kolothum
The HiSilicon erratum 161010801 describes the limitation of HiSilicon
platforms Hip06/Hip07 to support the SMMU mappings for MSI transactions.

On these platforms GICv3 ITS translator is presented with the deviceID
by extending the MSI payload data to 64 bits to include the deviceID.
Hence, the PCIe controller on this platforms has to differentiate the
MSI payload against other DMA payload and has to modify the MSI payload.
This basically makes it difficult for this platforms to have a SMMU
translation for MSI.

This patch implements a quirk to reserve the hw msi regions in the
smmu-v3 driver which means these address regions will not be
translated and will be excluded from iova allocations.

Signed-off-by: Shameer Kolothum 
[John: add DT support]
Signed-off-by: John Garry 
---
 drivers/iommu/arm-smmu-v3.c | 28 +++-
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 568c400..8503f4d 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -608,6 +608,7 @@ struct arm_smmu_device {
 
 #define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0)
 #define ARM_SMMU_OPT_PAGE0_REGS_ONLY   (1 << 1)
+#define ARM_SMMU_OPT_RESV_HW_MSI   (1 << 2)
u32 options;
 
struct arm_smmu_cmdqcmdq;
@@ -674,6 +675,7 @@ struct arm_smmu_option_prop {
 
 static struct arm_smmu_option_prop arm_smmu_options[] = {
{ ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" },
+   { ARM_SMMU_OPT_RESV_HW_MSI, "hisilicon,broken-untranslated-msi" },
{ ARM_SMMU_OPT_PAGE0_REGS_ONLY, "cavium,cn9900-broken-page1-regspace"},
{ 0, NULL},
 };
@@ -1934,14 +1936,29 @@ static void arm_smmu_get_resv_regions(struct device 
*dev,
  struct list_head *head)
 {
struct iommu_resv_region *region;
+   struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv;
+   struct arm_smmu_device *smmu = master->smmu;
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+   int resv = 0;
 
-   region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
-prot, IOMMU_RESV_SW_MSI);
-   if (!region)
-   return;
+   if ((smmu->options & ARM_SMMU_OPT_RESV_HW_MSI)) {
 
-   list_add_tail(>list, head);
+   resv = iommu_dma_get_msi_resv_regions(dev, head);
+
+   if (resv < 0) {
+   dev_warn(dev, "HW MSI region resv failed: %d\n", resv);
+   return;
+   }
+   }
+
+   if (!resv) {
+   region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
+prot, IOMMU_RESV_SW_MSI);
+   if (!region)
+   return;
+
+   list_add_tail(>list, head);
+   }
 
iommu_dma_get_resv_regions(dev, head);
 }
@@ -2667,6 +2684,7 @@ static void acpi_smmu_get_options(u32 model, struct 
arm_smmu_device *smmu)
break;
case ACPI_IORT_SMMU_HISILICON_HI161X:
smmu->options |= ARM_SMMU_OPT_SKIP_PREFETCH;
+   smmu->options |= ARM_SMMU_OPT_RESV_HW_MSI;
break;
}
 
-- 
1.9.1


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v7 1/5] Doc: iommu/arm-smmu-v3: Add workaround for HiSilicon erratum 161010801

2017-09-14 Thread Shameer Kolothum
From: John Garry 

The HiSilicon erratum 161010801 describes the limitation of HiSilicon platforms
hip06/hip07 to support the SMMU mappings for MSI transactions.

On these platforms, GICv3 ITS translator is presented with the deviceID
by extending the MSI payload data to 64 bits to include the deviceID.
Hence, the PCIe controller on this platforms has to differentiate the MSI
payload against other DMA payload and has to modify the MSI payload.
This basically makes it difficult for this platforms to have a SMMU
translation for MSI.

This patch adds a SMMUv3 binding to flag that the SMMU breaks msi
translation at ITS.

Also, the arm64 silicon errata is updated with this same erratum.

Signed-off-by: John Garry 
Signed-off-by: Shameer Kolothum 
---
 Documentation/arm64/silicon-errata.txt  | 1 +
 Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/Documentation/arm64/silicon-errata.txt 
b/Documentation/arm64/silicon-errata.txt
index 66e8ce1..02816b1 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -70,6 +70,7 @@ stable kernels.
 || | | 
|
 | Hisilicon  | Hip0{5,6,7} | #161010101  | 
HISILICON_ERRATUM_161010101 |
 | Hisilicon  | Hip0{6,7}   | #161010701  | N/A 
|
+| Hisilicon  | Hip0{6,7}   | #161010801  | N/A 
|
 || | | 
|
 | Qualcomm Tech. | Falkor v1   | E1003   | 
QCOM_FALKOR_ERRATUM_1003|
 | Qualcomm Tech. | Falkor v1   | E1009   | 
QCOM_FALKOR_ERRATUM_1009|
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt 
b/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
index c9abbf3..1f5f7f9 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
@@ -55,6 +55,9 @@ the PCIe specification.
 - hisilicon,broken-prefetch-cmd
 : Avoid sending CMD_PREFETCH_* commands to the SMMU.
 
+- hisilicon,broken-untranslated-msi
+: Reserve ITS HW region to avoid translating msi.
+
 - cavium,cn9900-broken-page1-regspace
 : Replaces all page 1 offsets used for EVTQ_PROD/CONS,
  PRIQ_PROD/CONS register access with page 0 offsets.
-- 
1.9.1


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu