1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * mkamsboot.c - a tool for merging bootloader code into an Sansa V2
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
33 |----------------------| 0x400
35 |----------------------| 0x400 + firmware block size
37 ---------------------- END
39 We replace the main firmware block (bytes 0x400..0x400+firmware_size)
43 ---------------------- 0x0
47 |----------------------|
49 |----------------------|
51 | compressed RB image |
53 |----------------------|
55 | compressed OF image |
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
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.
92 #include <sys/types.h>
100 #include "mkamsboot.h"
104 /* Header for ARM code binaries */
105 #include "dualboot.h"
107 /* Win32 compatibility */
112 /* 4 for m200, 2 for e200/c200, 1 or 2 for fuze/clop */
113 static const unsigned short hw_revisions
[] = {
122 /* version 2 is used in Clipv2 and Fuzev2 firmwares */
123 static const unsigned short fw_revisions
[] = {
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
[] = {
184 /* Checksums of unmodified original firmwares - for safety, and device
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 int firmware_revision(int model
)
221 return fw_revisions
[model
];
224 static off_t
filesize(int fd
)
228 if (fstat(fd
, &buf
) < 0) {
229 perror("[ERR] Checking filesize of input file");
236 static uint32_t get_uint32le(unsigned char* p
)
238 return p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
241 static uint32_t get_uint32be(unsigned char* p
)
243 return (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
246 static void put_uint32le(unsigned char* p
, uint32_t x
)
249 p
[1] = (x
>> 8) & 0xff;
250 p
[2] = (x
>> 16) & 0xff;
251 p
[3] = (x
>> 24) & 0xff;
254 void calc_MD5(unsigned char* buf
, int len
, char *md5str
)
258 unsigned char md5sum
[16];
261 md5_update(&ctx
, buf
, len
);
262 md5_finish(&ctx
, md5sum
);
264 for (i
= 0; i
< 16; ++i
)
265 sprintf(md5str
+ 2*i
, "%02x", md5sum
[i
]);
268 /* Calculate a simple checksum used in Sansa Original Firmwares */
269 static uint32_t calc_checksum(unsigned char* buf
, uint32_t n
)
275 sum
+= get_uint32le(buf
+ i
);
280 static int get_model(int model_id
)
297 return MODEL_UNKNOWN
;
300 /* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */
301 static unsigned char* uclpack(unsigned char* inbuf
, int insize
, int* outsize
)
304 unsigned char* outbuf
;
307 /* The following formula comes from the UCL documentation */
308 maxsize
= insize
+ (insize
/ 8) + 256;
310 /* Allocate some memory for the output buffer */
311 outbuf
= malloc(maxsize
);
316 r
= ucl_nrv2e_99_compress(
317 (const ucl_bytep
) inbuf
,
323 if (r
!= UCL_E_OK
|| *outsize
> maxsize
) {
324 /* this should NEVER happen, and implies memory corruption */
325 fprintf(stderr
, "internal error - compression failed: %d\n", r
);
333 #define ERROR(format, ...) \
335 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
339 /* Loads a Sansa AMS Original Firmware file into memory */
340 unsigned char* load_of_file(
341 char* filename
, off_t
* bufsize
, struct md5sums
*sum
,
342 int* firmware_size
, unsigned char** of_packed
,
343 int* of_packedsize
, char* errstr
, int errstrsize
)
346 unsigned char* buf
=NULL
;
351 unsigned int last_word
;
353 fd
= open(filename
, O_RDONLY
|O_BINARY
);
355 ERROR("[ERR] Could not open %s for reading\n", filename
);
357 *bufsize
= filesize(fd
);
359 buf
= malloc(*bufsize
);
361 ERROR("[ERR] Could not allocate memory for %s\n", filename
);
363 n
= read(fd
, buf
, *bufsize
);
366 ERROR("[ERR] Could not read file %s\n", filename
);
370 /* Calculate MD5 checksum of OF */
371 calc_MD5(buf
, *bufsize
, sum
->md5
);
373 while ((i
< NUM_MD5S
) && (strcmp(sansasums
[i
].md5
, sum
->md5
) != 0))
379 int fw_version
= (get_uint32le(&buf
[0x204]) == 0x0000f000) ? 2 : 1;
380 model_id
= buf
[(fw_version
== 2) ? 0x219 : 0x215];
381 sum
->model
= get_model(model_id
);
383 if (sum
->model
== MODEL_UNKNOWN
)
384 ERROR("[ERR] Unknown firmware model (v%d) - model id 0x%02x\n",
385 fw_version
, model_id
);
387 #if 1 /* comment to test new OFs */
388 char tested_versions
[100];
389 tested_versions
[0] = '\0';
391 for (i
= 0; i
< NUM_MD5S
; i
++)
392 if (sansasums
[i
].model
== sum
->model
) {
393 if (tested_versions
[0] != '\0') {
394 strncat(tested_versions
, ", ",
395 sizeof(tested_versions
) - strlen(tested_versions
) - 1);
397 strncat(tested_versions
, sansasums
[i
].version
,
398 sizeof(tested_versions
) - strlen(tested_versions
) - 1);
401 ERROR("[ERR] Original firmware unknown, please try an other version." \
402 " Tested %s versions are : %s\n",
403 model_names
[sum
->model
], tested_versions
);
407 /* TODO: Do some more sanity checks on the OF image. Some images (like
408 m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
409 last_word
= *bufsize
- 4;
410 checksum
= get_uint32le(buf
+ last_word
);
411 if (checksum
!= 0xefbeadde && checksum
!= calc_checksum(buf
, last_word
))
412 ERROR("%s", "[ERR] Whole file checksum failed\n");
414 if (bootloaders
[sum
->model
] == NULL
)
415 ERROR("[ERR] Unsupported model - \"%s\"\n", model_names
[sum
->model
]);
417 /* Get the firmware size */
418 if (fw_revisions
[sum
->model
] == 1)
419 *firmware_size
= get_uint32le(&buf
[0x0c]);
420 else if (fw_revisions
[sum
->model
] == 2)
421 *firmware_size
= get_uint32le(&buf
[0x10]);
423 /* Compress the original firmware image */
424 *of_packed
= uclpack(buf
+ 0x400, *firmware_size
, of_packedsize
);
425 if (*of_packed
== NULL
)
426 ERROR("[ERR] Could not compress %s\n", filename
);
435 /* Loads a rockbox bootloader file into memory */
436 unsigned char* load_rockbox_file(
437 char* filename
, int model
, int* bufsize
, int* rb_packedsize
,
438 char* errstr
, int errstrsize
)
441 unsigned char* buf
= NULL
;
442 unsigned char* packed
= NULL
;
443 unsigned char header
[8];
448 fd
= open(filename
, O_RDONLY
|O_BINARY
);
450 ERROR("[ERR] Could not open %s for reading\n", filename
);
452 /* Read Rockbox header */
453 n
= read(fd
, header
, sizeof(header
));
454 if (n
!= sizeof(header
))
455 ERROR("[ERR] Could not read file %s\n", filename
);
457 /* Check for correct model string */
458 if (memcmp(rb_model_names
[model
], header
+ 4, 4)!=0)
459 ERROR("[ERR] Model name \"%s\" not found in %s\n",
460 rb_model_names
[model
], filename
);
462 *bufsize
= filesize(fd
) - sizeof(header
);
464 buf
= malloc(*bufsize
);
466 ERROR("[ERR] Could not allocate memory for %s\n", filename
);
468 n
= read(fd
, buf
, *bufsize
);
471 ERROR("[ERR] Could not read file %s\n", filename
);
474 sum
= rb_model_num
[model
];
475 for (i
= 0; i
< *bufsize
; i
++) {
476 /* add 8 unsigned bits but keep a 32 bit sum */
480 if (sum
!= get_uint32be(header
))
481 ERROR("[ERR] Checksum mismatch in %s\n", filename
);
483 packed
= uclpack(buf
, *bufsize
, rb_packedsize
);
485 ERROR("[ERR] Could not compress %s\n", filename
);
497 /* Patches a Sansa AMS Original Firmware file */
499 int model
, int fw_revision
, int firmware_size
, unsigned char* buf
,
500 int len
, unsigned char* of_packed
, int of_packedsize
,
501 unsigned char* rb_packed
, int rb_packedsize
)
504 uint32_t sum
, filesum
;
507 /* Zero the original firmware area - not needed, but helps debugging */
508 memset(buf
+ 0x400, 0, firmware_size
);
510 /* Insert dual-boot bootloader at offset 0 */
511 memcpy(buf
+ 0x400, bootloaders
[model
], bootloader_sizes
[model
]);
513 /* We are filling the firmware buffer backwards from the end */
514 p
= buf
+ 0x400 + firmware_size
;
516 /* 1 - UCL unpack function */
517 p
-= sizeof(nrv2e_d8
);
518 memcpy(p
, nrv2e_d8
, sizeof(nrv2e_d8
));
520 /* 2 - Compressed copy of original firmware */
522 memcpy(p
, of_packed
, of_packedsize
);
524 /* 3 - Compressed copy of Rockbox bootloader */
526 memcpy(p
, rb_packed
, rb_packedsize
);
528 /* Write the locations of the various images to the variables at the
529 start of the dualboot image - we save the location of the last byte
530 in each image, along with the size in bytes */
532 /* UCL unpack function */
533 put_uint32le(&buf
[0x420], firmware_size
- 1);
534 put_uint32le(&buf
[0x424], sizeof(nrv2e_d8
));
536 /* Compressed original firmware image */
537 put_uint32le(&buf
[0x428], firmware_size
- sizeof(nrv2e_d8
) - 1);
538 put_uint32le(&buf
[0x42c], of_packedsize
);
540 /* Compressed Rockbox image */
541 put_uint32le(&buf
[0x430], firmware_size
- sizeof(nrv2e_d8
) - of_packedsize
543 put_uint32le(&buf
[0x434], rb_packedsize
);
546 /* Update the firmware block checksum */
547 sum
= calc_checksum(buf
+ 0x400, firmware_size
);
549 if (fw_revision
== 1) {
550 put_uint32le(&buf
[0x04], sum
);
551 put_uint32le(&buf
[0x204], sum
);
552 } else if (fw_revision
== 2) {
553 put_uint32le(&buf
[0x08], sum
);
554 put_uint32le(&buf
[0x208], sum
);
556 /* Update the header checksums */
557 put_uint32le(&buf
[0x1fc], calc_checksum(buf
, 0x1fc));
558 put_uint32le(&buf
[0x3fc], calc_checksum(buf
+ 0x200, 0x1fc));
561 /* Update the whole-file checksum */
563 for (i
=0;i
< (unsigned)len
- 4; i
+=4)
564 filesum
+= get_uint32le(&buf
[i
]);
566 put_uint32le(buf
+ len
- 4, filesum
);
569 /* returns size of new firmware block */
570 int total_size(int model
, int rb_packedsize
, int of_packedsize
)
572 return bootloader_sizes
[model
] + sizeof(nrv2e_d8
) + of_packedsize
+
577 /* standalone executable */
578 int main(int argc
, char* argv
[])
580 char *infile
, *bootfile
, *outfile
;
587 unsigned char* of_packed
;
589 unsigned char* rb_packed
;
594 char md5sum
[33]; /* 32 digits + \0 */
598 /* VERSION comes frome the Makefile */
600 "mkamsboot Version " VERSION
"\n"
601 "This is free software; see the source for copying conditions. There is NO\n"
602 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
606 printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n");
614 /* Load original firmware file */
615 buf
= load_of_file(infile
, &len
, &sum
,
616 &firmware_size
, &of_packed
, &of_packedsize
, errstr
, sizeof(errstr
));
619 fprintf(stderr
, "%s", errstr
);
620 fprintf(stderr
, "[ERR] Could not load %s\n", infile
);
624 fprintf(stderr
, "[INFO] Original firmware MD5 checksum match\n");
625 fprintf(stderr
, "[INFO] Model: Sansa %s v%d - Firmware version: %s\n",
626 model_names
[sum
.model
], hw_revisions
[sum
.model
], sum
.version
);
629 /* Load bootloader file */
630 rb_packed
= load_rockbox_file(bootfile
, sum
.model
, &bootloader_size
,
631 &rb_packedsize
, errstr
, sizeof(errstr
));
632 if (rb_packed
== NULL
) {
633 fprintf(stderr
, "%s", errstr
);
634 fprintf(stderr
, "[ERR] Could not load %s\n", bootfile
);
640 printf("[INFO] Firmware patching has begun !\n\n");
642 fprintf(stderr
, "[INFO] Original firmware size: %d bytes\n",
644 fprintf(stderr
, "[INFO] Packed OF size: %d bytes\n",
646 fprintf(stderr
, "[INFO] Bootloader size: %d bytes\n",
647 (int)bootloader_size
);
648 fprintf(stderr
, "[INFO] Packed bootloader size: %d bytes\n",
650 fprintf(stderr
, "[INFO] Dual-boot function size: %d bytes\n",
651 bootloader_sizes
[sum
.model
]);
652 fprintf(stderr
, "[INFO] UCL unpack function size: %u bytes\n",
653 (unsigned int)sizeof(nrv2e_d8
));
655 totalsize
= total_size(sum
.model
, of_packedsize
, rb_packedsize
);
657 fprintf(stderr
, "[INFO] Total size of new image: %d bytes\n", totalsize
);
659 if (totalsize
> firmware_size
) {
660 fprintf(stderr
, "[ERR] No room to insert bootloader, aborting\n");
667 patch_firmware(sum
.model
, fw_revisions
[sum
.model
], firmware_size
, buf
, len
,
668 of_packed
, of_packedsize
, rb_packed
, rb_packedsize
);
670 /* Write the new firmware */
671 fdout
= open(outfile
, O_CREAT
|O_TRUNC
|O_WRONLY
|O_BINARY
, 0666);
674 fprintf(stderr
, "[ERR] Could not open %s for writing\n", outfile
);
681 n
= write(fdout
, buf
, len
);
683 if (n
!= (unsigned)len
) {
684 fprintf(stderr
, "[ERR] Could not write firmware file\n");
695 fprintf(stderr
, "\n[INFO] Patching succeeded!\n");