blob: f47afe312a54ed9b6862b3482838f139c38e6569 [file] [log] [blame]
Dave Chapman3c65d2b2008-10-11 13:11:47 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Dave Chapman4f802742008-10-11 13:13:44 +00008 * $Id$
Dave Chapman3c65d2b2008-10-11 13:11:47 +00009 *
10 * mkamsboot.c - a tool for merging bootloader code into an Sansa V2
11 * (AMS) firmware file
12 *
13 * Copyright (C) 2008 Dave Chapman
14 *
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.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
Dave Chapman1aa6cde2008-05-11 18:29:53 +000024
25/*
26
Rafaël Carré96165ab2009-05-28 18:27:08 +000027Insert a Rockbox bootloader into a Sansa AMS original firmware file.
28
29Layout of a Sansa AMS original firmware file:
30
31 ---------------------- 0x0
32| HEADER |
33|----------------------| 0x400
34| FIRMWARE BLOCK |
35|----------------------| 0x400 + firmware block size
36| LIBRARIES/DATA |
37 ---------------------- END
Dave Chapman1aa6cde2008-05-11 18:29:53 +000038
Dave Chapmanc2933cd2008-10-02 12:37:47 +000039We replace the main firmware block (bytes 0x400..0x400+firmware_size)
40as follows:
Dave Chapman1aa6cde2008-05-11 18:29:53 +000041
Dave Chapman1aa6cde2008-05-11 18:29:53 +000042
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000043 ---------------------- 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 ----------------------
Dave Chapman1aa6cde2008-05-11 18:29:53 +000060
Dave Chapmanc2933cd2008-10-02 12:37:47 +000061This entire block fits into the space previously occupied by the main
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000062firmware block - the space saved by compressing the OF image is used
Rafaël Carré96165ab2009-05-28 18:27:08 +000063to store the compressed version of the Rockbox bootloader.
64
65On version 1 firmwares, the OF image is typically about 120KB, which allows
66us to store a Rockbox bootloader with an uncompressed size of about 60KB-70KB.
67Version 2 firmwares are bigger and are stored in SDRAM (instead of IRAM).
68In both cases, the RAM we are using is mapped at offset 0x0.
Dave Chapman1aa6cde2008-05-11 18:29:53 +000069
Dave Chapmanc2933cd2008-10-02 12:37:47 +000070mkamsboot then corrects the checksums and writes a new legal firmware
71file which can be installed on the device.
Dave Chapman1aa6cde2008-05-11 18:29:53 +000072
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000073When the Sansa device boots, this firmware block is loaded to RAM at
74address 0x0 and executed.
Dave Chapman1aa6cde2008-05-11 18:29:53 +000075
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000076Firstly, the dual-boot code will copy the UCL unpack function to the
77end of RAM.
Dave Chapman757f5112008-10-01 09:15:44 +000078
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000079Then, depending on the detection of the dual-boot keypress, either the
80OF image or the Rockbox image is copied to the end of RAM (just before
Rafaël Carré96165ab2009-05-28 18:27:08 +000081the ucl unpack function) and uncompressed to the start of RAM.
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000082
83Finally, the ucl unpack function branches to address 0x0, passing
84execution to the uncompressed firmware.
85
Dave Chapman757f5112008-10-01 09:15:44 +000086
Dave Chapman1aa6cde2008-05-11 18:29:53 +000087*/
88
Dave Chapman1aa6cde2008-05-11 18:29:53 +000089#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>
97
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000098#include <ucl/ucl.h>
99
Rafaël Carré96165ab2009-05-28 18:27:08 +0000100#include "mkamsboot.h"
101
Dave Chapmanc91d7872008-10-30 00:13:29 +0000102#include "md5.h"
103
Rafaël Carré96165ab2009-05-28 18:27:08 +0000104/* Header for ARM code binaries */
105#include "dualboot.h"
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000106
107/* Win32 compatibility */
108#ifndef O_BINARY
109#define O_BINARY 0
110#endif
111
Rafaël Carré8b926e92010-01-13 03:05:29 +0000112/* 4 for m200, 2 for e200/c200, 1 or 2 for fuze/clip, 1 for clip+ */
Dominik Riebelingea617802009-11-04 20:58:40 +0000113const unsigned short hw_revisions[] = {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000114 [MODEL_FUZE] = 1,
115 [MODEL_CLIP] = 1,
116 [MODEL_CLIPV2] = 2,
117 [MODEL_E200V2] = 2,
118 [MODEL_M200V4] = 4,
119 [MODEL_C200V2] = 2,
Rafaël Carré8b926e92010-01-13 03:05:29 +0000120 [MODEL_CLIPPLUS]= 1,
Thomas Martitzf6540e82010-02-26 13:55:17 +0000121 [MODEL_FUZEV2] = 2,
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000122};
123
Rafaël Carré8b926e92010-01-13 03:05:29 +0000124/* version 2 is used in Clipv2, Clip+ and Fuzev2 firmwares */
Dominik Riebelingea617802009-11-04 20:58:40 +0000125const unsigned short fw_revisions[] = {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000126 [MODEL_FUZE] = 1,
127 [MODEL_CLIP] = 1,
128 [MODEL_CLIPV2] = 2,
129 [MODEL_E200V2] = 1,
130 [MODEL_M200V4] = 1,
131 [MODEL_C200V2] = 1,
Rafaël Carré8b926e92010-01-13 03:05:29 +0000132 [MODEL_CLIPPLUS]= 2,
Thomas Martitzf6540e82010-02-26 13:55:17 +0000133 [MODEL_FUZEV2] = 2,
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000134};
135
Rafaël Carré96165ab2009-05-28 18:27:08 +0000136/* Descriptive name of these models */
Dominik Riebelingea617802009-11-04 20:58:40 +0000137const char* model_names[] = {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000138 [MODEL_FUZE] = "Fuze",
139 [MODEL_CLIP] = "Clip",
140 [MODEL_CLIPV2] = "Clip",
Rafaël Carré8b926e92010-01-13 03:05:29 +0000141 [MODEL_CLIPPLUS]= "Clip+",
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000142 [MODEL_E200V2] = "e200",
143 [MODEL_M200V4] = "m200",
144 [MODEL_C200V2] = "c200",
Thomas Martitzf6540e82010-02-26 13:55:17 +0000145 [MODEL_FUZEV2] = "Fuze",
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000146};
147
Rafaël Carré96165ab2009-05-28 18:27:08 +0000148/* Dualboot functions for these models */
149static const unsigned char* bootloaders[] = {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000150 [MODEL_FUZE] = dualboot_fuze,
151 [MODEL_CLIP] = dualboot_clip,
152 [MODEL_CLIPV2] = dualboot_clipv2,
153 [MODEL_E200V2] = dualboot_e200v2,
154 [MODEL_M200V4] = dualboot_m200v4,
155 [MODEL_C200V2] = dualboot_c200v2,
Rafaël Carré8b926e92010-01-13 03:05:29 +0000156 [MODEL_CLIPPLUS]= dualboot_clipplus,
Thomas Martitzf6540e82010-02-26 13:55:17 +0000157 [MODEL_FUZEV2] = dualboot_fuzev2,
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000158};
159
Rafaël Carré96165ab2009-05-28 18:27:08 +0000160/* Size of dualboot functions for these models */
Dominik Riebelingea617802009-11-04 20:58:40 +0000161const int bootloader_sizes[] = {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000162 [MODEL_FUZE] = sizeof(dualboot_fuze),
163 [MODEL_CLIP] = sizeof(dualboot_clip),
164 [MODEL_CLIPV2] = sizeof(dualboot_clipv2),
165 [MODEL_E200V2] = sizeof(dualboot_e200v2),
166 [MODEL_M200V4] = sizeof(dualboot_m200v4),
167 [MODEL_C200V2] = sizeof(dualboot_c200v2),
Rafaël Carré8b926e92010-01-13 03:05:29 +0000168 [MODEL_CLIPPLUS]= sizeof(dualboot_clipplus),
Thomas Martitzf6540e82010-02-26 13:55:17 +0000169 [MODEL_FUZEV2] = sizeof(dualboot_fuzev2),
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000170};
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000171
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000172/* Model names used in the Rockbox header in ".sansa" files - these match the
173 -add parameter to the "scramble" tool */
Rafaël Carré96165ab2009-05-28 18:27:08 +0000174static const char* rb_model_names[] = {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000175 [MODEL_FUZE] = "fuze",
176 [MODEL_CLIP] = "clip",
177 [MODEL_CLIPV2] = "clv2",
178 [MODEL_E200V2] = "e2v2",
179 [MODEL_M200V4] = "m2v4",
180 [MODEL_C200V2] = "c2v2",
Rafaël Carré8b926e92010-01-13 03:05:29 +0000181 [MODEL_CLIPPLUS]= "cli+",
Thomas Martitzf6540e82010-02-26 13:55:17 +0000182 [MODEL_FUZEV2] = "fuz2",
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000183};
184
185/* Model numbers used to initialise the checksum in the Rockbox header in
186 ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */
Rafaël Carré96165ab2009-05-28 18:27:08 +0000187static const int rb_model_num[] = {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000188 [MODEL_FUZE] = 43,
189 [MODEL_CLIP] = 40,
190 [MODEL_CLIPV2] = 60,
191 [MODEL_E200V2] = 41,
192 [MODEL_M200V4] = 42,
Rafaël Carré8b926e92010-01-13 03:05:29 +0000193 [MODEL_C200V2] = 44,
194 [MODEL_CLIPPLUS]= 66,
Thomas Martitzf6540e82010-02-26 13:55:17 +0000195 [MODEL_FUZEV2] = 68,
Dave Chapmanc91d7872008-10-30 00:13:29 +0000196};
197
198/* Checksums of unmodified original firmwares - for safety, and device
199 detection */
200static struct md5sums sansasums[] = {
201 /* NOTE: Different regional versions of the firmware normally only
202 differ in the filename - the md5sums are identical */
Dave Chapmanc91d7872008-10-30 00:13:29 +0000203
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000204 /* model version md5 */
205 { MODEL_E200V2, "3.01.11", "e622ca8cb6df423f54b8b39628a1f0a3" },
206 { MODEL_E200V2, "3.01.14", "2c1d0383fc3584b2cc83ba8cc2243af6" },
207 { MODEL_E200V2, "3.01.16", "12563ad71b25a1034cf2092d1e0218c4" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000208
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000209 { MODEL_FUZE, "1.01.11", "cac8ffa03c599330ac02c4d41de66166" },
210 { MODEL_FUZE, "1.01.15", "df0e2c1612727f722c19a3c764cff7f2" },
211 { MODEL_FUZE, "1.01.22", "5aff5486fe8dd64239cc71eac470af98" },
212 { MODEL_FUZE, "1.02.26", "7c632c479461c48c8833baed74eb5e4f" },
Rafaël Carrée51dbc02009-10-10 11:21:15 +0000213 { MODEL_FUZE, "1.02.28", "5b34260f6470e75f702a9c6825471752" },
Michael Chicoineb1dfdd42010-01-21 14:34:04 +0000214 { MODEL_FUZE, "1.02.31", "66d01b37462a5ef7ccc6ad37188b4235" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000215
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000216 { MODEL_C200V2, "3.02.05", "b6378ebd720b0ade3fad4dc7ab61c1a5" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000217
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000218 { MODEL_M200V4, "4.00.45", "82e3194310d1514e3bbcd06e84c4add3" },
219 { MODEL_M200V4, "4.01.08-A", "fc9dd6116001b3e6a150b898f1b091f0" },
220 { MODEL_M200V4, "4.01.08-E", "d3fb7d8ec8624ee65bc99f8dab0e2369" },
Rafaël Carré96165ab2009-05-28 18:27:08 +0000221
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000222 { MODEL_CLIP, "1.01.17", "12caad785d506219d73f538772afd99e" },
223 { MODEL_CLIP, "1.01.18", "d720b266bd5afa38a198986ef0508a45" },
224 { MODEL_CLIP, "1.01.20", "236d8f75189f468462c03f6d292cf2ac" },
225 { MODEL_CLIP, "1.01.29", "c12711342169c66e209540cd1f27cd26" },
226 { MODEL_CLIP, "1.01.30", "f2974d47c536549c9d8259170f1dbe4d" },
227 { MODEL_CLIP, "1.01.32", "d835d12342500732ffb9c4ee54abec15" },
Rafaël Carréa384cdf2010-03-16 02:11:04 +0000228 { MODEL_CLIP, "1.01.35", "b4d0edb3b8f2a4e8eee0a344f7f8e480" },
Rafaël Carré96165ab2009-05-28 18:27:08 +0000229
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000230 { MODEL_CLIPV2, "2.01.16", "c57fb3fcbe07c2c9b360f060938f80cb" },
Rafaël Carré8b926e92010-01-13 03:05:29 +0000231 { MODEL_CLIPV2, "2.01.32", "0ad3723e52022509089d938d0fbbf8c5" },
Rafaël Carré20fccd82010-03-22 08:32:04 +0000232 { MODEL_CLIPV2, "2.01.35", "a3cbbd22b9508d7f8a9a1a39acc342c2" },
Rafaël Carré8b926e92010-01-13 03:05:29 +0000233
Rafaël Carré8b926e92010-01-13 03:05:29 +0000234 { MODEL_CLIPPLUS, "01.02.09", "656d38114774c2001dc18e6726df3c5d" },
Rafaël Carréfdf8e2d2010-04-17 12:17:01 +0000235 { MODEL_CLIPPLUS, "01.02.13", "5f89872b79ef440b0e5ee3a7a44328b2" },
Rafaël Carréd9e508b2010-05-20 18:51:49 +0000236 { MODEL_CLIPPLUS, "01.02.15", "680a4f521e790ad25b93b1b16f3a207d" },
Thomas Martitzf6540e82010-02-26 13:55:17 +0000237
238 { MODEL_FUZEV2, "2.01.17", "8b85fb05bf645d08a4c8c3e344ec9ebe" },
239 { MODEL_FUZEV2, "2.02.26", "d4f6f85c3e4a8ea8f2e5acc421641801" },
Rafaël Carrébe920c42010-03-28 02:58:00 +0000240 { MODEL_FUZEV2, "2.03.31", "74fb197ccd51707388f3b233402186a6" },
Rafaël Carrée2d1eb62010-05-21 13:13:53 +0000241 { MODEL_FUZEV2, "2.03.33", "1599cc73d02ea7fe53fe2d4379c24b66" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000242};
243
244#define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0]))
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000245
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000246static unsigned int model_memory_size(int model)
247{
248 if(model == MODEL_CLIPV2)
249 {
250 /* The decompressed Clipv2 OF is around 380kB.
251 * Since it doesn't fit in the 0x50000 bytes IRAM, the OF starts
252 * with DRAM mapped at 0x0
253 *
254 * We could use all the available memory (supposedly 8MB)
255 * but 1MB ought to be enough for our use
256 */
257 return 1 << 20;
258 }
259 else
260 { /* The OF boots with IRAM (320kB) mapped at 0x0 */
261 return 320 << 10;
262 }
263}
264
Dominik Wengera66dfa42009-08-15 13:04:21 +0000265int firmware_revision(int model)
266{
267 return fw_revisions[model];
268}
269
Rafaël Carréa96f2372009-05-28 21:19:21 +0000270static off_t filesize(int fd)
271{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000272 struct stat buf;
273
Rafaël Carré96165ab2009-05-28 18:27:08 +0000274 if (fstat(fd, &buf) < 0) {
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000275 perror("[ERR] Checking filesize of input file");
276 return -1;
277 } else {
278 return(buf.st_size);
279 }
280}
281
Rafaël Carréa96f2372009-05-28 21:19:21 +0000282static uint32_t get_uint32le(unsigned char* p)
283{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000284 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
285}
286
Rafaël Carréa96f2372009-05-28 21:19:21 +0000287static uint32_t get_uint32be(unsigned char* p)
288{
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000289 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
290}
291
Rafaël Carréa96f2372009-05-28 21:19:21 +0000292static void put_uint32le(unsigned char* p, uint32_t x)
293{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000294 p[0] = x & 0xff;
295 p[1] = (x >> 8) & 0xff;
296 p[2] = (x >> 16) & 0xff;
297 p[3] = (x >> 24) & 0xff;
298}
299
Rafaël Carréa96f2372009-05-28 21:19:21 +0000300void calc_MD5(unsigned char* buf, int len, char *md5str)
301{
Dave Chapmanc91d7872008-10-30 00:13:29 +0000302 int i;
303 md5_context ctx;
304 unsigned char md5sum[16];
Rafaël Carré96165ab2009-05-28 18:27:08 +0000305
Dave Chapmanc91d7872008-10-30 00:13:29 +0000306 md5_starts(&ctx);
307 md5_update(&ctx, buf, len);
308 md5_finish(&ctx, md5sum);
309
310 for (i = 0; i < 16; ++i)
311 sprintf(md5str + 2*i, "%02x", md5sum[i]);
312}
313
Rafaël Carré96165ab2009-05-28 18:27:08 +0000314/* Calculate a simple checksum used in Sansa Original Firmwares */
Rafaël Carréa96f2372009-05-28 21:19:21 +0000315static uint32_t calc_checksum(unsigned char* buf, uint32_t n)
316{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000317 uint32_t sum = 0;
318 uint32_t i;
319
320 for (i=0;i<n;i+=4)
321 sum += get_uint32le(buf + i);
322
323 return sum;
324}
325
Rafaël Carré96165ab2009-05-28 18:27:08 +0000326/* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */
Rafaël Carréa96f2372009-05-28 21:19:21 +0000327static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize)
328{
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000329 int maxsize;
330 unsigned char* outbuf;
331 int r;
332
333 /* The following formula comes from the UCL documentation */
334 maxsize = insize + (insize / 8) + 256;
335
336 /* Allocate some memory for the output buffer */
337 outbuf = malloc(maxsize);
338
Rafaël Carré96165ab2009-05-28 18:27:08 +0000339 if (outbuf == NULL)
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000340 return NULL;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000341
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000342 r = ucl_nrv2e_99_compress(
343 (const ucl_bytep) inbuf,
344 (ucl_uint) insize,
345 (ucl_bytep) outbuf,
346 (ucl_uintp) outsize,
347 0, 10, NULL, NULL);
348
Rafaël Carré96165ab2009-05-28 18:27:08 +0000349 if (r != UCL_E_OK || *outsize > maxsize) {
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000350 /* this should NEVER happen, and implies memory corruption */
351 fprintf(stderr, "internal error - compression failed: %d\n", r);
352 free(outbuf);
353 return NULL;
354 }
355
356 return outbuf;
357}
358
Rafaël Carré96165ab2009-05-28 18:27:08 +0000359#define ERROR(format, ...) \
360 do { \
361 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
362 goto error; \
363 } while(0)
364
365/* Loads a Sansa AMS Original Firmware file into memory */
366unsigned char* load_of_file(
Rafaël Carréff6b0422010-05-24 10:06:52 +0000367 char* filename, int model, off_t* bufsize, struct md5sums *sum,
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000368 int* firmware_size, unsigned char** of_packed,
Rafaël Carréa96f2372009-05-28 21:19:21 +0000369 int* of_packedsize, char* errstr, int errstrsize)
370{
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000371 int fd;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000372 unsigned char* buf =NULL;
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000373 off_t n;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000374 unsigned int i=0;
375 uint32_t checksum;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000376 unsigned int last_word;
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000377
378 fd = open(filename, O_RDONLY|O_BINARY);
379 if (fd < 0)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000380 ERROR("[ERR] Could not open %s for reading\n", filename);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000381
382 *bufsize = filesize(fd);
383
384 buf = malloc(*bufsize);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000385 if (buf == NULL)
386 ERROR("[ERR] Could not allocate memory for %s\n", filename);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000387
388 n = read(fd, buf, *bufsize);
389
Rafaël Carré96165ab2009-05-28 18:27:08 +0000390 if (n != *bufsize)
391 ERROR("[ERR] Could not read file %s\n", filename);
392
393 /* check the file */
394
395 /* Calculate MD5 checksum of OF */
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000396 calc_MD5(buf, *bufsize, sum->md5);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000397
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000398 while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, sum->md5) != 0))
Rafaël Carré96165ab2009-05-28 18:27:08 +0000399 i++;
400
401 if (i < NUM_MD5S) {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000402 *sum = sansasums[i];
Rafaël Carréff6b0422010-05-24 10:06:52 +0000403 if(sum->model != model) {
404 ERROR("[ERR] OF File provided is %sv%d version %s, not for %sv%d\n",
405 model_names[sum->model], hw_revisions[sum->model],
406 sum->version, model_names[model], hw_revisions[model]
407 );
408 }
Rafaël Carré96165ab2009-05-28 18:27:08 +0000409 } else {
Rafaël Carréff6b0422010-05-24 10:06:52 +0000410 /* OF unknown, give a list of tested versions for the requested model */
Rafaël Carré96165ab2009-05-28 18:27:08 +0000411
Rafaël Carré8bb5e432009-06-04 16:31:11 +0000412 char tested_versions[100];
413 tested_versions[0] = '\0';
414
415 for (i = 0; i < NUM_MD5S ; i++)
Rafaël Carréff6b0422010-05-24 10:06:52 +0000416 if (sansasums[i].model == model) {
Rafaël Carré8bb5e432009-06-04 16:31:11 +0000417 if (tested_versions[0] != '\0') {
418 strncat(tested_versions, ", ",
419 sizeof(tested_versions) - strlen(tested_versions) - 1);
420 }
421 strncat(tested_versions, sansasums[i].version,
422 sizeof(tested_versions) - strlen(tested_versions) - 1);
423 }
424
425 ERROR("[ERR] Original firmware unknown, please try an other version." \
Rafaël Carréff6b0422010-05-24 10:06:52 +0000426 " Tested %sv%d versions are : %s\n",
427 model_names[model], hw_revisions[model], tested_versions);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000428 }
429
Rafaël Carré96165ab2009-05-28 18:27:08 +0000430 /* TODO: Do some more sanity checks on the OF image. Some images (like
431 m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
432 last_word = *bufsize - 4;
433 checksum = get_uint32le(buf + last_word);
434 if (checksum != 0xefbeadde && checksum != calc_checksum(buf, last_word))
435 ERROR("%s", "[ERR] Whole file checksum failed\n");
436
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000437 if (bootloaders[sum->model] == NULL)
438 ERROR("[ERR] Unsupported model - \"%s\"\n", model_names[sum->model]);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000439
440 /* Get the firmware size */
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000441 if (fw_revisions[sum->model] == 1)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000442 *firmware_size = get_uint32le(&buf[0x0c]);
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000443 else if (fw_revisions[sum->model] == 2)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000444 *firmware_size = get_uint32le(&buf[0x10]);
445
446 /* Compress the original firmware image */
447 *of_packed = uclpack(buf + 0x400, *firmware_size, of_packedsize);
448 if (*of_packed == NULL)
449 ERROR("[ERR] Could not compress %s\n", filename);
450
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000451 return buf;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000452
453error:
454 free(buf);
455 return NULL;
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000456}
457
Rafaël Carré96165ab2009-05-28 18:27:08 +0000458/* Loads a rockbox bootloader file into memory */
459unsigned char* load_rockbox_file(
Rafaël Carréff6b0422010-05-24 10:06:52 +0000460 char* filename, int *model, int* bufsize, int* rb_packedsize,
Rafaël Carréa96f2372009-05-28 21:19:21 +0000461 char* errstr, int errstrsize)
462{
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000463 int fd;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000464 unsigned char* buf = NULL;
465 unsigned char* packed = NULL;
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000466 unsigned char header[8];
467 uint32_t sum;
468 off_t n;
469 int i;
470
471 fd = open(filename, O_RDONLY|O_BINARY);
472 if (fd < 0)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000473 ERROR("[ERR] Could not open %s for reading\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000474
475 /* Read Rockbox header */
476 n = read(fd, header, sizeof(header));
Rafaël Carré96165ab2009-05-28 18:27:08 +0000477 if (n != sizeof(header))
478 ERROR("[ERR] Could not read file %s\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000479
Rafaël Carréff6b0422010-05-24 10:06:52 +0000480 for(*model = 0; *model < NUM_MODELS; (*model)++)
481 if (memcmp(rb_model_names[*model], header + 4, 4) == 0)
482 break;
483
484 if(*model == NUM_MODELS)
485 ERROR("[ERR] Model name \"%4.4s\" unknown. Is this really a rockbox bootloader?\n", header + 4);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000486
487 *bufsize = filesize(fd) - sizeof(header);
488
489 buf = malloc(*bufsize);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000490 if (buf == NULL)
491 ERROR("[ERR] Could not allocate memory for %s\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000492
493 n = read(fd, buf, *bufsize);
494
Rafaël Carré96165ab2009-05-28 18:27:08 +0000495 if (n != *bufsize)
496 ERROR("[ERR] Could not read file %s\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000497
498 /* Check checksum */
Rafaël Carréff6b0422010-05-24 10:06:52 +0000499 sum = rb_model_num[*model];
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000500 for (i = 0; i < *bufsize; i++) {
501 /* add 8 unsigned bits but keep a 32 bit sum */
502 sum += buf[i];
503 }
504
Rafaël Carré96165ab2009-05-28 18:27:08 +0000505 if (sum != get_uint32be(header))
506 ERROR("[ERR] Checksum mismatch in %s\n", filename);
507
508 packed = uclpack(buf, *bufsize, rb_packedsize);
509 if(packed == NULL)
510 ERROR("[ERR] Could not compress %s\n", filename);
511
512 free(buf);
513 return packed;
514
515error:
516 free(buf);
517 return NULL;
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000518}
519
Rafaël Carré96165ab2009-05-28 18:27:08 +0000520#undef ERROR
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000521
Rafaël Carré96165ab2009-05-28 18:27:08 +0000522/* Patches a Sansa AMS Original Firmware file */
523void patch_firmware(
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000524 int model, int fw_revision, int firmware_size, unsigned char* buf,
Rafaël Carré96165ab2009-05-28 18:27:08 +0000525 int len, unsigned char* of_packed, int of_packedsize,
Rafaël Carréa96f2372009-05-28 21:19:21 +0000526 unsigned char* rb_packed, int rb_packedsize)
527{
Rafaël Carré96165ab2009-05-28 18:27:08 +0000528 unsigned char *p;
529 uint32_t sum, filesum;
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000530 uint32_t ucl_dest;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000531 unsigned int i;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000532
Dave Chapman757f5112008-10-01 09:15:44 +0000533 /* Zero the original firmware area - not needed, but helps debugging */
534 memset(buf + 0x400, 0, firmware_size);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000535
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000536 /* Insert dual-boot bootloader at offset 0 */
537 memcpy(buf + 0x400, bootloaders[model], bootloader_sizes[model]);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000538
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000539 /* We are filling the firmware buffer backwards from the end */
540 p = buf + 0x400 + firmware_size;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000541
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000542 /* 1 - UCL unpack function */
Dave Chapmandcb0e432008-11-14 22:16:22 +0000543 p -= sizeof(nrv2e_d8);
544 memcpy(p, nrv2e_d8, sizeof(nrv2e_d8));
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000545
546 /* 2 - Compressed copy of original firmware */
547 p -= of_packedsize;
548 memcpy(p, of_packed, of_packedsize);
549
550 /* 3 - Compressed copy of Rockbox bootloader */
551 p -= rb_packedsize;
552 memcpy(p, rb_packed, rb_packedsize);
553
Rafaël Carré96165ab2009-05-28 18:27:08 +0000554 /* Write the locations of the various images to the variables at the
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000555 start of the dualboot image - we save the location of the last byte
556 in each image, along with the size in bytes */
557
558 /* UCL unpack function */
Dave Chapman1a549d42008-10-11 12:02:23 +0000559 put_uint32le(&buf[0x420], firmware_size - 1);
Dave Chapmandcb0e432008-11-14 22:16:22 +0000560 put_uint32le(&buf[0x424], sizeof(nrv2e_d8));
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000561
562 /* Compressed original firmware image */
Dave Chapmandcb0e432008-11-14 22:16:22 +0000563 put_uint32le(&buf[0x428], firmware_size - sizeof(nrv2e_d8) - 1);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000564 put_uint32le(&buf[0x42c], of_packedsize);
565
566 /* Compressed Rockbox image */
Rafaël Carré96165ab2009-05-28 18:27:08 +0000567 put_uint32le(&buf[0x430], firmware_size - sizeof(nrv2e_d8) - of_packedsize
568 - 1);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000569 put_uint32le(&buf[0x434], rb_packedsize);
Dave Chapman757f5112008-10-01 09:15:44 +0000570
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000571 ucl_dest = model_memory_size(model) - 1; /* last byte of memory */
572 put_uint32le(&buf[0x438], ucl_dest);
Dave Chapman757f5112008-10-01 09:15:44 +0000573
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000574 /* Update the firmware block checksum */
Rafaël Carré96165ab2009-05-28 18:27:08 +0000575 sum = calc_checksum(buf + 0x400, firmware_size);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000576
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000577 if (fw_revision == 1) {
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000578 put_uint32le(&buf[0x04], sum);
579 put_uint32le(&buf[0x204], sum);
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000580 } else if (fw_revision == 2) {
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000581 put_uint32le(&buf[0x08], sum);
582 put_uint32le(&buf[0x208], sum);
583
584 /* Update the header checksums */
585 put_uint32le(&buf[0x1fc], calc_checksum(buf, 0x1fc));
586 put_uint32le(&buf[0x3fc], calc_checksum(buf + 0x200, 0x1fc));
587 }
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000588
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000589 /* Update the whole-file checksum */
590 filesum = 0;
Dave Chapman757f5112008-10-01 09:15:44 +0000591 for (i=0;i < (unsigned)len - 4; i+=4)
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000592 filesum += get_uint32le(&buf[i]);
593
Dave Chapman757f5112008-10-01 09:15:44 +0000594 put_uint32le(buf + len - 4, filesum);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000595}
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000596
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000597/* returns != 0 if the firmware can be safely patched */
598int check_sizes(int model, int rb_packed_size, int rb_unpacked_size,
599 int of_packed_size, int of_unpacked_size, int *total_size,
600 char *errstr, int errstrsize)
Rafaël Carréa96f2372009-05-28 21:19:21 +0000601{
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000602 unsigned int packed_size = bootloader_sizes[model] + sizeof(nrv2e_d8) +
603 of_packed_size + rb_packed_size;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000604
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000605 /* how much memory is available */
606 unsigned int memory_size = model_memory_size(model);
607
608 /* the memory used when unpacking the OF */
609 unsigned int ram_of = sizeof(nrv2e_d8) + of_packed_size + of_unpacked_size;
610
611 /* the memory used when unpacking the bootloader */
612 unsigned int ram_rb = sizeof(nrv2e_d8) + rb_packed_size + rb_unpacked_size;
613
614 *total_size = packed_size;
615
616#define ERROR(format, ...) \
617 do { \
618 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
619 return 0; \
620 } while(0)
621
622 /* will packed data fit in the OF file ? */
623 if(packed_size > of_unpacked_size)
624 ERROR(
625 "[ERR] Packed data (%d bytes) doesn't fit in the firmware "
626 "(%d bytes)\n", packed_size, of_unpacked_size
627 );
628
629 else if(ram_rb > memory_size)
630 ERROR("[ERR] Rockbox can't be unpacked at runtime, needs %d bytes "
631 "of memory and only %d available\n", ram_rb, memory_size
632 );
633
634 else if(ram_of > memory_size)
635 ERROR("[ERR] OF can't be unpacked at runtime, needs %d bytes "
636 "of memory and only %d available\n", ram_of, memory_size
637 );
638
639 return 1;
640
641#undef ERROR
642}