summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/installer-x1000.c
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-07-11 02:32:05 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-07-11 15:39:50 +0100
commit84362141a0d1e33b29d12162caba7537dd5684b8 (patch)
treed966b33e6049cc54abe04d9b66ef0db794ebe948 /firmware/target/mips/ingenic_x1000/installer-x1000.c
parente9d228832ccbee7e486c45a51680c6f94ce1ab92 (diff)
downloadrockbox-84362141a0d1e33b29d12162caba7537dd5684b8.tar.gz
rockbox-84362141a0d1e33b29d12162caba7537dd5684b8.zip
x1000: Unified flash bootloader installer
Change-Id: Ib1d41d4e7d663ff8a21eb08108c13568f7408533
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/installer-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/installer-x1000.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.c b/firmware/target/mips/ingenic_x1000/installer-x1000.c
new file mode 100644
index 0000000000..617e6645b7
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/installer-x1000.c
@@ -0,0 +1,326 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "installer-x1000.h"
23#include "nand-x1000.h"
24#include "core_alloc.h"
25#include "file.h"
26#include "microtar.h"
27#include <stddef.h>
28
29struct update_part {
30 const char* filename;
31 size_t offset;
32 size_t length;
33};
34
35/* Parts of the flash to update. The offset and length are given in bytes,
36 * offset relative to start of flash. The region's new contents are given
37 * by the named file inside the update archive. If any file is missing, the
38 * update will fail. (gracefully! nothing is written unless the package has
39 * all its components)
40 *
41 * If the update file is smaller than the region size, unused space at the
42 * end of the region is padded with 0xff.
43 *
44 * NOTE: The current code assumes all parts are contiguous. The current
45 * update map fits in one eraseblock, but if it ever needs extending beyond
46 * that, better implement a bitmap to indicate which blocks need updating
47 * and which can be skipped. We don't want to erase and reprogram blocks
48 * for no good reason, it's bad for the flash lifespan.
49 */
50static const struct update_part updates[] = {
51 {
52 .filename = "spl." BOOTFILE_EXT,
53 .offset = 0,
54 .length = 12 * 1024,
55 },
56 {
57 .filename = "bootloader.ucl",
58 .offset = 0x6800,
59 .length = 102 * 1024,
60 },
61};
62
63static const int num_updates = sizeof(updates) / sizeof(struct update_part);
64
65/* calculate the offset and length of the update image; this is constant
66 * for a given target, based on the update parts and the NAND chip geometry.
67 */
68static void get_image_loc(nand_drv* ndrv, size_t* offptr, size_t* lenptr)
69{
70 size_t blk_size = ndrv->chip->page_size << ndrv->chip->log2_ppb;
71 size_t img_off = 0;
72 size_t img_len = 0;
73
74 /* calculate minimal image needed to contain all update blocks */
75 for(int i = 0; i < num_updates; ++i) {
76 img_len = MAX(img_len, updates[i].offset + updates[i].length);
77 img_off = MIN(img_off, updates[i].offset);
78 }
79
80 /* round everything to a multiple of the block size */
81 size_t r_off = blk_size * (img_off / blk_size);
82 size_t r_len = blk_size * ((img_len + img_off - r_off + blk_size - 1) / blk_size);
83
84 *offptr = r_off;
85 *lenptr = r_len;
86}
87
88/* Read in a single part of the update from the tarball, and patch it
89 * into the image */
90static int patch_part(mtar_t* tar, const struct update_part* part,
91 uint8_t* img_buf, size_t img_off)
92{
93 mtar_header_t h;
94 int rc = mtar_find(tar, part->filename, &h);
95 if(rc != MTAR_ESUCCESS)
96 return IERR_BAD_FORMAT;
97
98 if(h.type != 0 && h.type != MTAR_TREG)
99 return IERR_BAD_FORMAT;
100
101 if(h.size > part->length)
102 return IERR_BAD_FORMAT;
103
104 /* wipe the patched area, and read in the new data */
105 memset(&img_buf[part->offset - img_off], 0xff, part->length);
106 rc = mtar_read_data(tar, &img_buf[part->offset - img_off], h.size);
107 if(rc != MTAR_ESUCCESS)
108 return IERR_FILE_IO;
109
110 return IERR_SUCCESS;
111}
112
113struct updater {
114 int buf_hnd; /* core_alloc handle for our memory buffer */
115 size_t buf_len; /* sizeof the buffer */
116
117 uint8_t* img_buf;
118 size_t img_off; /* image address in flash */
119 size_t img_len; /* image length in flash = size of the buffer */
120
121 mtar_t* tar;
122 nand_drv* ndrv;
123};
124
125static int updater_init(struct updater* u)
126{
127 int rc;
128
129 /* initialize stuff correctly */
130 u->buf_hnd = -1;
131 u->buf_len = 0;
132 u->img_buf = NULL;
133 u->img_off = 0;
134 u->img_len = 0;
135 u->tar = NULL;
136 u->ndrv = NULL;
137
138 /* open NAND */
139 u->ndrv = nand_init();
140 nand_lock(u->ndrv);
141 rc = nand_open(u->ndrv);
142 if(rc != NAND_SUCCESS) {
143 rc = IERR_NAND_OPEN;
144 goto error;
145 }
146
147 get_image_loc(u->ndrv, &u->img_off, &u->img_len);
148
149 /* buf_len is a bit oversized here, but it's not really important */
150 u->buf_len = u->img_len + sizeof(mtar_t) + 2*CACHEALIGN_SIZE;
151 u->buf_hnd = core_alloc("boot_image", u->buf_len);
152 if(u->buf_hnd < 0) {
153 rc = IERR_OUT_OF_MEMORY;
154 goto error;
155 }
156
157 /* allocate from the buffer */
158 uint8_t* buffer = (uint8_t*)core_get_data(u->buf_hnd);
159 size_t buf_len = u->buf_len;
160
161 CACHEALIGN_BUFFER(buffer, buf_len);
162 u->img_buf = buffer;
163 buffer += u->img_len;
164 buf_len -= u->img_len;
165
166 CACHEALIGN_BUFFER(buffer, buf_len);
167 u->tar = (mtar_t*)buffer;
168 memset(u->tar, 0, sizeof(struct mtar_t));
169
170 rc = IERR_SUCCESS;
171
172 error:
173 return rc;
174}
175
176static void updater_cleanup(struct updater* u)
177{
178 if(u->tar && u->tar->close)
179 mtar_close(u->tar);
180
181 if(u->buf_hnd >= 0)
182 core_free(u->buf_hnd);
183
184 if(u->ndrv) {
185 nand_close(u->ndrv);
186 nand_unlock(u->ndrv);
187 }
188}
189
190int install_bootloader(const char* filename)
191{
192 struct updater u;
193 int rc = updater_init(&u);
194 if(rc != IERR_SUCCESS)
195 goto error;
196
197 /* get the image */
198 rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
199 if(rc != NAND_SUCCESS) {
200 rc = IERR_NAND_READ;
201 goto error;
202 }
203
204 /* get the tarball */
205 rc = mtar_open(u.tar, filename, "r");
206 if(rc != MTAR_ESUCCESS) {
207 if(rc == MTAR_EOPENFAIL)
208 rc = IERR_FILE_NOT_FOUND;
209 else if(rc == MTAR_EREADFAIL)
210 rc = IERR_FILE_IO;
211 else
212 rc = IERR_BAD_FORMAT;
213 goto error;
214 }
215
216 /* patch stuff */
217 for(int i = 0; i < num_updates; ++i) {
218 rc = patch_part(u.tar, &updates[i], u.img_buf, u.img_off);
219 if(rc != IERR_SUCCESS)
220 goto error;
221 }
222
223 /* write back the patched image */
224 rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
225 if(rc != NAND_SUCCESS) {
226 rc = IERR_NAND_WRITE;
227 goto error;
228 }
229
230 rc = IERR_SUCCESS;
231
232 error:
233 updater_cleanup(&u);
234 return rc;
235}
236
237int backup_bootloader(const char* filename)
238{
239 int rc, fd = 0;
240 struct updater u;
241
242 rc = updater_init(&u);
243 if(rc != IERR_SUCCESS)
244 goto error;
245
246 /* read image */
247 rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
248 if(rc != NAND_SUCCESS) {
249 rc = IERR_NAND_READ;
250 goto error;
251 }
252
253 /* write to file */
254 fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY);
255 if(fd < 0) {
256 rc = IERR_FILE_IO;
257 goto error;
258 }
259
260 ssize_t cnt = write(fd, u.img_buf, u.img_len);
261 if(cnt < 0 || (size_t)cnt != u.img_len) {
262 rc = IERR_FILE_IO;
263 goto error;
264 }
265
266 rc = IERR_SUCCESS;
267
268 error:
269 if(fd >= 0)
270 close(fd);
271 updater_cleanup(&u);
272 return rc;
273}
274
275int restore_bootloader(const char* filename)
276{
277 int rc, fd = 0;
278 struct updater u;
279
280 rc = updater_init(&u);
281 if(rc != IERR_SUCCESS)
282 goto error;
283
284 /* read from file */
285 fd = open(filename, O_RDONLY);
286 if(fd < 0) {
287 rc = IERR_FILE_NOT_FOUND;
288 goto error;
289 }
290
291 ssize_t cnt = read(fd, u.img_buf, u.img_len);
292 if(cnt < 0 || (size_t)cnt != u.img_len) {
293 rc = IERR_FILE_IO;
294 goto error;
295 }
296
297 /* write image */
298 rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
299 if(rc != NAND_SUCCESS) {
300 rc = IERR_NAND_WRITE;
301 goto error;
302 }
303
304 rc = IERR_SUCCESS;
305
306 error:
307 if(fd >= 0)
308 close(fd);
309 updater_cleanup(&u);
310 return rc;
311}
312
313const char* installer_strerror(int rc)
314{
315 switch(rc) {
316 case IERR_SUCCESS: return "Success";
317 case IERR_OUT_OF_MEMORY: return "Out of memory";
318 case IERR_FILE_NOT_FOUND: return "File not found";
319 case IERR_FILE_IO: return "Disk I/O error";
320 case IERR_BAD_FORMAT: return "Bad archive";
321 case IERR_NAND_OPEN: return "NAND open error";
322 case IERR_NAND_READ: return "NAND read error";
323 case IERR_NAND_WRITE: return "NAND write error";
324 default: return "Unknown error!?";
325 }
326}