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/clip, 1 for clip+ */
113 const unsigned short hw_revisions
[] = {
123 /* version 2 is used in Clipv2, Clip+ and Fuzev2 firmwares */
124 const unsigned short fw_revisions
[] = {
134 /* Descriptive name of these models */
135 const char* model_names
[] = {
136 [MODEL_FUZE
] = "Fuze",
137 [MODEL_CLIP
] = "Clip",
138 [MODEL_CLIPV2
] = "Clip",
139 [MODEL_CLIPPLUS
]= "Clip+",
140 [MODEL_E200V2
] = "e200",
141 [MODEL_M200V4
] = "m200",
142 [MODEL_C200V2
] = "c200",
145 /* Dualboot functions for these models */
146 static const unsigned char* bootloaders
[] = {
147 [MODEL_FUZE
] = dualboot_fuze
,
148 [MODEL_CLIP
] = dualboot_clip
,
149 [MODEL_CLIPV2
] = dualboot_clipv2
,
150 [MODEL_E200V2
] = dualboot_e200v2
,
151 [MODEL_M200V4
] = dualboot_m200v4
,
152 [MODEL_C200V2
] = dualboot_c200v2
,
153 [MODEL_CLIPPLUS
]= dualboot_clipplus
,
156 /* Size of dualboot functions for these models */
157 const int bootloader_sizes
[] = {
158 [MODEL_FUZE
] = sizeof(dualboot_fuze
),
159 [MODEL_CLIP
] = sizeof(dualboot_clip
),
160 [MODEL_CLIPV2
] = sizeof(dualboot_clipv2
),
161 [MODEL_E200V2
] = sizeof(dualboot_e200v2
),
162 [MODEL_M200V4
] = sizeof(dualboot_m200v4
),
163 [MODEL_C200V2
] = sizeof(dualboot_c200v2
),
164 [MODEL_CLIPPLUS
]= sizeof(dualboot_clipplus
),
167 /* Model names used in the Rockbox header in ".sansa" files - these match the
168 -add parameter to the "scramble" tool */
169 static const char* rb_model_names
[] = {
170 [MODEL_FUZE
] = "fuze",
171 [MODEL_CLIP
] = "clip",
172 [MODEL_CLIPV2
] = "clv2",
173 [MODEL_E200V2
] = "e2v2",
174 [MODEL_M200V4
] = "m2v4",
175 [MODEL_C200V2
] = "c2v2",
176 [MODEL_CLIPPLUS
]= "cli+",
179 /* Model numbers used to initialise the checksum in the Rockbox header in
180 ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */
181 static const int rb_model_num
[] = {
188 [MODEL_CLIPPLUS
]= 66,
191 /* Checksums of unmodified original firmwares - for safety, and device
193 static struct md5sums sansasums
[] = {
194 /* NOTE: Different regional versions of the firmware normally only
195 differ in the filename - the md5sums are identical */
197 /* model version md5 */
198 { MODEL_E200V2
, "3.01.11", "e622ca8cb6df423f54b8b39628a1f0a3" },
199 { MODEL_E200V2
, "3.01.14", "2c1d0383fc3584b2cc83ba8cc2243af6" },
200 { MODEL_E200V2
, "3.01.16", "12563ad71b25a1034cf2092d1e0218c4" },
202 { MODEL_FUZE
, "1.01.11", "cac8ffa03c599330ac02c4d41de66166" },
203 { MODEL_FUZE
, "1.01.15", "df0e2c1612727f722c19a3c764cff7f2" },
204 { MODEL_FUZE
, "1.01.22", "5aff5486fe8dd64239cc71eac470af98" },
205 { MODEL_FUZE
, "1.02.26", "7c632c479461c48c8833baed74eb5e4f" },
206 { MODEL_FUZE
, "1.02.28", "5b34260f6470e75f702a9c6825471752" },
207 { MODEL_FUZE
, "1.02.31", "66d01b37462a5ef7ccc6ad37188b4235" },
209 { MODEL_C200V2
, "3.02.05", "b6378ebd720b0ade3fad4dc7ab61c1a5" },
211 { MODEL_M200V4
, "4.00.45", "82e3194310d1514e3bbcd06e84c4add3" },
212 { MODEL_M200V4
, "4.01.08-A", "fc9dd6116001b3e6a150b898f1b091f0" },
213 { MODEL_M200V4
, "4.01.08-E", "d3fb7d8ec8624ee65bc99f8dab0e2369" },
215 { MODEL_CLIP
, "1.01.17", "12caad785d506219d73f538772afd99e" },
216 { MODEL_CLIP
, "1.01.18", "d720b266bd5afa38a198986ef0508a45" },
217 { MODEL_CLIP
, "1.01.20", "236d8f75189f468462c03f6d292cf2ac" },
218 { MODEL_CLIP
, "1.01.29", "c12711342169c66e209540cd1f27cd26" },
219 { MODEL_CLIP
, "1.01.30", "f2974d47c536549c9d8259170f1dbe4d" },
220 { MODEL_CLIP
, "1.01.32", "d835d12342500732ffb9c4ee54abec15" },
222 { MODEL_CLIPV2
, "2.01.16", "c57fb3fcbe07c2c9b360f060938f80cb" },
223 { MODEL_CLIPV2
, "2.01.32", "0ad3723e52022509089d938d0fbbf8c5" },
225 { MODEL_CLIPPLUS
, "01.02.09", "656d38114774c2001dc18e6726df3c5d" },
228 #define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0]))
230 static unsigned int model_memory_size(int model
)
232 if(model
== MODEL_CLIPV2
)
234 /* The decompressed Clipv2 OF is around 380kB.
235 * Since it doesn't fit in the 0x50000 bytes IRAM, the OF starts
236 * with DRAM mapped at 0x0
238 * We could use all the available memory (supposedly 8MB)
239 * but 1MB ought to be enough for our use
244 { /* The OF boots with IRAM (320kB) mapped at 0x0 */
249 int firmware_revision(int model
)
251 return fw_revisions
[model
];
254 static off_t
filesize(int fd
)
258 if (fstat(fd
, &buf
) < 0) {
259 perror("[ERR] Checking filesize of input file");
266 static uint32_t get_uint32le(unsigned char* p
)
268 return p
[0] | (p
[1] << 8) | (p
[2] << 16) | (p
[3] << 24);
271 static uint32_t get_uint32be(unsigned char* p
)
273 return (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
276 static void put_uint32le(unsigned char* p
, uint32_t x
)
279 p
[1] = (x
>> 8) & 0xff;
280 p
[2] = (x
>> 16) & 0xff;
281 p
[3] = (x
>> 24) & 0xff;
284 void calc_MD5(unsigned char* buf
, int len
, char *md5str
)
288 unsigned char md5sum
[16];
291 md5_update(&ctx
, buf
, len
);
292 md5_finish(&ctx
, md5sum
);
294 for (i
= 0; i
< 16; ++i
)
295 sprintf(md5str
+ 2*i
, "%02x", md5sum
[i
]);
298 /* Calculate a simple checksum used in Sansa Original Firmwares */
299 static uint32_t calc_checksum(unsigned char* buf
, uint32_t n
)
305 sum
+= get_uint32le(buf
+ i
);
310 static int get_model(int model_id
)
326 return MODEL_CLIPPLUS
;
329 return MODEL_UNKNOWN
;
332 /* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */
333 static unsigned char* uclpack(unsigned char* inbuf
, int insize
, int* outsize
)
336 unsigned char* outbuf
;
339 /* The following formula comes from the UCL documentation */
340 maxsize
= insize
+ (insize
/ 8) + 256;
342 /* Allocate some memory for the output buffer */
343 outbuf
= malloc(maxsize
);
348 r
= ucl_nrv2e_99_compress(
349 (const ucl_bytep
) inbuf
,
355 if (r
!= UCL_E_OK
|| *outsize
> maxsize
) {
356 /* this should NEVER happen, and implies memory corruption */
357 fprintf(stderr
, "internal error - compression failed: %d\n", r
);
365 #define ERROR(format, ...) \
367 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
371 /* Loads a Sansa AMS Original Firmware file into memory */
372 unsigned char* load_of_file(
373 char* filename
, off_t
* bufsize
, struct md5sums
*sum
,
374 int* firmware_size
, unsigned char** of_packed
,
375 int* of_packedsize
, char* errstr
, int errstrsize
)
378 unsigned char* buf
=NULL
;
383 unsigned int last_word
;
385 fd
= open(filename
, O_RDONLY
|O_BINARY
);
387 ERROR("[ERR] Could not open %s for reading\n", filename
);
389 *bufsize
= filesize(fd
);
391 buf
= malloc(*bufsize
);
393 ERROR("[ERR] Could not allocate memory for %s\n", filename
);
395 n
= read(fd
, buf
, *bufsize
);
398 ERROR("[ERR] Could not read file %s\n", filename
);
402 /* Calculate MD5 checksum of OF */
403 calc_MD5(buf
, *bufsize
, sum
->md5
);
405 while ((i
< NUM_MD5S
) && (strcmp(sansasums
[i
].md5
, sum
->md5
) != 0))
411 int fw_version
= (get_uint32le(&buf
[0x204]) == 0x0000f000) ? 2 : 1;
412 model_id
= buf
[(fw_version
== 2) ? 0x219 : 0x215];
413 sum
->model
= get_model(model_id
);
415 if (sum
->model
== MODEL_UNKNOWN
)
416 ERROR("[ERR] Unknown firmware model (v%d) - model id 0x%02x\n",
417 fw_version
, model_id
);
419 #if 1 /* comment to test new OFs */
420 char tested_versions
[100];
421 tested_versions
[0] = '\0';
423 for (i
= 0; i
< NUM_MD5S
; i
++)
424 if (sansasums
[i
].model
== sum
->model
) {
425 if (tested_versions
[0] != '\0') {
426 strncat(tested_versions
, ", ",
427 sizeof(tested_versions
) - strlen(tested_versions
) - 1);
429 strncat(tested_versions
, sansasums
[i
].version
,
430 sizeof(tested_versions
) - strlen(tested_versions
) - 1);
433 ERROR("[ERR] Original firmware unknown, please try an other version." \
434 " Tested %s versions are : %s\n",
435 model_names
[sum
->model
], tested_versions
);
439 /* TODO: Do some more sanity checks on the OF image. Some images (like
440 m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
441 last_word
= *bufsize
- 4;
442 checksum
= get_uint32le(buf
+ last_word
);
443 if (checksum
!= 0xefbeadde && checksum
!= calc_checksum(buf
, last_word
))
444 ERROR("%s", "[ERR] Whole file checksum failed\n");
446 if (bootloaders
[sum
->model
] == NULL
)
447 ERROR("[ERR] Unsupported model - \"%s\"\n", model_names
[sum
->model
]);
449 /* Get the firmware size */
450 if (fw_revisions
[sum
->model
] == 1)
451 *firmware_size
= get_uint32le(&buf
[0x0c]);
452 else if (fw_revisions
[sum
->model
] == 2)
453 *firmware_size
= get_uint32le(&buf
[0x10]);
455 /* Compress the original firmware image */
456 *of_packed
= uclpack(buf
+ 0x400, *firmware_size
, of_packedsize
);
457 if (*of_packed
== NULL
)
458 ERROR("[ERR] Could not compress %s\n", filename
);
467 /* Loads a rockbox bootloader file into memory */
468 unsigned char* load_rockbox_file(
469 char* filename
, int model
, int* bufsize
, int* rb_packedsize
,
470 char* errstr
, int errstrsize
)
473 unsigned char* buf
= NULL
;
474 unsigned char* packed
= NULL
;
475 unsigned char header
[8];
480 fd
= open(filename
, O_RDONLY
|O_BINARY
);
482 ERROR("[ERR] Could not open %s for reading\n", filename
);
484 /* Read Rockbox header */
485 n
= read(fd
, header
, sizeof(header
));
486 if (n
!= sizeof(header
))
487 ERROR("[ERR] Could not read file %s\n", filename
);
489 /* Check for correct model string */
490 if (memcmp(rb_model_names
[model
], header
+ 4, 4)!=0)
491 ERROR("[ERR] Expected model name \"%s\" in %s, not \"%4.4s\"\n",
492 rb_model_names
[model
], filename
, (char*)header
+4);
494 *bufsize
= filesize(fd
) - sizeof(header
);
496 buf
= malloc(*bufsize
);
498 ERROR("[ERR] Could not allocate memory for %s\n", filename
);
500 n
= read(fd
, buf
, *bufsize
);
503 ERROR("[ERR] Could not read file %s\n", filename
);
506 sum
= rb_model_num
[model
];
507 for (i
= 0; i
< *bufsize
; i
++) {
508 /* add 8 unsigned bits but keep a 32 bit sum */
512 if (sum
!= get_uint32be(header
))
513 ERROR("[ERR] Checksum mismatch in %s\n", filename
);
515 packed
= uclpack(buf
, *bufsize
, rb_packedsize
);
517 ERROR("[ERR] Could not compress %s\n", filename
);
529 /* Patches a Sansa AMS Original Firmware file */
531 int model
, int fw_revision
, int firmware_size
, unsigned char* buf
,
532 int len
, unsigned char* of_packed
, int of_packedsize
,
533 unsigned char* rb_packed
, int rb_packedsize
)
536 uint32_t sum
, filesum
;
540 /* Zero the original firmware area - not needed, but helps debugging */
541 memset(buf
+ 0x400, 0, firmware_size
);
543 /* Insert dual-boot bootloader at offset 0 */
544 memcpy(buf
+ 0x400, bootloaders
[model
], bootloader_sizes
[model
]);
546 /* We are filling the firmware buffer backwards from the end */
547 p
= buf
+ 0x400 + firmware_size
;
549 /* 1 - UCL unpack function */
550 p
-= sizeof(nrv2e_d8
);
551 memcpy(p
, nrv2e_d8
, sizeof(nrv2e_d8
));
553 /* 2 - Compressed copy of original firmware */
555 memcpy(p
, of_packed
, of_packedsize
);
557 /* 3 - Compressed copy of Rockbox bootloader */
559 memcpy(p
, rb_packed
, rb_packedsize
);
561 /* Write the locations of the various images to the variables at the
562 start of the dualboot image - we save the location of the last byte
563 in each image, along with the size in bytes */
565 /* UCL unpack function */
566 put_uint32le(&buf
[0x420], firmware_size
- 1);
567 put_uint32le(&buf
[0x424], sizeof(nrv2e_d8
));
569 /* Compressed original firmware image */
570 put_uint32le(&buf
[0x428], firmware_size
- sizeof(nrv2e_d8
) - 1);
571 put_uint32le(&buf
[0x42c], of_packedsize
);
573 /* Compressed Rockbox image */
574 put_uint32le(&buf
[0x430], firmware_size
- sizeof(nrv2e_d8
) - of_packedsize
576 put_uint32le(&buf
[0x434], rb_packedsize
);
578 ucl_dest
= model_memory_size(model
) - 1; /* last byte of memory */
579 put_uint32le(&buf
[0x438], ucl_dest
);
581 /* Update the firmware block checksum */
582 sum
= calc_checksum(buf
+ 0x400, firmware_size
);
584 if (fw_revision
== 1) {
585 put_uint32le(&buf
[0x04], sum
);
586 put_uint32le(&buf
[0x204], sum
);
587 } else if (fw_revision
== 2) {
588 put_uint32le(&buf
[0x08], sum
);
589 put_uint32le(&buf
[0x208], sum
);
591 /* Update the header checksums */
592 put_uint32le(&buf
[0x1fc], calc_checksum(buf
, 0x1fc));
593 put_uint32le(&buf
[0x3fc], calc_checksum(buf
+ 0x200, 0x1fc));
596 /* Update the whole-file checksum */
598 for (i
=0;i
< (unsigned)len
- 4; i
+=4)
599 filesum
+= get_uint32le(&buf
[i
]);
601 put_uint32le(buf
+ len
- 4, filesum
);
604 /* returns != 0 if the firmware can be safely patched */
605 int check_sizes(int model
, int rb_packed_size
, int rb_unpacked_size
,
606 int of_packed_size
, int of_unpacked_size
, int *total_size
,
607 char *errstr
, int errstrsize
)
609 unsigned int packed_size
= bootloader_sizes
[model
] + sizeof(nrv2e_d8
) +
610 of_packed_size
+ rb_packed_size
;
612 /* how much memory is available */
613 unsigned int memory_size
= model_memory_size(model
);
615 /* the memory used when unpacking the OF */
616 unsigned int ram_of
= sizeof(nrv2e_d8
) + of_packed_size
+ of_unpacked_size
;
618 /* the memory used when unpacking the bootloader */
619 unsigned int ram_rb
= sizeof(nrv2e_d8
) + rb_packed_size
+ rb_unpacked_size
;
621 *total_size
= packed_size
;
623 #define ERROR(format, ...) \
625 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
629 /* will packed data fit in the OF file ? */
630 if(packed_size
> of_unpacked_size
)
632 "[ERR] Packed data (%d bytes) doesn't fit in the firmware "
633 "(%d bytes)\n", packed_size
, of_unpacked_size
636 else if(ram_rb
> memory_size
)
637 ERROR("[ERR] Rockbox can't be unpacked at runtime, needs %d bytes "
638 "of memory and only %d available\n", ram_rb
, memory_size
641 else if(ram_of
> memory_size
)
642 ERROR("[ERR] OF can't be unpacked at runtime, needs %d bytes "
643 "of memory and only %d available\n", ram_of
, memory_size