diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2022-03-19 19:02:56 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2022-03-25 17:31:39 -0400 |
commit | 4cf36dfbf37edeb3f537296bb532008b8f206455 (patch) | |
tree | 43dec0bf1cd4f0f1cf59ce394fa3cfcd32cf41e5 | |
parent | 5b011c8dca39a082cd020d0713199b4e269c0270 (diff) | |
download | rockbox-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
-rw-r--r-- | bootloader/x1000/utils.c | 108 |
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, | |||
124 | struct nand_reader_data | 124 | struct 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 | ||
133 | static 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 | |||
131 | static ssize_t uimage_nand_reader(void* buf, size_t count, void* rctx) | 154 | static 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 | ||
146 | int load_uimage_flash(uint32_t addr, uint32_t length, | 200 | int 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 | ||
176 | int dump_flash(int fd, uint32_t addr, uint32_t length) | 234 | int dump_flash(int fd, uint32_t addr, uint32_t length) |