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 #define VERSION "1.0"
114 /* Supported models */
125 /* Descriptive name of these models */
126 static const char* model_names
[] = {
135 /* Dualboot functions for these models */
136 static const unsigned char* bootloaders
[] = {
145 /* Size of dualboot functions for these models */
146 static const int bootloader_sizes
[] = {
147 sizeof(dualboot_fuze
),
148 sizeof(dualboot_clip
),
149 sizeof(dualboot_clipv2
),
150 sizeof(dualboot_e200v2
),
151 sizeof(dualboot_m200v4
),
152 sizeof(dualboot_c200v2
),
155 /* Model names used in the Rockbox header in ".sansa" files - these match the
156 -add parameter to the "scramble" tool */
157 static const char* rb_model_names
[] = {
166 /* Model numbers used to initialise the checksum in the Rockbox header in
167 ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */
168 static const int rb_model_num
[] = {
180 int fw_version
; /* version 2 is used in Clipv2 and Fuzev2 firmwares */
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 fw_version md5 */
191 { MODEL_E200V2
, "3.01.11", 1, "e622ca8cb6df423f54b8b39628a1f0a3" },
192 { MODEL_E200V2
, "3.01.14", 1, "2c1d0383fc3584b2cc83ba8cc2243af6" },
193 { MODEL_E200V2
, "3.01.16", 1, "12563ad71b25a1034cf2092d1e0218c4" },
195 { MODEL_FUZE
, "1.01.11", 1, "cac8ffa03c599330ac02c4d41de66166" },
196 { MODEL_FUZE
, "1.01.15", 1, "df0e2c1612727f722c19a3c764cff7f2" },
197 { MODEL_FUZE
, "1.01.22", 1, "5aff5486fe8dd64239cc71eac470af98" },
198 { MODEL_FUZE
, "1.02.26", 1, "7c632c479461c48c8833baed74eb5e4f" },
200 { MODEL_C200V2
, "3.02.05", 1, "b6378ebd720b0ade3fad4dc7ab61c1a5" },
202 { MODEL_M200V4
, "4.00.45", 1, "82e3194310d1514e3bbcd06e84c4add3" },
203 { MODEL_M200V4
, "4.01.08-A", 1, "fc9dd6116001b3e6a150b898f1b091f0" },
204 { MODEL_M200V4
, "4.01.08-E", 1, "d3fb7d8ec8624ee65bc99f8dab0e2369" },
206 { MODEL_CLIP
, "1.01.17", 1, "12caad785d506219d73f538772afd99e" },
207 { MODEL_CLIP
, "1.01.18", 1, "d720b266bd5afa38a198986ef0508a45" },
208 { MODEL_CLIP
, "1.01.20", 1, "236d8f75189f468462c03f6d292cf2ac" },
209 { MODEL_CLIP
, "1.01.29", 1, "c12711342169c66e209540cd1f27cd26" },
210 { MODEL_CLIP
, "1.01.30", 1, "f2974d47c536549c9d8259170f1dbe4d" },
211 { MODEL_CLIP
, "1.01.32", 1, "d835d12342500732ffb9c4ee54abec15" },
213 { MODEL_CLIPV2
, "2.01.16", 2, "c57fb3fcbe07c2c9b360f060938f80cb" },
214 { MODEL_CLIPV2
, "2.01.32", 2, "0ad3723e52022509089d938d0fbbf8c5" }
217 #define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0]))
219 static off_t
filesize(int fd
)
223 if (fstat(fd
, &buf
) < 0) {
224 perror("[ERR] Checking filesize of input file");
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
)
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
)
253 unsigned char md5sum
[16];
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
)
270 sum
+= get_uint32le(buf
+ i
);
275 static int get_model(int model_id
)
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
)
299 unsigned char* outbuf
;
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
);
311 r
= ucl_nrv2e_99_compress(
312 (const ucl_bytep
) inbuf
,
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
);
328 #define ERROR(format, ...) \
330 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
334 /* Loads a Sansa AMS Original Firmware file into memory */
335 unsigned char* load_of_file(
336 char* filename
, off_t
* bufsize
, char* md5sum
, int* model
,
337 int* fw_version
, int* firmware_size
, unsigned char** of_packed
,
338 int* of_packedsize
, char* errstr
, int errstrsize
)
341 unsigned char* buf
=NULL
;
346 unsigned int last_word
;
348 fd
= open(filename
, O_RDONLY
|O_BINARY
);
350 ERROR("[ERR] Could not open %s for reading\n", filename
);
352 *bufsize
= filesize(fd
);
354 buf
= malloc(*bufsize
);
356 ERROR("[ERR] Could not allocate memory for %s\n", filename
);
358 n
= read(fd
, buf
, *bufsize
);
361 ERROR("[ERR] Could not read file %s\n", filename
);
365 /* Calculate MD5 checksum of OF */
366 calc_MD5(buf
, *bufsize
, md5sum
);
368 while ((i
< NUM_MD5S
) && (strcmp(sansasums
[i
].md5
, md5sum
) != 0))
372 *model
= sansasums
[i
].model
;
373 *fw_version
= sansasums
[i
].fw_version
;
375 if (get_uint32le(&buf
[0x204])==0x0000f000) {
377 model_id
= buf
[0x219];
380 model_id
= buf
[0x215];
382 *model
= get_model(model_id
);
384 if (*model
== MODEL_UNKNOWN
)
385 ERROR("[ERR] Unknown firmware model (v%d) - model id 0x%02x\n",
386 *fw_version
, model_id
);
388 #if 1 /* comment to test new OFs */
389 char tested_versions
[100];
390 tested_versions
[0] = '\0';
392 for (i
= 0; i
< NUM_MD5S
; i
++)
393 if (sansasums
[i
].model
== *model
) {
394 if (tested_versions
[0] != '\0') {
395 strncat(tested_versions
, ", ",
396 sizeof(tested_versions
) - strlen(tested_versions
) - 1);
398 strncat(tested_versions
, sansasums
[i
].version
,
399 sizeof(tested_versions
) - strlen(tested_versions
) - 1);
402 ERROR("[ERR] Original firmware unknown, please try an other version." \
403 " Tested %s versions are : %s\n",
404 model_names
[*model
], tested_versions
);
408 /* TODO: Do some more sanity checks on the OF image. Some images (like
409 m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
410 last_word
= *bufsize
- 4;
411 checksum
= get_uint32le(buf
+ last_word
);
412 if (checksum
!= 0xefbeadde && checksum
!= calc_checksum(buf
, last_word
))
413 ERROR("%s", "[ERR] Whole file checksum failed\n");
415 if (bootloaders
[*model
] == NULL
)
416 ERROR("[ERR] Unsupported model - \"%s\"\n", model_names
[*model
]);
419 /* Get the firmware size */
420 if (*fw_version
== 1)
421 *firmware_size
= get_uint32le(&buf
[0x0c]);
422 else /* fw_version == 2 */
423 *firmware_size
= get_uint32le(&buf
[0x10]);
425 /* Compress the original firmware image */
426 *of_packed
= uclpack(buf
+ 0x400, *firmware_size
, of_packedsize
);
427 if (*of_packed
== NULL
)
428 ERROR("[ERR] Could not compress %s\n", filename
);
437 /* Loads a rockbox bootloader file into memory */
438 unsigned char* load_rockbox_file(
439 char* filename
, int model
, int* bufsize
, int* rb_packedsize
,
440 char* errstr
, int errstrsize
)
443 unsigned char* buf
= NULL
;
444 unsigned char* packed
= NULL
;
445 unsigned char header
[8];
450 fd
= open(filename
, O_RDONLY
|O_BINARY
);
452 ERROR("[ERR] Could not open %s for reading\n", filename
);
454 /* Read Rockbox header */
455 n
= read(fd
, header
, sizeof(header
));
456 if (n
!= sizeof(header
))
457 ERROR("[ERR] Could not read file %s\n", filename
);
459 /* Check for correct model string */
460 if (memcmp(rb_model_names
[model
], header
+ 4, 4)!=0)
461 ERROR("[ERR] Model name \"%s\" not found in %s\n",
462 rb_model_names
[model
], filename
);
464 *bufsize
= filesize(fd
) - sizeof(header
);
466 buf
= malloc(*bufsize
);
468 ERROR("[ERR] Could not allocate memory for %s\n", filename
);
470 n
= read(fd
, buf
, *bufsize
);
473 ERROR("[ERR] Could not read file %s\n", filename
);
476 sum
= rb_model_num
[model
];
477 for (i
= 0; i
< *bufsize
; i
++) {
478 /* add 8 unsigned bits but keep a 32 bit sum */
482 if (sum
!= get_uint32be(header
))
483 ERROR("[ERR] Checksum mismatch in %s\n", filename
);
485 packed
= uclpack(buf
, *bufsize
, rb_packedsize
);
487 ERROR("[ERR] Could not compress %s\n", filename
);
499 /* Patches a Sansa AMS Original Firmware file */
501 int model
, int fw_version
, int firmware_size
, unsigned char* buf
,
502 int len
, unsigned char* of_packed
, int of_packedsize
,
503 unsigned char* rb_packed
, int rb_packedsize
)
506 uint32_t sum
, filesum
;
509 /* Zero the original firmware area - not needed, but helps debugging */
510 memset(buf
+ 0x400, 0, firmware_size
);
512 /* Insert dual-boot bootloader at offset 0 */
513 memcpy(buf
+ 0x400, bootloaders
[model
], bootloader_sizes
[model
]);
515 /* We are filling the firmware buffer backwards from the end */
516 p
= buf
+ 0x400 + firmware_size
;
518 /* 1 - UCL unpack function */
519 p
-= sizeof(nrv2e_d8
);
520 memcpy(p
, nrv2e_d8
, sizeof(nrv2e_d8
));
522 /* 2 - Compressed copy of original firmware */
524 memcpy(p
, of_packed
, of_packedsize
);
526 /* 3 - Compressed copy of Rockbox bootloader */
528 memcpy(p
, rb_packed
, rb_packedsize
);
530 /* Write the locations of the various images to the variables at the
531 start of the dualboot image - we save the location of the last byte
532 in each image, along with the size in bytes */
534 /* UCL unpack function */
535 put_uint32le(&buf
[0x420], firmware_size
- 1);
536 put_uint32le(&buf
[0x424], sizeof(nrv2e_d8
));
538 /* Compressed original firmware image */
539 put_uint32le(&buf
[0x428], firmware_size
- sizeof(nrv2e_d8
) - 1);
540 put_uint32le(&buf
[0x42c], of_packedsize
);
542 /* Compressed Rockbox image */
543 put_uint32le(&buf
[0x430], firmware_size
- sizeof(nrv2e_d8
) - of_packedsize
545 put_uint32le(&buf
[0x434], rb_packedsize
);
548 /* Update the firmware block checksum */
549 sum
= calc_checksum(buf
+ 0x400, firmware_size
);
551 if (fw_version
== 1) {
552 put_uint32le(&buf
[0x04], sum
);
553 put_uint32le(&buf
[0x204], sum
);
555 put_uint32le(&buf
[0x08], sum
);
556 put_uint32le(&buf
[0x208], sum
);
558 /* Update the header checksums */
559 put_uint32le(&buf
[0x1fc], calc_checksum(buf
, 0x1fc));
560 put_uint32le(&buf
[0x3fc], calc_checksum(buf
+ 0x200, 0x1fc));
563 /* Update the whole-file checksum */
565 for (i
=0;i
< (unsigned)len
- 4; i
+=4)
566 filesum
+= get_uint32le(&buf
[i
]);
568 put_uint32le(buf
+ len
- 4, filesum
);
571 /* returns size of new firmware block */
572 int total_size(int model
, int rb_packedsize
, int of_packedsize
)
574 return bootloader_sizes
[model
] + sizeof(nrv2e_d8
) + of_packedsize
+
579 /* standalone executable */
580 int main(int argc
, char* argv
[])
582 char *infile
, *bootfile
, *outfile
;
590 unsigned char* of_packed
;
592 unsigned char* rb_packed
;
596 char md5sum
[33]; /* 32 hex digits, plus terminating zero */
600 "mkamsboot v" VERSION
" - (C) Dave Chapman and Rafaël Carré 2008\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
, md5sum
, &model
, &fw_version
,
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 - %s\n",
628 /* Load bootloader file */
629 rb_packed
= load_rockbox_file(bootfile
, model
, &bootloader_size
,
630 &rb_packedsize
, errstr
, sizeof(errstr
));
631 if (rb_packed
== NULL
) {
632 fprintf(stderr
, "%s", errstr
);
633 fprintf(stderr
, "[ERR] Could not load %s\n", bootfile
);
639 printf("[INFO] Patching %s firmware\n", model_names
[model
]);
641 fprintf(stderr
, "[INFO] Original firmware size: %d bytes\n",
643 fprintf(stderr
, "[INFO] Packed OF size: %d bytes\n",
645 fprintf(stderr
, "[INFO] Bootloader size: %d bytes\n",
646 (int)bootloader_size
);
647 fprintf(stderr
, "[INFO] Packed bootloader size: %d bytes\n",
649 fprintf(stderr
, "[INFO] Dual-boot function size: %d bytes\n",
650 bootloader_sizes
[model
]);
651 fprintf(stderr
, "[INFO] UCL unpack function size: %d bytes\n",
654 totalsize
= total_size(model
, of_packedsize
, rb_packedsize
);
656 fprintf(stderr
, "[INFO] Total size of new image: %d bytes\n", totalsize
);
658 if (totalsize
> firmware_size
) {
659 fprintf(stderr
, "[ERR] No room to insert bootloader, aborting\n");
666 patch_firmware(model
, fw_version
, firmware_size
, buf
, len
, of_packed
,
667 of_packedsize
, rb_packed
, rb_packedsize
);
669 /* Write the new firmware */
670 fdout
= open(outfile
, O_CREAT
|O_TRUNC
|O_WRONLY
|O_BINARY
, 0666);
673 fprintf(stderr
, "[ERR] Could not open %s for writing\n", outfile
);
680 n
= write(fdout
, buf
, len
);
682 if (n
!= (unsigned)len
) {
683 fprintf(stderr
, "[ERR] Could not write firmware file\n");