diff options
author | Quanyang Wang <quanyang.wang@windriver.com> | 2021-04-16 02:46:52 +0200 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2021-04-16 14:44:15 +0200 |
commit | 126bdb606fd2802454e6048caef1be3e25dd121e (patch) | |
tree | 5e05643fab952d5b24e6aa41f7348fa5483d6446 /drivers/spi/spi-zynqmp-gqspi.c | |
parent | spi: spi-zynqmp-gqspi: fix use-after-free in zynqmp_qspi_exec_op (diff) | |
download | linux-126bdb606fd2802454e6048caef1be3e25dd121e.tar.xz linux-126bdb606fd2802454e6048caef1be3e25dd121e.zip |
spi: spi-zynqmp-gqspi: return -ENOMEM if dma_map_single fails
The spi controller supports 44-bit address space on AXI in DMA mode,
so set dma_addr_t width to 44-bit to avoid using a swiotlb mapping.
In addition, if dma_map_single fails, it should return immediately
instead of continuing doing the DMA operation which bases on invalid
address.
This fixes the following crash which occurs in reading a big block
from flash:
[ 123.633577] zynqmp-qspi ff0f0000.spi: swiotlb buffer is full (sz: 4194304 bytes), total 32768 (slots), used 0 (slots)
[ 123.644230] zynqmp-qspi ff0f0000.spi: ERR:rxdma:memory not mapped
[ 123.784625] Unable to handle kernel paging request at virtual address 00000000003fffc0
[ 123.792536] Mem abort info:
[ 123.795313] ESR = 0x96000145
[ 123.798351] EC = 0x25: DABT (current EL), IL = 32 bits
[ 123.803655] SET = 0, FnV = 0
[ 123.806693] EA = 0, S1PTW = 0
[ 123.809818] Data abort info:
[ 123.812683] ISV = 0, ISS = 0x00000145
[ 123.816503] CM = 1, WnR = 1
[ 123.819455] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000805047000
[ 123.825887] [00000000003fffc0] pgd=0000000803b45003, p4d=0000000803b45003, pud=0000000000000000
[ 123.834586] Internal error: Oops: 96000145 [#1] PREEMPT SMP
Fixes: 1c26372e5aa9 ("spi: spi-zynqmp-gqspi: Update driver to use spi-mem framework")
Signed-off-by: Quanyang Wang <quanyang.wang@windriver.com>
Link: https://lore.kernel.org/r/20210416004652.2975446-6-quanyang.wang@windriver.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-zynqmp-gqspi.c')
-rw-r--r-- | drivers/spi/spi-zynqmp-gqspi.c | 26 |
1 files changed, 20 insertions, 6 deletions
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 419bc1e6358b..328b6559bb19 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -733,7 +733,7 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) * zynqmp_qspi_setuprxdma - This function sets up the RX DMA operation * @xqspi: xqspi is a pointer to the GQSPI instance. */ -static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) +static int zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) { u32 rx_bytes, rx_rem, config_reg; dma_addr_t addr; @@ -747,7 +747,7 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); xqspi->mode = GQSPI_MODE_IO; xqspi->dma_rx_bytes = 0; - return; + return 0; } rx_rem = xqspi->bytes_to_receive % 4; @@ -755,8 +755,10 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) addr = dma_map_single(xqspi->dev, (void *)xqspi->rxbuf, rx_bytes, DMA_FROM_DEVICE); - if (dma_mapping_error(xqspi->dev, addr)) + if (dma_mapping_error(xqspi->dev, addr)) { dev_err(xqspi->dev, "ERR:rxdma:memory not mapped\n"); + return -ENOMEM; + } xqspi->dma_rx_bytes = rx_bytes; xqspi->dma_addr = addr; @@ -777,6 +779,8 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) /* Write the number of bytes to transfer */ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_SIZE_OFST, rx_bytes); + + return 0; } /** @@ -813,11 +817,17 @@ static void zynqmp_qspi_write_op(struct zynqmp_qspi *xqspi, u8 tx_nbits, * @genfifoentry: genfifoentry is pointer to the variable in which * GENFIFO mask is returned to calling function */ -static void zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits, +static int zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits, u32 genfifoentry) { - zynqmp_qspi_setuprxdma(xqspi); + int ret; + + ret = zynqmp_qspi_setuprxdma(xqspi); + if (ret) + return ret; zynqmp_qspi_fillgenfifo(xqspi, rx_nbits, genfifoentry); + + return 0; } /** @@ -1031,8 +1041,11 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem, xqspi->rxbuf = (u8 *)op->data.buf.in; xqspi->bytes_to_receive = op->data.nbytes; xqspi->bytes_to_transfer = 0; - zynqmp_qspi_read_op(xqspi, op->data.buswidth, + err = zynqmp_qspi_read_op(xqspi, op->data.buswidth, genfifoentry); + if (err) + goto return_err; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, zynqmp_gqspi_read (xqspi, GQSPI_CONFIG_OFST) | @@ -1159,6 +1172,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto clk_dis_all; } + dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); ctlr->bits_per_word_mask = SPI_BPW_MASK(8); ctlr->num_chipselect = GQSPI_DEFAULT_NUM_CS; ctlr->mem_ops = &zynqmp_qspi_mem_ops; |