diff options
author | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-15 21:04:28 +0100 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-24 18:05:53 +0100 |
commit | c876d3bbefe0dc00c27ca0c12d29da5874946962 (patch) | |
tree | 69f468a185a369b01998314bc3ecc19b70f4fcaa /utils/mkamsboot/mkamsboot.c | |
parent | 6c6f0757d7a902feb293be165d1490c42bc8e7ad (diff) | |
download | rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.tar.gz rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.zip |
rbutil: Merge rbutil with utils folder.
rbutil uses several components from the utils folder, and can be
considered part of utils too. Having it in a separate folder is an
arbitrary split that doesn't help anymore these days, so merge them.
This also allows other utils to easily use libtools.make without the
need to navigate to a different folder.
Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21
Diffstat (limited to 'utils/mkamsboot/mkamsboot.c')
-rw-r--r-- | utils/mkamsboot/mkamsboot.c | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/utils/mkamsboot/mkamsboot.c b/utils/mkamsboot/mkamsboot.c new file mode 100644 index 0000000000..09eb15ee43 --- /dev/null +++ b/utils/mkamsboot/mkamsboot.c | |||
@@ -0,0 +1,595 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * mkamsboot.c - a tool for merging bootloader code into an Sansa V2 | ||
11 | * (AMS) firmware file | ||
12 | * | ||
13 | * Copyright (C) 2008 Dave Chapman | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | |||
25 | /* | ||
26 | |||
27 | Insert a Rockbox bootloader into a Sansa AMS original firmware file. | ||
28 | |||
29 | Layout of a Sansa AMS original firmware file: | ||
30 | |||
31 | ---------------------- 0x0 | ||
32 | | HEADER | | ||
33 | |----------------------| 0x400 | ||
34 | | FIRMWARE BLOCK | (contains the OF version on some fuzev2 firmwares | ||
35 | |----------------------| 0x600 | ||
36 | | FIRMWARE BLOCK | | ||
37 | |----------------------| 0x400 + firmware block size | ||
38 | | LIBRARIES/DATA | | ||
39 | ---------------------- END | ||
40 | |||
41 | We replace the main firmware block while preserving the potential OF version | ||
42 | (bytes 0x600..0x400+firmware_size) as follows: | ||
43 | |||
44 | |||
45 | ---------------------- 0x0 | ||
46 | | | | ||
47 | | Dual-boot code | | ||
48 | | | | ||
49 | |----------------------| | ||
50 | | EMPTY SPACE | | ||
51 | |----------------------| | ||
52 | | | | ||
53 | | compressed RB image | | ||
54 | | | | ||
55 | |----------------------| | ||
56 | | | | ||
57 | | compressed OF image | | ||
58 | | | | ||
59 | |----------------------| | ||
60 | | UCL unpack function | | ||
61 | ---------------------- | ||
62 | |||
63 | This entire block fits into the space previously occupied by the main | ||
64 | firmware block - the space saved by compressing the OF image is used | ||
65 | to store the compressed version of the Rockbox bootloader. | ||
66 | |||
67 | On version 1 firmwares, the OF image is typically about 120KB, which allows | ||
68 | us to store a Rockbox bootloader with an uncompressed size of about 60KB-70KB. | ||
69 | Version 2 firmwares are bigger and are stored in SDRAM (instead of IRAM). | ||
70 | In both cases, the RAM we are using is mapped at offset 0x0. | ||
71 | |||
72 | mkamsboot then corrects the checksums and writes a new legal firmware | ||
73 | file which can be installed on the device. | ||
74 | |||
75 | When the Sansa device boots, this firmware block is loaded to RAM at | ||
76 | address 0x0 and executed. | ||
77 | |||
78 | Firstly, the dual-boot code will copy the UCL unpack function to the | ||
79 | end of RAM. | ||
80 | |||
81 | Then, depending on the detection of the dual-boot keypress, either the | ||
82 | OF image or the Rockbox image is copied to the end of RAM (just before | ||
83 | the ucl unpack function) and uncompressed to the start of RAM. | ||
84 | |||
85 | Finally, the ucl unpack function branches to address 0x0, passing | ||
86 | execution to the uncompressed firmware. | ||
87 | |||
88 | |||
89 | */ | ||
90 | |||
91 | #include <stdio.h> | ||
92 | #include <stdlib.h> | ||
93 | #include <stdint.h> | ||
94 | #include <sys/types.h> | ||
95 | #include <sys/stat.h> | ||
96 | #include <fcntl.h> | ||
97 | #include <unistd.h> | ||
98 | #include <string.h> | ||
99 | |||
100 | #include <ucl/ucl.h> | ||
101 | |||
102 | #include "mkamsboot.h" | ||
103 | |||
104 | #include "md5.h" | ||
105 | |||
106 | /* Header for ARM code binaries */ | ||
107 | #include "dualboot.h" | ||
108 | |||
109 | /* Win32 compatibility */ | ||
110 | #ifndef O_BINARY | ||
111 | #define O_BINARY 0 | ||
112 | #endif | ||
113 | |||
114 | /* fw_revision: version 2 is used in Clipv2, Clip+ and Fuzev2 firmwares */ | ||
115 | /* hw_revision: 4 for m200, 2 for e200/c200, 1 or 2 for fuze/clip, 1 for clip+ */ | ||
116 | const struct ams_models ams_identity[] = { | ||
117 | [MODEL_C200V2] = { 2, 1, "c200", dualboot_c200v2, sizeof(dualboot_c200v2), "c2v2", 44 }, | ||
118 | [MODEL_CLIPPLUS]= { 1, 2, "Clip+", dualboot_clipplus, sizeof(dualboot_clipplus), "cli+", 66 }, | ||
119 | [MODEL_CLIPV2] = { 2, 2, "Clip", dualboot_clipv2, sizeof(dualboot_clipv2), "clv2", 60 }, | ||
120 | [MODEL_CLIP] = { 1, 1, "Clip", dualboot_clip, sizeof(dualboot_clip), "clip", 40 }, | ||
121 | [MODEL_E200V2] = { 2, 1, "e200", dualboot_e200v2, sizeof(dualboot_e200v2), "e2v2", 41 }, | ||
122 | [MODEL_FUZEV2] = { 2, 2, "Fuze", dualboot_fuzev2, sizeof(dualboot_fuzev2), "fuz2", 68 }, | ||
123 | [MODEL_FUZE] = { 1, 1, "Fuze", dualboot_fuze, sizeof(dualboot_fuze), "fuze", 43 }, | ||
124 | [MODEL_M200V4] = { 4, 1, "m200", dualboot_m200v4, sizeof(dualboot_m200v4), "m2v4", 42 }, | ||
125 | [MODEL_CLIPZIP] = { 1, 2, "ClipZip", dualboot_clipzip, sizeof(dualboot_clipzip), "clzp", 79 }, | ||
126 | }; | ||
127 | |||
128 | |||
129 | /* Checksums of unmodified original firmwares - for safety, and device | ||
130 | detection */ | ||
131 | static struct md5sums sansasums[] = { | ||
132 | /* NOTE: Different regional versions of the firmware normally only | ||
133 | differ in the filename - the md5sums are identical */ | ||
134 | |||
135 | /* model version md5 */ | ||
136 | { MODEL_E200V2, "3.01.11", "e622ca8cb6df423f54b8b39628a1f0a3" }, | ||
137 | { MODEL_E200V2, "3.01.14", "2c1d0383fc3584b2cc83ba8cc2243af6" }, | ||
138 | { MODEL_E200V2, "3.01.16", "12563ad71b25a1034cf2092d1e0218c4" }, | ||
139 | |||
140 | { MODEL_FUZE, "1.01.11", "cac8ffa03c599330ac02c4d41de66166" }, | ||
141 | { MODEL_FUZE, "1.01.15", "df0e2c1612727f722c19a3c764cff7f2" }, | ||
142 | { MODEL_FUZE, "1.01.22", "5aff5486fe8dd64239cc71eac470af98" }, | ||
143 | { MODEL_FUZE, "1.02.26", "7c632c479461c48c8833baed74eb5e4f" }, | ||
144 | { MODEL_FUZE, "1.02.28", "5b34260f6470e75f702a9c6825471752" }, | ||
145 | { MODEL_FUZE, "1.02.31", "66d01b37462a5ef7ccc6ad37188b4235" }, | ||
146 | |||
147 | { MODEL_C200V2, "3.02.05", "b6378ebd720b0ade3fad4dc7ab61c1a5" }, | ||
148 | |||
149 | { MODEL_M200V4, "4.00.45", "82e3194310d1514e3bbcd06e84c4add3" }, | ||
150 | { MODEL_M200V4, "4.01.08-A", "fc9dd6116001b3e6a150b898f1b091f0" }, | ||
151 | { MODEL_M200V4, "4.01.08-E", "d3fb7d8ec8624ee65bc99f8dab0e2369" }, | ||
152 | |||
153 | { MODEL_CLIP, "1.01.17", "12caad785d506219d73f538772afd99e" }, | ||
154 | { MODEL_CLIP, "1.01.18", "d720b266bd5afa38a198986ef0508a45" }, | ||
155 | { MODEL_CLIP, "1.01.20", "236d8f75189f468462c03f6d292cf2ac" }, | ||
156 | { MODEL_CLIP, "1.01.29", "c12711342169c66e209540cd1f27cd26" }, | ||
157 | { MODEL_CLIP, "1.01.30", "f2974d47c536549c9d8259170f1dbe4d" }, | ||
158 | { MODEL_CLIP, "1.01.32", "d835d12342500732ffb9c4ee54abec15" }, | ||
159 | { MODEL_CLIP, "1.01.35", "b4d0edb3b8f2a4e8eee0a344f7f8e480" }, | ||
160 | |||
161 | { MODEL_CLIPV2, "2.01.16", "c57fb3fcbe07c2c9b360f060938f80cb" }, | ||
162 | { MODEL_CLIPV2, "2.01.32", "0ad3723e52022509089d938d0fbbf8c5" }, | ||
163 | { MODEL_CLIPV2, "2.01.35", "a3cbbd22b9508d7f8a9a1a39acc342c2" }, | ||
164 | |||
165 | { MODEL_CLIPPLUS, "01.02.09", "656d38114774c2001dc18e6726df3c5d" }, | ||
166 | { MODEL_CLIPPLUS, "01.02.13", "5f89872b79ef440b0e5ee3a7a44328b2" }, | ||
167 | { MODEL_CLIPPLUS, "01.02.15", "680a4f521e790ad25b93b1b16f3a207d" }, | ||
168 | { MODEL_CLIPPLUS, "01.02.16", "055a53de1dfb09f6cb71c504ad48bd13" }, | ||
169 | { MODEL_CLIPPLUS, "01.02.18", "80b547244438b113e2a55ff0305f12c0" }, | ||
170 | |||
171 | { MODEL_FUZEV2, "2.01.17", "8b85fb05bf645d08a4c8c3e344ec9ebe" }, | ||
172 | { MODEL_FUZEV2, "2.02.26", "d4f6f85c3e4a8ea8f2e5acc421641801" }, | ||
173 | { MODEL_FUZEV2, "2.03.31", "74fb197ccd51707388f3b233402186a6" }, | ||
174 | { MODEL_FUZEV2, "2.03.33", "1599cc73d02ea7fe53fe2d4379c24b66" }, | ||
175 | |||
176 | { MODEL_CLIPZIP, "1.01.12", "45adea0873326b5af34f096e5c402f78" }, | ||
177 | { MODEL_CLIPZIP, "1.01.15", "f62af954334cd9ba1a87a7fa58ec6074" }, | ||
178 | { MODEL_CLIPZIP, "1.01.17", "27bcb343d6950f35dc261629e22ba60c" }, | ||
179 | { MODEL_CLIPZIP, "1.01.18", "ef16aa9e02b49885ebede5aa149502e8" }, | ||
180 | { MODEL_CLIPZIP, "1.01.20", "d88c8977cc6a952d3f51ece105869d97" }, | ||
181 | { MODEL_CLIPZIP, "1.01.21", "92c814d6e3250189706a36d2b49b6152" }, | ||
182 | }; | ||
183 | |||
184 | #define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0])) | ||
185 | |||
186 | static unsigned int model_memory_size(int model) | ||
187 | { | ||
188 | /* The OF boots with IRAM (320kB) mapped at 0x0 */ | ||
189 | |||
190 | if(model == MODEL_CLIPV2) | ||
191 | { | ||
192 | /* The decompressed Clipv2 OF is around 380kB. | ||
193 | * Let's use the full IRAM (1MB on AMSv2) | ||
194 | */ | ||
195 | return 1 << 20; | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | /* The IRAM is 320kB on AMSv1, and 320 will be enough on Fuzev1/Clip+ */ | ||
200 | return 320 << 10; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | int firmware_revision(int model) | ||
205 | { | ||
206 | return ams_identity[model].fw_revision; | ||
207 | } | ||
208 | |||
209 | static off_t filesize(int fd) | ||
210 | { | ||
211 | struct stat buf; | ||
212 | |||
213 | if (fstat(fd, &buf) < 0) { | ||
214 | perror("[ERR] Checking filesize of input file"); | ||
215 | return -1; | ||
216 | } else { | ||
217 | return(buf.st_size); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static uint32_t get_uint32le(unsigned char* p) | ||
222 | { | ||
223 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
224 | } | ||
225 | |||
226 | static uint32_t get_uint32be(unsigned char* p) | ||
227 | { | ||
228 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; | ||
229 | } | ||
230 | |||
231 | static void put_uint32le(unsigned char* p, uint32_t x) | ||
232 | { | ||
233 | p[0] = x & 0xff; | ||
234 | p[1] = (x >> 8) & 0xff; | ||
235 | p[2] = (x >> 16) & 0xff; | ||
236 | p[3] = (x >> 24) & 0xff; | ||
237 | } | ||
238 | |||
239 | void calc_MD5(unsigned char* buf, int len, char *md5str) | ||
240 | { | ||
241 | int i; | ||
242 | md5_context ctx; | ||
243 | unsigned char md5sum[16]; | ||
244 | |||
245 | md5_starts(&ctx); | ||
246 | md5_update(&ctx, buf, len); | ||
247 | md5_finish(&ctx, md5sum); | ||
248 | |||
249 | for (i = 0; i < 16; ++i) | ||
250 | sprintf(md5str + 2*i, "%02x", md5sum[i]); | ||
251 | } | ||
252 | |||
253 | /* Calculate a simple checksum used in Sansa Original Firmwares */ | ||
254 | static uint32_t calc_checksum(unsigned char* buf, uint32_t n) | ||
255 | { | ||
256 | uint32_t sum = 0; | ||
257 | uint32_t i; | ||
258 | |||
259 | for (i=0;i<n;i+=4) | ||
260 | sum += get_uint32le(buf + i); | ||
261 | |||
262 | return sum; | ||
263 | } | ||
264 | |||
265 | /* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */ | ||
266 | static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize) | ||
267 | { | ||
268 | int maxsize; | ||
269 | unsigned char* outbuf; | ||
270 | int r; | ||
271 | |||
272 | /* The following formula comes from the UCL documentation */ | ||
273 | maxsize = insize + (insize / 8) + 256; | ||
274 | |||
275 | /* Allocate some memory for the output buffer */ | ||
276 | outbuf = malloc(maxsize); | ||
277 | |||
278 | if (outbuf == NULL) | ||
279 | return NULL; | ||
280 | |||
281 | r = ucl_nrv2e_99_compress( | ||
282 | (const ucl_bytep) inbuf, | ||
283 | (ucl_uint) insize, | ||
284 | (ucl_bytep) outbuf, | ||
285 | (ucl_uintp) outsize, | ||
286 | 0, 10, NULL, NULL); | ||
287 | |||
288 | if (r != UCL_E_OK || *outsize > maxsize) { | ||
289 | /* this should NEVER happen, and implies memory corruption */ | ||
290 | fprintf(stderr, "internal error - compression failed: %d\n", r); | ||
291 | free(outbuf); | ||
292 | return NULL; | ||
293 | } | ||
294 | |||
295 | return outbuf; | ||
296 | } | ||
297 | |||
298 | #define ERROR(format, ...) \ | ||
299 | do { \ | ||
300 | snprintf(errstr, errstrsize, format, __VA_ARGS__); \ | ||
301 | goto error; \ | ||
302 | } while(0) | ||
303 | |||
304 | /* Loads a Sansa AMS Original Firmware file into memory */ | ||
305 | unsigned char* load_of_file( | ||
306 | char* filename, int model, off_t* bufsize, struct md5sums *sum, | ||
307 | int* firmware_size, unsigned char** of_packed, | ||
308 | int* of_packedsize, char* errstr, int errstrsize) | ||
309 | { | ||
310 | int fd; | ||
311 | unsigned char* buf =NULL; | ||
312 | off_t n; | ||
313 | unsigned int i=0; | ||
314 | uint32_t checksum; | ||
315 | unsigned int last_word; | ||
316 | |||
317 | fd = open(filename, O_RDONLY|O_BINARY); | ||
318 | if (fd < 0) | ||
319 | ERROR("[ERR] Could not open %s for reading\n", filename); | ||
320 | |||
321 | *bufsize = filesize(fd); | ||
322 | |||
323 | buf = malloc(*bufsize); | ||
324 | if (buf == NULL) | ||
325 | ERROR("[ERR] Could not allocate memory for %s\n", filename); | ||
326 | |||
327 | n = read(fd, buf, *bufsize); | ||
328 | |||
329 | if (n != *bufsize) | ||
330 | ERROR("[ERR] Could not read file %s\n", filename); | ||
331 | |||
332 | /* check the file */ | ||
333 | |||
334 | /* Calculate MD5 checksum of OF */ | ||
335 | calc_MD5(buf, *bufsize, sum->md5); | ||
336 | |||
337 | while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, sum->md5) != 0)) | ||
338 | i++; | ||
339 | |||
340 | if (i < NUM_MD5S) { | ||
341 | *sum = sansasums[i]; | ||
342 | if(sum->model != model) { | ||
343 | ERROR("[ERR] OF File provided is %sv%d version %s, not for %sv%d\n", | ||
344 | ams_identity[sum->model].model_name, ams_identity[sum->model].hw_revision, | ||
345 | sum->version, ams_identity[model].model_name, ams_identity[model].hw_revision | ||
346 | ); | ||
347 | } | ||
348 | } else { | ||
349 | /* OF unknown, give a list of tested versions for the requested model */ | ||
350 | |||
351 | char tested_versions[100]; | ||
352 | tested_versions[0] = '\0'; | ||
353 | |||
354 | for (i = 0; i < NUM_MD5S ; i++) | ||
355 | if (sansasums[i].model == model) { | ||
356 | if (tested_versions[0] != '\0') { | ||
357 | strncat(tested_versions, ", ", | ||
358 | sizeof(tested_versions) - strlen(tested_versions) - 1); | ||
359 | } | ||
360 | strncat(tested_versions, sansasums[i].version, | ||
361 | sizeof(tested_versions) - strlen(tested_versions) - 1); | ||
362 | } | ||
363 | |||
364 | ERROR("[ERR] Original firmware unknown, please try another version." | ||
365 | " Tested %sv%d versions are: %s\n", | ||
366 | ams_identity[model].model_name, ams_identity[model].hw_revision, tested_versions); | ||
367 | } | ||
368 | |||
369 | /* TODO: Do some more sanity checks on the OF image. Some images (like | ||
370 | m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */ | ||
371 | last_word = *bufsize - 4; | ||
372 | checksum = get_uint32le(buf + last_word); | ||
373 | if (checksum != 0xefbeadde && checksum != calc_checksum(buf, last_word)) | ||
374 | ERROR("%s", "[ERR] Whole file checksum failed\n"); | ||
375 | |||
376 | if (ams_identity[sum->model].bootloader == NULL) | ||
377 | ERROR("[ERR] Unsupported model - \"%s\"\n", ams_identity[sum->model].model_name); | ||
378 | |||
379 | /* Get the firmware size */ | ||
380 | if (ams_identity[sum->model].fw_revision == 1) | ||
381 | *firmware_size = get_uint32le(&buf[0x0c]); | ||
382 | else if (ams_identity[sum->model].fw_revision == 2) | ||
383 | *firmware_size = get_uint32le(&buf[0x10]); | ||
384 | |||
385 | /* Compress the original firmware image */ | ||
386 | *of_packed = uclpack(buf + 0x400, *firmware_size, of_packedsize); | ||
387 | if (*of_packed == NULL) | ||
388 | ERROR("[ERR] Could not compress %s\n", filename); | ||
389 | |||
390 | return buf; | ||
391 | |||
392 | error: | ||
393 | free(buf); | ||
394 | return NULL; | ||
395 | } | ||
396 | |||
397 | /* Loads a rockbox bootloader file into memory */ | ||
398 | unsigned char* load_rockbox_file( | ||
399 | char* filename, int *model, int* bufsize, int* rb_packedsize, | ||
400 | char* errstr, int errstrsize) | ||
401 | { | ||
402 | int fd; | ||
403 | unsigned char* buf = NULL; | ||
404 | unsigned char* packed = NULL; | ||
405 | unsigned char header[8]; | ||
406 | uint32_t sum; | ||
407 | off_t n; | ||
408 | int i; | ||
409 | |||
410 | fd = open(filename, O_RDONLY|O_BINARY); | ||
411 | if (fd < 0) | ||
412 | ERROR("[ERR] Could not open %s for reading\n", filename); | ||
413 | |||
414 | /* Read Rockbox header */ | ||
415 | n = read(fd, header, sizeof(header)); | ||
416 | if (n != sizeof(header)) | ||
417 | ERROR("[ERR] Could not read file %s\n", filename); | ||
418 | |||
419 | for(*model = 0; *model < NUM_MODELS; (*model)++) | ||
420 | if (memcmp(ams_identity[*model].rb_model_name, header + 4, 4) == 0) | ||
421 | break; | ||
422 | |||
423 | if(*model == NUM_MODELS) | ||
424 | ERROR("[ERR] Model name \"%4.4s\" unknown. Is this really a rockbox bootloader?\n", header + 4); | ||
425 | |||
426 | *bufsize = filesize(fd) - sizeof(header); | ||
427 | |||
428 | buf = malloc(*bufsize); | ||
429 | if (buf == NULL) | ||
430 | ERROR("[ERR] Could not allocate memory for %s\n", filename); | ||
431 | |||
432 | n = read(fd, buf, *bufsize); | ||
433 | |||
434 | if (n != *bufsize) | ||
435 | ERROR("[ERR] Could not read file %s\n", filename); | ||
436 | |||
437 | /* Check checksum */ | ||
438 | sum = ams_identity[*model].rb_model_num; | ||
439 | for (i = 0; i < *bufsize; i++) { | ||
440 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
441 | sum += buf[i]; | ||
442 | } | ||
443 | |||
444 | if (sum != get_uint32be(header)) | ||
445 | ERROR("[ERR] Checksum mismatch in %s\n", filename); | ||
446 | |||
447 | packed = uclpack(buf, *bufsize, rb_packedsize); | ||
448 | if(packed == NULL) | ||
449 | ERROR("[ERR] Could not compress %s\n", filename); | ||
450 | |||
451 | free(buf); | ||
452 | return packed; | ||
453 | |||
454 | error: | ||
455 | free(buf); | ||
456 | return NULL; | ||
457 | } | ||
458 | |||
459 | #undef ERROR | ||
460 | |||
461 | /* Patches a Sansa AMS Original Firmware file */ | ||
462 | void patch_firmware( | ||
463 | int model, int fw_revision, int firmware_size, unsigned char* buf, | ||
464 | int len, unsigned char* of_packed, int of_packedsize, | ||
465 | unsigned char* rb_packed, int rb_packedsize) | ||
466 | { | ||
467 | unsigned char *p; | ||
468 | uint32_t sum, filesum; | ||
469 | uint32_t ucl_dest; | ||
470 | unsigned int i; | ||
471 | |||
472 | /* Zero the original firmware area - not needed, but helps debugging */ | ||
473 | memset(buf + 0x600, 0, firmware_size); | ||
474 | |||
475 | /* Insert dual-boot bootloader at offset 0x200, we preserve the OF | ||
476 | * version string located between 0x0 and 0x200 */ | ||
477 | memcpy(buf + 0x600, ams_identity[model].bootloader, ams_identity[model].bootloader_size); | ||
478 | |||
479 | /* Insert vectors, they won't overwrite the OF version string */ | ||
480 | static const uint32_t goto_start = 0xe3a0fc02; // mov pc, #0x200 | ||
481 | static const uint32_t infinite_loop = 0xeafffffe; // 1: b 1b | ||
482 | /* ALL vectors: infinite loop */ | ||
483 | for (i=0; i < 8; i++) | ||
484 | put_uint32le(buf + 0x400 + 4*i, infinite_loop); | ||
485 | /* Now change only the interesting vectors */ | ||
486 | /* Reset/SWI vectors: branch to our dualboot code at 0x200 */ | ||
487 | put_uint32le(buf + 0x400 + 4*0, goto_start); // Reset | ||
488 | put_uint32le(buf + 0x400 + 4*2, goto_start); // SWI | ||
489 | |||
490 | /* We are filling the firmware buffer backwards from the end */ | ||
491 | p = buf + 0x400 + firmware_size; | ||
492 | |||
493 | /* 1 - UCL unpack function */ | ||
494 | p -= sizeof(nrv2e_d8); | ||
495 | memcpy(p, nrv2e_d8, sizeof(nrv2e_d8)); | ||
496 | |||
497 | /* 2 - Compressed copy of original firmware */ | ||
498 | p -= of_packedsize; | ||
499 | memcpy(p, of_packed, of_packedsize); | ||
500 | |||
501 | /* 3 - Compressed copy of Rockbox bootloader */ | ||
502 | p -= rb_packedsize; | ||
503 | memcpy(p, rb_packed, rb_packedsize); | ||
504 | |||
505 | /* Write the locations of the various images to the variables at the | ||
506 | start of the dualboot image - we save the location of the last byte | ||
507 | in each image, along with the size in bytes */ | ||
508 | |||
509 | /* UCL unpack function */ | ||
510 | put_uint32le(&buf[0x604], firmware_size - 1); | ||
511 | put_uint32le(&buf[0x608], sizeof(nrv2e_d8)); | ||
512 | |||
513 | /* Compressed original firmware image */ | ||
514 | put_uint32le(&buf[0x60c], firmware_size - sizeof(nrv2e_d8) - 1); | ||
515 | put_uint32le(&buf[0x610], of_packedsize); | ||
516 | |||
517 | /* Compressed Rockbox image */ | ||
518 | put_uint32le(&buf[0x614], firmware_size - sizeof(nrv2e_d8) - of_packedsize | ||
519 | - 1); | ||
520 | put_uint32le(&buf[0x618], rb_packedsize); | ||
521 | |||
522 | ucl_dest = model_memory_size(model) - 1; /* last byte of memory */ | ||
523 | put_uint32le(&buf[0x61c], ucl_dest); | ||
524 | |||
525 | /* Update the firmware block checksum */ | ||
526 | sum = calc_checksum(buf + 0x400, firmware_size); | ||
527 | |||
528 | if (fw_revision == 1) { | ||
529 | put_uint32le(&buf[0x04], sum); | ||
530 | put_uint32le(&buf[0x204], sum); | ||
531 | } else if (fw_revision == 2) { | ||
532 | put_uint32le(&buf[0x08], sum); | ||
533 | put_uint32le(&buf[0x208], sum); | ||
534 | |||
535 | /* Update the header checksums */ | ||
536 | put_uint32le(&buf[0x1fc], calc_checksum(buf, 0x1fc)); | ||
537 | put_uint32le(&buf[0x3fc], calc_checksum(buf + 0x200, 0x1fc)); | ||
538 | } | ||
539 | |||
540 | /* Update the whole-file checksum */ | ||
541 | filesum = 0; | ||
542 | for (i=0;i < (unsigned)len - 4; i+=4) | ||
543 | filesum += get_uint32le(&buf[i]); | ||
544 | |||
545 | put_uint32le(buf + len - 4, filesum); | ||
546 | } | ||
547 | |||
548 | /* returns != 0 if the firmware can be safely patched */ | ||
549 | int check_sizes(int model, int rb_packed_size, int rb_unpacked_size, | ||
550 | int of_packed_size, int of_unpacked_size, int *total_size, | ||
551 | char *errstr, int errstrsize) | ||
552 | { | ||
553 | /* XXX: we keep the first 0x200 bytes block unmodified, we just replace | ||
554 | * the ARM vectors */ | ||
555 | unsigned int packed_size = ams_identity[model].bootloader_size + sizeof(nrv2e_d8) + | ||
556 | of_packed_size + rb_packed_size + 0x200; | ||
557 | |||
558 | /* how much memory is available */ | ||
559 | unsigned int memory_size = model_memory_size(model); | ||
560 | |||
561 | /* the memory used when unpacking the OF */ | ||
562 | unsigned int ram_of = sizeof(nrv2e_d8) + of_packed_size + of_unpacked_size; | ||
563 | |||
564 | /* the memory used when unpacking the bootloader */ | ||
565 | unsigned int ram_rb = sizeof(nrv2e_d8) + rb_packed_size + rb_unpacked_size; | ||
566 | |||
567 | *total_size = packed_size; | ||
568 | |||
569 | #define ERROR(format, ...) \ | ||
570 | do { \ | ||
571 | snprintf(errstr, errstrsize, format, __VA_ARGS__); \ | ||
572 | return 0; \ | ||
573 | } while(0) | ||
574 | |||
575 | /* will packed data fit in the OF file ? */ | ||
576 | if(packed_size > of_unpacked_size) | ||
577 | ERROR( | ||
578 | "[ERR] Packed data (%d bytes) doesn't fit in the firmware " | ||
579 | "(%d bytes)\n", packed_size, of_unpacked_size | ||
580 | ); | ||
581 | |||
582 | else if(ram_rb > memory_size) | ||
583 | ERROR("[ERR] Rockbox can't be unpacked at runtime, needs %d bytes " | ||
584 | "of memory and only %d available\n", ram_rb, memory_size | ||
585 | ); | ||
586 | |||
587 | else if(ram_of > memory_size) | ||
588 | ERROR("[ERR] OF can't be unpacked at runtime, needs %d bytes " | ||
589 | "of memory and only %d available\n", ram_of, memory_size | ||
590 | ); | ||
591 | |||
592 | return 1; | ||
593 | |||
594 | #undef ERROR | ||
595 | } | ||