Oops, source in deutsch.lang should match the english source.
[kugel-rb.git] / rbutil / mkamsboot / mkamsboot.c
blob71bc4868add6abc675fb5b07f68a9b4ee99fd82f
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 int firmware_revision(int model)
221 return fw_revisions[model];
224 static off_t filesize(int fd)
226 struct stat buf;
228 if (fstat(fd, &buf) < 0) {
229 perror("[ERR] Checking filesize of input file");
230 return -1;
231 } else {
232 return(buf.st_size);
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)
248 p[0] = x & 0xff;
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)
256 int i;
257 md5_context ctx;
258 unsigned char md5sum[16];
260 md5_starts(&ctx);
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)
271 uint32_t sum = 0;
272 uint32_t i;
274 for (i=0;i<n;i+=4)
275 sum += get_uint32le(buf + i);
277 return sum;
280 static int get_model(int model_id)
282 switch(model_id) {
283 case 0x1e:
284 return MODEL_FUZE;
285 case 0x22:
286 return MODEL_CLIP;
287 case 0x23:
288 return MODEL_C200V2;
289 case 0x24:
290 return MODEL_E200V2;
291 case 0x25:
292 return MODEL_M200V4;
293 case 0x27:
294 return MODEL_CLIPV2;
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)
303 int maxsize;
304 unsigned char* outbuf;
305 int r;
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);
313 if (outbuf == NULL)
314 return NULL;
316 r = ucl_nrv2e_99_compress(
317 (const ucl_bytep) inbuf,
318 (ucl_uint) insize,
319 (ucl_bytep) outbuf,
320 (ucl_uintp) outsize,
321 0, 10, NULL, NULL);
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);
326 free(outbuf);
327 return NULL;
330 return outbuf;
333 #define ERROR(format, ...) \
334 do { \
335 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
336 goto error; \
337 } while(0)
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)
345 int fd;
346 unsigned char* buf =NULL;
347 off_t n;
348 unsigned int i=0;
349 uint32_t checksum;
350 int model_id;
351 unsigned int last_word;
353 fd = open(filename, O_RDONLY|O_BINARY);
354 if (fd < 0)
355 ERROR("[ERR] Could not open %s for reading\n", filename);
357 *bufsize = filesize(fd);
359 buf = malloc(*bufsize);
360 if (buf == NULL)
361 ERROR("[ERR] Could not allocate memory for %s\n", filename);
363 n = read(fd, buf, *bufsize);
365 if (n != *bufsize)
366 ERROR("[ERR] Could not read file %s\n", filename);
368 /* check the file */
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))
374 i++;
376 if (i < NUM_MD5S) {
377 *sum = sansasums[i];
378 } else {
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);
404 #endif
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);
428 return buf;
430 error:
431 free(buf);
432 return NULL;
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)
440 int fd;
441 unsigned char* buf = NULL;
442 unsigned char* packed = NULL;
443 unsigned char header[8];
444 uint32_t sum;
445 off_t n;
446 int i;
448 fd = open(filename, O_RDONLY|O_BINARY);
449 if (fd < 0)
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);
465 if (buf == NULL)
466 ERROR("[ERR] Could not allocate memory for %s\n", filename);
468 n = read(fd, buf, *bufsize);
470 if (n != *bufsize)
471 ERROR("[ERR] Could not read file %s\n", filename);
473 /* Check checksum */
474 sum = rb_model_num[model];
475 for (i = 0; i < *bufsize; i++) {
476 /* add 8 unsigned bits but keep a 32 bit sum */
477 sum += buf[i];
480 if (sum != get_uint32be(header))
481 ERROR("[ERR] Checksum mismatch in %s\n", filename);
483 packed = uclpack(buf, *bufsize, rb_packedsize);
484 if(packed == NULL)
485 ERROR("[ERR] Could not compress %s\n", filename);
487 free(buf);
488 return packed;
490 error:
491 free(buf);
492 return NULL;
495 #undef ERROR
497 /* Patches a Sansa AMS Original Firmware file */
498 void patch_firmware(
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)
503 unsigned char *p;
504 uint32_t sum, filesum;
505 unsigned int i;
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 */
521 p -= of_packedsize;
522 memcpy(p, of_packed, of_packedsize);
524 /* 3 - Compressed copy of Rockbox bootloader */
525 p -= rb_packedsize;
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
542 - 1);
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 */
562 filesum = 0;
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 +
573 rb_packedsize;
576 #ifndef LIB
577 /* standalone executable */
578 int main(int argc, char* argv[])
580 char *infile, *bootfile, *outfile;
581 int fdout;
582 off_t len;
583 uint32_t n;
584 unsigned char* buf;
585 int firmware_size;
586 int bootloader_size;
587 unsigned char* of_packed;
588 int of_packedsize;
589 unsigned char* rb_packed;
590 int rb_packedsize;
591 int totalsize;
592 char errstr[200];
593 struct md5sums sum;
594 char md5sum[33]; /* 32 digits + \0 */
596 sum.md5 = md5sum;
598 /* VERSION comes frome the Makefile */
599 fprintf(stderr,
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"
603 "\n");
605 if(argc != 4) {
606 printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n");
607 return 1;
610 infile = argv[1];
611 bootfile = argv[2];
612 outfile = argv[3];
614 /* Load original firmware file */
615 buf = load_of_file(infile, &len, &sum,
616 &firmware_size, &of_packed, &of_packedsize, errstr, sizeof(errstr));
618 if (buf == NULL) {
619 fprintf(stderr, "%s", errstr);
620 fprintf(stderr, "[ERR] Could not load %s\n", infile);
621 return 1;
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);
635 free(buf);
636 free(of_packed);
637 return 1;
640 printf("[INFO] Firmware patching has begun !\n\n");
642 fprintf(stderr, "[INFO] Original firmware size: %d bytes\n",
643 firmware_size);
644 fprintf(stderr, "[INFO] Packed OF size: %d bytes\n",
645 of_packedsize);
646 fprintf(stderr, "[INFO] Bootloader size: %d bytes\n",
647 (int)bootloader_size);
648 fprintf(stderr, "[INFO] Packed bootloader size: %d bytes\n",
649 rb_packedsize);
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");
661 free(buf);
662 free(of_packed);
663 free(rb_packed);
664 return 1;
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);
673 if (fdout < 0) {
674 fprintf(stderr, "[ERR] Could not open %s for writing\n", outfile);
675 free(buf);
676 free(of_packed);
677 free(rb_packed);
678 return 1;
681 n = write(fdout, buf, len);
683 if (n != (unsigned)len) {
684 fprintf(stderr, "[ERR] Could not write firmware file\n");
685 free(buf);
686 free(of_packed);
687 free(rb_packed);
688 return 1;
691 close(fdout);
692 free(buf);
693 free(of_packed);
694 free(rb_packed);
695 fprintf(stderr, "\n[INFO] Patching succeeded!\n");
697 return 0;
699 #endif