Takashi Iwai 7bb6d7
From 47016b341fc3b3fd4909e058c6fa38f165b53646 Mon Sep 17 00:00:00 2001
Takashi Iwai 7bb6d7
From: Thor Thayer <thor.thayer@linux.intel.com>
Takashi Iwai 7bb6d7
Date: Mon, 23 Apr 2018 12:45:11 -0500
Takashi Iwai 7bb6d7
Subject: [PATCH] mtd: spi-nor: cadence-quadspi: Fix page fault kernel panic
Takashi Iwai 7bb6d7
Git-commit: 47016b341fc3b3fd4909e058c6fa38f165b53646
Takashi Iwai 7bb6d7
Patch-mainline: v4.17-rc3
Takashi Iwai 7bb6d7
References: bsc#1051510
Takashi Iwai 7bb6d7
Takashi Iwai 7bb6d7
The current Cadence QSPI driver caused a kernel panic when loading
Takashi Iwai 7bb6d7
a Root Filesystem from QSPI. The problem was caused by reading more
Takashi Iwai 7bb6d7
bytes than needed because the QSPI operated on 4 bytes at a time.
Takashi Iwai 7bb6d7
<snip>
Takashi Iwai 7bb6d7
[    7.947754] spi_nor_read[1048]:from 0x037cad74, len 1 [bfe07fff]
Takashi Iwai 7bb6d7
[    7.956247] cqspi_read[910]:offset 0x58502516, buffer=bfe07fff
Takashi Iwai 7bb6d7
[    7.956247]
Takashi Iwai 7bb6d7
[    7.966046] Unable to handle kernel paging request at virtual
Takashi Iwai 7bb6d7
address bfe08002
Takashi Iwai 7bb6d7
[    7.973239] pgd = eebfc000
Takashi Iwai 7bb6d7
[    7.975931] [bfe08002] *pgd=2fffb811, *pte=00000000, *ppte=00000000
Takashi Iwai 7bb6d7
</snip>
Takashi Iwai 7bb6d7
Notice above how only 1 byte needed to be read but by reading 4 bytes
Takashi Iwai 7bb6d7
into the end of a mapped page, an unrecoverable page fault occurred.
Takashi Iwai 7bb6d7
Takashi Iwai 7bb6d7
This patch uses a temporary buffer to hold the 4 bytes read and then
Takashi Iwai 7bb6d7
copies only the bytes required into the buffer. A min() function is
Takashi Iwai 7bb6d7
used to limit the length to prevent buffer overflows.
Takashi Iwai 7bb6d7
Takashi Iwai 7bb6d7
Request testing of this patch on other platforms. This was tested
Takashi Iwai 7bb6d7
on the Intel Arria10 SoCFPGA DevKit.
Takashi Iwai 7bb6d7
Takashi Iwai 7bb6d7
Fixes: 0cf1725676a97fc8 ("mtd: spi-nor: cqspi: Fix build on arches missing readsl/writesl")
Takashi Iwai 7bb6d7
Signed-off-by: Thor Thayer <thor.thayer@linux.intel.com>
Takashi Iwai 7bb6d7
Cc: <stable@vger.kernel.org>
Takashi Iwai 7bb6d7
Reviewed-by: Marek Vasut <marek.vasut@gmail.com>
Takashi Iwai 7bb6d7
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Takashi Iwai 7bb6d7
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 7bb6d7
Takashi Iwai 7bb6d7
---
Takashi Iwai 7bb6d7
 drivers/mtd/spi-nor/cadence-quadspi.c |   19 +++++++++++++++++--
Takashi Iwai 7bb6d7
 1 file changed, 17 insertions(+), 2 deletions(-)
Takashi Iwai 7bb6d7
Takashi Iwai 7bb6d7
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
Takashi Iwai 7bb6d7
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
Takashi Iwai 7bb6d7
@@ -495,7 +495,9 @@ static int cqspi_indirect_read_execute(s
Takashi Iwai 7bb6d7
 	void __iomem *reg_base = cqspi->iobase;
Takashi Iwai 7bb6d7
 	void __iomem *ahb_base = cqspi->ahb_base;
Takashi Iwai 7bb6d7
 	unsigned int remaining = n_rx;
Takashi Iwai 7bb6d7
+	unsigned int mod_bytes = n_rx % 4;
Takashi Iwai 7bb6d7
 	unsigned int bytes_to_read = 0;
Takashi Iwai 7bb6d7
+	u8 *rxbuf_end = rxbuf + n_rx;
Takashi Iwai 7bb6d7
 	int ret = 0;
Takashi Iwai 7bb6d7
 
Takashi Iwai 7bb6d7
 	writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES);
Takashi Iwai 7bb6d7
@@ -523,11 +525,24 @@ static int cqspi_indirect_read_execute(s
Takashi Iwai 7bb6d7
 		}
Takashi Iwai 7bb6d7
 
Takashi Iwai 7bb6d7
 		while (bytes_to_read != 0) {
Takashi Iwai 7bb6d7
+			unsigned int word_remain = round_down(remaining, 4);
Takashi Iwai 7bb6d7
+
Takashi Iwai 7bb6d7
 			bytes_to_read *= cqspi->fifo_width;
Takashi Iwai 7bb6d7
 			bytes_to_read = bytes_to_read > remaining ?
Takashi Iwai 7bb6d7
 					remaining : bytes_to_read;
Takashi Iwai 7bb6d7
-			ioread32_rep(ahb_base, rxbuf,
Takashi Iwai 7bb6d7
-				     DIV_ROUND_UP(bytes_to_read, 4));
Takashi Iwai 7bb6d7
+			bytes_to_read = round_down(bytes_to_read, 4);
Takashi Iwai 7bb6d7
+			/* Read 4 byte word chunks then single bytes */
Takashi Iwai 7bb6d7
+			if (bytes_to_read) {
Takashi Iwai 7bb6d7
+				ioread32_rep(ahb_base, rxbuf,
Takashi Iwai 7bb6d7
+					     (bytes_to_read / 4));
Takashi Iwai 7bb6d7
+			} else if (!word_remain && mod_bytes) {
Takashi Iwai 7bb6d7
+				unsigned int temp = ioread32(ahb_base);
Takashi Iwai 7bb6d7
+
Takashi Iwai 7bb6d7
+				bytes_to_read = mod_bytes;
Takashi Iwai 7bb6d7
+				memcpy(rxbuf, &temp, min((unsigned int)
Takashi Iwai 7bb6d7
+							 (rxbuf_end - rxbuf),
Takashi Iwai 7bb6d7
+							 bytes_to_read));
Takashi Iwai 7bb6d7
+			}
Takashi Iwai 7bb6d7
 			rxbuf += bytes_to_read;
Takashi Iwai 7bb6d7
 			remaining -= bytes_to_read;
Takashi Iwai 7bb6d7
 			bytes_to_read = cqspi_get_rd_sram_level(cqspi);