From 4cf36dfbf37edeb3f537296bb532008b8f206455 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sat, 19 Mar 2022 19:02:56 +0000 Subject: x1000: bootloader: skip bad blocks when loading flashed kernels Bad blocks in a kernel flash partition seem to be handled by skipping ahead to the next block; this is a common bad block management scheme for simple software like bootloaders (and is the default method for reading NAND partitions in u-boot). Extend the uImage flash reader to skip bad blocks while reading. Change-Id: I815875e83a2418e2642f736e04a3437c31b354ba --- bootloader/x1000/utils.c | 108 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 25 deletions(-) (limited to 'bootloader/x1000') diff --git a/bootloader/x1000/utils.c b/bootloader/x1000/utils.c index 837160a113..629a2c6280 100644 --- a/bootloader/x1000/utils.c +++ b/bootloader/x1000/utils.c @@ -124,53 +124,111 @@ int load_uimage_file(const char* filename, struct nand_reader_data { nand_drv* ndrv; - uint32_t addr; - uint32_t end_addr; + nand_page_t page; + nand_page_t end_page; + unsigned offset; + uint32_t count; }; +static int uimage_nand_reader_init(struct nand_reader_data* d, nand_drv* ndrv, + uint32_t addr, uint32_t length) +{ + unsigned pg_size = ndrv->chip->page_size; + + /* must start at a block address */ + if(addr % (pg_size << ndrv->chip->log2_ppb)) + return -1; + + d->ndrv = ndrv; + d->page = addr / ndrv->chip->page_size; + d->end_page = d->page + (length + pg_size - 1) / pg_size; + d->offset = 0; + d->count = length; + + if(d->end_page > ndrv->chip->nr_blocks << ndrv->chip->log2_ppb) + return -2; + + return 0; +} + static ssize_t uimage_nand_reader(void* buf, size_t count, void* rctx) { struct nand_reader_data* d = rctx; + nand_drv* ndrv = d->ndrv; + unsigned pg_size = ndrv->chip->page_size; + size_t read_count = 0; + int rc; + + /* truncate overlong reads */ + if(count > d->count) + count = d->count; + + while(d->page < d->end_page && read_count < count) { + rc = nand_page_read(ndrv, d->page, ndrv->page_buf); + if(rc < 0) + return -1; + + /* Check the first page of a block for the bad block marker. + * Any bad blocks are silently skipped. */ + if(!(d->page & (ndrv->ppb - 1))) { + if(ndrv->page_buf[ndrv->chip->bbm_pos] != 0xff) { + if(d->offset != 0) + return -1; /* shouldn't happen but just in case... */ + d->page += ndrv->ppb; + continue; + } + } - if(d->addr + count > d->end_addr) - count = d->end_addr - d->addr; + size_t copy_len = MIN(count - read_count, pg_size - d->offset); + memcpy(buf, &ndrv->page_buf[d->offset], copy_len); - int ret = nand_read_bytes(d->ndrv, d->addr, count, buf); - if(ret != NAND_SUCCESS) - return -1; + /* this seems like an excessive amount of arithmetic... */ + buf += copy_len; + read_count += copy_len; + d->count -= copy_len; - d->addr += count; - return count; + d->offset += copy_len; + if(d->offset >= pg_size) { + d->offset -= pg_size; + d->page++; + } + } + + return read_count; } int load_uimage_flash(uint32_t addr, uint32_t length, struct uimage_header* uh, size_t* sizep) { - int handle = -1; - - struct nand_reader_data n; - n.ndrv = nand_init(); - n.addr = addr; - n.end_addr = addr + length; - - nand_lock(n.ndrv); - if(nand_open(n.ndrv) != NAND_SUCCESS) { + nand_drv* ndrv = nand_init(); + nand_lock(ndrv); + if(nand_open(ndrv) != NAND_SUCCESS) { splashf(5*HZ, "NAND open failed"); - nand_unlock(n.ndrv); + nand_unlock(ndrv); return -1; } - handle = uimage_load(uh, sizep, uimage_nand_reader, &n); - - nand_close(n.ndrv); - nand_unlock(n.ndrv); + struct nand_reader_data n; + int ret = uimage_nand_reader_init(&n, ndrv, addr, length); + if(ret != 0) { + splashf(5*HZ, "Bad image params\nAddr: %08lx\nLength: %lu", addr, length); + ret = -2; + goto out; + } + int handle = uimage_load(uh, sizep, uimage_nand_reader, &n); if(handle <= 0) { splashf(5*HZ, "uImage load failed (%d)", handle); - return -2; + ret = -3; + goto out; } - return handle; + ret = handle; + + out: + nand_close(ndrv); + nand_unlock(ndrv); + return ret; } int dump_flash(int fd, uint32_t addr, uint32_t length) -- cgit v1.2.3