Re: [PATCH] mtd: spi-nor: fix DMA unsafe buffer issue in spi_nor_read_sfdp()

From: Geert Uytterhoeven
Date: Thu Sep 07 2017 - 04:00:58 EST


Hi Cyrille,

On Wed, Sep 6, 2017 at 11:45 PM, Cyrille Pitchen
<cyrille.pitchen@xxxxxxxxxx> wrote:
> spi_nor_read_sfdp() calls nor->read() to read the SFDP data.
> When the m25p80 driver is used (pretty common case), nor->read() is then
> implemented by the m25p80_read() function, which is likely to initialize a
> 'struct spi_transfer' from its buf argument before appending this
> structure inside the 'struct spi_message' argument of spi_sync().
>
> Besides the SPI sub-system states that both .tx_buf and .rx_buf members of
> 'struct spi_transfer' must point into dma-safe memory. However, two of the
> three calls of spi_nor_read_sfdp() were given pointers to stack allocated
> memory as buf argument, hence not in a dma-safe area.
> Hopefully, the third and last call of spi_nor_read_sfdp() was already
> given a kmalloc'ed buffer argument, hence dma-safe.
>
> So this patch fixes this issue by introducing a
> spi_nor_read_sfdp_dma_unsafe() function which simply wraps the existing
> spi_nor_read_sfdp() function and uses some kmalloc'ed memory as a bounce
> buffer.
>
> Reported-by: Geert Uytterhoeven <geert@xxxxxxxxxxxxxx>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@xxxxxxxxxx>

While this patch got rid of the warning, it does not fix the SPI FLASH
identification
issue:

m25p80 spi0.0: s25fl512s (0 Kbytes)
3 ofpart partitions found on MTD device spi0.0
Creating 3 MTD partitions on "spi0.0":
0x000000000000-0x000000040000 : "loader"
mtd: partition "loader" is out of reach -- disabled
0x000000040000-0x000000080000 : "system"
mtd: partition "system" is out of reach -- disabled
0x000000080000-0x000004000000 : "user"
mtd: partition "user" is out of reach -- disabled

I noticed there's still one direct call to spi_nor_read_sfdp() left in
spi_nor_parse_sfdp().
I tried changing that to spi_nor_read_sfdp_dma_unsafe(), but that didn't help.

> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -1784,7 +1784,7 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
> * @nor: pointer to a 'struct spi_nor'
> * @addr: offset in the SFDP area to start reading data from
> * @len: number of bytes to read
> - * @buf: buffer where the SFDP data are copied into
> + * @buf: buffer where the SFDP data are copied into (dma-safe memory)
> *
> * Whatever the actual numbers of bytes for address and dummy cycles are
> * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always
> @@ -1829,6 +1829,36 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
> return ret;
> }
>
> +/**
> + * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters.
> + * @nor: pointer to a 'struct spi_nor'
> + * @addr: offset in the SFDP area to start reading data from
> + * @len: number of bytes to read
> + * @buf: buffer where the SFDP data are copied into
> + *
> + * Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not
> + * guaranteed to be dma-safe.
> + *
> + * Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp()
> + * otherwise.
> + */
> +static int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr,
> + size_t len, void *buf)
> +{
> + void *dma_safe_buf;
> + int ret;
> +
> + dma_safe_buf = kmalloc(len, GFP_KERNEL);
> + if (!dma_safe_buf)
> + return -ENOMEM;
> +
> + ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf);
> + memcpy(buf, dma_safe_buf, len);
> + kfree(dma_safe_buf);
> +
> + return ret;
> +}
> +
> struct sfdp_parameter_header {
> u8 id_lsb;
> u8 minor;
> @@ -2101,7 +2131,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
> bfpt_header->length * sizeof(u32));
> addr = SFDP_PARAM_HEADER_PTP(bfpt_header);
> memset(&bfpt, 0, sizeof(bfpt));
> - err = spi_nor_read_sfdp(nor, addr, len, &bfpt);
> + err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt);
> if (err < 0)
> return err;
>
> @@ -2243,7 +2273,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
> int i, err;
>
> /* Get the SFDP header. */
> - err = spi_nor_read_sfdp(nor, 0, sizeof(header), &header);
> + err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header);
> if (err < 0)
> return err;
>

Instead of having buffers on the stack, passing them around through multiple
call levels, and then kmalloc()ing a buffer, what about using the helpers in
<linux/spi/spi.h> instead, which take care of the issue through the
static bounce
buffer or kmalloc() themselves?

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds