summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorJuan Gutierrez <juan.gutierrez@nxp.com>2016-11-16 16:45:11 -0600
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commiteaad2290408b4152c5a55710de08e9f926e5e474 (patch)
tree459b51e0baee55026ee11f2a3a0dd0944a6ccfe7 /drivers/spi
parentbe0b5e0e451ba7c93d1414e87517602f2dff164a (diff)
spi: use sg_next for walking through the allocated scatterlist table
A null dereference or Oops exception might occurs when reading at once the whole content of an spi-nor of big enough size that requires an scatterlist table that does not fit into one single page. The spi_map_buf function is ignoring the chained sg case by dereferenceing the scatterlist elements in an array fashion. This wrongly assumes that the allocation of the scatterlist elements are contiguous. This is true as long as the scatterlist table fits within a PAGE_SIZE. However, for allocation where the scatter table is bigger than that, the pages allocated by sg_alloc might not be contigous. The sg table can be properly walked by sg_next instead of using an array. Signed-off-by: Juan Gutierrez <juan.gutierrez@nxp.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi.c7
1 files changed, 5 insertions, 2 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index c2e85e23d538..2ebcd1851ab0 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -722,6 +722,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
int desc_len;
int sgs;
struct page *vm_page;
+ struct scatterlist *sg;
void *sg_buf;
size_t min;
int i, ret;
@@ -740,6 +741,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
if (ret != 0)
return ret;
+ sg = &sgt->sgl[0];
for (i = 0; i < sgs; i++) {
if (vmalloced_buf || kmap_buf) {
@@ -759,16 +761,17 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
sg_free_table(sgt);
return -ENOMEM;
}
- sg_set_page(&sgt->sgl[i], vm_page,
+ sg_set_page(sg, vm_page,
min, offset_in_page(buf));
} else {
min = min_t(size_t, len, desc_len);
sg_buf = buf;
- sg_set_buf(&sgt->sgl[i], sg_buf, min);
+ sg_set_buf(sg, sg_buf, min);
}
buf += min;
len -= min;
+ sg = sg_next(sg);
}
ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);