diff mbox series

[2/2] mtd: rawnand: nand_spl_loaders: allow partial nand page reads during nand_spl_load_image

Message ID 20230510002034.1673517-3-colin.foster@in-advantage.com
State New
Delegated to: Dario Binacchi
Headers show
Series fix partial spl nand reads | expand

Commit Message

Colin Foster May 10, 2023, 12:20 a.m. UTC
The nand_spl_load_image function was guaranteed to read an entire block
into RAM, regardless of how many bytes were to be read. This is
particularly problematic when spl_load_legacy_image is called, as this
function attempts to load a struct image_header but gets surprised with
a full flash sector.

Allow partial reads to restore functionality to the code path
spl_nand_load_element() -> spl_load_legacy_img() ->
spl_nand_legacy_read(load, header, sizeof(hdr), header);

Signed-off-by: Colin Foster <colin.foster@in-advantage.com>
Reviewed-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---

v2:
    Add Dario reviewed tag
    Use existing heap instead of .bss buffer
    Rename "size_remaining" to "size_left" which helps alignment
    Change commit prefix to "mtd: rawnand: nand_spl_loaders:" to match
    the most recent commit

---
 drivers/mtd/nand/raw/nand_spl_loaders.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/drivers/mtd/nand/raw/nand_spl_loaders.c b/drivers/mtd/nand/raw/nand_spl_loaders.c
index 8f243ce1b6..708cdc1587 100644
--- a/drivers/mtd/nand/raw/nand_spl_loaders.c
+++ b/drivers/mtd/nand/raw/nand_spl_loaders.c
@@ -8,10 +8,17 @@  static u8 *scratch_buf;
 
 int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
 {
+	unsigned int read_this_time = CONFIG_SYS_NAND_PAGE_SIZE;
+	unsigned int size_left = size;
 	unsigned int block, lastblock;
 	unsigned int page, page_offset;
 
-	/* offs has to be aligned to a page address! */
+	if (!scratch_buf)
+		scratch_buf = malloc(CONFIG_SYS_NAND_PAGE_SIZE);
+
+	if (!scratch_buf)
+		return -ENOMEM;
+
 	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
 	lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
 	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
@@ -20,8 +27,16 @@  int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
 	while (block <= lastblock) {
 		if (!nand_is_bad_block(block)) {
 			/* Skip bad blocks */
-			while (page < CONFIG_SYS_NAND_PAGE_COUNT) {
-				nand_read_page(block, page, dst);
+			while (page < CONFIG_SYS_NAND_PAGE_COUNT &&
+			       size_left > 0) {
+				if (size_left < CONFIG_SYS_NAND_PAGE_SIZE) {
+					nand_read_page(block, page,
+						       scratch_buf);
+					memcpy(dst, scratch_buf, size_left);
+					read_this_time = size_left;
+				} else {
+					nand_read_page(block, page, dst);
+				}
 				/*
 				 * When offs is not aligned to page address the
 				 * extra offset is copied to dst as well. Copy
@@ -34,7 +49,8 @@  int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
 					dst = (void *)(dst - page_offset);
 					page_offset = 0;
 				}
-				dst += CONFIG_SYS_NAND_PAGE_SIZE;
+				dst += read_this_time;
+				size_left -= read_this_time;
 				page++;
 			}