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

[Xen-devel] [PATCH resend 12/13] acpi: arm: Provide support for iort iommu configuration hooks



From: Manish Jaggi <mjaggi@xxxxxxxxxxxxxxxxxx>

This patch copies the basic code from linux (iort.c) required to parse
IORT and
hooks for iommu configuration and initializes smmu device.

Provides a top level initcall acpi_iort_init which would call
parse_iort (next patch) to parse and prepare the rid-devid and rid-sid
maps.

This patch is dependent on next patch. The code is cleanly split into
two patches

Signed-off-by: Manish Jaggi <manish.jaggi@xxxxxxxxxx>
---
 xen/arch/arm/acpi/Makefile   |   1 +
 xen/arch/arm/acpi/iort.c     | 401 +++++++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/device.h |  12 +-
 3 files changed, 413 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/acpi/Makefile b/xen/arch/arm/acpi/Makefile
index 073339603c..a596559205 100644
--- a/xen/arch/arm/acpi/Makefile
+++ b/xen/arch/arm/acpi/Makefile
@@ -2,3 +2,4 @@ obj-y += lib.o
 obj-y += boot.init.o
 obj-y += ridmap.o
 obj-y += gen-iort.o
+obj-y += iort.o
diff --git a/xen/arch/arm/acpi/iort.c b/xen/arch/arm/acpi/iort.c
new file mode 100644
index 0000000000..4238611ef7
--- /dev/null
+++ b/xen/arch/arm/acpi/iort.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2016, Semihalf
+ *    Author: Tomasz Nowicki <tn@xxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * This file implements early detection/parsing of I/O mapping
+ * reported to OS through firmware via I/O Remapping Table (IORT)
+ * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux 4.14.0
+ * Xen Modifications : Manish Jaggi <manish.jaggi@xxxxxxxxxx>
+ * Coding Style: Xen
+ */
+
+#define pr_fmt(fmt)    "ACPI: IORT: " fmt
+
+#include <asm/acpi/ridmap.h>
+#include <asm/acpi/acpi_iort.h>
+#include <asm/fwnode.h>
+#include <asm/fwspec.h>
+#include <xen/iommu.h>
+#include <xen/kernel.h>
+#include <xen/list.h>
+#include <xen/lib.h>
+#include <xen/pci.h>
+
+#define IORT_TYPE_MASK(type)   (1 << (type))
+#define IORT_MSI_TYPE          (1 << ACPI_IORT_NODE_ITS_GROUP)
+#define IORT_IOMMU_TYPE        ((1 << ACPI_IORT_NODE_SMMU) |    \
+                               (1 << ACPI_IORT_NODE_SMMU_V3))
+
+/* Until ACPICA headers cover IORT rev. C */
+#ifndef ACPI_IORT_SMMU_V3_CAVIUM_CN99XX
+#define ACPI_IORT_SMMU_V3_CAVIUM_CN99XX        0x2
+#endif
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({                    \
+    int __ret_warn_on = !!(condition);                \
+    if (unlikely(__ret_warn_on))                    \
+        printk(format);                        \
+    unlikely(__ret_warn_on);                    \
+})
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)
+
+#define MAX_ERRNO    4095
+#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned 
long)-MAX_ERRNO)
+
+struct iort_its_msi_chip {
+    struct list_head list;
+    struct fwnode_handle *fw_node;
+    u32 translation_id;
+};
+
+struct iort_fwnode {
+    struct list_head list;
+    struct acpi_iort_node *iort_node;
+    struct fwnode_handle *fwnode;
+};
+
+static LIST_HEAD(iort_fwnode_list);
+static DEFINE_SPINLOCK(iort_fwnode_lock);
+const struct fwnode_operations acpi_static_fwnode_ops;
+
+/**
+ * iort_set_fwnode() - Create iort_fwnode and use it to register
+ *               iommu data in the iort_fwnode_list
+ *
+ * @node: IORT table node associated with the IOMMU
+ * @fwnode: fwnode associated with the IORT node
+ *
+ * Returns: 0 on success
+ *          <0 on failure
+ */
+static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
+                  struct fwnode_handle *fwnode)
+{
+    struct iort_fwnode *np;
+
+    np = xzalloc(struct iort_fwnode);
+
+    if ( WARN_ON(!np) )
+        return -ENOMEM;
+
+    INIT_LIST_HEAD(&np->list);
+    np->iort_node = iort_node;
+    np->fwnode = fwnode;
+    spin_lock(&iort_fwnode_lock);
+    list_add_tail(&np->list, &iort_fwnode_list);
+    spin_unlock(&iort_fwnode_lock);
+
+    return 0;
+}
+
+/**
+ * iort_get_fwnode() - Retrieve fwnode associated with an IORT node
+ *
+ * @node: IORT table node to be looked-up
+ *
+ * Returns: fwnode_handle pointer on success, NULL on failure
+ */
+static inline
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node)
+{
+    struct iort_fwnode *curr;
+    struct fwnode_handle *fwnode = NULL;
+    spin_lock(&iort_fwnode_lock);
+    list_for_each_entry(curr, &iort_fwnode_list, list)
+    {
+        if ( curr->iort_node == node )
+        {
+            fwnode = curr->fwnode;
+            break;
+        }
+    }
+    spin_unlock(&iort_fwnode_lock);
+
+    return fwnode;
+}
+
+/**
+ * iort_delete_fwnode() - Delete fwnode associated with an IORT node
+ *
+ * @node: IORT table node associated with fwnode to delete
+ */
+static inline void iort_delete_fwnode(struct acpi_iort_node *node)
+{
+    struct iort_fwnode *curr, *tmp;
+
+    spin_lock(&iort_fwnode_lock);
+    list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list)
+    {
+        if ( curr->iort_node == node )
+        {
+            list_del(&curr->list);
+            xfree(curr);
+            break;
+        }
+    }
+    spin_unlock(&iort_fwnode_lock);
+}
+
+typedef acpi_status (*iort_find_node_callback)
+    (struct acpi_iort_node *node, void *context);
+
+/* Root pointer to the mapped IORT table */
+static struct acpi_table_header *iort_table;
+
+static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
+                         iort_find_node_callback callback,
+                         void *context)
+{
+    struct acpi_iort_node *iort_node, *iort_end;
+    struct acpi_table_iort *iort;
+    int i;
+
+    if ( !iort_table )
+        return NULL;
+
+    /* Get the first IORT node */
+    iort = (struct acpi_table_iort *)iort_table;
+    iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+                             iort->node_offset);
+    iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+                            iort_table->length);
+
+    for ( i = 0; i < iort->node_count; i++ )
+    {
+        if ( WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
+                   "IORT node pointer overflows, bad table!\n") )
+            return NULL;
+
+        if ( iort_node->type == type &&
+             ACPI_SUCCESS(callback(iort_node, context)) )
+            return iort_node;
+
+        iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+                     iort_node->length);
+    }
+
+    return NULL;
+}
+
+static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
+                        void *context)
+{
+    struct device *dev = context;
+    acpi_status status = AE_NOT_FOUND;
+
+    if ( node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX )
+    {
+        struct acpi_iort_root_complex *pci_rc;
+        struct pci_dev *pdev;
+
+        pdev = to_pci_dev(dev);
+        pci_rc = (struct acpi_iort_root_complex *)node->node_data;
+
+        /*
+         * It is assumed that PCI segment numbers maps one-to-one
+         * with root complexes. Each segment number can represent only
+         * one root complex.
+         */
+        status = pci_rc->pci_segment_number == pci_domain_nr(pdev) ?
+                                               AE_OK : AE_NOT_FOUND;
+    }
+
+    return status;
+}
+
+static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
+                   struct fwnode_handle *fwnode,
+                   const struct iommu_ops *ops)
+{
+    int ret;
+    ret  = iommu_fwspec_init(dev, fwnode, ops);
+
+    if ( !ret )
+        ret = iommu_fwspec_add_ids(dev, &streamid, 1);
+
+    return ret;
+}
+
+static inline
+const struct iommu_ops *iort_fwspec_iommu_ops(struct iommu_fwspec *fwspec)
+{
+    return (fwspec && fwspec->ops) ? fwspec->ops : NULL;
+}
+
+static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
+                            u32 streamid)
+{
+    const struct iommu_ops *ops;
+    struct fwnode_handle *iort_fwnode;
+
+    if ( !node )
+        return -ENODEV;
+
+    iort_fwnode = iort_get_fwnode(node);
+    if ( !iort_fwnode )
+        return -ENODEV;
+
+    /*
+     * If the ops look-up fails, this means that either
+     * the SMMU drivers have not been probed yet or that
+     * the SMMU drivers are not built in the kernel;
+     * Depending on whether the SMMU drivers are built-in
+     * in the kernel or not, defer the IOMMU configuration
+     * or just abort it.
+     */
+    ops = iommu_ops_from_fwnode(iort_fwnode);
+    if (!ops)
+        return -1;
+
+    return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops);
+}
+
+struct iort_pci_alias_info {
+    struct device *dev;
+    struct acpi_iort_node *node;
+};
+
+static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
+{
+    struct iort_pci_alias_info *info = data;
+    struct acpi_iort_node *smmu_node;
+    u32 streamid;
+
+    if ( query_sid(info->node, alias, &streamid, &smmu_node) )
+        return iort_iommu_xlate(info->dev, smmu_node, streamid);
+
+    return -1;
+}
+
+int pci_for_each_dma_alias(struct pci_dev *pdev,
+               int (*fn)(struct pci_dev *pdev,
+                   u16 alias, void *data), void *data)
+{
+    return fn(pdev, PCI_BDF2(pdev->bus, pdev->devfn), data);
+}
+
+/**
+ * iort_iommu_configure - Set-up IOMMU configuration for a device.
+ *
+ * @dev: device to configure
+ *
+ * Returns: iommu_ops pointer on configuration success
+ *          NULL on configuration failure
+ */
+const struct iommu_ops *iort_iommu_configure(struct device *dev)
+{
+    struct acpi_iort_node *node;
+    const struct iommu_ops *ops;
+    int err = -ENODEV;
+
+    /*
+     * If we already translated the fwspec there
+     * is nothing left to do, return the iommu_ops.
+     */
+    ops = iort_fwspec_iommu_ops(dev->iommu_fwspec);
+    if ( ops )
+        return ops;
+
+    if ( dev_is_pci(dev) )
+    {
+        struct iort_pci_alias_info info = { .dev = dev };
+        node = iort_scan_node(
+                              ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+                              iort_match_node_callback,
+                              dev);
+        if ( !node )
+            return NULL;
+
+        info.node = node;
+        err = pci_for_each_dma_alias(
+                                     to_pci_dev(dev),
+                                     iort_pci_iommu_init, &info);
+    }
+
+    return ops;
+}
+
+int iort_add_smmu_platform_device(struct acpi_iort_node *node)
+{
+    struct device *dev;
+    struct fwnode_handle *fwnode;
+    int ret;
+
+    dev = xzalloc(struct device);
+    if ( !dev )
+        return -ENOMEM;
+
+    dev->type = DEV_ACPI;
+    dev->acpi_node = node;
+
+    fwnode = iort_get_fwnode(node);
+
+    if ( !fwnode )
+    {
+        ret = -ENODEV;
+        goto end;
+    }
+
+    dev->fwnode = fwnode;
+    dev->iommu_fwspec = xzalloc(struct iommu_fwspec);
+
+    if ( !dev->iommu_fwspec )
+    {
+        ret = -ENOMEM;
+        goto end;
+    }
+
+    /* Call the acpi init functions for IOMMU devices */
+    ret = acpi_device_init(DEVICE_IOMMU, (void *) dev, node->type);
+end:
+    return ret;
+}
+
+static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+   struct fwnode_handle *fwnode;
+
+    fwnode = xzalloc(struct fwnode_handle);
+    if ( !fwnode )
+        return NULL;
+
+    fwnode->ops = &acpi_static_fwnode_ops;
+
+    return fwnode;
+}
+
+int __init acpi_iort_init(void)
+{
+    acpi_status status;
+
+    status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
+
+    if ( ACPI_FAILURE(status) )
+    {
+        if ( status != AE_NOT_FOUND )
+        {
+            const char *msg = acpi_format_exception(status);
+            printk("Failed to get table, %s\n", msg);
+        }
+
+        return -1;
+    }
+
+    return 0;
+}
+__initcall(acpi_iort_init);
diff --git a/xen/include/asm-arm/device.h b/xen/include/asm-arm/device.h
index b75d79b793..19a76f59d6 100644
--- a/xen/include/asm-arm/device.h
+++ b/xen/include/asm-arm/device.h
@@ -7,6 +7,7 @@ enum device_type
 {
     DEV_DT,
     DEV_PCI,
+    DEV_ACPI,
 };
 
 struct dev_archdata {
@@ -18,9 +19,18 @@ struct device
 {
     enum device_type type;
 #ifdef CONFIG_HAS_DEVICE_TREE
-    struct dt_device_node *of_node; /* Used by drivers imported from Linux */
+    /*
+     * TODO: of_node is redundant by addition of fwnode.
+     * Will be cleaned in future, kept here for compatability
+     * with smmuv2 driver
+     */
+    struct dt_device_node *of_node;
+#endif
+#ifdef CONFIG_ACPI
+    void *acpi_node;
 #endif
     struct dev_archdata archdata;
+    struct fwnode_handle *fwnode;
     struct iommu_fwspec *iommu_fwspec;
 };
 
-- 
2.14.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

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