Clip+: show the same real time in OF and rockbox
[kugel-rb.git] / rbutil / mkamsboot / mkamsboot.c
blob9e0099d1b6c306556b26d84f4f6424e117b3a43a
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/clip, 1 for clip+ */
113 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,
120 [MODEL_CLIPPLUS]= 1,
123 /* version 2 is used in Clipv2, Clip+ and Fuzev2 firmwares */
124 const unsigned short fw_revisions[] = {
125 [MODEL_FUZE] = 1,
126 [MODEL_CLIP] = 1,
127 [MODEL_CLIPV2] = 2,
128 [MODEL_E200V2] = 1,
129 [MODEL_M200V4] = 1,
130 [MODEL_C200V2] = 1,
131 [MODEL_CLIPPLUS]= 2,
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[] = {
182 [MODEL_FUZE] = 43,
183 [MODEL_CLIP] = 40,
184 [MODEL_CLIPV2] = 60,
185 [MODEL_E200V2] = 41,
186 [MODEL_M200V4] = 42,
187 [MODEL_C200V2] = 44,
188 [MODEL_CLIPPLUS]= 66,
191 /* Checksums of unmodified original firmwares - for safety, and device
192 detection */
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
241 return 1 << 20;
243 else
244 { /* The OF boots with IRAM (320kB) mapped at 0x0 */
245 return 320 << 10;
249 int firmware_revision(int model)
251 return fw_revisions[model];
254 static off_t filesize(int fd)
256 struct stat buf;
258 if (fstat(fd, &buf) < 0) {
259 perror("[ERR] Checking filesize of input file");
260 return -1;
261 } else {
262 return(buf.st_size);
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)
278 p[0] = x & 0xff;
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)
286 int i;
287 md5_context ctx;
288 unsigned char md5sum[16];
290 md5_starts(&ctx);
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)
301 uint32_t sum = 0;
302 uint32_t i;
304 for (i=0;i<n;i+=4)
305 sum += get_uint32le(buf + i);
307 return sum;
310 static int get_model(int model_id)
312 switch(model_id) {
313 case 0x1e:
314 return MODEL_FUZE;
315 case 0x22:
316 return MODEL_CLIP;
317 case 0x23:
318 return MODEL_C200V2;
319 case 0x24:
320 return MODEL_E200V2;
321 case 0x25:
322 return MODEL_M200V4;
323 case 0x27:
324 return MODEL_CLIPV2;
325 case 0x28:
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)
335 int maxsize;
336 unsigned char* outbuf;
337 int r;
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);
345 if (outbuf == NULL)
346 return NULL;
348 r = ucl_nrv2e_99_compress(
349 (const ucl_bytep) inbuf,
350 (ucl_uint) insize,
351 (ucl_bytep) outbuf,
352 (ucl_uintp) outsize,
353 0, 10, NULL, NULL);
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);
358 free(outbuf);
359 return NULL;
362 return outbuf;
365 #define ERROR(format, ...) \
366 do { \
367 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
368 goto error; \
369 } while(0)
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)
377 int fd;
378 unsigned char* buf =NULL;
379 off_t n;
380 unsigned int i=0;
381 uint32_t checksum;
382 int model_id;
383 unsigned int last_word;
385 fd = open(filename, O_RDONLY|O_BINARY);
386 if (fd < 0)
387 ERROR("[ERR] Could not open %s for reading\n", filename);
389 *bufsize = filesize(fd);
391 buf = malloc(*bufsize);
392 if (buf == NULL)
393 ERROR("[ERR] Could not allocate memory for %s\n", filename);
395 n = read(fd, buf, *bufsize);
397 if (n != *bufsize)
398 ERROR("[ERR] Could not read file %s\n", filename);
400 /* check the file */
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))
406 i++;
408 if (i < NUM_MD5S) {
409 *sum = sansasums[i];
410 } else {
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);
436 #endif
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);
460 return buf;
462 error:
463 free(buf);
464 return NULL;
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)
472 int fd;
473 unsigned char* buf = NULL;
474 unsigned char* packed = NULL;
475 unsigned char header[8];
476 uint32_t sum;
477 off_t n;
478 int i;
480 fd = open(filename, O_RDONLY|O_BINARY);
481 if (fd < 0)
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);
497 if (buf == NULL)
498 ERROR("[ERR] Could not allocate memory for %s\n", filename);
500 n = read(fd, buf, *bufsize);
502 if (n != *bufsize)
503 ERROR("[ERR] Could not read file %s\n", filename);
505 /* Check checksum */
506 sum = rb_model_num[model];
507 for (i = 0; i < *bufsize; i++) {
508 /* add 8 unsigned bits but keep a 32 bit sum */
509 sum += buf[i];
512 if (sum != get_uint32be(header))
513 ERROR("[ERR] Checksum mismatch in %s\n", filename);
515 packed = uclpack(buf, *bufsize, rb_packedsize);
516 if(packed == NULL)
517 ERROR("[ERR] Could not compress %s\n", filename);
519 free(buf);
520 return packed;
522 error:
523 free(buf);
524 return NULL;
527 #undef ERROR
529 /* Patches a Sansa AMS Original Firmware file */
530 void patch_firmware(
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)
535 unsigned char *p;
536 uint32_t sum, filesum;
537 uint32_t ucl_dest;
538 unsigned int i;
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 */
554 p -= of_packedsize;
555 memcpy(p, of_packed, of_packedsize);
557 /* 3 - Compressed copy of Rockbox bootloader */
558 p -= rb_packedsize;
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
575 - 1);
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 */
597 filesum = 0;
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, ...) \
624 do { \
625 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
626 return 0; \
627 } while(0)
629 /* will packed data fit in the OF file ? */
630 if(packed_size > of_unpacked_size)
631 ERROR(
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
646 return 1;
648 #undef ERROR