* [PATCH v5 10/14] drivers: iommu: arm-smmu: split probe functions into DT/generic portions
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
Current ARM SMMU probe functions intermingle HW and DT probing
in the initialization functions to detect and programme the ARM SMMU
driver features. In order to allow probing the ARM SMMU with other
firmwares than DT, this patch splits the ARM SMMU init functions into
DT and HW specific portions so that other FW interfaces (ie ACPI) can
reuse the HW probing functions and skip the DT portion accordingly.
This patch implements no functional change, only code reshuffling.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Robin Murphy <robin.murphy@arm.com>
---
drivers/iommu/arm-smmu.c | 65 +++++++++++++++++++++++++++++-------------------
1 file changed, 39 insertions(+), 26 deletions(-)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index d453c55..bdb4e26 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1665,7 +1665,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
unsigned long size;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
u32 id;
- bool cttw_dt, cttw_reg;
+ bool cttw_reg, cttw_fw = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK;
int i;
dev_notice(smmu->dev, "probing hardware configuration...\n");
@@ -1710,20 +1710,17 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
/*
* In order for DMA API calls to work properly, we must defer to what
- * the DT says about coherency, regardless of what the hardware claims.
+ * the FW says about coherency, regardless of what the hardware claims.
* Fortunately, this also opens up a workaround for systems where the
* ID register value has ended up configured incorrectly.
*/
- cttw_dt = of_dma_is_coherent(smmu->dev->of_node);
cttw_reg = !!(id & ID0_CTTW);
- if (cttw_dt)
- smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK;
- if (cttw_dt || cttw_reg)
+ if (cttw_fw || cttw_reg)
dev_notice(smmu->dev, "\t%scoherent table walk\n",
- cttw_dt ? "" : "non-");
- if (cttw_dt != cttw_reg)
+ cttw_fw ? "" : "non-");
+ if (cttw_fw != cttw_reg)
dev_notice(smmu->dev,
- "\t(IDR0.CTTW overridden by dma-coherent property)\n");
+ "\t(IDR0.CTTW overridden by FW configuration)\n");
/* Max. number of entries we have for stream matching/indexing */
size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK);
@@ -1904,15 +1901,25 @@ static const struct of_device_id arm_smmu_of_match[] = {
};
MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
-static int arm_smmu_device_dt_probe(struct platform_device *pdev)
+static int arm_smmu_device_dt_probe(struct platform_device *pdev,
+ struct arm_smmu_device *smmu)
{
const struct arm_smmu_match_data *data;
- struct resource *res;
- struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
- int num_irqs, i, err;
bool legacy_binding;
+ if (of_property_read_u32(dev->of_node, "#global-interrupts",
+ &smmu->num_global_irqs)) {
+ dev_err(dev, "missing #global-interrupts property\n");
+ return -ENODEV;
+ }
+
+ data = of_device_get_match_data(dev);
+ smmu->version = data->version;
+ smmu->model = data->model;
+
+ parse_driver_options(smmu);
+
legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL);
if (legacy_binding && !using_generic_binding) {
pr_notice("deprecated \"mmu-masters\" DT property in use; DMA API support unavailable\n");
@@ -1924,6 +1931,19 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
return -ENODEV;
}
+ if (of_dma_is_coherent(smmu->dev->of_node))
+ smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK;
+
+ return 0;
+}
+
+static int arm_smmu_device_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct arm_smmu_device *smmu;
+ struct device *dev = &pdev->dev;
+ int num_irqs, i, err;
+
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
if (!smmu) {
dev_err(dev, "failed to allocate arm_smmu_device\n");
@@ -1931,9 +1951,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
}
smmu->dev = dev;
- data = of_device_get_match_data(dev);
- smmu->version = data->version;
- smmu->model = data->model;
+ err = arm_smmu_device_dt_probe(pdev, smmu);
+
+ if (err)
+ return err;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
smmu->base = devm_ioremap_resource(dev, res);
@@ -1941,12 +1962,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
return PTR_ERR(smmu->base);
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;
- }
-
num_irqs = 0;
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) {
num_irqs++;
@@ -1981,8 +1996,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (err)
return err;
- parse_driver_options(smmu);
-
if (smmu->version == ARM_SMMU_V2 &&
smmu->num_context_banks != smmu->num_context_irqs) {
dev_err(dev,
@@ -2004,7 +2017,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
}
}
- of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
+ fwspec_iommu_set_ops(&dev->of_node->fwnode, &arm_smmu_ops);
platform_set_drvdata(pdev, smmu);
arm_smmu_device_reset(smmu);
@@ -2044,7 +2057,7 @@ static struct platform_driver arm_smmu_driver = {
.name = "arm-smmu",
.of_match_table = of_match_ptr(arm_smmu_of_match),
},
- .probe = arm_smmu_device_dt_probe,
+ .probe = arm_smmu_device_probe,
.remove = arm_smmu_device_remove,
};
--
2.10.0
^ permalink raw reply related
* [PATCH v5 09/14] drivers: iommu: arm-smmu-v3: add IORT configuration
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
In ACPI bases systems, in order to be able to create platform
devices and initialize them for ARM SMMU v3 components, the IORT
kernel implementation requires a set of static functions to be
used by the IORT kernel layer to configure platform devices for
ARM SMMU v3 components.
Add static configuration functions to the IORT kernel layer for
the ARM SMMU v3 components, so that the ARM SMMU v3 driver can
initialize its respective platform device by relying on the IORT
kernel infrastructure and by adding a corresponding ACPI device
early probe section entry.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Joerg Roedel <joro@8bytes.org>
---
drivers/acpi/arm64/iort.c | 103 +++++++++++++++++++++++++++++++++++++++++++-
drivers/iommu/arm-smmu-v3.c | 95 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 195 insertions(+), 3 deletions(-)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index e0a9b16..a2ad102 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -425,6 +425,95 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id)
return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
}
+static void __init acpi_iort_register_irq(int hwirq, const char *name,
+ int trigger,
+ struct resource *res)
+{
+ int irq = acpi_register_gsi(NULL, hwirq, trigger,
+ ACPI_ACTIVE_HIGH);
+
+ if (irq < 0) {
+ pr_err("could not register gsi hwirq %d name [%s]\n", hwirq,
+ name);
+ return;
+ }
+
+ res->start = irq;
+ res->end = irq;
+ res->flags = IORESOURCE_IRQ;
+ res->name = name;
+}
+
+static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ /* Always present mem resource */
+ int num_res = 1;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ if (smmu->event_gsiv)
+ num_res++;
+
+ if (smmu->pri_gsiv)
+ num_res++;
+
+ if (smmu->gerr_gsiv)
+ num_res++;
+
+ if (smmu->sync_gsiv)
+ num_res++;
+
+ return num_res;
+}
+
+static void __init arm_smmu_v3_init_resources(struct resource *res,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ int num_res = 0;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ res[num_res].start = smmu->base_address;
+ res[num_res].end = smmu->base_address + SZ_128K - 1;
+ res[num_res].flags = IORESOURCE_MEM;
+
+ num_res++;
+
+ if (smmu->event_gsiv)
+ acpi_iort_register_irq(smmu->event_gsiv, "eventq",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+
+ if (smmu->pri_gsiv)
+ acpi_iort_register_irq(smmu->pri_gsiv, "priq",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+
+ if (smmu->gerr_gsiv)
+ acpi_iort_register_irq(smmu->gerr_gsiv, "gerror",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+
+ if (smmu->sync_gsiv)
+ acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync",
+ ACPI_EDGE_SENSITIVE,
+ &res[num_res++]);
+}
+
+static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE;
+}
+
struct iort_iommu_config {
const char *name;
int (*iommu_init)(struct acpi_iort_node *node);
@@ -434,10 +523,22 @@ struct iort_iommu_config {
struct acpi_iort_node *node);
};
+static const struct iort_iommu_config iort_arm_smmu_v3_cfg __initconst = {
+ .name = "arm-smmu-v3",
+ .iommu_is_coherent = arm_smmu_v3_is_coherent,
+ .iommu_count_resources = arm_smmu_v3_count_resources,
+ .iommu_init_resources = arm_smmu_v3_init_resources
+};
+
static __init
const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
{
- return NULL;
+ switch (node->type) {
+ case ACPI_IORT_NODE_SMMU_V3:
+ return &iort_arm_smmu_v3_cfg;
+ default:
+ return NULL;
+ }
}
/**
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index dbc21e3..9463f3f 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -20,6 +20,8 @@
* This driver is powered by bad coffee and bombay mix.
*/
+#include <linux/acpi.h>
+#include <linux/acpi_iort.h>
#include <linux/delay.h>
#include <linux/dma-iommu.h>
#include <linux/err.h>
@@ -2539,6 +2541,32 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
return 0;
}
+#ifdef CONFIG_ACPI
+static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
+ struct arm_smmu_device *smmu)
+{
+ struct acpi_iort_smmu_v3 *iort_smmu;
+ struct device *dev = smmu->dev;
+ struct acpi_iort_node *node;
+
+ node = *(struct acpi_iort_node **)dev_get_platdata(dev);
+
+ /* Retrieve SMMUv3 specific data */
+ iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE)
+ smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+ return 0;
+}
+#else
+static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
+ struct arm_smmu_device *smmu)
+{
+ return -ENODEV;
+}
+#endif
+
static int arm_smmu_device_dt_probe(struct platform_device *pdev,
struct arm_smmu_device *smmu)
{
@@ -2556,6 +2584,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
struct resource *res;
struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
+ struct fwnode_handle *fwnode;
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
if (!smmu) {
@@ -2592,7 +2621,10 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
if (irq > 0)
smmu->gerr_irq = irq;
- ret = arm_smmu_device_dt_probe(pdev, smmu);
+ if (dev->of_node)
+ ret = arm_smmu_device_dt_probe(pdev, smmu);
+ else
+ ret = arm_smmu_device_acpi_probe(pdev, smmu);
if (ret)
return ret;
@@ -2615,8 +2647,12 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
if (ret)
return ret;
+ /* FIXME: DT code path does not set up dev->fwnode pointer */
+ fwnode = dev->of_node ? &dev->of_node->fwnode : dev->fwnode;
+
/* And we're up. Go go go! */
- fwspec_iommu_set_ops(&dev->of_node->fwnode, &arm_smmu_ops);
+ fwspec_iommu_set_ops(fwnode, &arm_smmu_ops);
+
#ifdef CONFIG_PCI
pci_request_acs();
ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
@@ -2688,6 +2724,61 @@ static int __init arm_smmu_of_init(struct device_node *np)
}
IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", arm_smmu_of_init);
+#ifdef CONFIG_ACPI
+static int __init acpi_smmu_v3_init(struct acpi_table_header *table)
+{
+ struct acpi_iort_node *iort_node, *iort_end;
+ struct acpi_table_iort *iort;
+ struct fwnode_handle *fwnode;
+ int i, ret;
+
+ /*
+ * table and iort will both point to the start of IORT table, but
+ * have different struct types
+ */
+ iort = (struct acpi_table_iort *)table;
+
+ /* Get the first IORT node */
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, table,
+ iort->node_offset);
+ iort_end = ACPI_ADD_PTR(struct acpi_iort_node, table,
+ table->length);
+
+ for (i = 0; i < iort->node_count; i++) {
+
+ if (iort_node >= iort_end) {
+ pr_err("iort node pointer overflows, bad table\n");
+ return -EINVAL;
+ }
+
+ if (iort_node->type == ACPI_IORT_NODE_SMMU_V3) {
+ ret = arm_smmu_init();
+ if (ret)
+ return ret;
+
+ fwnode = iommu_alloc_fwnode();
+
+ if (!fwnode)
+ return -ENOMEM;
+
+ ret = iort_set_fwnode(iort_node, fwnode);
+ if (ret)
+ goto free;
+ }
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+ iort_node->length);
+ }
+
+ return 0;
+free:
+ iommu_free_fwnode(fwnode);
+ return ret;
+
+}
+IORT_ACPI_DECLARE(arm_smmu_v3, ACPI_SIG_IORT, acpi_smmu_v3_init);
+#endif
+
MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
MODULE_LICENSE("GPL v2");
--
2.10.0
^ permalink raw reply related
* [PATCH v5 08/14] drivers: iommu: arm-smmu-v3: split probe functions into DT/generic portions
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
Current ARM SMMUv3 probe functions intermingle HW and DT probing in the
initialization functions to detect and programme the ARM SMMU v3 driver
features. In order to allow probing the ARM SMMUv3 with other firmwares
than DT, this patch splits the ARM SMMUv3 init functions into DT and HW
specific portions so that other FW interfaces (ie ACPI) can reuse the HW
probing functions and skip the DT portion accordingly.
This patch implements no functional change, only code reshuffling.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Will Deacon <will.deacon@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Joerg Roedel <joro@8bytes.org>
---
drivers/iommu/arm-smmu-v3.c | 34 ++++++++++++++++++++++------------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index a7e9de9..dbc21e3 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2361,10 +2361,10 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
return 0;
}
-static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
+static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
{
u32 reg;
- bool coherent;
+ bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY;
/* IDR0 */
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
@@ -2416,13 +2416,9 @@ static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
smmu->features |= ARM_SMMU_FEAT_HYP;
/*
- * The dma-coherent property is used in preference to the ID
+ * The coherency feature as set by FW is used in preference to the ID
* register, but warn on mismatch.
*/
- coherent = of_dma_is_coherent(smmu->dev->of_node);
- if (coherent)
- smmu->features |= ARM_SMMU_FEAT_COHERENCY;
-
if (!!(reg & IDR0_COHACC) != coherent)
dev_warn(smmu->dev, "IDR0.COHACC overridden by dma-coherent property (%s)\n",
coherent ? "true" : "false");
@@ -2543,7 +2539,18 @@ static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
return 0;
}
-static int arm_smmu_device_dt_probe(struct platform_device *pdev)
+static int arm_smmu_device_dt_probe(struct platform_device *pdev,
+ struct arm_smmu_device *smmu)
+{
+ parse_driver_options(smmu);
+
+ if (of_dma_is_coherent(smmu->dev->of_node))
+ smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+ return 0;
+}
+
+static int arm_smmu_device_probe(struct platform_device *pdev)
{
int irq, ret;
struct resource *res;
@@ -2585,10 +2592,13 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (irq > 0)
smmu->gerr_irq = irq;
- parse_driver_options(smmu);
+ ret = arm_smmu_device_dt_probe(pdev, smmu);
+
+ if (ret)
+ return ret;
/* Probe the h/w */
- ret = arm_smmu_device_probe(smmu);
+ ret = arm_smmu_device_hw_probe(smmu);
if (ret)
return ret;
@@ -2606,7 +2616,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
return ret;
/* And we're up. Go go go! */
- of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
+ fwspec_iommu_set_ops(&dev->of_node->fwnode, &arm_smmu_ops);
#ifdef CONFIG_PCI
pci_request_acs();
ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
@@ -2640,7 +2650,7 @@ static struct platform_driver arm_smmu_driver = {
.name = "arm-smmu-v3",
.of_match_table = of_match_ptr(arm_smmu_of_match),
},
- .probe = arm_smmu_device_dt_probe,
+ .probe = arm_smmu_device_probe,
.remove = arm_smmu_device_remove,
};
--
2.10.0
^ permalink raw reply related
* [PATCH v5 07/14] drivers: acpi: iort: add support for ARM SMMU platform devices creation
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
In ARM ACPI systems, IOMMU components are specified through static
IORT table entries. In order to create platform devices for the
corresponding ARM SMMU components, IORT kernel code should be made
able to parse IORT table entries and create platform devices
dynamically.
This patch adds the generic IORT infrastructure required to create
platform devices for ARM SMMUs.
ARM SMMU versions have different resources requirement therefore this
patch also introduces an IORT specific structure (ie iort_iommu_config)
that contains hooks (to be defined when the corresponding ARM SMMU
driver support is added to the kernel) to be used to define the
platform devices names, init the IOMMUs, count their resources and
finally initialize them.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Tomasz Nowicki <tn@semihalf.com>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
---
drivers/acpi/arm64/iort.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 131 insertions(+)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index b89b3d3..e0a9b16 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
struct iort_its_msi_chip {
@@ -424,6 +425,135 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id)
return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
}
+struct iort_iommu_config {
+ const char *name;
+ int (*iommu_init)(struct acpi_iort_node *node);
+ bool (*iommu_is_coherent)(struct acpi_iort_node *node);
+ int (*iommu_count_resources)(struct acpi_iort_node *node);
+ void (*iommu_init_resources)(struct resource *res,
+ struct acpi_iort_node *node);
+};
+
+static __init
+const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
+{
+ return NULL;
+}
+
+/**
+ * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
+ * @fwnode: IORT node associated fwnode handle
+ * @node: Pointer to SMMU ACPI IORT node
+ *
+ * Returns: 0 on success, <0 failure
+ */
+static int __init iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
+ struct acpi_iort_node *node)
+{
+ struct platform_device *pdev;
+ struct resource *r;
+ enum dev_dma_attr attr;
+ int ret, count;
+ const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
+
+ if (!ops)
+ return -ENODEV;
+
+ pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
+ if (!pdev)
+ return PTR_ERR(pdev);
+
+ count = ops->iommu_count_resources(node);
+
+ r = kcalloc(count, sizeof(*r), GFP_KERNEL);
+ if (!r) {
+ ret = -ENOMEM;
+ goto dev_put;
+ }
+
+ ops->iommu_init_resources(r, node);
+
+ ret = platform_device_add_resources(pdev, r, count);
+ /*
+ * Resources are duplicated in platform_device_add_resources,
+ * free their allocated memory
+ */
+ kfree(r);
+
+ if (ret)
+ goto dev_put;
+
+ /*
+ * Add a copy of IORT node pointer to platform_data to
+ * be used to retrieve IORT data information.
+ */
+ ret = platform_device_add_data(pdev, &node, sizeof(node));
+ if (ret)
+ goto dev_put;
+
+ pdev->dev.dma_mask = kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
+ if (!pdev->dev.dma_mask) {
+ ret = -ENOMEM;
+ goto dev_put;
+ }
+
+ pdev->dev.fwnode = fwnode;
+
+ /*
+ * Set default dma mask value for the table walker,
+ * to be overridden on probing with correct value.
+ */
+ *pdev->dev.dma_mask = DMA_BIT_MASK(32);
+ pdev->dev.coherent_dma_mask = *pdev->dev.dma_mask;
+
+ attr = ops->iommu_is_coherent(node) ?
+ DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
+
+ /* Configure DMA for the page table walker */
+ acpi_dma_configure(&pdev->dev, attr);
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto dma_deconfigure;
+
+ return 0;
+
+dma_deconfigure:
+ acpi_dma_deconfigure(&pdev->dev);
+ kfree(pdev->dev.dma_mask);
+
+dev_put:
+ platform_device_put(pdev);
+
+ return ret;
+}
+
+static acpi_status __init iort_match_iommu_callback(struct acpi_iort_node *node,
+ void *context)
+{
+ int ret;
+ struct fwnode_handle *fwnode;
+
+ fwnode = iort_get_fwnode(node);
+
+ if (!fwnode)
+ return AE_NOT_FOUND;
+
+ ret = iort_add_smmu_platform_device(fwnode, node);
+ if (ret) {
+ pr_err("Error in platform device creation\n");
+ return AE_ERROR;
+ }
+
+ return AE_OK;
+}
+
+static void __init iort_smmu_init(void)
+{
+ iort_scan_node(ACPI_IORT_NODE_SMMU, iort_match_iommu_callback, NULL);
+ iort_scan_node(ACPI_IORT_NODE_SMMU_V3, iort_match_iommu_callback, NULL);
+}
+
void __init acpi_iort_init(void)
{
acpi_status status;
@@ -436,4 +566,5 @@ void __init acpi_iort_init(void)
}
acpi_probe_device_table(iort);
+ iort_smmu_init();
}
--
2.10.0
^ permalink raw reply related
* [PATCH v5 06/14] drivers: acpi: implement acpi_dma_configure
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
On DT based systems, the of_dma_configure() API implements DMA
configuration for a given device. On ACPI systems an API equivalent to
of_dma_configure() is missing which implies that it is currently not
possible to set-up DMA operations for devices through the ACPI generic
kernel layer.
This patch fills the gap by introducing acpi_dma_configure/deconfigure()
calls that for now are just wrappers around arch_setup_dma_ops() and
arch_teardown_dma_ops() and also updates ACPI and PCI core code to use
the newly introduced acpi_dma_configure/acpi_dma_deconfigure functions.
The DMA range size passed to arch_setup_dma_ops() is sized according
to the device coherent_dma_mask (starting at address 0x0), mirroring the
DT probing path behaviour when a dma-ranges property is not provided
for the device being probed; this changes the current arch_setup_dma_ops()
call parameters in the ACPI probing case, but since arch_setup_dma_ops()
is a NOP on all architectures but ARM/ARM64 this patch does not change
the current kernel behaviour on them.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com> [pci]
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Tomasz Nowicki <tn@semihalf.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
---
drivers/acpi/glue.c | 4 ++--
drivers/acpi/scan.c | 24 ++++++++++++++++++++++++
drivers/pci/probe.c | 3 +--
include/acpi/acpi_bus.h | 2 ++
include/linux/acpi.h | 5 +++++
5 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 5ea5dc2..f8d6564 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -227,8 +227,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
attr = acpi_get_dma_attr(acpi_dev);
if (attr != DEV_DMA_NOT_SUPPORTED)
- arch_setup_dma_ops(dev, 0, 0, NULL,
- attr == DEV_DMA_COHERENT);
+ acpi_dma_configure(dev, attr);
acpi_physnode_link_name(physical_node_name, node_id);
retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
@@ -251,6 +250,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
return 0;
err:
+ acpi_dma_deconfigure(dev);
ACPI_COMPANION_SET(dev, NULL);
put_device(dev);
put_device(&acpi_dev->dev);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index e878fc7..9614232 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1370,6 +1370,30 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
return DEV_DMA_NON_COHERENT;
}
+/**
+ * acpi_dma_configure - Set-up DMA configuration for the device.
+ * @dev: The pointer to the device
+ * @attr: device dma attributes
+ */
+void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr)
+{
+ /*
+ * Assume dma valid range starts at 0 and covers the whole
+ * coherent_dma_mask.
+ */
+ arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, NULL,
+ attr == DEV_DMA_COHERENT);
+}
+
+/**
+ * acpi_dma_deconfigure - Tear-down DMA configuration for the device.
+ * @dev: The pointer to the device
+ */
+void acpi_dma_deconfigure(struct device *dev)
+{
+ arch_teardown_dma_ops(dev);
+}
+
static void acpi_init_coherency(struct acpi_device *adev)
{
unsigned long long cca = 0;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 93f280d..e96d482 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1734,8 +1734,7 @@ static void pci_dma_configure(struct pci_dev *dev)
if (attr == DEV_DMA_NOT_SUPPORTED)
dev_warn(&dev->dev, "DMA not supported.\n");
else
- arch_setup_dma_ops(&dev->dev, 0, 0, NULL,
- attr == DEV_DMA_COHERENT);
+ acpi_dma_configure(&dev->dev, attr);
}
pci_put_host_bridge_device(bridge);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index c1a524d..4242c31 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -573,6 +573,8 @@ struct acpi_pci_root {
bool acpi_dma_supported(struct acpi_device *adev);
enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev);
+void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr);
+void acpi_dma_deconfigure(struct device *dev);
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index c5eaf2f..05d4e48 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -729,6 +729,11 @@ static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
return DEV_DMA_NOT_SUPPORTED;
}
+static inline void acpi_dma_configure(struct device *dev,
+ enum dev_dma_attr attr) { }
+
+static inline void acpi_dma_deconfigure(struct device *dev) { }
+
#define ACPI_PTR(_ptr) (NULL)
static inline void acpi_device_set_enumerated(struct acpi_device *adev)
--
2.10.0
^ permalink raw reply related
* [PATCH v5 05/14] drivers: iommu: make iommu_fwspec OF agnostic
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
The iommu_fwspec structure, used to hold per device iommu configuration
data is not OF specific and therefore can be moved to a generic
and OF independent compilation unit.
In particular, the iommu_fwspec handling hinges on the device_node
pointer to identify the IOMMU device associated with the iommu_fwspec
structure, that is easily converted to a more generic fwnode_handle
pointer that can cater for OF and non-OF (ie ACPI) systems.
Create the files and related Kconfig entry to decouple iommu_fwspec
structure from the OF iommu kernel layer.
Given that the current iommu_fwspec implementation relies on
the arch specific struct device.archdata.iommu field in its
implementation, by making the code standalone and independent
of the OF layer this patch makes sure that the iommu_fwspec
kernel code can be selected only on arches implementing the
struct device.archdata.iommu field by adding an explicit
arch dependency in its config entry.
Current drivers using the iommu_fwspec for streamid translation
are converted to the new iommu_fwspec API by simply converting
the device_node to its fwnode_handle pointer.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Joerg Roedel <joro@8bytes.org>
---
drivers/iommu/Kconfig | 4 ++
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 16 ++++--
drivers/iommu/arm-smmu.c | 17 +++---
drivers/iommu/iommu-fwspec.c | 126 +++++++++++++++++++++++++++++++++++++++++++
drivers/iommu/of_iommu.c | 93 --------------------------------
include/linux/iommu-fwspec.h | 70 ++++++++++++++++++++++++
include/linux/of_iommu.h | 38 ++++---------
8 files changed, 234 insertions(+), 131 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 101cb17..873bd41 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -70,6 +70,10 @@ config OF_IOMMU
config HAVE_IOMMU_FWSPEC
bool
+config IOMMU_FWSPEC
+ def_bool y
+ depends on IOMMU_API
+
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
bool
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 195f7b9..bbbc6d6 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
+obj-$(CONFIG_IOMMU_FWSPEC) += iommu-fwspec.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index be293b5..a7e9de9 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -25,6 +25,7 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
+#include <linux/iommu-fwspec.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/msi.h>
@@ -1720,13 +1721,18 @@ static struct platform_driver arm_smmu_driver;
static int arm_smmu_match_node(struct device *dev, void *data)
{
- return dev->of_node == data;
+ struct fwnode_handle *fwnode;
+
+ fwnode = dev->of_node ? &dev->of_node->fwnode : dev->fwnode;
+
+ return fwnode == data;
}
-static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
+static struct arm_smmu_device *
+arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
{
struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
- np, arm_smmu_match_node);
+ fwnode, arm_smmu_match_node);
put_device(dev);
return dev ? dev_get_drvdata(dev) : NULL;
}
@@ -1762,7 +1768,7 @@ static int arm_smmu_add_device(struct device *dev)
master = fwspec->iommu_priv;
smmu = master->smmu;
} else {
- smmu = arm_smmu_get_by_node(fwspec->iommu_np);
+ smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
if (!smmu)
return -ENODEV;
master = kzalloc(sizeof(*master), GFP_KERNEL);
@@ -1874,7 +1880,7 @@ out_unlock:
static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
{
- int ret = iommu_fwspec_init(dev, args->np);
+ int ret = iommu_fwspec_init(dev, &args->np->fwnode);
if (!ret)
ret = iommu_fwspec_add_ids(dev, &args->args[0], 1);
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 2e20cdc..d453c55 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -517,7 +517,7 @@ static int arm_smmu_register_legacy_master(struct device *dev,
it.cur_count = 1;
}
- err = iommu_fwspec_init(dev, smmu_dev->of_node);
+ err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode);
if (err)
return err;
@@ -1368,13 +1368,18 @@ static bool arm_smmu_capable(enum iommu_cap cap)
static int arm_smmu_match_node(struct device *dev, void *data)
{
- return dev->of_node == data;
+ struct fwnode_handle *fwnode;
+
+ fwnode = dev->of_node ? &dev->of_node->fwnode : dev->fwnode;
+
+ return fwnode == data;
}
-static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
+static struct arm_smmu_device *
+arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
{
struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
- np, arm_smmu_match_node);
+ fwnode, arm_smmu_match_node);
put_device(dev);
return dev ? dev_get_drvdata(dev) : NULL;
}
@@ -1392,7 +1397,7 @@ static int arm_smmu_add_device(struct device *dev)
if (ret)
goto out_free;
} else if (fwspec) {
- smmu = arm_smmu_get_by_node(fwspec->iommu_np);
+ smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
} else {
return -ENODEV;
}
@@ -1524,7 +1529,7 @@ out_unlock:
static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
{
u32 fwid = 0;
- int ret = iommu_fwspec_init(dev, args->np);
+ int ret = iommu_fwspec_init(dev, &args->np->fwnode);
if (ret)
return ret;
diff --git a/drivers/iommu/iommu-fwspec.c b/drivers/iommu/iommu-fwspec.c
new file mode 100644
index 0000000..be19102
--- /dev/null
+++ b/drivers/iommu/iommu-fwspec.c
@@ -0,0 +1,126 @@
+/*
+ * Firmware handling helpers for IOMMU
+ *
+ * Copyright (c) 2016 ARM Ltd. All rights reserved.
+ *
+ * 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.
+ */
+
+#include <linux/iommu.h>
+#include <linux/iommu-fwspec.h>
+#include <linux/of_iommu.h>
+#include <linux/slab.h>
+
+struct fwspec_iommu_node {
+ struct list_head list;
+ struct fwnode_handle *fwnode;
+ const struct iommu_ops *ops;
+};
+static LIST_HEAD(fwnode_iommu_list);
+static DEFINE_SPINLOCK(fwspec_iommu_lock);
+
+void fwspec_iommu_set_ops(struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops)
+{
+ struct fwspec_iommu_node *iommu =
+ kzalloc(sizeof(*iommu), GFP_KERNEL);
+
+ if (WARN_ON(!iommu))
+ return;
+
+ if (is_of_node(fwnode))
+ of_node_get(to_of_node(fwnode));
+
+ INIT_LIST_HEAD(&iommu->list);
+ iommu->fwnode = fwnode;
+ iommu->ops = ops;
+ spin_lock(&fwspec_iommu_lock);
+ list_add_tail(&iommu->list, &fwnode_iommu_list);
+ spin_unlock(&fwspec_iommu_lock);
+}
+
+const struct iommu_ops *fwspec_iommu_get_ops(struct fwnode_handle *fwnode)
+{
+ struct fwspec_iommu_node *node;
+ const struct iommu_ops *ops = NULL;
+
+ spin_lock(&fwspec_iommu_lock);
+ list_for_each_entry(node, &fwnode_iommu_list, list)
+ if (node->fwnode == fwnode) {
+ ops = node->ops;
+ break;
+ }
+ spin_unlock(&fwspec_iommu_lock);
+ return ops;
+}
+
+int iommu_fwspec_init(struct device *dev,
+ struct fwnode_handle *iommu_fwnode)
+{
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
+ const struct iommu_ops *ops;
+
+ if (fwspec)
+ return 0;
+
+ fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return -ENOMEM;
+
+ if (is_of_node(iommu_fwnode)) {
+ ops = of_iommu_get_ops(to_of_node(iommu_fwnode));
+ of_node_get(to_of_node(iommu_fwnode));
+ } else {
+ ops = fwspec_iommu_get_ops(iommu_fwnode);
+ }
+
+ fwspec->iommu_fwnode = iommu_fwnode;
+ fwspec->iommu_ops = ops;
+
+ arch_set_iommu_fwspec(dev, fwspec);
+ return 0;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
+
+ if (fwspec) {
+ if (is_of_node(fwspec->iommu_fwnode))
+ of_node_put(to_of_node(fwspec->iommu_fwnode));
+
+ kfree(fwspec);
+ }
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
+ size_t size;
+
+ if (!fwspec)
+ return -EINVAL;
+
+ size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + 1]);
+ fwspec = krealloc(fwspec, size, GFP_KERNEL);
+ if (!fwspec)
+ return -ENOMEM;
+
+ while (num_ids--)
+ fwspec->ids[fwspec->num_ids++] = *ids++;
+
+ arch_set_iommu_fwspec(dev, fwspec);
+ return 0;
+}
+
+inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+ return arch_get_iommu_fwspec(dev);
+}
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 38669b8..ab3c069 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -96,45 +96,6 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index,
}
EXPORT_SYMBOL_GPL(of_get_dma_window);
-struct of_iommu_node {
- struct list_head list;
- struct device_node *np;
- const struct iommu_ops *ops;
-};
-static LIST_HEAD(of_iommu_list);
-static DEFINE_SPINLOCK(of_iommu_lock);
-
-void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops)
-{
- struct of_iommu_node *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
-
- if (WARN_ON(!iommu))
- return;
-
- of_node_get(np);
- INIT_LIST_HEAD(&iommu->list);
- iommu->np = np;
- iommu->ops = ops;
- spin_lock(&of_iommu_lock);
- list_add_tail(&iommu->list, &of_iommu_list);
- spin_unlock(&of_iommu_lock);
-}
-
-const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
-{
- struct of_iommu_node *node;
- const struct iommu_ops *ops = NULL;
-
- spin_lock(&of_iommu_lock);
- list_for_each_entry(node, &of_iommu_list, list)
- if (node->np == np) {
- ops = node->ops;
- break;
- }
- spin_unlock(&of_iommu_lock);
- return ops;
-}
-
static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
{
struct of_phandle_args *iommu_spec = data;
@@ -226,57 +187,3 @@ static int __init of_iommu_init(void)
return 0;
}
postcore_initcall_sync(of_iommu_init);
-
-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)
-{
- struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
-
- if (fwspec)
- return 0;
-
- fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
- if (!fwspec)
- return -ENOMEM;
-
- fwspec->iommu_np = of_node_get(iommu_np);
- fwspec->iommu_ops = of_iommu_get_ops(iommu_np);
- arch_set_iommu_fwspec(dev, fwspec);
- return 0;
-}
-
-void iommu_fwspec_free(struct device *dev)
-{
- struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
-
- if (fwspec) {
- of_node_put(fwspec->iommu_np);
- kfree(fwspec);
- }
-}
-
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
-{
- struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
- size_t size;
- int i;
-
- if (!fwspec)
- return -EINVAL;
-
- size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
- fwspec = krealloc(fwspec, size, GFP_KERNEL);
- if (!fwspec)
- return -ENOMEM;
-
- for (i = 0; i < num_ids; i++)
- fwspec->ids[fwspec->num_ids + i] = ids[i];
-
- fwspec->num_ids += num_ids;
- arch_set_iommu_fwspec(dev, fwspec);
- return 0;
-}
-
-inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
-{
- return arch_get_iommu_fwspec(dev);
-}
diff --git a/include/linux/iommu-fwspec.h b/include/linux/iommu-fwspec.h
new file mode 100644
index 0000000..f88b635
--- /dev/null
+++ b/include/linux/iommu-fwspec.h
@@ -0,0 +1,70 @@
+#ifndef __IOMMU_FWSPEC_H
+#define __IOMMU_FWSPEC_H
+
+#include <linux/device.h>
+#include <linux/iommu.h>
+
+struct iommu_fwspec {
+ const struct iommu_ops *iommu_ops;
+ struct fwnode_handle *iommu_fwnode;
+ void *iommu_priv;
+ unsigned int num_ids;
+ u32 ids[];
+};
+
+#ifdef CONFIG_IOMMU_FWSPEC
+int iommu_fwspec_init(struct device *dev,
+ struct fwnode_handle *iommu_fwnode);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+
+void fwspec_iommu_set_ops(struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops);
+const struct iommu_ops *fwspec_iommu_get_ops(struct fwnode_handle *fwnode);
+
+#ifdef CONFIG_HAVE_IOMMU_FWSPEC
+#include <asm/iommu-fwspec.h>
+#else /* !CONFIG_HAVE_IOMMU_FWSPEC */
+static inline void arch_set_iommu_fwspec(struct device *dev,
+ struct iommu_fwspec *fwspec) {}
+
+static inline struct iommu_fwspec *
+arch_get_iommu_fwspec(struct device *dev) { return NULL; }
+#endif
+#else /* CONFIG_IOMMU_FWSPEC */
+static inline int iommu_fwspec_init(struct device *dev,
+ struct fwnode_handle *iommu_fwnode)
+{
+ return -ENODEV;
+}
+
+static inline void iommu_fwspec_free(struct device *dev)
+{
+}
+
+static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
+ int num_ids)
+{
+ return -ENODEV;
+}
+
+static inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+ return NULL;
+}
+
+static inline void fwspec_iommu_set_ops(struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops)
+{
+}
+
+static inline const struct iommu_ops *
+fwspec_iommu_get_ops(struct fwnode_handle *fwnode)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_IOMMU_FWSPEC */
+
+#endif /* __IOMMU_FWSPEC_H */
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 358db49..4b02861 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -3,6 +3,7 @@
#include <linux/device.h>
#include <linux/iommu.h>
+#include <linux/iommu-fwspec.h>
#include <linux/of.h>
#ifdef CONFIG_OF_IOMMU
@@ -14,14 +15,6 @@ extern int of_get_dma_window(struct device_node *dn, const char *prefix,
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);
-struct iommu_fwspec {
- const struct iommu_ops *iommu_ops;
- struct device_node *iommu_np;
- void *iommu_priv;
- unsigned int num_ids;
- u32 ids[];
-};
-
#else
static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -36,28 +29,19 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
{
return NULL;
}
-
-struct iommu_fwspec;
-
#endif /* CONFIG_OF_IOMMU */
-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
-void iommu_fwspec_free(struct device *dev);
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
-struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
-
-#ifdef CONFIG_HAVE_IOMMU_FWSPEC
-#include <asm/iommu-fwspec.h>
-#else /* !CONFIG_HAVE_IOMMU_FWSPEC */
-static inline void arch_set_iommu_fwspec(struct device *dev,
- struct iommu_fwspec *fwspec) {}
-
-static inline struct iommu_fwspec *
-arch_get_iommu_fwspec(struct device *dev) { return NULL; }
-#endif
+static inline void of_iommu_set_ops(struct device_node *np,
+ const struct iommu_ops *ops)
+{
+ fwspec_iommu_set_ops(&np->fwnode, ops);
+}
-void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
-const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
+static inline const struct iommu_ops *
+of_iommu_get_ops(struct device_node *np)
+{
+ return fwspec_iommu_get_ops(&np->fwnode);
+}
extern struct of_device_id __iommu_of_table;
--
2.10.0
^ permalink raw reply related
* [PATCH v5 04/14] drivers: acpi: iort: add support for IOMMU fwnode registration
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
The ACPI IORT table provide entries for IOMMU (aka SMMU in ARM world)
components that allow creating the kernel data structures required to
probe and initialize the IOMMU devices.
This patch provides support in the IORT kernel code to register IOMMU
components and their respective fwnode.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Tomasz Nowicki <tn@semihalf.com>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
---
drivers/acpi/arm64/iort.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/acpi_iort.h | 9 +++++++
2 files changed, 74 insertions(+)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 4cbeb707..b89b3d3 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -20,7 +20,9 @@
#include <linux/acpi_iort.h>
#include <linux/kernel.h>
+#include <linux/list.h>
#include <linux/pci.h>
+#include <linux/slab.h>
struct iort_its_msi_chip {
struct list_head list;
@@ -28,6 +30,69 @@ struct iort_its_msi_chip {
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);
+
+/**
+ * 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
+ */
+int iort_set_fwnode(struct acpi_iort_node *iort_node,
+ struct fwnode_handle *fwnode)
+{
+ struct iort_fwnode *np;
+
+ np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC);
+
+ 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
+*/
+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;
+}
+
typedef acpi_status (*iort_find_node_callback)
(struct acpi_iort_node *node, void *context);
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index d16fdda..c851646 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -30,6 +30,9 @@ struct fwnode_handle *iort_find_domain_token(int trans_id);
void acpi_iort_init(void);
u32 iort_msi_map_rid(struct device *dev, u32 req_id);
struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
+int iort_set_fwnode(struct acpi_iort_node *iort_node,
+ struct fwnode_handle *fwnode);
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node);
#else
static inline void acpi_iort_init(void) { }
static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
@@ -37,6 +40,12 @@ static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
static inline struct irq_domain *iort_get_device_domain(struct device *dev,
u32 req_id)
{ return NULL; }
+static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
+ struct fwnode_handle *fwnode)
+{ return -ENODEV; }
+static inline
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node)
+{ return NULL; }
#endif
#define IORT_ACPI_DECLARE(name, table_id, fn) \
--
2.10.0
^ permalink raw reply related
* [PATCH v5 03/14] drivers: acpi: iort: introduce linker section for IORT entries probing
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
Since commit e647b532275b ("ACPI: Add early device probing
infrastructure") the kernel has gained the infrastructure that allows
adding linker script section entries to execute ACPI driver callbacks
(ie probe routines) for all subsystems that register a table entry
in the respective kernel section (eg clocksource, irqchip).
Since ARM IOMMU devices data is described through IORT tables when
booting with ACPI, the ARM IOMMU drivers must be made able to hook ACPI
callback routines that are called to probe IORT entries and initialize
the respective IOMMU devices.
To avoid adding driver specific hooks into IORT table initialization
code (breaking therefore code modularity - ie ACPI IORT code must be made
aware of ARM SMMU drivers ACPI init callbacks), this patch adds code
that allows ARM SMMU drivers to take advantage of the ACPI early probing
infrastructure, so that they can add linker script section entries
containing drivers callback to be executed on IORT tables detection.
Since IORT nodes are differentiated by a type, the callback routines
can easily parse the IORT table entries, check the IORT nodes and
carry out some actions whenever the IORT node type associated with
the driver specific callback is matched.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Tomasz Nowicki <tn@semihalf.com>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Marc Zyngier <marc.zyngier@arm.com>
---
drivers/acpi/arm64/iort.c | 3 +++
include/asm-generic/vmlinux.lds.h | 1 +
include/linux/acpi_iort.h | 3 +++
3 files changed, 7 insertions(+)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index a6fba4d..4cbeb707 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -367,5 +367,8 @@ void __init acpi_iort_init(void)
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
const char *msg = acpi_format_exception(status);
pr_err("Failed to get table, %s\n", msg);
+ return;
}
+
+ acpi_probe_device_table(iort);
}
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 2456397..7ee4e0c 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -546,6 +546,7 @@
IRQCHIP_OF_MATCH_TABLE() \
ACPI_PROBE_TABLE(irqchip) \
ACPI_PROBE_TABLE(clksrc) \
+ ACPI_PROBE_TABLE(iort) \
EARLYCON_TABLE()
#define INIT_TEXT \
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 0e32dac..d16fdda 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -39,4 +39,7 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
{ return NULL; }
#endif
+#define IORT_ACPI_DECLARE(name, table_id, fn) \
+ ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn)
+
#endif /* __ACPI_IORT_H__ */
--
2.10.0
^ permalink raw reply related
* [PATCH v5 02/14] drivers: iommu: implement arch_{set/get}_iommu_fwspec API
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
The iommu fwspec configuration mechanism currently relies on
the arch specific struct dev_archdata.iommu member to stash
the struct iommu_fwspec pointer set-up for streamid translation.
The struct dev_archdata.iommu member is arch specific and is not present
on all arches that make use of the struct iommu_fwspec infrastructure,
hence an arch specific kernel API is required to set-up and retrieve
struct iommu_fwspec pointers safely from generic iommu code, hiding
the arch specific details.
Implement the arch_{set/get}_iommu_fwspec() generic kernel infrastructure
and add the ARM/ARM64 back-end implementations.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
---
arch/arm/Kconfig | 1 +
arch/arm/include/asm/iommu-fwspec.h | 30 ++++++++++++++++++++++++++++++
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/iommu-fwspec.h | 30 ++++++++++++++++++++++++++++++
drivers/iommu/Kconfig | 3 +++
drivers/iommu/of_iommu.c | 14 +++++++-------
include/linux/of_iommu.h | 10 ++++++++++
7 files changed, 82 insertions(+), 7 deletions(-)
create mode 100644 arch/arm/include/asm/iommu-fwspec.h
create mode 100644 arch/arm64/include/asm/iommu-fwspec.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a9c4e48..e84f62e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -59,6 +59,7 @@ config ARM
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7))
select HAVE_IDE if PCI || ISA || PCMCIA
+ select HAVE_IOMMU_FWSPEC if IOMMU_API
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZ4
diff --git a/arch/arm/include/asm/iommu-fwspec.h b/arch/arm/include/asm/iommu-fwspec.h
new file mode 100644
index 0000000..2e87d8b
--- /dev/null
+++ b/arch/arm/include/asm/iommu-fwspec.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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_IOMMU_FWSPEC_H
+#define __ASM_IOMMU_FWSPEC_H
+
+static inline void arch_set_iommu_fwspec(struct device *dev,
+ struct iommu_fwspec *fwspec)
+{
+ dev->archdata.iommu = fwspec;
+}
+
+static inline struct iommu_fwspec *arch_get_iommu_fwspec(struct device *dev)
+{
+ return dev->archdata.iommu;
+}
+#endif
+
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index bc3f00f..10c9b3d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -83,6 +83,7 @@ config ARM64
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if PERF_EVENTS
+ select HAVE_IOMMU_FWSPEC if IOMMU_API
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_MEMBLOCK
select HAVE_MEMBLOCK_NODE_MAP if NUMA
diff --git a/arch/arm64/include/asm/iommu-fwspec.h b/arch/arm64/include/asm/iommu-fwspec.h
new file mode 100644
index 0000000..2e87d8b
--- /dev/null
+++ b/arch/arm64/include/asm/iommu-fwspec.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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_IOMMU_FWSPEC_H
+#define __ASM_IOMMU_FWSPEC_H
+
+static inline void arch_set_iommu_fwspec(struct device *dev,
+ struct iommu_fwspec *fwspec)
+{
+ dev->archdata.iommu = fwspec;
+}
+
+static inline struct iommu_fwspec *arch_get_iommu_fwspec(struct device *dev)
+{
+ return dev->archdata.iommu;
+}
+#endif
+
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 8ee54d7..101cb17 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -67,6 +67,9 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
+config HAVE_IOMMU_FWSPEC
+ bool
+
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
bool
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 04d616d..38669b8 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -229,7 +229,7 @@ postcore_initcall_sync(of_iommu_init);
int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)
{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
if (fwspec)
return 0;
@@ -240,13 +240,13 @@ int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)
fwspec->iommu_np = of_node_get(iommu_np);
fwspec->iommu_ops = of_iommu_get_ops(iommu_np);
- dev->archdata.iommu = fwspec;
+ arch_set_iommu_fwspec(dev, fwspec);
return 0;
}
void iommu_fwspec_free(struct device *dev)
{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
if (fwspec) {
of_node_put(fwspec->iommu_np);
@@ -256,7 +256,7 @@ void iommu_fwspec_free(struct device *dev)
int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
size_t size;
int i;
@@ -264,7 +264,7 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
return -EINVAL;
size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
- fwspec = krealloc(dev->archdata.iommu, size, GFP_KERNEL);
+ fwspec = krealloc(fwspec, size, GFP_KERNEL);
if (!fwspec)
return -ENOMEM;
@@ -272,11 +272,11 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
fwspec->ids[fwspec->num_ids + i] = ids[i];
fwspec->num_ids += num_ids;
- dev->archdata.iommu = fwspec;
+ arch_set_iommu_fwspec(dev, fwspec);
return 0;
}
inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
{
- return dev->archdata.iommu;
+ return arch_get_iommu_fwspec(dev);
}
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index accdc05..358db49 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -46,6 +46,16 @@ void iommu_fwspec_free(struct device *dev);
int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+#ifdef CONFIG_HAVE_IOMMU_FWSPEC
+#include <asm/iommu-fwspec.h>
+#else /* !CONFIG_HAVE_IOMMU_FWSPEC */
+static inline void arch_set_iommu_fwspec(struct device *dev,
+ struct iommu_fwspec *fwspec) {}
+
+static inline struct iommu_fwspec *
+arch_get_iommu_fwspec(struct device *dev) { return NULL; }
+#endif
+
void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
--
2.10.0
^ permalink raw reply related
* [PATCH v5 01/14] drivers: iommu: add FWNODE_IOMMU fwnode type
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160909142343.13314-1-lorenzo.pieralisi@arm.com>
On systems booting with a device tree, every struct device is
associated with a struct device_node, that represents its DT
representation. The device node can be used in generic kernel
contexts (eg IRQ translation, IOMMU streamid mapping), to
retrieve the properties associated with the device and carry
out kernel operation accordingly. Owing to the 1:1 relationship
between the device and its device_node, the device_node can also
be used as a look-up token for the device (eg looking up a device
through its device_node), to retrieve the device in kernel paths
where the device_node is available.
On systems booting with ACPI, the same abstraction provided by
the device_node is required to provide look-up functionality.
Therefore, mirroring the approach implemented in the IRQ domain
kernel layer, this patch adds an additional fwnode type FWNODE_IOMMU.
This patch also implements a glue kernel layer that allows to
allocate/free FWNODE_IOMMU fwnode_handle structures and associate
them with IOMMU devices.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
---
include/linux/fwnode.h | 1 +
include/linux/iommu.h | 25 +++++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 8516717..6e10050 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -19,6 +19,7 @@ enum fwnode_type {
FWNODE_ACPI_DATA,
FWNODE_PDATA,
FWNODE_IRQCHIP,
+ FWNODE_IOMMU,
};
struct fwnode_handle {
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index a35fb8b..6456528 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -38,6 +38,7 @@ struct bus_type;
struct device;
struct iommu_domain;
struct notifier_block;
+struct fwnode_handle;
/* iommu fault flags */
#define IOMMU_FAULT_READ 0x0
@@ -543,4 +544,28 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)
#endif /* CONFIG_IOMMU_API */
+/* IOMMU fwnode handling */
+static inline bool is_fwnode_iommu(struct fwnode_handle *fwnode)
+{
+ return fwnode && fwnode->type == FWNODE_IOMMU;
+}
+
+static inline struct fwnode_handle *iommu_alloc_fwnode(void)
+{
+ struct fwnode_handle *fwnode;
+
+ fwnode = kzalloc(sizeof(struct fwnode_handle), GFP_KERNEL);
+ fwnode->type = FWNODE_IOMMU;
+
+ return fwnode;
+}
+
+static inline void iommu_free_fwnode(struct fwnode_handle *fwnode)
+{
+ if (WARN_ON(!is_fwnode_iommu(fwnode)))
+ return;
+
+ kfree(fwnode);
+}
+
#endif /* __LINUX_IOMMU_H */
--
2.10.0
^ permalink raw reply related
* [PATCH v5 00/14] ACPI IORT ARM SMMU support
From: Lorenzo Pieralisi @ 2016-09-09 14:23 UTC (permalink / raw)
To: linux-arm-kernel
This patch series is v5 of a previous posting:
https://lkml.org/lkml/2016/8/15/394
v4 -> v5
- Added SMMUv1/v2 support
- Rebased against v4.8-rc5 and dependencies series
- Consolidated IORT platform devices creation
v3 -> v4
- Added single mapping API (for IORT named components)
- Fixed arm_smmu_iort_xlate() return value
- Reworked fwnode registration and platform device creation
ordering to fix probe ordering dependencies
- Added code to keep device_node ref count with new iommu
fwspec API
- Added patch to make iommu_fwspec arch agnostic
- Dropped RFC status
- Rebased against v4.8-rc2
v2 -> v3
- Rebased on top of dependencies series [1][2][3](v4.7-rc3)
- Added back reliance on ACPI early probing infrastructure
- Patch[1-3] merged through other dependent series
- Added back IOMMU fwnode generalization
- Move SMMU v3 static functions configuration to IORT code
- Implemented generic IOMMU fwspec API
- Added code to implement fwnode platform device look-up
v1 -> v2:
- Rebased on top of dependencies series [1][2][3](v4.7-rc1)
- Removed IOMMU fwnode generalization
- Implemented ARM SMMU v3 ACPI probing instead of ARM SMMU v2
owing to patch series dependencies [1]
- Moved platform device creation logic to IORT code to
generalize its usage for ARM SMMU v1-v2-v3 components
- Removed reliance on ACPI early device probing
- Created IORT specific iommu_xlate() translation hook leaving
OF code unchanged according to v1 reviews
The ACPI IORT table provides information that allows instantiating
ARM SMMU devices and carrying out id mappings between components on
ARM based systems (devices, IOMMUs, interrupt controllers).
http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf
Building on basic IORT support, available through [2],
this patchset enables ARM SMMUs support on ACPI systems.
Most of the code is aimed at building the required generic ACPI
infrastructure to create and enable IOMMU components and to bring
the IOMMU infrastructure for ACPI on par with DT, which is going to
make future ARM SMMU components easier to integrate.
PATCH (1) adds a FWNODE_IOMMU type to the struct fwnode_handle type.
It is required to attach a fwnode identifier to platform
devices allocated/detected through IORT tables entries;
IOMMU devices have to have an identifier to look them up
eg IOMMU core layer carrying out id translation. This can be
done through a fwnode_handle (ie IOMMU platform devices created
out of IORT tables are not ACPI devices hence they can't be
allocated as such, otherwise they would have a fwnode_handle of
type FWNODE_ACPI).
PATCH (2) Add kernel infrastructure to make the struct iommu_fwspec
handling arch agnostic and generic.
PATCH (3) makes use of the ACPI early probing API to add a linker script
section for probing devices via IORT ACPI kernel code.
PATCH (4) provides IORT support for registering IOMMU IORT node through
their fwnode handle.
PATCH (5) extends iommu_fwspec so that it can be used on ACPI based
system by creating a generic IOMMU fwspec kernel layer.
PATCH (6) implements the of_dma_configure() API in ACPI world -
acpi_dma_configure() - and patches PCI and ACPI core code to
start making use of it.
PATCH (7) creates the kernel infrastructure required to create ARM SMMU
platform devices for IORT nodes.
PATCH (8) refactors the ARM SMMU v3 driver so that the init functions are
split in a way that groups together code that probes through DT
and code that carries out HW registers FW agnostic probing, in
preparation for adding the ACPI probing path.
PATCH (9) Building on patch (7), it adds ARM SMMU v3 IORT IOMMU
operations to create and probe ARM SMMU v3 components.
PATCH (10) refactors the ARM SMMU v1/v2 driver so that the init functions
are split in a way that groups together code that probes
through DT and code that carries out HW registers FW agnostic
probing, in preparation for adding the ACPI probing path.
PATCH (11) Building on patch (7), it adds ARM SMMU v1/v2 IORT IOMMU
operations to create and probe ARM SMMU v1/v2 components.
PATCH (12) Extend the IORT iort_node_map_rid() to work on a type mask
instead of a single type so that the translation API can
be used on a range of components.
PATCH (13) Add IORT API to carry out id mappings for components that do
do not have an input identifier/RIDs (ie named components).
PATCH (14) provides IORT infrastructure to carry out IOMMU configuration
for devices and hook it up to the previously introduced ACPI
DMA configure API.
This patchset is built on top and depends on these two patch series:
[1] R.Murphy "Generic DT bindings for PCI IOMMUS and ARM SMMU" v6
https://marc.info/?l=devicetree&m=147317605521320&w=2
[2] T.Nowicki "Introduce ACPI world to ITS irqchip" v10
https://marc.info/?l=linux-acpi&m=147315429713512&w=2
and is provided for review/testing purposes here:
git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux.git acpi/iort-smmu-v5
Tested on Juno and FVP models for ARM SMMU v1 and v3 probing path.
Lorenzo Pieralisi (14):
drivers: iommu: add FWNODE_IOMMU fwnode type
drivers: iommu: implement arch_{set/get}_iommu_fwspec API
drivers: acpi: iort: introduce linker section for IORT entries probing
drivers: acpi: iort: add support for IOMMU fwnode registration
drivers: iommu: make iommu_fwspec OF agnostic
drivers: acpi: implement acpi_dma_configure
drivers: acpi: iort: add support for ARM SMMU platform devices
creation
drivers: iommu: arm-smmu-v3: split probe functions into DT/generic
portions
drivers: iommu: arm-smmu-v3: add IORT configuration
drivers: iommu: arm-smmu: split probe functions into DT/generic
portions
drivers: iommu: arm-smmu: add IORT configuration
drivers: acpi: iort: replace rid map type with type mask
drivers: acpi: iort: add single mapping function
drivers: acpi: iort: introduce iort_iommu_configure
arch/arm/Kconfig | 1 +
arch/arm/include/asm/iommu-fwspec.h | 30 ++
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/iommu-fwspec.h | 30 ++
drivers/acpi/arm64/iort.c | 525 +++++++++++++++++++++++++++++++++-
drivers/acpi/glue.c | 4 +-
drivers/acpi/scan.c | 29 ++
drivers/iommu/Kconfig | 7 +
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 141 +++++++--
drivers/iommu/arm-smmu.c | 209 +++++++++++---
drivers/iommu/iommu-fwspec.c | 126 ++++++++
drivers/iommu/of_iommu.c | 93 ------
drivers/pci/probe.c | 3 +-
include/acpi/acpi_bus.h | 2 +
include/asm-generic/vmlinux.lds.h | 1 +
include/linux/acpi.h | 5 +
include/linux/acpi_iort.h | 21 ++
include/linux/fwnode.h | 1 +
include/linux/iommu-fwspec.h | 70 +++++
include/linux/iommu.h | 25 ++
include/linux/of_iommu.h | 28 +-
22 files changed, 1187 insertions(+), 166 deletions(-)
create mode 100644 arch/arm/include/asm/iommu-fwspec.h
create mode 100644 arch/arm64/include/asm/iommu-fwspec.h
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
--
2.10.0
^ permalink raw reply
* [PATCH 10/10] PM / Domains: Add support for removing nested PM domains by provider
From: Ulf Hansson @ 2016-09-09 14:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f31e57aa-52aa-d340-ca2b-73dbdf769035@nvidia.com>
[...]
>>> /**
>>> + * of_genpd_remove_tail - Remove the last PM domain registered for a provider
>>> + * @provider: Pointer to device structure associated with provider
>>
>> The naming of this function would be okay, if we only have added
>> genpds in the gpd_list by using list_add_tail(), although we don't.
>> Instead we use list_add() and put them first in the list.
>>
>> So, unless we change to use list_add_tail() when adding genpds (I
>> assume we can do that!), I would rather change the name of this
>> function to of_genpd_remove_first().
>>
>> What option do you prefer?
>
> I think that I would prefer either of_genpd_remove_last() or
> of_genpd_remove_one(). Although _first is accurate from the list
> perspective it seems odd from the user perspective. I think that _last
> is more meaningful as we are removing the last that was added regardless
> or how things appear on the list. Alternatively, _one could be a good
> compromise.
I am fine with of_genpd_remove_last().
[...]
Kind regards
Uffe
^ permalink raw reply
* [PATCH] iommu/arm-smmu: Disable interrupts whilst holding the cmdq lock
From: Jean-Philippe Brucker @ 2016-09-09 14:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473429014-17926-1-git-send-email-will.deacon@arm.com>
Hi Will,
On 09/09/16 14:50, Will Deacon wrote:
> The cmdq lock is taken whenever we issue comments into the command queue,
s/comments/commands/
> which can occur in IRQ context (as a result if unmap) or in process
s/if/of/
> context (as a result of a threaded IRQ handler or device probe).
>
> This can lead to a theoretical deadlock if the interrupt handler
> performing the unmap hits whilst the lock is taken, so explicitly use
> the {irqsave,irqrestore} spin_lock accessors for the cmdq lock.
>
> Signed-off-by: Will Deacon <will.deacon@arm.com>
Works for me and keeps lockdep quiet, so FWIW
Tested-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> ---
> drivers/iommu/arm-smmu-v3.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
> index c040e246bc59..5db6931c715c 100644
> --- a/drivers/iommu/arm-smmu-v3.c
> +++ b/drivers/iommu/arm-smmu-v3.c
> @@ -893,6 +893,7 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
> struct arm_smmu_cmdq_ent *ent)
> {
> u64 cmd[CMDQ_ENT_DWORDS];
> + unsigned long flags;
> bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV);
> struct arm_smmu_queue *q = &smmu->cmdq.q;
>
> @@ -902,7 +903,7 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
> return;
> }
>
> - spin_lock(&smmu->cmdq.lock);
> + spin_lock_irqsave(&smmu->cmdq.lock, flags);
> while (queue_insert_raw(q, cmd) == -ENOSPC) {
> if (queue_poll_cons(q, false, wfe))
> dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
> @@ -910,7 +911,7 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
>
> if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, true, wfe))
> dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
> - spin_unlock(&smmu->cmdq.lock);
> + spin_unlock_irqrestore(&smmu->cmdq.lock, flags);
> }
>
> /* Context descriptor manipulation functions */
>
^ permalink raw reply
* [PATCH v3 1/8] arm64: KVM: Use static keys for selecting the GIC backend
From: Marc Zyngier @ 2016-09-09 14:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <57D2BCEB.3030306@arm.com>
On 09/09/16 14:45, Vladimir Murzin wrote:
> On 09/09/16 10:33, Vladimir Murzin wrote:
>> Hi Marc,
>>
>> On 09/09/16 10:19, Marc Zyngier wrote:
>>>> Hi Vladimir,
>>>>
>> ...
>>>>>>
>>>>>> +extern struct static_key_false kvm_gicv3_cpuif;
>>>>
>>>> I think we should follow the model set by kvm_vgic_global_state, which
>>>> is declared in arm_vgic.h. Even better, we should *embed* the static key
>>>> in this structure. This will reduce the clutter and we wouldn't have to
>>>> deal with all the section stuff (the hyp_data thing is a good cleanup,
>>>> but I'd like to see it as a separate patch if possible).
>> Yes, it is what I was thinking about too, but was not sure about which
>> way to go, so hyp_data seemed me something we might reuse latter.
>> However, I agree that we can defer hyp_data thing...
>>
>
> I've just tried it out and it seems that static keys are not happy to
> accept a key after kern_hyp_va is applied at &kvm_vgic_global_state:
Ah, there is a trick. You do not need kern_hyp_va at all, because this
is not evaluated as an expression at runtime (so the pointer doesn't matter).
>> In file included from ./include/linux/jump_label.h:105:0,
>> from arch/arm64/kvm/hyp/switch.c:19:
>> ./arch/arm64/include/asm/jump_label.h: In function ?__guest_run?:
>> ./arch/arm64/include/asm/jump_label.h:31:2: warning: asm operand 0 probably doesn?t match constraints
>> asm goto("1: nop\n\t"
>> ^
>> ./arch/arm64/include/asm/jump_label.h:31:2: warning: asm operand 0 probably doesn?t match constraints
>> asm goto("1: nop\n\t"
>> ^
>> ./arch/arm64/include/asm/jump_label.h:31:2: error: impossible constraint in ?asm?
>> asm goto("1: nop\n\t"
>> ^
>> ./arch/arm64/include/asm/jump_label.h:31:2: error: impossible constraint in ?asm?
>> asm goto("1: nop\n\t"
>> ^
>> make[1]: *** [arch/arm64/kvm/hyp/switch.o] Error 1
>> make: *** [arch/arm64/kvm/hyp/switch.o] Error 2
>
> it looks like we cannot avoid hyp_data thing... if you don't mind I can
> do hyp_data clean-up in separate patch. Alternatively, we can do
> conversion to static keys for both architectures later as an
> optimisation step.
Can you try the above first? I've just tried the same approach with my
vgic-trap series, and it compiles fine (untested though):
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index 194184c..397e240 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -301,7 +301,7 @@ again:
if (exit_code == ARM_EXCEPTION_TRAP && !__populate_fault_info(vcpu))
goto again;
- if (static_branch_unlikely(&vgic_v2_cpuif_trap) &&
+ if (static_branch_unlikely(&kvm_vgic_global_state.vgic_v2_cpuif_trap) &&
exit_code == ARM_EXCEPTION_TRAP) {
bool valid;
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index bb46c03..cc59a28 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -67,6 +67,9 @@ struct vgic_global {
/* Only needed for the legacy KVM_CREATE_IRQCHIP */
bool can_emulate_gicv2;
+
+ /* Trapping GICv2 GICV region? */
+ struct static_key_false vgic_v2_cpuif_trap;
};
extern struct vgic_global kvm_vgic_global_state;
@@ -269,8 +272,6 @@ struct vgic_cpu {
bool lpis_enabled;
};
-extern struct static_key_false vgic_v2_cpuif_trap;
-
int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
void kvm_vgic_early_init(struct kvm *kvm);
int kvm_vgic_create(struct kvm *kvm, u32 type);
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 0a063af..4af600e 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -278,7 +278,7 @@ int vgic_v2_map_resources(struct kvm *kvm)
goto out;
}
- if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
+ if (!static_branch_unlikely(&kvm_vgic_global_state.vgic_v2_cpuif_trap)) {
ret = kvm_phys_addr_ioremap(kvm, dist->vgic_cpu_base,
kvm_vgic_global_state.vcpu_base,
KVM_VGIC_V2_CPU_SIZE, true);
@@ -296,8 +296,6 @@ out:
return ret;
}
-DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
-
/**
* vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
* @node: pointer to the DT node
@@ -314,6 +312,8 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
return -ENXIO;
}
+ kvm_vgic_global_state.vgic_v2_cpuif_trap = STATIC_KEY_FALSE_INIT;
+
if (!PAGE_ALIGNED(info->vcpu.start) ||
!PAGE_ALIGNED(resource_size(&info->vcpu))) {
kvm_info("GICV region size/alignment is unsafe, using trapping (reduced performance)\n");
@@ -332,7 +332,7 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
goto out;
}
- static_branch_enable(&vgic_v2_cpuif_trap);
+ static_branch_enable(&kvm_vgic_global_state.vgic_v2_cpuif_trap);
}
kvm_vgic_global_state.vctrl_base = ioremap(info->vctrl.start,
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply related
* [PATCH v3 3/9] ARM: sun8i: dt: Add DT bindings documentation for Allwinner sun8i-emac
From: Andrew Lunn @ 2016-09-09 14:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473425117-18645-4-git-send-email-clabbe.montjoie@gmail.com>
> +Optional properties:
> +- allwinner,tx-delay: TX clock delay chain value. Range value is 0-0x07. Default is 0)
> +- allwinner,rx-delay: RX clock delay chain value. Range value is 0-0x1F. Default is 0)
What are the units? pS? nS?
Andrew
^ permalink raw reply
* [RFC PATCH 5/5] arm64: signal: Parse extra_context during sigreturn
From: Dave Martin @ 2016-09-09 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473430576-20792-1-git-send-email-Dave.Martin@arm.com>
If extra_context is present, parse it.
To avoid abuse by userspace, this patch attempts to ensure that:
* that no more than one extra_context is accepted;
* that the extra_context is a sensible size;
* that the extra context data is properly aligned.
This patch relies on the user accessors in order to ensure that the
user-supplied extra context data pointer is an honest userspace
address.
Other than that, the kernel doesn't care specially whether the
pointer supplied is sensible (e.g., not garbage, doesn't overlap
sigcontext.__reserved[], etc.) since this cannot harm the kernel.
More checks may be added later in order to aid debugging of
botched sigreturns from userspace.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kernel/signal.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index d7cdb51..c5f18ef 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -221,6 +221,7 @@ static int parse_user_sigframe(struct user_ctxs *user,
char __user *base = (char __user *)&sc->__reserved;
size_t offset = 0;
size_t limit = sizeof(sc->__reserved);
+ bool have_extra_context = false;
user->fpsimd = NULL;
@@ -230,6 +231,9 @@ static int parse_user_sigframe(struct user_ctxs *user,
while (1) {
int err = 0;
u32 magic, size;
+ struct extra_context const __user *extra;
+ void __user *extra_data;
+ u32 extra_size;
if (limit - offset < sizeof(*head))
goto invalid;
@@ -267,6 +271,42 @@ static int parse_user_sigframe(struct user_ctxs *user,
/* ignore */
break;
+ case EXTRA_MAGIC:
+ if (have_extra_context)
+ goto invalid;
+
+ if (size < sizeof(*extra))
+ goto invalid;
+
+ extra = (struct extra_context const __user *)head;
+ __get_user_error(extra_data, &extra->data, err);
+ __get_user_error(extra_size, &extra->size, err);
+ if (err)
+ return err;
+
+ /* Prevent looping/repeated parsing of extra_conext */
+ have_extra_context = true;
+
+ /*
+ * Rely on the __user accessors to reject bogus
+ * pointers.
+ */
+ base = extra_data;
+ if (!IS_ALIGNED((unsigned long)base, 16))
+ goto invalid;
+
+ /* Reject "unreasonably large" frames: */
+ limit = extra_size;
+ if (limit > SIGFRAME_MAXSZ - sizeof(sc->__reserved))
+ goto invalid;
+
+ /*
+ * Ignore trailing terminator in __reserved[]
+ * and start parsing extra_data:
+ */
+ offset = 0;
+ continue;
+
default:
goto invalid;
}
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 4/5] arm64: signal: Allocate extra sigcontext space as needed
From: Dave Martin @ 2016-09-09 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473430576-20792-1-git-send-email-Dave.Martin@arm.com>
This patch modifies the context block allocator to create an
extra_context expansion block as necessary, and adds the necessary
code to populate, parse and decode this block.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/uapi/asm/sigcontext.h | 27 ++++++++
arch/arm64/kernel/signal.c | 112 +++++++++++++++++++++++++------
2 files changed, 120 insertions(+), 19 deletions(-)
diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h
index ee469be..1af8437 100644
--- a/arch/arm64/include/uapi/asm/sigcontext.h
+++ b/arch/arm64/include/uapi/asm/sigcontext.h
@@ -61,4 +61,31 @@ struct esr_context {
__u64 esr;
};
+/*
+ * Pointer to extra space for additional structures that don't fit in
+ * sigcontext.__reserved[]. Note:
+ *
+ * 1) fpsimd_context, esr_context and extra_context must be placed in
+ * sigcontext.__reserved[] if present. They cannot be placed in the
+ * extra space. Any other record can be placed either in the extra
+ * space or in sigcontext.__reserved[].
+ *
+ * 2) There must not be more than one extra_context.
+ *
+ * 3) If extra_context is present, it must be followed immediately in
+ * sigcontext.__reserved[] by the terminating null _aarch64_ctx (i.e.,
+ * extra_context must be the last record in sigcontext.__reserved[]
+ * except for the terminator).
+ *
+ * 4) The extra space must itself be terminated with a null
+ * _aarch64_ctx.
+ */
+#define EXTRA_MAGIC 0x45585401
+
+struct extra_context {
+ struct _aarch64_ctx head;
+ void *data; /* 16-byte aligned pointer to the extra space */
+ __u32 size; /* size in bytes of the extra space */
+};
+
#endif /* _UAPI__ASM_SIGCONTEXT_H */
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 963d2ba..d7cdb51 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -25,6 +25,7 @@
#include <linux/freezer.h>
#include <linux/stddef.h>
#include <linux/uaccess.h>
+#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/tracehook.h>
#include <linux/ratelimit.h>
@@ -56,18 +57,22 @@ struct rt_sigframe_user_layout {
unsigned long fpsimd_offset;
unsigned long esr_offset;
+ unsigned long extra_offset;
unsigned long end_offset;
};
static void init_user_layout(struct rt_sigframe_user_layout *user)
{
+ const size_t __reserved_size =
+ sizeof(user->sigframe->uc.uc_mcontext.__reserved);
+ const size_t terminator_size =
+ round_up(sizeof(struct _aarch64_ctx), 16);
+
memset(user, 0, sizeof *user);
user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);
-
- user->limit = user->size +
- sizeof(user->sigframe->uc.uc_mcontext.__reserved) -
- round_up(sizeof(struct _aarch64_ctx), 16);
- /* ^ reserve space for terminator */
+ user->limit = user->size + (__reserved_size - terminator_size -
+ sizeof(struct extra_context));
+ /* Reserve space for extension and terminator ^ */
}
static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
@@ -75,6 +80,49 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
return round_up(max(user->size, sizeof(struct rt_sigframe)), 16);
}
+/* Sanity limit on the maximum size of signal frame we'll try to generate. */
+/* This is NOT ABI. */
+#define SIGFRAME_MAXSZ SZ_64K
+
+static int __sigframe_alloc(struct rt_sigframe_user_layout *user,
+ unsigned long *offset, size_t size, bool extend)
+{
+ size_t padded_size = round_up(size, 16);
+
+ if (padded_size > user->limit - user->size &&
+ !user->extra_offset &&
+ extend) {
+ int ret;
+
+ ret = __sigframe_alloc(user, &user->extra_offset,
+ sizeof(struct extra_context), false);
+ if (ret)
+ return ret;
+
+ /*
+ * Further allocations must go after the fixed-size
+ * part of the signal frame:
+ */
+ user->size = round_up(sizeof(struct rt_sigframe), 16);
+
+ /*
+ * Allow expansion up to SIGFRAME_MAXSZ, ensuring space for
+ * the terminator:
+ */
+ user->limit = SIGFRAME_MAXSZ -
+ round_up(sizeof(struct _aarch64_ctx), 16);
+ }
+
+ /* Still not enough space? Bad luck! */
+ if (padded_size > user->limit - user->size)
+ return -ENOMEM;
+
+ *offset = user->size;
+ user->size += padded_size;
+
+ return 0;
+}
+
/*
* Allocate space for an optional record of <size> bytes in the user
* signal frame. The offset from the signal frame base address to the
@@ -83,11 +131,26 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
static int sigframe_alloc(struct rt_sigframe_user_layout *user,
unsigned long *offset, size_t size)
{
- size_t padded_size = round_up(size, 16);
+ return __sigframe_alloc(user, offset, size, true);
+}
- *offset = user->size;
- user->size += padded_size;
+/* Allocate the null terminator record and prevent further allocations */
+static int sigframe_alloc_end(struct rt_sigframe_user_layout *user)
+{
+ int ret;
+ const size_t terminator_size =
+ round_up(sizeof(struct _aarch64_ctx), 16);
+
+ /* Un-reserve the space reserved for the terminator: */
+ user->limit += terminator_size;
+
+ ret = sigframe_alloc(user, &user->end_offset,
+ sizeof(struct _aarch64_ctx));
+ if (ret)
+ return ret;
+ /* Prevent further allocation: */
+ user->limit = user->size;
return 0;
}
@@ -314,17 +377,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
return err;
}
- /*
- * Allocate space for the terminator record.
- * HACK: here we undo the reservation of space for the end record.
- * This bodge should be replaced with a cleaner approach later on.
- */
- user->limit = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved) +
- sizeof(user->sigframe->uc.uc_mcontext.__reserved);
-
- err = sigframe_alloc(user, &user->end_offset,
- sizeof(struct _aarch64_ctx));
- return err;
+ return sigframe_alloc_end(user);
}
@@ -365,6 +418,27 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
}
+ if (err == 0 && user->extra_offset) {
+ struct extra_context __user *extra =
+ apply_user_offset(user, user->extra_offset);
+ struct _aarch64_ctx __user *end =
+ (struct _aarch64_ctx __user *)((char __user *)extra +
+ round_up(sizeof(*extra), 16));
+ void __user *extra_data = apply_user_offset(user,
+ round_up(sizeof(struct rt_sigframe), 16));
+ u32 extra_size = round_up(user->size, 16) -
+ round_up(sizeof(struct rt_sigframe), 16);
+
+ __put_user_error(EXTRA_MAGIC, &extra->head.magic, err);
+ __put_user_error(sizeof(*extra), &extra->head.size, err);
+ __put_user_error(extra_data, &extra->data, err);
+ __put_user_error(extra_size, &extra->size, err);
+
+ /* Add the terminator */
+ __put_user_error(0, &end->magic, err);
+ __put_user_error(0, &end->size, err);
+ }
+
/* set the "end" magic */
if (err == 0) {
struct _aarch64_ctx __user *end =
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 3/5] arm64: signal: factor out signal frame record allocation
From: Dave Martin @ 2016-09-09 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473430576-20792-1-git-send-email-Dave.Martin@arm.com>
Factor out the allocator for signal frame optional records into a
separate function, to ensure consistency and facilitate later
expansion of the signal frame.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kernel/signal.c | 43 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 3bce940..963d2ba 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -75,6 +75,22 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
return round_up(max(user->size, sizeof(struct rt_sigframe)), 16);
}
+/*
+ * Allocate space for an optional record of <size> bytes in the user
+ * signal frame. The offset from the signal frame base address to the
+ * allocated block is assigned to *offset.
+ */
+static int sigframe_alloc(struct rt_sigframe_user_layout *user,
+ unsigned long *offset, size_t size)
+{
+ size_t padded_size = round_up(size, 16);
+
+ *offset = user->size;
+ user->size += padded_size;
+
+ return 0;
+}
+
static void __user *apply_user_offset(
struct rt_sigframe_user_layout const *user, unsigned long offset)
{
@@ -283,19 +299,32 @@ badframe:
/* Determine the layout of optional records in the signal frame */
static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
{
- user->fpsimd_offset = user->size;
- user->size += round_up(sizeof(struct fpsimd_context), 16);
+ int err;
+
+ err = sigframe_alloc(user, &user->fpsimd_offset,
+ sizeof(struct fpsimd_context));
+ if (err)
+ return err;
/* fault information, if valid */
if (current->thread.fault_code) {
- user->esr_offset = user->size;
- user->size += round_up(sizeof(struct esr_context), 16);
+ err = sigframe_alloc(user, &user->esr_offset,
+ sizeof(struct esr_context));
+ if (err)
+ return err;
}
- /* set the "end" magic */
- user->end_offset = user->size;
+ /*
+ * Allocate space for the terminator record.
+ * HACK: here we undo the reservation of space for the end record.
+ * This bodge should be replaced with a cleaner approach later on.
+ */
+ user->limit = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved) +
+ sizeof(user->sigframe->uc.uc_mcontext.__reserved);
- return 0;
+ err = sigframe_alloc(user, &user->end_offset,
+ sizeof(struct _aarch64_ctx));
+ return err;
}
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 2/5] arm64: signal: factor frame layout and population into separate passes
From: Dave Martin @ 2016-09-09 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473430576-20792-1-git-send-email-Dave.Martin@arm.com>
In preparation for expanding the signal frame, this patch refactors
the signal frame setup code in setup_sigframe() into two separate
passes.
The first pass, setup_sigframe_layout(), determines the size of the
signal frame and its internal layout, including the presence and
location of optional records. The resulting knowledge is used to
allocate and locate the user stack space required for the signal
frame and to determine which optional records to include.
The second pass, setup_sigframe(), is called once the stack frame
is allocated in order to populate it with the necessary context
information.
This change has no effect on the signal ABI, but will make it
easier to expand the signal frame in future patches.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kernel/signal.c | 111 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 89 insertions(+), 22 deletions(-)
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 8e0e0a3..3bce940 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -25,6 +25,7 @@
#include <linux/freezer.h>
#include <linux/stddef.h>
#include <linux/uaccess.h>
+#include <linux/string.h>
#include <linux/tracehook.h>
#include <linux/ratelimit.h>
@@ -49,8 +50,39 @@ struct rt_sigframe {
struct rt_sigframe_user_layout {
struct rt_sigframe __user *sigframe;
+
+ unsigned long size; /* size of allocated sigframe data */
+ unsigned long limit; /* largest allowed size */
+
+ unsigned long fpsimd_offset;
+ unsigned long esr_offset;
+ unsigned long end_offset;
};
+static void init_user_layout(struct rt_sigframe_user_layout *user)
+{
+ memset(user, 0, sizeof *user);
+ user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);
+
+ user->limit = user->size +
+ sizeof(user->sigframe->uc.uc_mcontext.__reserved) -
+ round_up(sizeof(struct _aarch64_ctx), 16);
+ /* ^ reserve space for terminator */
+}
+
+static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
+{
+ return round_up(max(user->size, sizeof(struct rt_sigframe)), 16);
+}
+
+static void __user *apply_user_offset(
+ struct rt_sigframe_user_layout const *user, unsigned long offset)
+{
+ char __user *base = (char __user *)user->sigframe;
+
+ return base + offset;
+}
+
static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
{
struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state;
@@ -106,26 +138,35 @@ static int parse_user_sigframe(struct user_ctxs *user,
struct rt_sigframe __user *sf)
{
struct sigcontext __user *sc = &sf->uc.uc_mcontext;
- struct _aarch64_ctx __user *head =
- (struct _aarch64_ctx __user *)&sc->__reserved;
+ struct _aarch64_ctx __user *head;
+ char __user *base = (char __user *)&sc->__reserved;
size_t offset = 0;
+ size_t limit = sizeof(sc->__reserved);
user->fpsimd = NULL;
+ if (!IS_ALIGNED((unsigned long)base, 16))
+ goto invalid;
+
while (1) {
- int err;
+ int err = 0;
u32 magic, size;
- head = (struct _aarch64_ctx __user *)&sc->__reserved[offset];
- if (!IS_ALIGNED((unsigned long)head, 16))
+ if (limit - offset < sizeof(*head))
goto invalid;
- err = 0;
+ if (!IS_ALIGNED(offset, 16))
+ goto invalid;
+
+ head = (struct _aarch64_ctx __user *)(base + offset);
__get_user_error(magic, &head->magic, err);
__get_user_error(size, &head->size, err);
if (err)
return err;
+ if (limit - offset < size)
+ goto invalid;
+
switch (magic) {
case 0:
if (size)
@@ -137,9 +178,7 @@ static int parse_user_sigframe(struct user_ctxs *user,
if (user->fpsimd)
goto invalid;
- if (offset > sizeof(sc->__reserved) -
- sizeof(*user->fpsimd) ||
- size < sizeof(*user->fpsimd))
+ if (size < sizeof(*user->fpsimd))
goto invalid;
user->fpsimd = (struct fpsimd_context __user *)head;
@@ -156,7 +195,7 @@ static int parse_user_sigframe(struct user_ctxs *user,
if (size < sizeof(*head))
goto invalid;
- if (size > sizeof(sc->__reserved) - (sizeof(*head) + offset))
+ if (limit - offset < size)
goto invalid;
offset += size;
@@ -241,13 +280,30 @@ badframe:
return 0;
}
+/* Determine the layout of optional records in the signal frame */
+static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
+{
+ user->fpsimd_offset = user->size;
+ user->size += round_up(sizeof(struct fpsimd_context), 16);
+
+ /* fault information, if valid */
+ if (current->thread.fault_code) {
+ user->esr_offset = user->size;
+ user->size += round_up(sizeof(struct esr_context), 16);
+ }
+
+ /* set the "end" magic */
+ user->end_offset = user->size;
+
+ return 0;
+}
+
+
static int setup_sigframe(struct rt_sigframe_user_layout *user,
struct pt_regs *regs, sigset_t *set)
{
int i, err = 0;
struct rt_sigframe __user *sf = user->sigframe;
- void *aux = sf->uc.uc_mcontext.__reserved;
- struct _aarch64_ctx *end;
/* set up the stack frame for unwinding */
__put_user_error(regs->regs[29], &sf->fp, err);
@@ -265,26 +321,29 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
if (err == 0) {
- struct fpsimd_context *fpsimd_ctx =
- container_of(aux, struct fpsimd_context, head);
+ struct fpsimd_context __user *fpsimd_ctx =
+ apply_user_offset(user, user->fpsimd_offset);
err |= preserve_fpsimd_context(fpsimd_ctx);
- aux += sizeof(*fpsimd_ctx);
}
/* fault information, if valid */
- if (current->thread.fault_code) {
- struct esr_context *esr_ctx =
- container_of(aux, struct esr_context, head);
+ if (err == 0 && user->esr_offset) {
+ struct esr_context __user *esr_ctx =
+ apply_user_offset(user, user->esr_offset);
+
__put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err);
__put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err);
__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
- aux += sizeof(*esr_ctx);
}
/* set the "end" magic */
- end = aux;
- __put_user_error(0, &end->magic, err);
- __put_user_error(0, &end->size, err);
+ if (err == 0) {
+ struct _aarch64_ctx __user *end =
+ apply_user_offset(user, user->end_offset);
+
+ __put_user_error(0, &end->magic, err);
+ __put_user_error(0, &end->size, err);
+ }
return err;
}
@@ -293,10 +352,18 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,
struct ksignal *ksig, struct pt_regs *regs)
{
unsigned long sp, sp_top;
+ int err;
+
+ init_user_layout(user);
sp = sp_top = sigsp(regs->sp, ksig);
sp = (sp - sizeof(struct rt_sigframe)) & ~15;
+ err = setup_sigframe_layout(user);
+ if (err)
+ return err;
+
+ sp -= sigframe_size(user);
user->sigframe = (struct rt_sigframe __user *)sp;
/*
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 1/5] arm64: signal: Refactor sigcontext parsing in rt_sigreturn
From: Dave Martin @ 2016-09-09 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473430576-20792-1-git-send-email-Dave.Martin@arm.com>
Currently, rt_sigreturn does very limited checking on the
sigcontext coming from userspace.
Future additions of extra dynamic sigcontext data will increase the
potential for surprises. Also, it is not clear whether the
sigcontext extension records are supposed to occur in a particular
order.
This patch factors out the sigcontext parsing into a separate
function, and adds extra checks to validate the well-formedness of
the sigcontext structure.
To help with this, an abstraction for the signal frame layout is
also added, using offsets to track the location of different
records in the frame. Although trivial, this provides a base to
extend upon in order to track more complex layouts.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kernel/signal.c | 121 +++++++++++++++++++++++++++++++++++++--------
1 file changed, 101 insertions(+), 20 deletions(-)
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index a8eafdb..8e0e0a3 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -19,9 +19,11 @@
#include <linux/compat.h>
#include <linux/errno.h>
+#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/personality.h>
#include <linux/freezer.h>
+#include <linux/stddef.h>
#include <linux/uaccess.h>
#include <linux/tracehook.h>
#include <linux/ratelimit.h>
@@ -45,6 +47,10 @@ struct rt_sigframe {
u64 lr;
};
+struct rt_sigframe_user_layout {
+ struct rt_sigframe __user *sigframe;
+};
+
static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
{
struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state;
@@ -92,12 +98,86 @@ static int restore_fpsimd_context(struct fpsimd_context __user *ctx)
return err ? -EFAULT : 0;
}
+struct user_ctxs {
+ struct fpsimd_context __user *fpsimd;
+};
+
+static int parse_user_sigframe(struct user_ctxs *user,
+ struct rt_sigframe __user *sf)
+{
+ struct sigcontext __user *sc = &sf->uc.uc_mcontext;
+ struct _aarch64_ctx __user *head =
+ (struct _aarch64_ctx __user *)&sc->__reserved;
+ size_t offset = 0;
+
+ user->fpsimd = NULL;
+
+ while (1) {
+ int err;
+ u32 magic, size;
+
+ head = (struct _aarch64_ctx __user *)&sc->__reserved[offset];
+ if (!IS_ALIGNED((unsigned long)head, 16))
+ goto invalid;
+
+ err = 0;
+ __get_user_error(magic, &head->magic, err);
+ __get_user_error(size, &head->size, err);
+ if (err)
+ return err;
+
+ switch (magic) {
+ case 0:
+ if (size)
+ goto invalid;
+
+ goto done;
+
+ case FPSIMD_MAGIC:
+ if (user->fpsimd)
+ goto invalid;
+
+ if (offset > sizeof(sc->__reserved) -
+ sizeof(*user->fpsimd) ||
+ size < sizeof(*user->fpsimd))
+ goto invalid;
+
+ user->fpsimd = (struct fpsimd_context __user *)head;
+ break;
+
+ case ESR_MAGIC:
+ /* ignore */
+ break;
+
+ default:
+ goto invalid;
+ }
+
+ if (size < sizeof(*head))
+ goto invalid;
+
+ if (size > sizeof(sc->__reserved) - (sizeof(*head) + offset))
+ goto invalid;
+
+ offset += size;
+ }
+
+done:
+ if (!user->fpsimd)
+ goto invalid;
+
+ return 0;
+
+invalid:
+ return -EINVAL;
+}
+
static int restore_sigframe(struct pt_regs *regs,
struct rt_sigframe __user *sf)
{
sigset_t set;
int i, err;
- void *aux = sf->uc.uc_mcontext.__reserved;
+ struct user_ctxs user;
err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
if (err == 0)
@@ -116,12 +196,11 @@ static int restore_sigframe(struct pt_regs *regs,
regs->syscallno = ~0UL;
err |= !valid_user_regs(®s->user_regs, current);
+ if (err == 0)
+ err = parse_user_sigframe(&user, sf);
- if (err == 0) {
- struct fpsimd_context *fpsimd_ctx =
- container_of(aux, struct fpsimd_context, head);
- err |= restore_fpsimd_context(fpsimd_ctx);
- }
+ if (err == 0)
+ err = restore_fpsimd_context(user.fpsimd);
return err;
}
@@ -162,10 +241,11 @@ badframe:
return 0;
}
-static int setup_sigframe(struct rt_sigframe __user *sf,
+static int setup_sigframe(struct rt_sigframe_user_layout *user,
struct pt_regs *regs, sigset_t *set)
{
int i, err = 0;
+ struct rt_sigframe __user *sf = user->sigframe;
void *aux = sf->uc.uc_mcontext.__reserved;
struct _aarch64_ctx *end;
@@ -209,33 +289,32 @@ static int setup_sigframe(struct rt_sigframe __user *sf,
return err;
}
-static struct rt_sigframe __user *get_sigframe(struct ksignal *ksig,
- struct pt_regs *regs)
+static int get_sigframe(struct rt_sigframe_user_layout *user,
+ struct ksignal *ksig, struct pt_regs *regs)
{
unsigned long sp, sp_top;
- struct rt_sigframe __user *frame;
sp = sp_top = sigsp(regs->sp, ksig);
sp = (sp - sizeof(struct rt_sigframe)) & ~15;
- frame = (struct rt_sigframe __user *)sp;
+ user->sigframe = (struct rt_sigframe __user *)sp;
/*
* Check that we can actually write to the signal frame.
*/
- if (!access_ok(VERIFY_WRITE, frame, sp_top - sp))
- frame = NULL;
+ if (!access_ok(VERIFY_WRITE, user->sigframe, sp_top - sp))
+ return -EFAULT;
- return frame;
+ return 0;
}
static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
- void __user *frame, int usig)
+ struct rt_sigframe_user_layout *user, int usig)
{
__sigrestore_t sigtramp;
regs->regs[0] = usig;
- regs->sp = (unsigned long)frame;
+ regs->sp = (unsigned long)user->sigframe;
regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp);
regs->pc = (unsigned long)ka->sa.sa_handler;
@@ -250,20 +329,22 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs)
{
+ struct rt_sigframe_user_layout user;
struct rt_sigframe __user *frame;
int err = 0;
- frame = get_sigframe(ksig, regs);
- if (!frame)
+ if (get_sigframe(&user, ksig, regs))
return 1;
+ frame = user.sigframe;
+
__put_user_error(0, &frame->uc.uc_flags, err);
__put_user_error(NULL, &frame->uc.uc_link, err);
err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
- err |= setup_sigframe(frame, regs, set);
+ err |= setup_sigframe(&user, regs, set);
if (err == 0) {
- setup_return(regs, &ksig->ka, frame, usig);
+ setup_return(regs, &ksig->ka, &user, usig);
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
regs->regs[1] = (unsigned long)&frame->info;
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 0/5] arm64: Signal context expansion
From: Dave Martin @ 2016-09-09 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In preparation for supporting the ARM Scalable Vector Extension in the
Linux kernel, it is necessary to be able to save more data in the signal
frame than can be accommodated in the existing arm64 signal frame
structure.
To minimise ABI impact the current structures are retained as-is, and a
new extra_context record is defined, which (if present) declares some
additional space beyond the standard arm64 signal frame.
This new record can be added in sigframe.__reserved[] if there is a
need to allocate extra space beyond the standard signal frame. The
extra block of memory referenced by extra_context can then be parsed in
the same way as sigcontext.__reserved[]. Old code should just ignore
the whole thing as an unrecognised record. To maintain backward
compatibility, signal context records defined today are always placed
directly in __reserved[], never in the block referenced by
extra_context.
This series also does some refactoring in order to make it easier to add
records to the signal frame in the future, using the new expansion
mechanism. The extra_context mechanism is hidden behind a simple
allocator that grows and signal frame only as needed.
Note: this series makes rt_sigreturn somewhat stricter about parsing the
signal frame than previously: violators will be SEGV'd. This should
only be an ABI break for software that was doing something wrong in the
first place, but I'm open to suggestions if people have concerns about
it.
Dave Martin (5):
arm64: signal: Refactor sigcontext parsing in rt_sigreturn
arm64: signal: factor frame layout and population into separate passes
arm64: signal: factor out signal frame record allocation
arm64: signal: Allocate extra sigcontext space as needed
arm64: signal: Parse extra_context during sigreturn
arch/arm64/include/uapi/asm/sigcontext.h | 27 +++
arch/arm64/kernel/signal.c | 355 ++++++++++++++++++++++++++++---
2 files changed, 350 insertions(+), 32 deletions(-)
--
2.1.4
^ permalink raw reply
* [PATCH v3 1/9] ethernet: add sun8i-emac driver
From: Andrew Lunn @ 2016-09-09 14:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473425117-18645-2-git-send-email-clabbe.montjoie@gmail.com>
Hi Corentin
> +static int sun8i_emac_mdio_register(struct net_device *ndev)
> +{
> + struct sun8i_emac_priv *priv = netdev_priv(ndev);
> + struct mii_bus *bus;
> + int ret;
> +
> + bus = mdiobus_alloc();
You can use devm_mdiobus_alloc() which will simplify your error
handling and unregister code.
Andrew
^ permalink raw reply
* [PATCH] arm64: Expose TASK_SIZE to userspace via auxv
From: Christopher Covington @ 2016-09-09 14:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAFLxGvx2YB9D1rECjDrALYWoEtGk=4WL9SKXEGgpxH1c_4Sfxg@mail.gmail.com>
Hi Richard,
On 08/18/2016 08:17 AM, Richard Weinberger wrote:
> On Wed, Aug 17, 2016 at 1:12 PM, Christopher Covington
> <cov@codeaurora.org> wrote:
>>
>>
>> On August 17, 2016 6:30:06 AM EDT, Catalin Marinas <catalin.marinas@arm.com> wrote:
>>> On Tue, Aug 16, 2016 at 02:32:29PM -0400, Christopher Covington wrote:
>>>> Some userspace applications need to know the maximum virtual address
>>> they can
>>>> use (TASK_SIZE).
>>>
>>> Just curious, what are the cases needing TASK_SIZE in user space?
>>
>> Checkpoint/Restore In Userspace and the Mozilla Javascript Engine https://bugzilla.mozilla.org/show_bug.cgi?id=1143022 are the specific cases I've run into. I've heard LuaJIT might have a similar situation. In general I think making allocations from the top down is a shortcut for finding a large unused region of memory.
>
> I think this makes sense for all archs.
> At lest UserModeLinux on x86 also needs to know bottom and top
> addresses of the usable
> address space.
> Currently it figures by scanning and catching SIGSEGV.
For the bottom, can you use /proc/sys/vm/mmap_min_addr?
Cov
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code
Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH 1/8] ARM: dts: bcm283x: Define standard pinctrl groups in the gpio node.
From: Stefan Wahren @ 2016-09-09 14:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1473411924.6698.85.camel@redhat.com>
Am 09.09.2016 um 11:05 schrieb Gerd Hoffmann:
> Hi,
>
>> According to this page [1] the pinctrl group for parallel display interface is
>> missing. Is it intended?
>>
>> [1] - http://elinux.org/RPi_BCM2835_GPIOs
> Just an oversight I guess. Eric?
>
> Does this look correct?
>
> + dpi_gpio4: dpi_gpio4 {
> + brcm,pins = <4 5 6 7 8 9 10 11 12 13
> + 14 15 16 17 18 19 20 21
> + 22 23 24 25 26 27>;
> + brcm,function = <BCM2835_FSEL_ALT2>;
> + };
According to the wiki and the website [1] it starts at gpio 0
[1] -
https://www.raspberrypi.org/documentation/hardware/raspberrypi/dpi/README.md
>
> thanks,
> Gerd
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [RFC PATCH 2/2] macb: Enable 1588 support in SAMA5D2 platform.
From: Andrei Pistirica @ 2016-09-09 14:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160906163718.GB7012@localhost.localdomain>
Hi Richard,
I will take your indications into account in next version of the patch.
Regards,
Andrei
On 06.09.2016 18:37, Richard Cochran wrote:
> On Fri, Sep 02, 2016 at 02:53:37PM +0200, Andrei Pistirica wrote:
>> Hardware time stamp on the PTP Ethernet packets are received using the
>> SO_TIMESTAMPING API. Timers are obtained from the PTP event/peer
>> gem registers.
>>
>> Signed-off-by: Andrei Pistirica <andrei.pistirica@microchip.com>
>> ---
>> Integration with SAMA5D2 only. This feature wasn't tested on any
>> other platform that might use cadence/gem.
>
> What does that mean? I didn't see any references to SAMA5D2 anywhere
> in your patch.
>
> The driver needs to positively identify the HW that supports this
> feature.
>
>> diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
>> index 8d54e7b..18f0715 100644
>> --- a/drivers/net/ethernet/cadence/macb.c
>> +++ b/drivers/net/ethernet/cadence/macb.c
>> @@ -697,6 +697,11 @@ static void macb_tx_interrupt(struct macb_queue *queue)
>>
>> /* First, update TX stats if needed */
>> if (skb) {
>> +/* guard the hot-path */
>> +#ifdef CONFIG_MACB_USE_HWSTAMP
>> + if (bp->hwts_tx_en)
>> + macb_ptp_do_txstamp(bp, skb);
>> +#endif
>
> Pull the test into the helper function, and then you can drop the
> ifdef and the funny comment.
>
>> netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
>> macb_tx_ring_wrap(tail), skb->data);
>> bp->stats.tx_packets++;
>> @@ -853,6 +858,11 @@ static int gem_rx(struct macb *bp, int budget)
>> GEM_BFEXT(RX_CSUM, ctrl) & GEM_RX_CSUM_CHECKED_MASK)
>> skb->ip_summed = CHECKSUM_UNNECESSARY;
>>
>> +/* guard the hot-path */
>> +#ifdef CONFIG_MACB_USE_HWSTAMP
>> + if (bp->hwts_rx_en)
>> + macb_ptp_do_rxstamp(bp, skb);
>> +#endif
>
> Same here.
>
>> bp->stats.rx_packets++;
>> bp->stats.rx_bytes += skb->len;
>>
>> @@ -1723,6 +1733,11 @@ static void macb_init_hw(struct macb *bp)
>>
>> /* Enable TX and RX */
>> macb_writel(bp, NCR, MACB_BIT(RE) | MACB_BIT(TE) | MACB_BIT(MPE));
>> +
>> +#ifdef CONFIG_MACB_USE_HWSTAMP
>> + bp->hwts_tx_en = 0;
>> + bp->hwts_rx_en = 0;
>> +#endif
>
> We don't initialize to zero unless we have to.
>
>> }
>>
>> /*
>> @@ -1885,6 +1900,8 @@ static int macb_open(struct net_device *dev)
>>
>> netif_tx_start_all_queues(dev);
>>
>> + macb_ptp_init(dev);
>> +
>> return 0;
>> }
>>
>> @@ -2143,7 +2160,7 @@ static const struct ethtool_ops gem_ethtool_ops = {
>> .get_regs_len = macb_get_regs_len,
>> .get_regs = macb_get_regs,
>> .get_link = ethtool_op_get_link,
>> - .get_ts_info = ethtool_op_get_ts_info,
>> + .get_ts_info = macb_get_ts_info,
>> .get_ethtool_stats = gem_get_ethtool_stats,
>> .get_strings = gem_get_ethtool_strings,
>> .get_sset_count = gem_get_sset_count,
>> @@ -2157,6 +2174,12 @@ static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
>> if (!netif_running(dev))
>> return -EINVAL;
>>
>> + if (cmd == SIOCSHWTSTAMP)
>> + return macb_hwtst_set(dev, rq, cmd);
>> +
>> + if (cmd == SIOCGHWTSTAMP)
>> + return macb_hwtst_get(dev, rq);
>
> switch/case?
>
>> +
>> if (!phydev)
>> return -ENODEV;
>>
>> diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
>> index 8c3779d..555316a 100644
>> --- a/drivers/net/ethernet/cadence/macb.h
>> +++ b/drivers/net/ethernet/cadence/macb.h
>> @@ -920,8 +920,21 @@ struct macb {
>>
>> #ifdef CONFIG_MACB_USE_HWSTAMP
>> void macb_ptp_init(struct net_device *ndev);
>> +void macb_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb);
>> +void macb_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb);
>> +int macb_ptp_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
>> +#define macb_get_ts_info macb_ptp_get_ts_info
>> +int macb_hwtst_set(struct net_device *netdev, struct ifreq *ifr, int cmd);
>> +int macb_hwtst_get(struct net_device *netdev, struct ifreq *ifr);
>> #else
>> void macb_ptp_init(struct net_device *ndev) { }
>> +void macb_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb) { }
>> +void macb_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb) { }
>> +#define macb_get_ts_info ethtool_op_get_ts_info
>> +int macb_hwtst_set(struct net_device *netdev, struct ifreq *ifr, int cmd)
>> + { return -1; }
>> +int macb_hwtst_get(struct net_device *netdev, struct ifreq *ifr)
>> + { return -1; }
>
> Use a proper return code please.
>
>> #endif
>>
>> static inline bool macb_is_gem(struct macb *bp)
>> diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c
>> index 6d6a6ec..e3f784a 100644
>> --- a/drivers/net/ethernet/cadence/macb_ptp.c
>> +++ b/drivers/net/ethernet/cadence/macb_ptp.c
>> @@ -5,6 +5,7 @@
>> * Copyright (C) 2016 Microchip Technology
>> *
>> * Authors: Harini Katakam <harinik@xilinx.com>
>> + * Andrei Pistirica <andrei.pistirica@microchip.com>
>
> We don't add additional authors any more, because git tells us that info.
>
>> *
>> * This file is licensed under the terms of the GNU General Public
>> * License version 2. This program is licensed "as is" without any
>> @@ -222,3 +223,219 @@ void macb_ptp_init(struct net_device *ndev)
>>
>> dev_info(&bp->pdev->dev, "%s ptp clock registered.\n", GMAC_TIMER_NAME);
>> }
>> +
>> +/* While the GEM can timestamp PTP packets, it does not mark the RX descriptor
>> + * to identify them. This is entirely the wrong place to be parsing UDP
>> + * headers, but some minimal effort must be made.
>
> If you must parse, then it isn't the wrong place.
>
>> + *
>> + * Note: Inspired from drivers/net/ethernet/ti/cpts.c
>> + */
>> +static inline int macb_get_ptp_peer(struct sk_buff *skb, int ptp_class)
>
> Leave off inline, let the compiler decide.
>
>> +{
>> + unsigned int offset = 0;
>> + u8 *msgtype, *data = skb->data;
>> +
>> + if (ptp_class == PTP_CLASS_NONE)
>> + return -1;
>> +
>> + if (ptp_class & PTP_CLASS_VLAN)
>> + offset += VLAN_HLEN;
>> +
>> + switch (ptp_class & PTP_CLASS_PMASK) {
>> + case PTP_CLASS_IPV4:
>> + offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
>> + break;
>> + case PTP_CLASS_IPV6:
>> + offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
>> + break;
>> + case PTP_CLASS_L2:
>> + offset += ETH_HLEN;
>> + break;
>> +
>> + /* something went wrong! */
>> + default:
>> + return -1;
>> + }
>> +
>> + if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID)
>> + return -1;
>> +
>> + if (unlikely(ptp_class & PTP_CLASS_V1))
>> + msgtype = data + offset + OFF_PTP_CONTROL;
>> + else
>> + msgtype = data + offset;
>> +
>> + return (*msgtype) & 0x2;
>> +}
>> +
>> +static inline void macb_ptp_tx_hwtstamp(struct macb *bp, struct sk_buff *skb,
>> + int peer_ev)
>
> no 'inline' please
>
>> +{
>> + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
>> + struct timespec64 ts;
>> + u64 ns;
>> +
>> + /* PTP Peer Event Frame packets */
>> + if (peer_ev) {
>> + ts.tv_sec = gem_readl(bp, PEFTSL);
>> + ts.tv_nsec = gem_readl(bp, PEFTN);
>> +
>> + /* PTP Event Frame packets */
>> + } else {
>> + ts.tv_sec = gem_readl(bp, EFTSL);
>> + ts.tv_nsec = gem_readl(bp, EFTN);
>> + }
>> + ns = timespec64_to_ns(&ts);
>> +
>> + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
>> + shhwtstamps->hwtstamp = ns_to_ktime(ns);
>> + skb_tstamp_tx(skb, skb_hwtstamps(skb));
>> +}
>> +
>> +inline void macb_ptp_do_txstamp(struct macb *bp, struct sk_buff *skb)
>
> s/inline/static/
>
>> +{
>> + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
>> + int class = ptp_classify_raw(skb);
>> + int peer;
>> +
>> + peer = macb_get_ptp_peer(skb, class);
>> + if (peer < 0)
>> + return;
>> +
>> + /* Timestamp this packet */
>> + macb_ptp_tx_hwtstamp(bp, skb, peer);
>> + }
>> +}
>> +
>> +static inline void macb_ptp_rx_hwtstamp(struct macb *bp, struct sk_buff *skb,
>> + int peer_ev)
>> +{
>> + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
>> + struct timespec64 ts;
>> + u64 ns;
>> +
>> + if (peer_ev) {
>> + /* PTP Peer Event Frame packets */
>> + ts.tv_sec = gem_readl(bp, PEFRSL);
>> + ts.tv_nsec = gem_readl(bp, PEFRN);
>> + } else {
>> + /* PTP Event Frame packets */
>> + ts.tv_sec = gem_readl(bp, EFRSL);
>> + ts.tv_nsec = gem_readl(bp, EFRN);
>> + }
>
> So you say the HW provides no matching information? Then it is really
> poor. I surely don't want to let this out into the wild. I'll get
> questions on the linuxptp list, like "PTP on mainline Linux is
> mysteriously broken!"
>
>> + ns = timespec64_to_ns(&ts);
>> +
>> + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
>> + shhwtstamps->hwtstamp = ns_to_ktime(ns);
>> +}
>> +
>> +inline void macb_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb)
>> +{
>> + int class;
>> + int peer;
>> +
>> + /* ffs !!! */
>
> What is this comment for?
>
>> + __skb_push(skb, ETH_HLEN);
>> + class = ptp_classify_raw(skb);
>> + __skb_pull(skb, ETH_HLEN);
>> +
>> + peer = macb_get_ptp_peer(skb, class);
>> + if (peer < 0)
>> + return;
>> +
>> + macb_ptp_rx_hwtstamp(bp, skb, peer);
>> +}
>
> Thanks,
> Richard
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox