[PATCH v4 3/6] of: fix size when dma-range is not used
From: Murali Karicheri <hidden>
Date: 2015-01-27 18:56:09
Also in:
linux-devicetree, linux-iommu, linux-pci, lkml
On 01/27/2015 06:27 AM, Robin Murphy wrote:
Hi Murali, On 23/01/15 22:32, Murali Karicheri wrote:quoted
Fix the dma-range size when the DT attribute is missing. i.e set size to dev->coherent_dma_mask + 1 instead of dev->coherent_dma_mask. To detect overflow when mask is set to max of u64, add a check, log error and return. Some platform use mask format for size in DTS. So add a work around to catch this and fix. Cc: Joerg Roedel <joro@8bytes.org> Cc: Grant Likely <redacted> Cc: Rob Herring <robh+dt@kernel.org> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Will Deacon <redacted> Cc: Russell King <redacted> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com> Signed-off-by: Murali Karicheri <redacted> --- drivers/of/device.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)diff --git a/drivers/of/device.c b/drivers/of/device.c index 2de320d..0a5ff54 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c@@ -105,12 +105,24 @@ void of_dma_configure(struct device *dev, structdevice_node *np) ret = of_dma_get_range(np, &dma_addr, &paddr, &size); if (ret < 0) { dma_addr = offset = 0; - size = dev->coherent_dma_mask; + size = dev->coherent_dma_mask + 1; } else { offset = PFN_DOWN(paddr - dma_addr); + /* + * Add a work around to treat the size as mask + 1 in case + * it is defined in DT as a mask. + */ + if (size & 1) + size = size + 1; dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); } + /* if size is 0, we have an overflow of u64 */ + if (!size) { + dev_err(dev, "invalid size\n"); + return; + } +This seems potentially fragile to dodgy DTs given that we might also be using size to make a mask later. Would it make sense to double-up a sanity check as mask-format detection? Something like: if is_power_of_2(size) // use size else if is_power_of_2(size + 1) // use size + 1 else // cry
Robin,
How about having the logic like this?
ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
if (ret < 0) {
dma_addr = offset = 0;
size = dev->coherent_dma_mask + 1;
} else {
offset = PFN_DOWN(paddr - dma_addr);
dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset);
}
if (is_power_of_2(size + 1))
size = size + 1;
else if (!is_power_of_2(size))
{
dev_err(dev, "invalid size\n");
return;
}
MuraliRobin.quoted
dev->dma_pfn_offset = offset; coherent = of_dma_is_coherent(np);
-- Murali Karicheri Linux Kernel, Texas Instruments