Change to versioning for mkamsboot to <rXXXXX>-<DATE> for svn builds. A fixed number...
[kugel-rb/myfork.git] / rbutil / mkamsboot / mkamsboot.c
blob62c33186d742b40121f0e4a3e5a0343ff88f41c5
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * mkamsboot.c - a tool for merging bootloader code into an Sansa V2
11 * (AMS) firmware file
13 * Copyright (C) 2008 Dave Chapman
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.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
27 Insert a Rockbox bootloader into a Sansa AMS original firmware file.
29 Layout of a Sansa AMS original firmware file:
31 ---------------------- 0x0
32 | HEADER |
33 |----------------------| 0x400
34 | FIRMWARE BLOCK |
35 |----------------------| 0x400 + firmware block size
36 | LIBRARIES/DATA |
37 ---------------------- END
39 We replace the main firmware block (bytes 0x400..0x400+firmware_size)
40 as follows:
43 ---------------------- 0x0
44 | |
45 | Dual-boot code |
46 | |
47 |----------------------|
48 | EMPTY SPACE |
49 |----------------------|
50 | |
51 | compressed RB image |
52 | |
53 |----------------------|
54 | |
55 | compressed OF image |
56 | |
57 |----------------------|
58 | UCL unpack function |
59 ----------------------
61 This entire block fits into the space previously occupied by the main
62 firmware block - the space saved by compressing the OF image is used
63 to store the compressed version of the Rockbox bootloader.
65 On version 1 firmwares, the OF image is typically about 120KB, which allows
66 us to store a Rockbox bootloader with an uncompressed size of about 60KB-70KB.
67 Version 2 firmwares are bigger and are stored in SDRAM (instead of IRAM).
68 In both cases, the RAM we are using is mapped at offset 0x0.
70 mkamsboot then corrects the checksums and writes a new legal firmware
71 file which can be installed on the device.
73 When the Sansa device boots, this firmware block is loaded to RAM at
74 address 0x0 and executed.
76 Firstly, the dual-boot code will copy the UCL unpack function to the
77 end of RAM.
79 Then, depending on the detection of the dual-boot keypress, either the
80 OF image or the Rockbox image is copied to the end of RAM (just before
81 the ucl unpack function) and uncompressed to the start of RAM.
83 Finally, the ucl unpack function branches to address 0x0, passing
84 execution to the uncompressed firmware.
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <stdint.h>
92 #include <sys/types.h>
93 #include <sys/stat.h>
94 #include <fcntl.h>
95 #include <unistd.h>
96 #include <string.h>
98 #include <ucl/ucl.h>
100 #include "mkamsboot.h"
102 #include "md5.h"
104 /* Header for ARM code binaries */
105 #include "dualboot.h"
107 /* Win32 compatibility */
108 #ifndef O_BINARY
109 #define O_BINARY 0
110 #endif
112 /* 4 for m200, 2 for e200/c200, 1 or 2 for fuze/clop */
113 static const unsigned short hw_revisions[] = {
114 [MODEL_FUZE] = 1,
115 [MODEL_CLIP] = 1,
116 [MODEL_CLIPV2] = 2,
117 [MODEL_E200V2] = 2,
118 [MODEL_M200V4] = 4,
119 [MODEL_C200V2] = 2,
122 /* version 2 is used in Clipv2 and Fuzev2 firmwares */
123 static const unsigned short fw_revisions[] = {
124 [MODEL_FUZE] = 1,
125 [MODEL_CLIP] = 1,
126 [MODEL_CLIPV2] = 2,
127 [MODEL_E200V2] = 1,
128 [MODEL_M200V4] = 1,
129 [MODEL_C200V2] = 1,
132 /* Descriptive name of these models */
133 static const char* model_names[] = {
134 [MODEL_FUZE] = "Fuze",
135 [MODEL_CLIP] = "Clip",
136 [MODEL_CLIPV2] = "Clip",
137 [MODEL_E200V2] = "e200",
138 [MODEL_M200V4] = "m200",
139 [MODEL_C200V2] = "c200",
142 /* Dualboot functions for these models */
143 static const unsigned char* bootloaders[] = {
144 [MODEL_FUZE] = dualboot_fuze,
145 [MODEL_CLIP] = dualboot_clip,
146 [MODEL_CLIPV2] = dualboot_clipv2,
147 [MODEL_E200V2] = dualboot_e200v2,
148 [MODEL_M200V4] = dualboot_m200v4,
149 [MODEL_C200V2] = dualboot_c200v2,
152 /* Size of dualboot functions for these models */
153 static const int bootloader_sizes[] = {
154 [MODEL_FUZE] = sizeof(dualboot_fuze),
155 [MODEL_CLIP] = sizeof(dualboot_clip),
156 [MODEL_CLIPV2] = sizeof(dualboot_clipv2),
157 [MODEL_E200V2] = sizeof(dualboot_e200v2),
158 [MODEL_M200V4] = sizeof(dualboot_m200v4),
159 [MODEL_C200V2] = sizeof(dualboot_c200v2),
162 /* Model names used in the Rockbox header in ".sansa" files - these match the
163 -add parameter to the "scramble" tool */
164 static const char* rb_model_names[] = {
165 [MODEL_FUZE] = "fuze",
166 [MODEL_CLIP] = "clip",
167 [MODEL_CLIPV2] = "clv2",
168 [MODEL_E200V2] = "e2v2",
169 [MODEL_M200V4] = "m2v4",
170 [MODEL_C200V2] = "c2v2",
173 /* Model numbers used to initialise the checksum in the Rockbox header in
174 ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */
175 static const int rb_model_num[] = {
176 [MODEL_FUZE] = 43,
177 [MODEL_CLIP] = 40,
178 [MODEL_CLIPV2] = 60,
179 [MODEL_E200V2] = 41,
180 [MODEL_M200V4] = 42,
181 [MODEL_C200V2] = 44
184 /* Checksums of unmodified original firmwares - for safety, and device
185 detection */
186 static struct md5sums sansasums[] = {
187 /* NOTE: Different regional versions of the firmware normally only
188 differ in the filename - the md5sums are identical */
190 /* model version md5 */
191 { MODEL_E200V2, "3.01.11", "e622ca8cb6df423f54b8b39628a1f0a3" },
192 { MODEL_E200V2, "3.01.14", "2c1d0383fc3584b2cc83ba8cc2243af6" },
193 { MODEL_E200V2, "3.01.16", "12563ad71b25a1034cf2092d1e0218c4" },
195 { MODEL_FUZE, "1.01.11", "cac8ffa03c599330ac02c4d41de66166" },
196 { MODEL_FUZE, "1.01.15", "df0e2c1612727f722c19a3c764cff7f2" },
197 { MODEL_FUZE, "1.01.22", "5aff5486fe8dd64239cc71eac470af98" },
198 { MODEL_FUZE, "1.02.26", "7c632c479461c48c8833baed74eb5e4f" },
200 { MODEL_C200V2, "3.02.05", "b6378ebd720b0ade3fad4dc7ab61c1a5" },
202 { MODEL_M200V4, "4.00.45", "82e3194310d1514e3bbcd06e84c4add3" },
203 { MODEL_M200V4, "4.01.08-A", "fc9dd6116001b3e6a150b898f1b091f0" },
204 { MODEL_M200V4, "4.01.08-E", "d3fb7d8ec8624ee65bc99f8dab0e2369" },
206 { MODEL_CLIP, "1.01.17", "12caad785d506219d73f538772afd99e" },
207 { MODEL_CLIP, "1.01.18", "d720b266bd5afa38a198986ef0508a45" },
208 { MODEL_CLIP, "1.01.20", "236d8f75189f468462c03f6d292cf2ac" },
209 { MODEL_CLIP, "1.01.29", "c12711342169c66e209540cd1f27cd26" },
210 { MODEL_CLIP, "1.01.30", "f2974d47c536549c9d8259170f1dbe4d" },
211 { MODEL_CLIP, "1.01.32", "d835d12342500732ffb9c4ee54abec15" },
213 { MODEL_CLIPV2, "2.01.16", "c57fb3fcbe07c2c9b360f060938f80cb" },
214 { MODEL_CLIPV2, "2.01.32", "0ad3723e52022509089d938d0fbbf8c5" }
217 #define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0]))
219 static off_t filesize(int fd)
221 struct stat buf;
223 if (fstat(fd, &buf) < 0) {
224 perror("[ERR] Checking filesize of input file");
225 return -1;
226 } else {
227 return(buf.st_size);
231 static uint32_t get_uint32le(unsigned char* p)
233 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
236 static uint32_t get_uint32be(unsigned char* p)
238 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
241 static void put_uint32le(unsigned char* p, uint32_t x)
243 p[0] = x & 0xff;
244 p[1] = (x >> 8) & 0xff;
245 p[2] = (x >> 16) & 0xff;
246 p[3] = (x >> 24) & 0xff;
249 void calc_MD5(unsigned char* buf, int len, char *md5str)
251 int i;
252 md5_context ctx;
253 unsigned char md5sum[16];
255 md5_starts(&ctx);
256 md5_update(&ctx, buf, len);
257 md5_finish(&ctx, md5sum);
259 for (i = 0; i < 16; ++i)
260 sprintf(md5str + 2*i, "%02x", md5sum[i]);
263 /* Calculate a simple checksum used in Sansa Original Firmwares */
264 static uint32_t calc_checksum(unsigned char* buf, uint32_t n)
266 uint32_t sum = 0;
267 uint32_t i;
269 for (i=0;i<n;i+=4)
270 sum += get_uint32le(buf + i);
272 return sum;
275 static int get_model(int model_id)
277 switch(model_id) {
278 case 0x1e:
279 return MODEL_FUZE;
280 case 0x22:
281 return MODEL_CLIP;
282 case 0x23:
283 return MODEL_C200V2;
284 case 0x24:
285 return MODEL_E200V2;
286 case 0x25:
287 return MODEL_M200V4;
288 case 0x27:
289 return MODEL_CLIPV2;
292 return MODEL_UNKNOWN;
295 /* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */
296 static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize)
298 int maxsize;
299 unsigned char* outbuf;
300 int r;
302 /* The following formula comes from the UCL documentation */
303 maxsize = insize + (insize / 8) + 256;
305 /* Allocate some memory for the output buffer */
306 outbuf = malloc(maxsize);
308 if (outbuf == NULL)
309 return NULL;
311 r = ucl_nrv2e_99_compress(
312 (const ucl_bytep) inbuf,
313 (ucl_uint) insize,
314 (ucl_bytep) outbuf,
315 (ucl_uintp) outsize,
316 0, 10, NULL, NULL);
318 if (r != UCL_E_OK || *outsize > maxsize) {
319 /* this should NEVER happen, and implies memory corruption */
320 fprintf(stderr, "internal error - compression failed: %d\n", r);
321 free(outbuf);
322 return NULL;
325 return outbuf;
328 #define ERROR(format, ...) \
329 do { \
330 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
331 goto error; \
332 } while(0)
334 /* Loads a Sansa AMS Original Firmware file into memory */
335 unsigned char* load_of_file(
336 char* filename, off_t* bufsize, struct md5sums *sum,
337 int* firmware_size, unsigned char** of_packed,
338 int* of_packedsize, char* errstr, int errstrsize)
340 int fd;
341 unsigned char* buf =NULL;
342 off_t n;
343 unsigned int i=0;
344 uint32_t checksum;
345 int model_id;
346 unsigned int last_word;
348 fd = open(filename, O_RDONLY|O_BINARY);
349 if (fd < 0)
350 ERROR("[ERR] Could not open %s for reading\n", filename);
352 *bufsize = filesize(fd);
354 buf = malloc(*bufsize);
355 if (buf == NULL)
356 ERROR("[ERR] Could not allocate memory for %s\n", filename);
358 n = read(fd, buf, *bufsize);
360 if (n != *bufsize)
361 ERROR("[ERR] Could not read file %s\n", filename);
363 /* check the file */
365 /* Calculate MD5 checksum of OF */
366 calc_MD5(buf, *bufsize, sum->md5);
368 while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, sum->md5) != 0))
369 i++;
371 if (i < NUM_MD5S) {
372 *sum = sansasums[i];
373 } else {
374 int fw_version = (get_uint32le(&buf[0x204]) == 0x0000f000) ? 2 : 1;
375 model_id = buf[(fw_version == 2) ? 0x219 : 0x215];
376 sum->model = get_model(model_id);
378 if (sum->model == MODEL_UNKNOWN)
379 ERROR("[ERR] Unknown firmware model (v%d) - model id 0x%02x\n",
380 fw_version, model_id);
382 #if 1 /* comment to test new OFs */
383 char tested_versions[100];
384 tested_versions[0] = '\0';
386 for (i = 0; i < NUM_MD5S ; i++)
387 if (sansasums[i].model == sum->model) {
388 if (tested_versions[0] != '\0') {
389 strncat(tested_versions, ", ",
390 sizeof(tested_versions) - strlen(tested_versions) - 1);
392 strncat(tested_versions, sansasums[i].version,
393 sizeof(tested_versions) - strlen(tested_versions) - 1);
396 ERROR("[ERR] Original firmware unknown, please try an other version." \
397 " Tested %s versions are : %s\n",
398 model_names[sum->model], tested_versions);
399 #endif
402 /* TODO: Do some more sanity checks on the OF image. Some images (like
403 m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
404 last_word = *bufsize - 4;
405 checksum = get_uint32le(buf + last_word);
406 if (checksum != 0xefbeadde && checksum != calc_checksum(buf, last_word))
407 ERROR("%s", "[ERR] Whole file checksum failed\n");
409 if (bootloaders[sum->model] == NULL)
410 ERROR("[ERR] Unsupported model - \"%s\"\n", model_names[sum->model]);
412 /* Get the firmware size */
413 if (fw_revisions[sum->model] == 1)
414 *firmware_size = get_uint32le(&buf[0x0c]);
415 else if (fw_revisions[sum->model] == 2)
416 *firmware_size = get_uint32le(&buf[0x10]);
418 /* Compress the original firmware image */
419 *of_packed = uclpack(buf + 0x400, *firmware_size, of_packedsize);
420 if (*of_packed == NULL)
421 ERROR("[ERR] Could not compress %s\n", filename);
423 return buf;
425 error:
426 free(buf);
427 return NULL;
430 /* Loads a rockbox bootloader file into memory */
431 unsigned char* load_rockbox_file(
432 char* filename, int model, int* bufsize, int* rb_packedsize,
433 char* errstr, int errstrsize)
435 int fd;
436 unsigned char* buf = NULL;
437 unsigned char* packed = NULL;
438 unsigned char header[8];
439 uint32_t sum;
440 off_t n;
441 int i;
443 fd = open(filename, O_RDONLY|O_BINARY);
444 if (fd < 0)
445 ERROR("[ERR] Could not open %s for reading\n", filename);
447 /* Read Rockbox header */
448 n = read(fd, header, sizeof(header));
449 if (n != sizeof(header))
450 ERROR("[ERR] Could not read file %s\n", filename);
452 /* Check for correct model string */
453 if (memcmp(rb_model_names[model], header + 4, 4)!=0)
454 ERROR("[ERR] Model name \"%s\" not found in %s\n",
455 rb_model_names[model], filename);
457 *bufsize = filesize(fd) - sizeof(header);
459 buf = malloc(*bufsize);
460 if (buf == NULL)
461 ERROR("[ERR] Could not allocate memory for %s\n", filename);
463 n = read(fd, buf, *bufsize);
465 if (n != *bufsize)
466 ERROR("[ERR] Could not read file %s\n", filename);
468 /* Check checksum */
469 sum = rb_model_num[model];
470 for (i = 0; i < *bufsize; i++) {
471 /* add 8 unsigned bits but keep a 32 bit sum */
472 sum += buf[i];
475 if (sum != get_uint32be(header))
476 ERROR("[ERR] Checksum mismatch in %s\n", filename);
478 packed = uclpack(buf, *bufsize, rb_packedsize);
479 if(packed == NULL)
480 ERROR("[ERR] Could not compress %s\n", filename);
482 free(buf);
483 return packed;
485 error:
486 free(buf);
487 return NULL;
490 #undef ERROR
492 /* Patches a Sansa AMS Original Firmware file */
493 void patch_firmware(
494 int model, int fw_revision, int firmware_size, unsigned char* buf,
495 int len, unsigned char* of_packed, int of_packedsize,
496 unsigned char* rb_packed, int rb_packedsize)
498 unsigned char *p;
499 uint32_t sum, filesum;
500 unsigned int i;
502 /* Zero the original firmware area - not needed, but helps debugging */
503 memset(buf + 0x400, 0, firmware_size);
505 /* Insert dual-boot bootloader at offset 0 */
506 memcpy(buf + 0x400, bootloaders[model], bootloader_sizes[model]);
508 /* We are filling the firmware buffer backwards from the end */
509 p = buf + 0x400 + firmware_size;
511 /* 1 - UCL unpack function */
512 p -= sizeof(nrv2e_d8);
513 memcpy(p, nrv2e_d8, sizeof(nrv2e_d8));
515 /* 2 - Compressed copy of original firmware */
516 p -= of_packedsize;
517 memcpy(p, of_packed, of_packedsize);
519 /* 3 - Compressed copy of Rockbox bootloader */
520 p -= rb_packedsize;
521 memcpy(p, rb_packed, rb_packedsize);
523 /* Write the locations of the various images to the variables at the
524 start of the dualboot image - we save the location of the last byte
525 in each image, along with the size in bytes */
527 /* UCL unpack function */
528 put_uint32le(&buf[0x420], firmware_size - 1);
529 put_uint32le(&buf[0x424], sizeof(nrv2e_d8));
531 /* Compressed original firmware image */
532 put_uint32le(&buf[0x428], firmware_size - sizeof(nrv2e_d8) - 1);
533 put_uint32le(&buf[0x42c], of_packedsize);
535 /* Compressed Rockbox image */
536 put_uint32le(&buf[0x430], firmware_size - sizeof(nrv2e_d8) - of_packedsize
537 - 1);
538 put_uint32le(&buf[0x434], rb_packedsize);
541 /* Update the firmware block checksum */
542 sum = calc_checksum(buf + 0x400, firmware_size);
544 if (fw_revision == 1) {
545 put_uint32le(&buf[0x04], sum);
546 put_uint32le(&buf[0x204], sum);
547 } else if (fw_revision == 2) {
548 put_uint32le(&buf[0x08], sum);
549 put_uint32le(&buf[0x208], sum);
551 /* Update the header checksums */
552 put_uint32le(&buf[0x1fc], calc_checksum(buf, 0x1fc));
553 put_uint32le(&buf[0x3fc], calc_checksum(buf + 0x200, 0x1fc));
556 /* Update the whole-file checksum */
557 filesum = 0;
558 for (i=0;i < (unsigned)len - 4; i+=4)
559 filesum += get_uint32le(&buf[i]);
561 put_uint32le(buf + len - 4, filesum);
564 /* returns size of new firmware block */
565 int total_size(int model, int rb_packedsize, int of_packedsize)
567 return bootloader_sizes[model] + sizeof(nrv2e_d8) + of_packedsize +
568 rb_packedsize;
571 #ifndef LIB
572 /* standalone executable */
573 int main(int argc, char* argv[])
575 char *infile, *bootfile, *outfile;
576 int fdout;
577 off_t len;
578 uint32_t n;
579 unsigned char* buf;
580 int firmware_size;
581 int bootloader_size;
582 unsigned char* of_packed;
583 int of_packedsize;
584 unsigned char* rb_packed;
585 int rb_packedsize;
586 int totalsize;
587 char errstr[200];
588 struct md5sums sum;
589 char md5sum[33]; /* 32 digits + \0 */
591 sum.md5 = md5sum;
593 /* VERSION comes frome the Makefile */
594 fprintf(stderr,
595 "mkamsboot Version " VERSION " - (C) Dave Chapman and Rafaël Carré 2008\n"
596 "This is free software; see the source for copying conditions. There is NO\n"
597 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
598 "\n");
600 if(argc != 4) {
601 printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n");
602 return 1;
605 infile = argv[1];
606 bootfile = argv[2];
607 outfile = argv[3];
609 /* Load original firmware file */
610 buf = load_of_file(infile, &len, &sum,
611 &firmware_size, &of_packed, &of_packedsize, errstr, sizeof(errstr));
613 if (buf == NULL) {
614 fprintf(stderr, "%s", errstr);
615 fprintf(stderr, "[ERR] Could not load %s\n", infile);
616 return 1;
619 fprintf(stderr, "[INFO] Original firmware MD5 checksum match\n");
620 fprintf(stderr, "[INFO] Model: Sansa %s v%d - Firmware version: %s\n",
621 model_names[sum.model], hw_revisions[sum.model], sum.version);
624 /* Load bootloader file */
625 rb_packed = load_rockbox_file(bootfile, sum.model, &bootloader_size,
626 &rb_packedsize, errstr, sizeof(errstr));
627 if (rb_packed == NULL) {
628 fprintf(stderr, "%s", errstr);
629 fprintf(stderr, "[ERR] Could not load %s\n", bootfile);
630 free(buf);
631 free(of_packed);
632 return 1;
635 printf("[INFO] Firmware patching has begun !\n\n");
637 fprintf(stderr, "[INFO] Original firmware size: %d bytes\n",
638 firmware_size);
639 fprintf(stderr, "[INFO] Packed OF size: %d bytes\n",
640 of_packedsize);
641 fprintf(stderr, "[INFO] Bootloader size: %d bytes\n",
642 (int)bootloader_size);
643 fprintf(stderr, "[INFO] Packed bootloader size: %d bytes\n",
644 rb_packedsize);
645 fprintf(stderr, "[INFO] Dual-boot function size: %d bytes\n",
646 bootloader_sizes[sum.model]);
647 fprintf(stderr, "[INFO] UCL unpack function size: %u bytes\n",
648 sizeof(nrv2e_d8));
650 totalsize = total_size(sum.model, of_packedsize, rb_packedsize);
652 fprintf(stderr, "[INFO] Total size of new image: %d bytes\n", totalsize);
654 if (totalsize > firmware_size) {
655 fprintf(stderr, "[ERR] No room to insert bootloader, aborting\n");
656 free(buf);
657 free(of_packed);
658 free(rb_packed);
659 return 1;
662 patch_firmware(sum.model, fw_revisions[sum.model], firmware_size, buf, len,
663 of_packed, of_packedsize, rb_packed, rb_packedsize);
665 /* Write the new firmware */
666 fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666);
668 if (fdout < 0) {
669 fprintf(stderr, "[ERR] Could not open %s for writing\n", outfile);
670 free(buf);
671 free(of_packed);
672 free(rb_packed);
673 return 1;
676 n = write(fdout, buf, len);
678 if (n != (unsigned)len) {
679 fprintf(stderr, "[ERR] Could not write firmware file\n");
680 free(buf);
681 free(of_packed);
682 free(rb_packed);
683 return 1;
686 close(fdout);
687 free(buf);
688 free(of_packed);
689 free(rb_packed);
690 fprintf(stderr, "\n[INFO] Patching succeeded!\n");
692 return 0;
694 #endif