diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/installer-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/installer-x1000.c | 326 |
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 | |||
29 | struct 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 | */ | ||
50 | static 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 | |||
63 | static 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 | */ | ||
68 | static 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 */ | ||
90 | static 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 | |||
113 | struct 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 | |||
125 | static 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 | |||
176 | static 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 | |||
190 | int 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 | |||
237 | int 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 | |||
275 | int 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 | |||
313 | const 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 | } | ||