Ingenic Jz4740 PCM driver: add some locking here and there (doesn't fix all PCM issues)
[kugel-rb.git] / rbutil / mkamsboot / mkamsboot.c
blob9a80d4892a57fd11756bb3e1b85f2171401b76d3
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 #define VERSION "1.0"
114 /* Supported models */
115 enum {
116 MODEL_UNKNOWN = -1,
117 MODEL_FUZE = 0,
118 MODEL_CLIP,
119 MODEL_CLIPV2,
120 MODEL_E200V2,
121 MODEL_M200V4,
122 MODEL_C200V2,
125 /* Descriptive name of these models */
126 static const char* model_names[] = {
127 "Fuze",
128 "Clip",
129 "Clip v2",
130 "e200 v2",
131 "m200 v4",
132 "c200 v2"
135 /* Dualboot functions for these models */
136 static const unsigned char* bootloaders[] = {
137 dualboot_fuze,
138 dualboot_clip,
139 dualboot_clipv2,
140 dualboot_e200v2,
141 dualboot_m200v4,
142 dualboot_c200v2,
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[] = {
158 "fuze",
159 "clip",
160 "clv2",
161 "e2v2",
162 "m2v4",
163 "c2v2",
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[] = {
177 struct md5sums {
178 int model;
179 char *version;
180 int fw_version; /* version 2 is used in Clipv2 and Fuzev2 firmwares */
181 char *md5;
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 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)
221 struct stat buf;
223 if (fstat(fd, &buf) < 0) {
224 perror("[ERR] Checking filesize of input file");
225 return -1;
226 } else {
227 return(buf.st_size);
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)
243 p[0] = x & 0xff;
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)
251 int i;
252 md5_context ctx;
253 unsigned char md5sum[16];
255 md5_starts(&ctx);
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)
266 uint32_t sum = 0;
267 uint32_t i;
269 for (i=0;i<n;i+=4)
270 sum += get_uint32le(buf + i);
272 return sum;
275 static int get_model(int model_id)
277 switch(model_id) {
278 case 0x1e:
279 return MODEL_FUZE;
280 case 0x22:
281 return MODEL_CLIP;
282 case 0x23:
283 return MODEL_C200V2;
284 case 0x24:
285 return MODEL_E200V2;
286 case 0x25:
287 return MODEL_M200V4;
288 case 0x27:
289 return MODEL_CLIPV2;
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)
298 int maxsize;
299 unsigned char* outbuf;
300 int r;
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);
308 if (outbuf == NULL)
309 return NULL;
311 r = ucl_nrv2e_99_compress(
312 (const ucl_bytep) inbuf,
313 (ucl_uint) insize,
314 (ucl_bytep) outbuf,
315 (ucl_uintp) outsize,
316 0, 10, NULL, NULL);
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);
321 free(outbuf);
322 return NULL;
325 return outbuf;
328 #define ERROR(format, ...) \
329 do { \
330 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
331 goto error; \
332 } while(0)
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)
340 int fd;
341 unsigned char* buf =NULL;
342 off_t n;
343 unsigned int i=0;
344 uint32_t checksum;
345 int model_id;
346 unsigned int last_word;
348 fd = open(filename, O_RDONLY|O_BINARY);
349 if (fd < 0)
350 ERROR("[ERR] Could not open %s for reading\n", filename);
352 *bufsize = filesize(fd);
354 buf = malloc(*bufsize);
355 if (buf == NULL)
356 ERROR("[ERR] Could not allocate memory for %s\n", filename);
358 n = read(fd, buf, *bufsize);
360 if (n != *bufsize)
361 ERROR("[ERR] Could not read file %s\n", filename);
363 /* check the file */
365 /* Calculate MD5 checksum of OF */
366 calc_MD5(buf, *bufsize, md5sum);
368 while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, md5sum) != 0))
369 i++;
371 if (i < NUM_MD5S) {
372 *model = sansasums[i].model;
373 *fw_version = sansasums[i].fw_version;
374 } else {
375 if (get_uint32le(&buf[0x204])==0x0000f000) {
376 *fw_version = 2;
377 model_id = buf[0x219];
378 } else {
379 *fw_version = 1;
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);
405 #endif
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);
430 return buf;
432 error:
433 free(buf);
434 return NULL;
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)
442 int fd;
443 unsigned char* buf = NULL;
444 unsigned char* packed = NULL;
445 unsigned char header[8];
446 uint32_t sum;
447 off_t n;
448 int i;
450 fd = open(filename, O_RDONLY|O_BINARY);
451 if (fd < 0)
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);
467 if (buf == NULL)
468 ERROR("[ERR] Could not allocate memory for %s\n", filename);
470 n = read(fd, buf, *bufsize);
472 if (n != *bufsize)
473 ERROR("[ERR] Could not read file %s\n", filename);
475 /* Check checksum */
476 sum = rb_model_num[model];
477 for (i = 0; i < *bufsize; i++) {
478 /* add 8 unsigned bits but keep a 32 bit sum */
479 sum += buf[i];
482 if (sum != get_uint32be(header))
483 ERROR("[ERR] Checksum mismatch in %s\n", filename);
485 packed = uclpack(buf, *bufsize, rb_packedsize);
486 if(packed == NULL)
487 ERROR("[ERR] Could not compress %s\n", filename);
489 free(buf);
490 return packed;
492 error:
493 free(buf);
494 return NULL;
497 #undef ERROR
499 /* Patches a Sansa AMS Original Firmware file */
500 void patch_firmware(
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)
505 unsigned char *p;
506 uint32_t sum, filesum;
507 unsigned int i;
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 */
523 p -= of_packedsize;
524 memcpy(p, of_packed, of_packedsize);
526 /* 3 - Compressed copy of Rockbox bootloader */
527 p -= rb_packedsize;
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
544 - 1);
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);
554 } else {
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 */
564 filesum = 0;
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 +
575 rb_packedsize;
578 #ifndef LIB
579 /* standalone executable */
580 int main(int argc, char* argv[])
582 char *infile, *bootfile, *outfile;
583 int fdout;
584 off_t len;
585 uint32_t n;
586 unsigned char* buf;
587 int firmware_size;
588 int bootloader_size;
589 int model;
590 unsigned char* of_packed;
591 int of_packedsize;
592 unsigned char* rb_packed;
593 int rb_packedsize;
594 int fw_version;
595 int totalsize;
596 char md5sum[33]; /* 32 hex digits, plus terminating zero */
597 char errstr[200];
599 fprintf(stderr,
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"
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, md5sum, &model, &fw_version,
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 - %s\n",
625 model_names[model]);
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);
634 free(buf);
635 free(of_packed);
636 return 1;
639 printf("[INFO] Patching %s firmware\n", model_names[model]);
641 fprintf(stderr, "[INFO] Original firmware size: %d bytes\n",
642 firmware_size);
643 fprintf(stderr, "[INFO] Packed OF size: %d bytes\n",
644 of_packedsize);
645 fprintf(stderr, "[INFO] Bootloader size: %d bytes\n",
646 (int)bootloader_size);
647 fprintf(stderr, "[INFO] Packed bootloader size: %d bytes\n",
648 rb_packedsize);
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",
652 sizeof(nrv2e_d8));
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");
660 free(buf);
661 free(of_packed);
662 free(rb_packed);
663 return 1;
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);
672 if (fdout < 0) {
673 fprintf(stderr, "[ERR] Could not open %s for writing\n", outfile);
674 free(buf);
675 free(of_packed);
676 free(rb_packed);
677 return 1;
680 n = write(fdout, buf, len);
682 if (n != (unsigned)len) {
683 fprintf(stderr, "[ERR] Could not write firmware file\n");
684 free(buf);
685 free(of_packed);
686 free(rb_packed);
687 return 1;
690 close(fdout);
691 free(buf);
692 free(of_packed);
693 free(rb_packed);
695 return 0;
697 #endif