2018-10-09 05:50:36

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 0/7] NULL pointer deref fix for stm32-dma

Hi Greg,

While looking at android-4.14, I found a NULL pointer deref with
stm32-dma driver using Coccicheck errors. I found that upstream had a
bunch of patches on stm32-dma that have fixed this and other issues, I
applied these patches cleanly onto Android 4.14. I believe these should
goto stable and flow into Android 4.14 from there, but I haven't tested
this since I have no hardware to do so.

Atleast I can say that the coccicheck error below goes away when running:
make coccicheck MODE=report
./drivers/dma/stm32-dma.c:567:18-24: ERROR: chan -> desc is NULL but dereferenced.

Anyway, please consider this series for 4.14 stable, I have CC'd the
author and others, thanks.

Pierre Yves MORDRET (7):
dmaengine: stm32-dma: threshold manages with bitfield feature
dmaengine: stm32-dma: fix incomplete configuration in cyclic mode
dmaengine: stm32-dma: fix typo and reported checkpatch warnings
dmaengine: stm32-dma: Improve memory burst management
dmaengine: stm32-dma: fix DMA IRQ status handling
dmaengine: stm32-dma: fix max items per transfer
dmaengine: stm32-dma: properly mask irq bits

drivers/dma/stm32-dma.c | 287 +++++++++++++++++++++++++++++++++-------
1 file changed, 240 insertions(+), 47 deletions(-)

--
2.19.0.605.g01d371f741-goog


2018-10-09 05:48:53

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 1/7] dmaengine: stm32-dma: threshold manages with bitfield feature

From: Pierre Yves MORDRET <[email protected]>

>From now on, DMA bitfield is to manage DMA FIFO Threshold.

Signed-off-by: Pierre-Yves MORDRET <[email protected]>
Signed-off-by: Vinod Koul <[email protected]>
---
drivers/dma/stm32-dma.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 786fc8fcc38e..4099948b6914 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -116,6 +116,10 @@
#define STM32_DMA_MAX_DATA_PARAM 0x03
#define STM32_DMA_MAX_BURST 16

+/* DMA Features */
+#define STM32_DMA_THRESHOLD_FTR_MASK GENMASK(1, 0)
+#define STM32_DMA_THRESHOLD_FTR_GET(n) ((n) & STM32_DMA_THRESHOLD_FTR_MASK)
+
enum stm32_dma_width {
STM32_DMA_BYTE,
STM32_DMA_HALF_WORD,
@@ -129,11 +133,18 @@ enum stm32_dma_burst_size {
STM32_DMA_BURST_INCR16,
};

+/**
+ * struct stm32_dma_cfg - STM32 DMA custom configuration
+ * @channel_id: channel ID
+ * @request_line: DMA request
+ * @stream_config: 32bit mask specifying the DMA channel configuration
+ * @features: 32bit mask specifying the DMA Feature list
+ */
struct stm32_dma_cfg {
u32 channel_id;
u32 request_line;
u32 stream_config;
- u32 threshold;
+ u32 features;
};

struct stm32_dma_chan_reg {
@@ -171,6 +182,7 @@ struct stm32_dma_chan {
u32 next_sg;
struct dma_slave_config dma_sconfig;
struct stm32_dma_chan_reg chan_reg;
+ u32 threshold;
};

struct stm32_dma_device {
@@ -976,7 +988,8 @@ static void stm32_dma_set_config(struct stm32_dma_chan *chan,
/* Enable Interrupts */
chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE;

- chan->chan_reg.dma_sfcr = cfg->threshold & STM32_DMA_SFCR_FTH_MASK;
+ chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features);
+ chan->chan_reg.dma_sfcr = STM32_DMA_SFCR_FTH(chan->threshold);
}

static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
@@ -996,7 +1009,7 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
cfg.channel_id = dma_spec->args[0];
cfg.request_line = dma_spec->args[1];
cfg.stream_config = dma_spec->args[2];
- cfg.threshold = dma_spec->args[3];
+ cfg.features = dma_spec->args[3];

if ((cfg.channel_id >= STM32_DMA_MAX_CHANNELS) ||
(cfg.request_line >= STM32_DMA_MAX_REQUEST_ID)) {
--
2.19.0.605.g01d371f741-goog


2018-10-09 05:49:03

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 7/7] dmaengine: stm32-dma: properly mask irq bits

From: Pierre Yves MORDRET <[email protected]>

A single register of the controller holds the information for four dma
channels.
The functions stm32_dma_irq_status() don't mask the relevant bits after
the shift, thus adjacent channel's status is also reported in the returned
value.
Fixed by masking the value before returning it.

Similarly, the function stm32_dma_irq_clear() don't mask the input value
before shifting it, thus an incorrect input value could disable the
interrupts of adjacent channels.
Fixed by masking the input value before using it.

Signed-off-by: Pierre-Yves MORDRET <[email protected]>
Signed-off-by: Antonio Borneo <[email protected]>
Signed-off-by: Vinod Koul <[email protected]>
---
drivers/dma/stm32-dma.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 05a2974cd2c0..8c5807362a25 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -38,6 +38,10 @@
#define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */
#define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */
#define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */
+#define STM32_DMA_MASKI (STM32_DMA_TCI \
+ | STM32_DMA_TEI \
+ | STM32_DMA_DMEI \
+ | STM32_DMA_FEI)

/* DMA Stream x Configuration Register */
#define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */
@@ -405,7 +409,7 @@ static u32 stm32_dma_irq_status(struct stm32_dma_chan *chan)

flags = dma_isr >> (((chan->id & 2) << 3) | ((chan->id & 1) * 6));

- return flags;
+ return flags & STM32_DMA_MASKI;
}

static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
@@ -420,6 +424,7 @@ static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
* If (ch % 4) is 2 or 3, left shift the mask by 16 bits.
* If (ch % 4) is 1 or 3, additionally left shift the mask by 6 bits.
*/
+ flags &= STM32_DMA_MASKI;
dma_ifcr = flags << (((chan->id & 2) << 3) | ((chan->id & 1) * 6));

if (chan->id & 4)
--
2.19.0.605.g01d371f741-goog


2018-10-09 05:49:10

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 6/7] dmaengine: stm32-dma: fix max items per transfer

From: Pierre Yves MORDRET <[email protected]>

Having 0 in item counter register is valid and stands for a "No or Ended
transfer". Therefore valid transfer starts from @+0 to @+0xFFFE leading to
unaligned scatter gather at boundary. Thus it's safer to round down this
value on its FIFO size (16 Bytes).

Signed-off-by: Pierre-Yves MORDRET <[email protected]>
Signed-off-by: Vinod Koul <[email protected]>
---
drivers/dma/stm32-dma.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index b40486454a2c..05a2974cd2c0 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -38,10 +38,6 @@
#define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */
#define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */
#define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */
-#define STM32_DMA_MASKI (STM32_DMA_TCI \
- | STM32_DMA_TEI \
- | STM32_DMA_DMEI \
- | STM32_DMA_FEI)

/* DMA Stream x Configuration Register */
#define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */
@@ -118,6 +114,13 @@
#define STM32_DMA_FIFO_THRESHOLD_FULL 0x03

#define STM32_DMA_MAX_DATA_ITEMS 0xffff
+/*
+ * Valid transfer starts from @0 to @0xFFFE leading to unaligned scatter
+ * gather at boundary. Thus it's safer to round down this value on FIFO
+ * size (16 Bytes)
+ */
+#define STM32_DMA_ALIGNED_MAX_DATA_ITEMS \
+ ALIGN_DOWN(STM32_DMA_MAX_DATA_ITEMS, 16)
#define STM32_DMA_MAX_CHANNELS 0x08
#define STM32_DMA_MAX_REQUEST_ID 0x08
#define STM32_DMA_MAX_DATA_PARAM 0x03
@@ -869,7 +872,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
desc->sg_req[i].len = sg_dma_len(sg);

nb_data_items = desc->sg_req[i].len / buswidth;
- if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
+ if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
dev_err(chan2dev(chan), "nb items not supported\n");
goto err;
}
@@ -935,7 +938,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
return NULL;

nb_data_items = period_len / buswidth;
- if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
+ if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) {
dev_err(chan2dev(chan), "number of items not supported\n");
return NULL;
}
@@ -985,7 +988,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
u32 num_sgs, best_burst, dma_burst, threshold;
int i;

- num_sgs = DIV_ROUND_UP(len, STM32_DMA_MAX_DATA_ITEMS);
+ num_sgs = DIV_ROUND_UP(len, STM32_DMA_ALIGNED_MAX_DATA_ITEMS);
desc = stm32_dma_alloc_desc(num_sgs);
if (!desc)
return NULL;
@@ -994,7 +997,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(

for (offset = 0, i = 0; offset < len; offset += xfer_count, i++) {
xfer_count = min_t(size_t, len - offset,
- STM32_DMA_MAX_DATA_ITEMS);
+ STM32_DMA_ALIGNED_MAX_DATA_ITEMS);

/* Compute best burst size */
max_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
--
2.19.0.605.g01d371f741-goog


2018-10-09 05:49:21

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 5/7] dmaengine: stm32-dma: fix DMA IRQ status handling

From: Pierre Yves MORDRET <[email protected]>

Update the way Transfer Complete and Half Transfer Complete status are
acknowledge. Even if HTI is not enabled its status is shown when reading
registers, driver has to clear it gently and not raise an error.

Signed-off-by: Pierre-Yves MORDRET <[email protected]>
Signed-off-by: Vinod Koul <[email protected]>
---
drivers/dma/stm32-dma.c | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 21ad359a5a59..b40486454a2c 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -34,9 +34,14 @@
#define STM32_DMA_LIFCR 0x0008 /* DMA Low Int Flag Clear Reg */
#define STM32_DMA_HIFCR 0x000c /* DMA High Int Flag Clear Reg */
#define STM32_DMA_TCI BIT(5) /* Transfer Complete Interrupt */
+#define STM32_DMA_HTI BIT(4) /* Half Transfer Interrupt */
#define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */
#define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */
#define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */
+#define STM32_DMA_MASKI (STM32_DMA_TCI \
+ | STM32_DMA_TEI \
+ | STM32_DMA_DMEI \
+ | STM32_DMA_FEI)

/* DMA Stream x Configuration Register */
#define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */
@@ -643,13 +648,29 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
status = stm32_dma_irq_status(chan);
scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));

- if ((status & STM32_DMA_TCI) && (scr & STM32_DMA_SCR_TCIE)) {
+ if (status & STM32_DMA_TCI) {
stm32_dma_irq_clear(chan, STM32_DMA_TCI);
- stm32_dma_handle_chan_done(chan);
-
- } else {
+ if (scr & STM32_DMA_SCR_TCIE)
+ stm32_dma_handle_chan_done(chan);
+ status &= ~STM32_DMA_TCI;
+ }
+ if (status & STM32_DMA_HTI) {
+ stm32_dma_irq_clear(chan, STM32_DMA_HTI);
+ status &= ~STM32_DMA_HTI;
+ }
+ if (status & STM32_DMA_FEI) {
+ stm32_dma_irq_clear(chan, STM32_DMA_FEI);
+ status &= ~STM32_DMA_FEI;
+ if (!(scr & STM32_DMA_SCR_EN))
+ dev_err(chan2dev(chan), "FIFO Error\n");
+ else
+ dev_dbg(chan2dev(chan), "FIFO over/underrun\n");
+ }
+ if (status) {
stm32_dma_irq_clear(chan, status);
dev_err(chan2dev(chan), "DMA error: status=0x%08x\n", status);
+ if (!(scr & STM32_DMA_SCR_EN))
+ dev_err(chan2dev(chan), "chan disabled by HW\n");
}

spin_unlock(&chan->vchan.lock);
--
2.19.0.605.g01d371f741-goog


2018-10-09 05:49:26

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 4/7] dmaengine: stm32-dma: Improve memory burst management

From: Pierre Yves MORDRET <[email protected]>

This patch improves memory burst capability using best burst size
according to transferred buffer size from/to memory.

>From now on, memory burst is not necessarily same as with peripheral
burst one and fifo threshold is directly managed by this driver in order
to fit with computed memory burst.

Signed-off-by: M'boumba Cedric Madianga <[email protected]>
Signed-off-by: Pierre-Yves MORDRET <[email protected]>
Signed-off-by: Vinod Koul <[email protected]>
---
drivers/dma/stm32-dma.c | 204 ++++++++++++++++++++++++++++++++++------
1 file changed, 175 insertions(+), 29 deletions(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index b64e14a83dec..21ad359a5a59 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -5,6 +5,7 @@
*
* Copyright (C) M'boumba Cedric Madianga 2015
* Author: M'boumba Cedric Madianga <[email protected]>
+ * Pierre-Yves Mordret <[email protected]>
*
* License terms: GNU General Public License (GPL), version 2
*/
@@ -115,6 +116,8 @@
#define STM32_DMA_MAX_CHANNELS 0x08
#define STM32_DMA_MAX_REQUEST_ID 0x08
#define STM32_DMA_MAX_DATA_PARAM 0x03
+#define STM32_DMA_FIFO_SIZE 16 /* FIFO is 16 bytes */
+#define STM32_DMA_MIN_BURST 4
#define STM32_DMA_MAX_BURST 16

/* DMA Features */
@@ -184,6 +187,8 @@ struct stm32_dma_chan {
struct dma_slave_config dma_sconfig;
struct stm32_dma_chan_reg chan_reg;
u32 threshold;
+ u32 mem_burst;
+ u32 mem_width;
};

struct stm32_dma_device {
@@ -248,6 +253,85 @@ static int stm32_dma_get_width(struct stm32_dma_chan *chan,
}
}

+static enum dma_slave_buswidth stm32_dma_get_max_width(u32 buf_len,
+ u32 threshold)
+{
+ enum dma_slave_buswidth max_width;
+
+ if (threshold == STM32_DMA_FIFO_THRESHOLD_FULL)
+ max_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ else
+ max_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ while ((buf_len < max_width || buf_len % max_width) &&
+ max_width > DMA_SLAVE_BUSWIDTH_1_BYTE)
+ max_width = max_width >> 1;
+
+ return max_width;
+}
+
+static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold,
+ enum dma_slave_buswidth width)
+{
+ u32 remaining;
+
+ if (width != DMA_SLAVE_BUSWIDTH_UNDEFINED) {
+ if (burst != 0) {
+ /*
+ * If number of beats fit in several whole bursts
+ * this configuration is allowed.
+ */
+ remaining = ((STM32_DMA_FIFO_SIZE / width) *
+ (threshold + 1) / 4) % burst;
+
+ if (remaining == 0)
+ return true;
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold)
+{
+ switch (threshold) {
+ case STM32_DMA_FIFO_THRESHOLD_FULL:
+ if (buf_len >= STM32_DMA_MAX_BURST)
+ return true;
+ else
+ return false;
+ case STM32_DMA_FIFO_THRESHOLD_HALFFULL:
+ if (buf_len >= STM32_DMA_MAX_BURST / 2)
+ return true;
+ else
+ return false;
+ default:
+ return false;
+ }
+}
+
+static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold,
+ enum dma_slave_buswidth width)
+{
+ u32 best_burst = max_burst;
+
+ if (best_burst == 1 || !stm32_dma_is_burst_possible(buf_len, threshold))
+ return 0;
+
+ while ((buf_len < best_burst * width && best_burst > 1) ||
+ !stm32_dma_fifo_threshold_is_allowed(best_burst, threshold,
+ width)) {
+ if (best_burst > STM32_DMA_MIN_BURST)
+ best_burst = best_burst >> 1;
+ else
+ best_burst = 0;
+ }
+
+ return best_burst;
+}
+
static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst)
{
switch (maxburst) {
@@ -267,12 +351,12 @@ static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst)
}

static void stm32_dma_set_fifo_config(struct stm32_dma_chan *chan,
- u32 src_maxburst, u32 dst_maxburst)
+ u32 src_burst, u32 dst_burst)
{
chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_MASK;
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_DMEIE;

- if ((!src_maxburst) && (!dst_maxburst)) {
+ if (!src_burst && !dst_burst) {
/* Using direct mode */
chan->chan_reg.dma_scr |= STM32_DMA_SCR_DMEIE;
} else {
@@ -589,37 +673,52 @@ static void stm32_dma_issue_pending(struct dma_chan *c)

static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
enum dma_transfer_direction direction,
- enum dma_slave_buswidth *buswidth)
+ enum dma_slave_buswidth *buswidth,
+ u32 buf_len)
{
enum dma_slave_buswidth src_addr_width, dst_addr_width;
int src_bus_width, dst_bus_width;
int src_burst_size, dst_burst_size;
- u32 src_maxburst, dst_maxburst;
- u32 dma_scr = 0;
+ u32 src_maxburst, dst_maxburst, src_best_burst, dst_best_burst;
+ u32 dma_scr, threshold;

src_addr_width = chan->dma_sconfig.src_addr_width;
dst_addr_width = chan->dma_sconfig.dst_addr_width;
src_maxburst = chan->dma_sconfig.src_maxburst;
dst_maxburst = chan->dma_sconfig.dst_maxburst;
+ threshold = chan->threshold;

switch (direction) {
case DMA_MEM_TO_DEV:
+ /* Set device data size */
dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
if (dst_bus_width < 0)
return dst_bus_width;

- dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
+ /* Set device burst size */
+ dst_best_burst = stm32_dma_get_best_burst(buf_len,
+ dst_maxburst,
+ threshold,
+ dst_addr_width);
+
+ dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst);
if (dst_burst_size < 0)
return dst_burst_size;

- if (!src_addr_width)
- src_addr_width = dst_addr_width;
-
+ /* Set memory data size */
+ src_addr_width = stm32_dma_get_max_width(buf_len, threshold);
+ chan->mem_width = src_addr_width;
src_bus_width = stm32_dma_get_width(chan, src_addr_width);
if (src_bus_width < 0)
return src_bus_width;

- src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
+ /* Set memory burst size */
+ src_maxburst = STM32_DMA_MAX_BURST;
+ src_best_burst = stm32_dma_get_best_burst(buf_len,
+ src_maxburst,
+ threshold,
+ src_addr_width);
+ src_burst_size = stm32_dma_get_burst(chan, src_best_burst);
if (src_burst_size < 0)
return src_burst_size;

@@ -629,27 +728,46 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
STM32_DMA_SCR_PBURST(dst_burst_size) |
STM32_DMA_SCR_MBURST(src_burst_size);

+ /* Set FIFO threshold */
+ chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK;
+ chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold);
+
+ /* Set peripheral address */
chan->chan_reg.dma_spar = chan->dma_sconfig.dst_addr;
*buswidth = dst_addr_width;
break;

case DMA_DEV_TO_MEM:
+ /* Set device data size */
src_bus_width = stm32_dma_get_width(chan, src_addr_width);
if (src_bus_width < 0)
return src_bus_width;

- src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
+ /* Set device burst size */
+ src_best_burst = stm32_dma_get_best_burst(buf_len,
+ src_maxburst,
+ threshold,
+ src_addr_width);
+ chan->mem_burst = src_best_burst;
+ src_burst_size = stm32_dma_get_burst(chan, src_best_burst);
if (src_burst_size < 0)
return src_burst_size;

- if (!dst_addr_width)
- dst_addr_width = src_addr_width;
-
+ /* Set memory data size */
+ dst_addr_width = stm32_dma_get_max_width(buf_len, threshold);
+ chan->mem_width = dst_addr_width;
dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
if (dst_bus_width < 0)
return dst_bus_width;

- dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
+ /* Set memory burst size */
+ dst_maxburst = STM32_DMA_MAX_BURST;
+ dst_best_burst = stm32_dma_get_best_burst(buf_len,
+ dst_maxburst,
+ threshold,
+ dst_addr_width);
+ chan->mem_burst = dst_best_burst;
+ dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst);
if (dst_burst_size < 0)
return dst_burst_size;

@@ -659,6 +777,11 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
STM32_DMA_SCR_PBURST(src_burst_size) |
STM32_DMA_SCR_MBURST(dst_burst_size);

+ /* Set FIFO threshold */
+ chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK;
+ chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold);
+
+ /* Set peripheral address */
chan->chan_reg.dma_spar = chan->dma_sconfig.src_addr;
*buswidth = chan->dma_sconfig.src_addr_width;
break;
@@ -668,8 +791,9 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
return -EINVAL;
}

- stm32_dma_set_fifo_config(chan, src_maxburst, dst_maxburst);
+ stm32_dma_set_fifo_config(chan, src_best_burst, dst_best_burst);

+ /* Set DMA control register */
chan->chan_reg.dma_scr &= ~(STM32_DMA_SCR_DIR_MASK |
STM32_DMA_SCR_PSIZE_MASK | STM32_DMA_SCR_MSIZE_MASK |
STM32_DMA_SCR_PBURST_MASK | STM32_DMA_SCR_MBURST_MASK);
@@ -709,10 +833,6 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
if (!desc)
return NULL;

- ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
- if (ret < 0)
- goto err;
-
/* Set peripheral flow controller */
if (chan->dma_sconfig.device_fc)
chan->chan_reg.dma_scr |= STM32_DMA_SCR_PFCTRL;
@@ -720,6 +840,11 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL;

for_each_sg(sgl, sg, sg_len, i) {
+ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth,
+ sg_dma_len(sg));
+ if (ret < 0)
+ goto err;
+
desc->sg_req[i].len = sg_dma_len(sg);

nb_data_items = desc->sg_req[i].len / buswidth;
@@ -784,7 +909,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
return NULL;
}

- ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
+ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, period_len);
if (ret < 0)
return NULL;

@@ -833,9 +958,10 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
dma_addr_t src, size_t len, unsigned long flags)
{
struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
- u32 num_sgs;
+ enum dma_slave_buswidth max_width;
struct stm32_dma_desc *desc;
size_t xfer_count, offset;
+ u32 num_sgs, best_burst, dma_burst, threshold;
int i;

num_sgs = DIV_ROUND_UP(len, STM32_DMA_MAX_DATA_ITEMS);
@@ -843,25 +969,34 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
if (!desc)
return NULL;

+ threshold = chan->threshold;
+
for (offset = 0, i = 0; offset < len; offset += xfer_count, i++) {
xfer_count = min_t(size_t, len - offset,
STM32_DMA_MAX_DATA_ITEMS);

- desc->sg_req[i].len = xfer_count;
+ /* Compute best burst size */
+ max_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ best_burst = stm32_dma_get_best_burst(len, STM32_DMA_MAX_BURST,
+ threshold, max_width);
+ dma_burst = stm32_dma_get_burst(chan, best_burst);

stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
desc->sg_req[i].chan_reg.dma_scr =
STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_MEM) |
+ STM32_DMA_SCR_PBURST(dma_burst) |
+ STM32_DMA_SCR_MBURST(dma_burst) |
STM32_DMA_SCR_MINC |
STM32_DMA_SCR_PINC |
STM32_DMA_SCR_TCIE |
STM32_DMA_SCR_TEIE;
- desc->sg_req[i].chan_reg.dma_sfcr = STM32_DMA_SFCR_DMDIS |
- STM32_DMA_SFCR_FTH(STM32_DMA_FIFO_THRESHOLD_FULL) |
- STM32_DMA_SFCR_FEIE;
+ desc->sg_req[i].chan_reg.dma_sfcr |= STM32_DMA_SFCR_MASK;
+ desc->sg_req[i].chan_reg.dma_sfcr |=
+ STM32_DMA_SFCR_FTH(threshold);
desc->sg_req[i].chan_reg.dma_spar = src + offset;
desc->sg_req[i].chan_reg.dma_sm0ar = dest + offset;
desc->sg_req[i].chan_reg.dma_sndtr = xfer_count;
+ desc->sg_req[i].len = xfer_count;
}

desc->num_sgs = num_sgs;
@@ -886,6 +1021,7 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
struct stm32_dma_desc *desc,
u32 next_sg)
{
+ u32 modulo, burst_size;
u32 residue = 0;
int i;

@@ -893,8 +1029,10 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
* In cyclic mode, for the last period, residue = remaining bytes from
* NDTR
*/
- if (chan->desc->cyclic && next_sg == 0)
- return stm32_dma_get_remaining_bytes(chan);
+ if (chan->desc->cyclic && next_sg == 0) {
+ residue = stm32_dma_get_remaining_bytes(chan);
+ goto end;
+ }

/*
* For all other periods in cyclic mode, and in sg mode,
@@ -905,6 +1043,15 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
residue += desc->sg_req[i].len;
residue += stm32_dma_get_remaining_bytes(chan);

+end:
+ if (!chan->mem_burst)
+ return residue;
+
+ burst_size = chan->mem_burst * chan->mem_width;
+ modulo = residue % burst_size;
+ if (modulo)
+ residue = residue - modulo + burst_size;
+
return residue;
}

@@ -994,7 +1141,6 @@ static void stm32_dma_set_config(struct stm32_dma_chan *chan,
chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE;

chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features);
- chan->chan_reg.dma_sfcr = STM32_DMA_SFCR_FTH(chan->threshold);
}

static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
--
2.19.0.605.g01d371f741-goog


2018-10-09 05:49:36

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 3/7] dmaengine: stm32-dma: fix typo and reported checkpatch warnings

From: Pierre Yves MORDRET <[email protected]>

Fix typo in a comment and solved reported checkpatch warnings.

Signed-off-by: Pierre-Yves MORDRET <[email protected]>
Signed-off-by: Vinod Koul <[email protected]>
---
drivers/dma/stm32-dma.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index fae7de54f00a..b64e14a83dec 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -60,7 +60,8 @@
#define STM32_DMA_SCR_PINC BIT(9) /* Peripheral increment mode */
#define STM32_DMA_SCR_CIRC BIT(8) /* Circular mode */
#define STM32_DMA_SCR_PFCTRL BIT(5) /* Peripheral Flow Controller */
-#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Cplete Int Enable*/
+#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Complete Int Enable
+ */
#define STM32_DMA_SCR_TEIE BIT(2) /* Transfer Error Int Enable */
#define STM32_DMA_SCR_DMEIE BIT(1) /* Direct Mode Err Int Enable */
#define STM32_DMA_SCR_EN BIT(0) /* Stream Enable */
@@ -918,7 +919,7 @@ static enum dma_status stm32_dma_tx_status(struct dma_chan *c,
u32 residue = 0;

status = dma_cookie_status(c, cookie, state);
- if ((status == DMA_COMPLETE) || (!state))
+ if (status == DMA_COMPLETE || !state)
return status;

spin_lock_irqsave(&chan->vchan.lock, flags);
@@ -982,7 +983,7 @@ static void stm32_dma_desc_free(struct virt_dma_desc *vdesc)
}

static void stm32_dma_set_config(struct stm32_dma_chan *chan,
- struct stm32_dma_cfg *cfg)
+ struct stm32_dma_cfg *cfg)
{
stm32_dma_clear_reg(&chan->chan_reg);

@@ -1015,8 +1016,8 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
cfg.stream_config = dma_spec->args[2];
cfg.features = dma_spec->args[3];

- if ((cfg.channel_id >= STM32_DMA_MAX_CHANNELS) ||
- (cfg.request_line >= STM32_DMA_MAX_REQUEST_ID)) {
+ if (cfg.channel_id >= STM32_DMA_MAX_CHANNELS ||
+ cfg.request_line >= STM32_DMA_MAX_REQUEST_ID) {
dev_err(dev, "Bad channel and/or request id\n");
return NULL;
}
--
2.19.0.605.g01d371f741-goog


2018-10-09 05:50:06

by Joel Fernandes

[permalink] [raw]
Subject: [PATCH 2/7] dmaengine: stm32-dma: fix incomplete configuration in cyclic mode

From: Pierre Yves MORDRET <[email protected]>

When in cyclic mode, the configuration is updated after having started the
DMA hardware (STM32_DMA_SCR_EN) leading to incomplete configuration of
SMxAR registers.

Signed-off-by: Pierre-Yves MORDRET <[email protected]>
Signed-off-by: Hugues Fruchet <[email protected]>
Signed-off-by: Vinod Koul <[email protected]>
---
drivers/dma/stm32-dma.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 4099948b6914..fae7de54f00a 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -441,6 +441,8 @@ static void stm32_dma_dump_reg(struct stm32_dma_chan *chan)
dev_dbg(chan2dev(chan), "SFCR: 0x%08x\n", sfcr);
}

+static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan);
+
static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
{
struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
@@ -483,6 +485,9 @@ static void stm32_dma_start_transfer(struct stm32_dma_chan *chan)
if (status)
stm32_dma_irq_clear(chan, status);

+ if (chan->desc->cyclic)
+ stm32_dma_configure_next_sg(chan);
+
stm32_dma_dump_reg(chan);

/* Start DMA */
@@ -576,8 +581,7 @@ static void stm32_dma_issue_pending(struct dma_chan *c)
if (vchan_issue_pending(&chan->vchan) && !chan->desc && !chan->busy) {
dev_dbg(chan2dev(chan), "vchan %p: issued\n", &chan->vchan);
stm32_dma_start_transfer(chan);
- if (chan->desc->cyclic)
- stm32_dma_configure_next_sg(chan);
+
}
spin_unlock_irqrestore(&chan->vchan.lock, flags);
}
--
2.19.0.605.g01d371f741-goog


2018-10-15 17:01:34

by Vinod Koul

[permalink] [raw]
Subject: Re: [PATCH 0/7] NULL pointer deref fix for stm32-dma

Hi Joel,

On 08-10-18, 22:47, Joel Fernandes (Google) wrote:
> Hi Greg,
>
> While looking at android-4.14, I found a NULL pointer deref with
> stm32-dma driver using Coccicheck errors. I found that upstream had a
> bunch of patches on stm32-dma that have fixed this and other issues, I
> applied these patches cleanly onto Android 4.14. I believe these should
> goto stable and flow into Android 4.14 from there, but I haven't tested
> this since I have no hardware to do so.
>
> Atleast I can say that the coccicheck error below goes away when running:
> make coccicheck MODE=report
> ./drivers/dma/stm32-dma.c:567:18-24: ERROR: chan -> desc is NULL but dereferenced.
>
> Anyway, please consider this series for 4.14 stable, I have CC'd the
> author and others, thanks.
>
> Pierre Yves MORDRET (7):
> dmaengine: stm32-dma: threshold manages with bitfield feature
> dmaengine: stm32-dma: fix incomplete configuration in cyclic mode
> dmaengine: stm32-dma: fix typo and reported checkpatch warnings
> dmaengine: stm32-dma: Improve memory burst management
> dmaengine: stm32-dma: fix DMA IRQ status handling
> dmaengine: stm32-dma: fix max items per transfer
> dmaengine: stm32-dma: properly mask irq bits

It would be good to only cherry pick fixes for this. I do not feel that
some of them which are adding or enhancing driver belong to stable.

Thanks
--
~Vinod

2018-10-16 16:12:13

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH 0/7] NULL pointer deref fix for stm32-dma

On Mon, Oct 08, 2018 at 10:47:45PM -0700, Joel Fernandes (Google) wrote:
> Hi Greg,
>
> While looking at android-4.14, I found a NULL pointer deref with
> stm32-dma driver using Coccicheck errors. I found that upstream had a
> bunch of patches on stm32-dma that have fixed this and other issues, I
> applied these patches cleanly onto Android 4.14. I believe these should
> goto stable and flow into Android 4.14 from there, but I haven't tested
> this since I have no hardware to do so.
>
> Atleast I can say that the coccicheck error below goes away when running:
> make coccicheck MODE=report
> ./drivers/dma/stm32-dma.c:567:18-24: ERROR: chan -> desc is NULL but dereferenced.
>
> Anyway, please consider this series for 4.14 stable, I have CC'd the
> author and others, thanks.
>
> Pierre Yves MORDRET (7):
> dmaengine: stm32-dma: threshold manages with bitfield feature
> dmaengine: stm32-dma: fix incomplete configuration in cyclic mode
> dmaengine: stm32-dma: fix typo and reported checkpatch warnings
> dmaengine: stm32-dma: Improve memory burst management
> dmaengine: stm32-dma: fix DMA IRQ status handling
> dmaengine: stm32-dma: fix max items per transfer
> dmaengine: stm32-dma: properly mask irq bits
>
> drivers/dma/stm32-dma.c | 287 +++++++++++++++++++++++++++++++++-------
> 1 file changed, 240 insertions(+), 47 deletions(-)


I need git commit ids for all of these for what the patch in mainline
is.

Can you fix that up and resend them?

thanks,

greg k-h

2018-10-16 23:50:38

by Joel Fernandes

[permalink] [raw]
Subject: Re: [PATCH 0/7] NULL pointer deref fix for stm32-dma

On Tue, Oct 16, 2018 at 06:11:29PM +0200, Greg KH wrote:
> On Mon, Oct 08, 2018 at 10:47:45PM -0700, Joel Fernandes (Google) wrote:
> > Hi Greg,
> >
> > While looking at android-4.14, I found a NULL pointer deref with
> > stm32-dma driver using Coccicheck errors. I found that upstream had a
> > bunch of patches on stm32-dma that have fixed this and other issues, I
> > applied these patches cleanly onto Android 4.14. I believe these should
> > goto stable and flow into Android 4.14 from there, but I haven't tested
> > this since I have no hardware to do so.
> >
> > Atleast I can say that the coccicheck error below goes away when running:
> > make coccicheck MODE=report
> > ./drivers/dma/stm32-dma.c:567:18-24: ERROR: chan -> desc is NULL but dereferenced.
> >
> > Anyway, please consider this series for 4.14 stable, I have CC'd the
> > author and others, thanks.
> >
> > Pierre Yves MORDRET (7):
> > dmaengine: stm32-dma: threshold manages with bitfield feature
> > dmaengine: stm32-dma: fix incomplete configuration in cyclic mode
> > dmaengine: stm32-dma: fix typo and reported checkpatch warnings
> > dmaengine: stm32-dma: Improve memory burst management
> > dmaengine: stm32-dma: fix DMA IRQ status handling
> > dmaengine: stm32-dma: fix max items per transfer
> > dmaengine: stm32-dma: properly mask irq bits
> >
> > drivers/dma/stm32-dma.c | 287 +++++++++++++++++++++++++++++++++-------
> > 1 file changed, 240 insertions(+), 47 deletions(-)
>
>
> I need git commit ids for all of these for what the patch in mainline
> is.
>
> Can you fix that up and resend them?
>

Vinod raised a concern in the other thread that he does not want all of the
"feature patches" that I posted in these series into -stable, since not
everything is a fix.

So what I'll do is I'll only repost:
dmaengine: stm32-dma: fix incomplete configuration in cyclic mode

which fixes the null pointer issue that raised the coccinelle error I found
and reported.

I'll let Vinod/Pierre decide on posting the other fixes to stable since they
may have more background on whether something is a fix or not.

thanks,

- Joel