summaryrefslogtreecommitdiff
path: root/bootloader
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-03-19 19:02:56 +0000
committerAidan MacDonald <amachronic@protonmail.com>2022-03-25 17:31:39 -0400
commit4cf36dfbf37edeb3f537296bb532008b8f206455 (patch)
tree43dec0bf1cd4f0f1cf59ce394fa3cfcd32cf41e5 /bootloader
parent5b011c8dca39a082cd020d0713199b4e269c0270 (diff)
downloadrockbox-4cf36dfbf37edeb3f537296bb532008b8f206455.tar.gz
rockbox-4cf36dfbf37edeb3f537296bb532008b8f206455.zip
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
Diffstat (limited to 'bootloader')
-rw-r--r--bootloader/x1000/utils.c108
1 files changed, 83 insertions, 25 deletions
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,
124struct nand_reader_data 124struct nand_reader_data
125{ 125{
126 nand_drv* ndrv; 126 nand_drv* ndrv;
127 uint32_t addr; 127 nand_page_t page;
128 uint32_t end_addr; 128 nand_page_t end_page;
129 unsigned offset;
130 uint32_t count;
129}; 131};
130 132
133static int uimage_nand_reader_init(struct nand_reader_data* d, nand_drv* ndrv,
134 uint32_t addr, uint32_t length)
135{
136 unsigned pg_size = ndrv->chip->page_size;
137
138 /* must start at a block address */
139 if(addr % (pg_size << ndrv->chip->log2_ppb))
140 return -1;
141
142 d->ndrv = ndrv;
143 d->page = addr / ndrv->chip->page_size;
144 d->end_page = d->page + (length + pg_size - 1) / pg_size;
145 d->offset = 0;
146 d->count = length;
147
148 if(d->end_page > ndrv->chip->nr_blocks << ndrv->chip->log2_ppb)
149 return -2;
150
151 return 0;
152}
153
131static ssize_t uimage_nand_reader(void* buf, size_t count, void* rctx) 154static ssize_t uimage_nand_reader(void* buf, size_t count, void* rctx)
132{ 155{
133 struct nand_reader_data* d = rctx; 156 struct nand_reader_data* d = rctx;
157 nand_drv* ndrv = d->ndrv;
158 unsigned pg_size = ndrv->chip->page_size;
159 size_t read_count = 0;
160 int rc;
161
162 /* truncate overlong reads */
163 if(count > d->count)
164 count = d->count;
165
166 while(d->page < d->end_page && read_count < count) {
167 rc = nand_page_read(ndrv, d->page, ndrv->page_buf);
168 if(rc < 0)
169 return -1;
170
171 /* Check the first page of a block for the bad block marker.
172 * Any bad blocks are silently skipped. */
173 if(!(d->page & (ndrv->ppb - 1))) {
174 if(ndrv->page_buf[ndrv->chip->bbm_pos] != 0xff) {
175 if(d->offset != 0)
176 return -1; /* shouldn't happen but just in case... */
177 d->page += ndrv->ppb;
178 continue;
179 }
180 }
134 181
135 if(d->addr + count > d->end_addr) 182 size_t copy_len = MIN(count - read_count, pg_size - d->offset);
136 count = d->end_addr - d->addr; 183 memcpy(buf, &ndrv->page_buf[d->offset], copy_len);
137 184
138 int ret = nand_read_bytes(d->ndrv, d->addr, count, buf); 185 /* this seems like an excessive amount of arithmetic... */
139 if(ret != NAND_SUCCESS) 186 buf += copy_len;
140 return -1; 187 read_count += copy_len;
188 d->count -= copy_len;
141 189
142 d->addr += count; 190 d->offset += copy_len;
143 return count; 191 if(d->offset >= pg_size) {
192 d->offset -= pg_size;
193 d->page++;
194 }
195 }
196
197 return read_count;
144} 198}
145 199
146int load_uimage_flash(uint32_t addr, uint32_t length, 200int load_uimage_flash(uint32_t addr, uint32_t length,
147 struct uimage_header* uh, size_t* sizep) 201 struct uimage_header* uh, size_t* sizep)
148{ 202{
149 int handle = -1; 203 nand_drv* ndrv = nand_init();
150 204 nand_lock(ndrv);
151 struct nand_reader_data n; 205 if(nand_open(ndrv) != NAND_SUCCESS) {
152 n.ndrv = nand_init();
153 n.addr = addr;
154 n.end_addr = addr + length;
155
156 nand_lock(n.ndrv);
157 if(nand_open(n.ndrv) != NAND_SUCCESS) {
158 splashf(5*HZ, "NAND open failed"); 206 splashf(5*HZ, "NAND open failed");
159 nand_unlock(n.ndrv); 207 nand_unlock(ndrv);
160 return -1; 208 return -1;
161 } 209 }
162 210
163 handle = uimage_load(uh, sizep, uimage_nand_reader, &n); 211 struct nand_reader_data n;
164 212 int ret = uimage_nand_reader_init(&n, ndrv, addr, length);
165 nand_close(n.ndrv); 213 if(ret != 0) {
166 nand_unlock(n.ndrv); 214 splashf(5*HZ, "Bad image params\nAddr: %08lx\nLength: %lu", addr, length);
215 ret = -2;
216 goto out;
217 }
167 218
219 int handle = uimage_load(uh, sizep, uimage_nand_reader, &n);
168 if(handle <= 0) { 220 if(handle <= 0) {
169 splashf(5*HZ, "uImage load failed (%d)", handle); 221 splashf(5*HZ, "uImage load failed (%d)", handle);
170 return -2; 222 ret = -3;
223 goto out;
171 } 224 }
172 225
173 return handle; 226 ret = handle;
227
228 out:
229 nand_close(ndrv);
230 nand_unlock(ndrv);
231 return ret;
174} 232}
175 233
176int dump_flash(int fd, uint32_t addr, uint32_t length) 234int dump_flash(int fd, uint32_t addr, uint32_t length)