blob: 2897e48657b8be8e27ebd84373175c51b62ed322 [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
Rafaël Carréf5c79102010-07-16 00:00:29 +000034| FIRMWARE BLOCK | (contains the OF version on some fuzev2 firmwares
35|----------------------| 0x600
Rafaël Carré96165ab2009-05-28 18:27:08 +000036| FIRMWARE BLOCK |
37|----------------------| 0x400 + firmware block size
38| LIBRARIES/DATA |
39 ---------------------- END
Dave Chapman1aa6cde2008-05-11 18:29:53 +000040
Rafaël Carréf5c79102010-07-16 00:00:29 +000041We replace the main firmware block while preserving the potential OF version
42(bytes 0x600..0x400+firmware_size) as follows:
Dave Chapman1aa6cde2008-05-11 18:29:53 +000043
Dave Chapman1aa6cde2008-05-11 18:29:53 +000044
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000045 ---------------------- 0x0
46| |
47| Dual-boot code |
48| |
49|----------------------|
50| EMPTY SPACE |
51|----------------------|
52| |
53| compressed RB image |
54| |
55|----------------------|
56| |
57| compressed OF image |
58| |
59|----------------------|
60| UCL unpack function |
61 ----------------------
Dave Chapman1aa6cde2008-05-11 18:29:53 +000062
Dave Chapmanc2933cd2008-10-02 12:37:47 +000063This entire block fits into the space previously occupied by the main
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000064firmware block - the space saved by compressing the OF image is used
Rafaël Carré96165ab2009-05-28 18:27:08 +000065to store the compressed version of the Rockbox bootloader.
66
67On version 1 firmwares, the OF image is typically about 120KB, which allows
68us to store a Rockbox bootloader with an uncompressed size of about 60KB-70KB.
69Version 2 firmwares are bigger and are stored in SDRAM (instead of IRAM).
70In both cases, the RAM we are using is mapped at offset 0x0.
Dave Chapman1aa6cde2008-05-11 18:29:53 +000071
Dave Chapmanc2933cd2008-10-02 12:37:47 +000072mkamsboot then corrects the checksums and writes a new legal firmware
73file which can be installed on the device.
Dave Chapman1aa6cde2008-05-11 18:29:53 +000074
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000075When the Sansa device boots, this firmware block is loaded to RAM at
76address 0x0 and executed.
Dave Chapman1aa6cde2008-05-11 18:29:53 +000077
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000078Firstly, the dual-boot code will copy the UCL unpack function to the
79end of RAM.
Dave Chapman757f5112008-10-01 09:15:44 +000080
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000081Then, depending on the detection of the dual-boot keypress, either the
82OF image or the Rockbox image is copied to the end of RAM (just before
Rafaël Carré96165ab2009-05-28 18:27:08 +000083the ucl unpack function) and uncompressed to the start of RAM.
Dave Chapman1c4bcfa2008-10-11 11:35:59 +000084
85Finally, the ucl unpack function branches to address 0x0, passing
86execution to the uncompressed firmware.
87
Dave Chapman757f5112008-10-01 09:15:44 +000088
Dave Chapman1aa6cde2008-05-11 18:29:53 +000089*/
90
Dave Chapman1aa6cde2008-05-11 18:29:53 +000091#include <stdio.h>
92#include <stdlib.h>
93#include <stdint.h>
94#include <sys/types.h>
95#include <sys/stat.h>
96#include <fcntl.h>
97#include <unistd.h>
98#include <string.h>
99
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000100#include <ucl/ucl.h>
101
Rafaël Carré96165ab2009-05-28 18:27:08 +0000102#include "mkamsboot.h"
103
Dave Chapmanc91d7872008-10-30 00:13:29 +0000104#include "md5.h"
105
Rafaël Carré96165ab2009-05-28 18:27:08 +0000106/* Header for ARM code binaries */
107#include "dualboot.h"
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000108
109/* Win32 compatibility */
110#ifndef O_BINARY
111#define O_BINARY 0
112#endif
113
Dominik Riebeling02dce452011-01-31 20:15:50 +0000114/* fw_revision: version 2 is used in Clipv2, Clip+ and Fuzev2 firmwares */
115/* hw_revision: 4 for m200, 2 for e200/c200, 1 or 2 for fuze/clip, 1 for clip+ */
116const struct ams_models ams_identity[] = {
117 [MODEL_C200V2] = { 2, 1, "c200", dualboot_c200v2, sizeof(dualboot_c200v2), "c2v2", 44 },
118 [MODEL_CLIPPLUS]= { 1, 2, "Clip+", dualboot_clipplus, sizeof(dualboot_clipplus), "cli+", 66 },
119 [MODEL_CLIPV2] = { 2, 2, "Clip", dualboot_clipv2, sizeof(dualboot_clipv2), "clv2", 60 },
120 [MODEL_CLIP] = { 1, 1, "Clip", dualboot_clip, sizeof(dualboot_clip), "clip", 40 },
121 [MODEL_E200V2] = { 2, 1, "e200", dualboot_e200v2, sizeof(dualboot_e200v2), "e2v2", 41 },
122 [MODEL_FUZEV2] = { 2, 2, "Fuze", dualboot_fuzev2, sizeof(dualboot_fuzev2), "fuz2", 68 },
123 [MODEL_FUZE] = { 1, 1, "Fuze", dualboot_fuze, sizeof(dualboot_fuze), "fuze", 43 },
124 [MODEL_M200V4] = { 4, 1, "m200", dualboot_m200v4, sizeof(dualboot_m200v4), "m2v4", 42 },
Bertrik Sikken20525402011-09-05 18:14:29 +0000125 [MODEL_CLIPZIP] = { 1, 2, "ClipZip", dualboot_clipzip, sizeof(dualboot_clipzip), "clzp", 79 },
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000126};
127
Dave Chapmanc91d7872008-10-30 00:13:29 +0000128
129/* Checksums of unmodified original firmwares - for safety, and device
130 detection */
131static struct md5sums sansasums[] = {
132 /* NOTE: Different regional versions of the firmware normally only
133 differ in the filename - the md5sums are identical */
Dave Chapmanc91d7872008-10-30 00:13:29 +0000134
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000135 /* model version md5 */
136 { MODEL_E200V2, "3.01.11", "e622ca8cb6df423f54b8b39628a1f0a3" },
137 { MODEL_E200V2, "3.01.14", "2c1d0383fc3584b2cc83ba8cc2243af6" },
138 { MODEL_E200V2, "3.01.16", "12563ad71b25a1034cf2092d1e0218c4" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000139
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000140 { MODEL_FUZE, "1.01.11", "cac8ffa03c599330ac02c4d41de66166" },
141 { MODEL_FUZE, "1.01.15", "df0e2c1612727f722c19a3c764cff7f2" },
142 { MODEL_FUZE, "1.01.22", "5aff5486fe8dd64239cc71eac470af98" },
143 { MODEL_FUZE, "1.02.26", "7c632c479461c48c8833baed74eb5e4f" },
Rafaël Carrée51dbc02009-10-10 11:21:15 +0000144 { MODEL_FUZE, "1.02.28", "5b34260f6470e75f702a9c6825471752" },
Michael Chicoineb1dfdd42010-01-21 14:34:04 +0000145 { MODEL_FUZE, "1.02.31", "66d01b37462a5ef7ccc6ad37188b4235" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000146
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000147 { MODEL_C200V2, "3.02.05", "b6378ebd720b0ade3fad4dc7ab61c1a5" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000148
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000149 { MODEL_M200V4, "4.00.45", "82e3194310d1514e3bbcd06e84c4add3" },
150 { MODEL_M200V4, "4.01.08-A", "fc9dd6116001b3e6a150b898f1b091f0" },
151 { MODEL_M200V4, "4.01.08-E", "d3fb7d8ec8624ee65bc99f8dab0e2369" },
Rafaël Carré96165ab2009-05-28 18:27:08 +0000152
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000153 { MODEL_CLIP, "1.01.17", "12caad785d506219d73f538772afd99e" },
154 { MODEL_CLIP, "1.01.18", "d720b266bd5afa38a198986ef0508a45" },
155 { MODEL_CLIP, "1.01.20", "236d8f75189f468462c03f6d292cf2ac" },
156 { MODEL_CLIP, "1.01.29", "c12711342169c66e209540cd1f27cd26" },
157 { MODEL_CLIP, "1.01.30", "f2974d47c536549c9d8259170f1dbe4d" },
158 { MODEL_CLIP, "1.01.32", "d835d12342500732ffb9c4ee54abec15" },
Rafaël Carréa384cdf2010-03-16 02:11:04 +0000159 { MODEL_CLIP, "1.01.35", "b4d0edb3b8f2a4e8eee0a344f7f8e480" },
Rafaël Carré96165ab2009-05-28 18:27:08 +0000160
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000161 { MODEL_CLIPV2, "2.01.16", "c57fb3fcbe07c2c9b360f060938f80cb" },
Rafaël Carré8b926e92010-01-13 03:05:29 +0000162 { MODEL_CLIPV2, "2.01.32", "0ad3723e52022509089d938d0fbbf8c5" },
Rafaël Carré20fccd82010-03-22 08:32:04 +0000163 { MODEL_CLIPV2, "2.01.35", "a3cbbd22b9508d7f8a9a1a39acc342c2" },
Rafaël Carré8b926e92010-01-13 03:05:29 +0000164
Rafaël Carré8b926e92010-01-13 03:05:29 +0000165 { MODEL_CLIPPLUS, "01.02.09", "656d38114774c2001dc18e6726df3c5d" },
Rafaël Carréfdf8e2d2010-04-17 12:17:01 +0000166 { MODEL_CLIPPLUS, "01.02.13", "5f89872b79ef440b0e5ee3a7a44328b2" },
Rafaël Carréd9e508b2010-05-20 18:51:49 +0000167 { MODEL_CLIPPLUS, "01.02.15", "680a4f521e790ad25b93b1b16f3a207d" },
Bertrik Sikken1195b232011-10-07 22:49:37 +0000168 { MODEL_CLIPPLUS, "01.02.16", "055a53de1dfb09f6cb71c504ad48bd13" },
Thomas Martitzf6540e82010-02-26 13:55:17 +0000169
170 { MODEL_FUZEV2, "2.01.17", "8b85fb05bf645d08a4c8c3e344ec9ebe" },
171 { MODEL_FUZEV2, "2.02.26", "d4f6f85c3e4a8ea8f2e5acc421641801" },
Rafaël Carrébe920c42010-03-28 02:58:00 +0000172 { MODEL_FUZEV2, "2.03.31", "74fb197ccd51707388f3b233402186a6" },
Rafaël Carrée2d1eb62010-05-21 13:13:53 +0000173 { MODEL_FUZEV2, "2.03.33", "1599cc73d02ea7fe53fe2d4379c24b66" },
Bertrik Sikken1ab2aa12011-10-30 08:09:46 +0000174
Bertrik Sikken463b3ed2011-08-27 16:21:19 +0000175 { MODEL_CLIPZIP, "1.01.12", "45adea0873326b5af34f096e5c402f78" },
Bertrik Sikken20525402011-09-05 18:14:29 +0000176 { MODEL_CLIPZIP, "1.01.15", "f62af954334cd9ba1a87a7fa58ec6074" },
Bertrik Sikkene90f9612011-10-29 17:08:05 +0000177 { MODEL_CLIPZIP, "1.01.17", "27bcb343d6950f35dc261629e22ba60c" },
Bertrik Sikkenc9a80312011-12-10 09:45:28 +0000178 { MODEL_CLIPZIP, "1.01.18", "ef16aa9e02b49885ebede5aa149502e8" },
Dave Chapmanc91d7872008-10-30 00:13:29 +0000179};
180
181#define NUM_MD5S (sizeof(sansasums)/sizeof(sansasums[0]))
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000182
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000183static unsigned int model_memory_size(int model)
184{
Rafaël Carré765c4f12010-07-16 00:05:52 +0000185 /* The OF boots with IRAM (320kB) mapped at 0x0 */
186
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000187 if(model == MODEL_CLIPV2)
188 {
189 /* The decompressed Clipv2 OF is around 380kB.
Rafaël Carré765c4f12010-07-16 00:05:52 +0000190 * Let's use the full IRAM (1MB on AMSv2)
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000191 */
192 return 1 << 20;
193 }
194 else
Rafaël Carré765c4f12010-07-16 00:05:52 +0000195 {
196 /* The IRAM is 320kB on AMSv1, and 320 will be enough on Fuzev1/Clip+ */
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000197 return 320 << 10;
198 }
199}
200
Dominik Wengera66dfa42009-08-15 13:04:21 +0000201int firmware_revision(int model)
202{
Dominik Riebeling02dce452011-01-31 20:15:50 +0000203 return ams_identity[model].fw_revision;
Dominik Wengera66dfa42009-08-15 13:04:21 +0000204}
205
Rafaël Carréa96f2372009-05-28 21:19:21 +0000206static off_t filesize(int fd)
207{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000208 struct stat buf;
209
Rafaël Carré96165ab2009-05-28 18:27:08 +0000210 if (fstat(fd, &buf) < 0) {
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000211 perror("[ERR] Checking filesize of input file");
212 return -1;
213 } else {
214 return(buf.st_size);
215 }
216}
217
Rafaël Carréa96f2372009-05-28 21:19:21 +0000218static uint32_t get_uint32le(unsigned char* p)
219{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000220 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
221}
222
Rafaël Carréa96f2372009-05-28 21:19:21 +0000223static uint32_t get_uint32be(unsigned char* p)
224{
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000225 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
226}
227
Rafaël Carréa96f2372009-05-28 21:19:21 +0000228static void put_uint32le(unsigned char* p, uint32_t x)
229{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000230 p[0] = x & 0xff;
231 p[1] = (x >> 8) & 0xff;
232 p[2] = (x >> 16) & 0xff;
233 p[3] = (x >> 24) & 0xff;
234}
235
Rafaël Carréa96f2372009-05-28 21:19:21 +0000236void calc_MD5(unsigned char* buf, int len, char *md5str)
237{
Dave Chapmanc91d7872008-10-30 00:13:29 +0000238 int i;
239 md5_context ctx;
240 unsigned char md5sum[16];
Rafaël Carré96165ab2009-05-28 18:27:08 +0000241
Dave Chapmanc91d7872008-10-30 00:13:29 +0000242 md5_starts(&ctx);
243 md5_update(&ctx, buf, len);
244 md5_finish(&ctx, md5sum);
245
246 for (i = 0; i < 16; ++i)
247 sprintf(md5str + 2*i, "%02x", md5sum[i]);
248}
249
Rafaël Carré96165ab2009-05-28 18:27:08 +0000250/* Calculate a simple checksum used in Sansa Original Firmwares */
Rafaël Carréa96f2372009-05-28 21:19:21 +0000251static uint32_t calc_checksum(unsigned char* buf, uint32_t n)
252{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000253 uint32_t sum = 0;
254 uint32_t i;
255
256 for (i=0;i<n;i+=4)
257 sum += get_uint32le(buf + i);
258
259 return sum;
260}
261
Rafaël Carré96165ab2009-05-28 18:27:08 +0000262/* Compress using nrv2e algorithm : Thumb decompressor fits in 168 bytes ! */
Rafaël Carréa96f2372009-05-28 21:19:21 +0000263static unsigned char* uclpack(unsigned char* inbuf, int insize, int* outsize)
264{
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000265 int maxsize;
266 unsigned char* outbuf;
267 int r;
268
269 /* The following formula comes from the UCL documentation */
270 maxsize = insize + (insize / 8) + 256;
271
272 /* Allocate some memory for the output buffer */
273 outbuf = malloc(maxsize);
274
Rafaël Carré96165ab2009-05-28 18:27:08 +0000275 if (outbuf == NULL)
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000276 return NULL;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000277
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000278 r = ucl_nrv2e_99_compress(
279 (const ucl_bytep) inbuf,
280 (ucl_uint) insize,
281 (ucl_bytep) outbuf,
282 (ucl_uintp) outsize,
283 0, 10, NULL, NULL);
284
Rafaël Carré96165ab2009-05-28 18:27:08 +0000285 if (r != UCL_E_OK || *outsize > maxsize) {
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000286 /* this should NEVER happen, and implies memory corruption */
287 fprintf(stderr, "internal error - compression failed: %d\n", r);
288 free(outbuf);
289 return NULL;
290 }
291
292 return outbuf;
293}
294
Rafaël Carré96165ab2009-05-28 18:27:08 +0000295#define ERROR(format, ...) \
296 do { \
297 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
298 goto error; \
299 } while(0)
300
301/* Loads a Sansa AMS Original Firmware file into memory */
302unsigned char* load_of_file(
Rafaël Carréff6b0422010-05-24 10:06:52 +0000303 char* filename, int model, off_t* bufsize, struct md5sums *sum,
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000304 int* firmware_size, unsigned char** of_packed,
Rafaël Carréa96f2372009-05-28 21:19:21 +0000305 int* of_packedsize, char* errstr, int errstrsize)
306{
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000307 int fd;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000308 unsigned char* buf =NULL;
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000309 off_t n;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000310 unsigned int i=0;
311 uint32_t checksum;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000312 unsigned int last_word;
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000313
314 fd = open(filename, O_RDONLY|O_BINARY);
315 if (fd < 0)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000316 ERROR("[ERR] Could not open %s for reading\n", filename);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000317
318 *bufsize = filesize(fd);
319
320 buf = malloc(*bufsize);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000321 if (buf == NULL)
322 ERROR("[ERR] Could not allocate memory for %s\n", filename);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000323
324 n = read(fd, buf, *bufsize);
325
Rafaël Carré96165ab2009-05-28 18:27:08 +0000326 if (n != *bufsize)
327 ERROR("[ERR] Could not read file %s\n", filename);
328
329 /* check the file */
330
331 /* Calculate MD5 checksum of OF */
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000332 calc_MD5(buf, *bufsize, sum->md5);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000333
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000334 while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, sum->md5) != 0))
Rafaël Carré96165ab2009-05-28 18:27:08 +0000335 i++;
336
337 if (i < NUM_MD5S) {
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000338 *sum = sansasums[i];
Rafaël Carréff6b0422010-05-24 10:06:52 +0000339 if(sum->model != model) {
340 ERROR("[ERR] OF File provided is %sv%d version %s, not for %sv%d\n",
Dominik Riebeling02dce452011-01-31 20:15:50 +0000341 ams_identity[sum->model].model_name, ams_identity[sum->model].hw_revision,
342 sum->version, ams_identity[model].model_name, ams_identity[model].hw_revision
Rafaël Carréff6b0422010-05-24 10:06:52 +0000343 );
344 }
Rafaël Carré96165ab2009-05-28 18:27:08 +0000345 } else {
Rafaël Carréff6b0422010-05-24 10:06:52 +0000346 /* OF unknown, give a list of tested versions for the requested model */
Rafaël Carré96165ab2009-05-28 18:27:08 +0000347
Rafaël Carré8bb5e432009-06-04 16:31:11 +0000348 char tested_versions[100];
349 tested_versions[0] = '\0';
350
351 for (i = 0; i < NUM_MD5S ; i++)
Rafaël Carréff6b0422010-05-24 10:06:52 +0000352 if (sansasums[i].model == model) {
Rafaël Carré8bb5e432009-06-04 16:31:11 +0000353 if (tested_versions[0] != '\0') {
354 strncat(tested_versions, ", ",
355 sizeof(tested_versions) - strlen(tested_versions) - 1);
356 }
357 strncat(tested_versions, sansasums[i].version,
358 sizeof(tested_versions) - strlen(tested_versions) - 1);
359 }
360
361 ERROR("[ERR] Original firmware unknown, please try an other version." \
Rafaël Carréff6b0422010-05-24 10:06:52 +0000362 " Tested %sv%d versions are : %s\n",
Dominik Riebeling02dce452011-01-31 20:15:50 +0000363 ams_identity[model].model_name, ams_identity[model].hw_revision, tested_versions);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000364 }
365
Rafaël Carré96165ab2009-05-28 18:27:08 +0000366 /* TODO: Do some more sanity checks on the OF image. Some images (like
367 m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */
368 last_word = *bufsize - 4;
369 checksum = get_uint32le(buf + last_word);
370 if (checksum != 0xefbeadde && checksum != calc_checksum(buf, last_word))
371 ERROR("%s", "[ERR] Whole file checksum failed\n");
372
Dominik Riebeling02dce452011-01-31 20:15:50 +0000373 if (ams_identity[sum->model].bootloader == NULL)
374 ERROR("[ERR] Unsupported model - \"%s\"\n", ams_identity[sum->model].model_name);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000375
376 /* Get the firmware size */
Dominik Riebeling02dce452011-01-31 20:15:50 +0000377 if (ams_identity[sum->model].fw_revision == 1)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000378 *firmware_size = get_uint32le(&buf[0x0c]);
Dominik Riebeling02dce452011-01-31 20:15:50 +0000379 else if (ams_identity[sum->model].fw_revision == 2)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000380 *firmware_size = get_uint32le(&buf[0x10]);
381
382 /* Compress the original firmware image */
383 *of_packed = uclpack(buf + 0x400, *firmware_size, of_packedsize);
384 if (*of_packed == NULL)
385 ERROR("[ERR] Could not compress %s\n", filename);
386
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000387 return buf;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000388
389error:
390 free(buf);
391 return NULL;
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000392}
393
Rafaël Carré96165ab2009-05-28 18:27:08 +0000394/* Loads a rockbox bootloader file into memory */
395unsigned char* load_rockbox_file(
Rafaël Carréff6b0422010-05-24 10:06:52 +0000396 char* filename, int *model, int* bufsize, int* rb_packedsize,
Rafaël Carréa96f2372009-05-28 21:19:21 +0000397 char* errstr, int errstrsize)
398{
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000399 int fd;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000400 unsigned char* buf = NULL;
401 unsigned char* packed = NULL;
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000402 unsigned char header[8];
403 uint32_t sum;
404 off_t n;
405 int i;
406
407 fd = open(filename, O_RDONLY|O_BINARY);
408 if (fd < 0)
Rafaël Carré96165ab2009-05-28 18:27:08 +0000409 ERROR("[ERR] Could not open %s for reading\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000410
411 /* Read Rockbox header */
412 n = read(fd, header, sizeof(header));
Rafaël Carré96165ab2009-05-28 18:27:08 +0000413 if (n != sizeof(header))
414 ERROR("[ERR] Could not read file %s\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000415
Rafaël Carréff6b0422010-05-24 10:06:52 +0000416 for(*model = 0; *model < NUM_MODELS; (*model)++)
Dominik Riebeling02dce452011-01-31 20:15:50 +0000417 if (memcmp(ams_identity[*model].rb_model_name, header + 4, 4) == 0)
Rafaël Carréff6b0422010-05-24 10:06:52 +0000418 break;
419
420 if(*model == NUM_MODELS)
421 ERROR("[ERR] Model name \"%4.4s\" unknown. Is this really a rockbox bootloader?\n", header + 4);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000422
423 *bufsize = filesize(fd) - sizeof(header);
424
425 buf = malloc(*bufsize);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000426 if (buf == NULL)
427 ERROR("[ERR] Could not allocate memory for %s\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000428
429 n = read(fd, buf, *bufsize);
430
Rafaël Carré96165ab2009-05-28 18:27:08 +0000431 if (n != *bufsize)
432 ERROR("[ERR] Could not read file %s\n", filename);
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000433
434 /* Check checksum */
Dominik Riebeling02dce452011-01-31 20:15:50 +0000435 sum = ams_identity[*model].rb_model_num;
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000436 for (i = 0; i < *bufsize; i++) {
437 /* add 8 unsigned bits but keep a 32 bit sum */
438 sum += buf[i];
439 }
440
Rafaël Carré96165ab2009-05-28 18:27:08 +0000441 if (sum != get_uint32be(header))
442 ERROR("[ERR] Checksum mismatch in %s\n", filename);
443
444 packed = uclpack(buf, *bufsize, rb_packedsize);
445 if(packed == NULL)
446 ERROR("[ERR] Could not compress %s\n", filename);
447
448 free(buf);
449 return packed;
450
451error:
452 free(buf);
453 return NULL;
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000454}
455
Rafaël Carré96165ab2009-05-28 18:27:08 +0000456#undef ERROR
Dave Chapman6bbe66a2008-10-12 19:34:47 +0000457
Rafaël Carré96165ab2009-05-28 18:27:08 +0000458/* Patches a Sansa AMS Original Firmware file */
459void patch_firmware(
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000460 int model, int fw_revision, int firmware_size, unsigned char* buf,
Rafaël Carré96165ab2009-05-28 18:27:08 +0000461 int len, unsigned char* of_packed, int of_packedsize,
Rafaël Carréa96f2372009-05-28 21:19:21 +0000462 unsigned char* rb_packed, int rb_packedsize)
463{
Rafaël Carré96165ab2009-05-28 18:27:08 +0000464 unsigned char *p;
465 uint32_t sum, filesum;
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000466 uint32_t ucl_dest;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000467 unsigned int i;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000468
Dave Chapman757f5112008-10-01 09:15:44 +0000469 /* Zero the original firmware area - not needed, but helps debugging */
Rafaël Carréf5c79102010-07-16 00:00:29 +0000470 memset(buf + 0x600, 0, firmware_size);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000471
Rafaël Carréf5c79102010-07-16 00:00:29 +0000472 /* Insert dual-boot bootloader at offset 0x200, we preserve the OF
473 * version string located between 0x0 and 0x200 */
Dominik Riebeling02dce452011-01-31 20:15:50 +0000474 memcpy(buf + 0x600, ams_identity[model].bootloader, ams_identity[model].bootloader_size);
Rafaël Carréf5c79102010-07-16 00:00:29 +0000475
476 /* Insert vectors, they won't overwrite the OF version string */
477
478 /* Reset vector: branch 0x200 bytes away, to our dualboot code */
479 static const uint8_t b_0x200[4] = { 0x7e, 0x00, 0x00, 0xea }; // b 0x200
480 memcpy(buf + 0x400, b_0x200, sizeof(b_0x200));
481
482 /* Other vectors: infinite loops */
483 static const uint8_t b_1b[4] = { 0xfe, 0xff, 0xff, 0xea }; // 1: b 1b
484 for (i=1; i < 8; i++)
485 memcpy(buf + 0x400 + 4*i, b_1b, sizeof(b_1b));
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000486
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000487 /* We are filling the firmware buffer backwards from the end */
488 p = buf + 0x400 + firmware_size;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000489
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000490 /* 1 - UCL unpack function */
Dave Chapmandcb0e432008-11-14 22:16:22 +0000491 p -= sizeof(nrv2e_d8);
492 memcpy(p, nrv2e_d8, sizeof(nrv2e_d8));
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000493
494 /* 2 - Compressed copy of original firmware */
495 p -= of_packedsize;
496 memcpy(p, of_packed, of_packedsize);
497
498 /* 3 - Compressed copy of Rockbox bootloader */
499 p -= rb_packedsize;
500 memcpy(p, rb_packed, rb_packedsize);
501
Rafaël Carré96165ab2009-05-28 18:27:08 +0000502 /* Write the locations of the various images to the variables at the
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000503 start of the dualboot image - we save the location of the last byte
504 in each image, along with the size in bytes */
505
506 /* UCL unpack function */
Rafaël Carréf5c79102010-07-16 00:00:29 +0000507 put_uint32le(&buf[0x604], firmware_size - 1);
508 put_uint32le(&buf[0x608], sizeof(nrv2e_d8));
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000509
510 /* Compressed original firmware image */
Rafaël Carréf5c79102010-07-16 00:00:29 +0000511 put_uint32le(&buf[0x60c], firmware_size - sizeof(nrv2e_d8) - 1);
512 put_uint32le(&buf[0x610], of_packedsize);
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000513
514 /* Compressed Rockbox image */
Rafaël Carréf5c79102010-07-16 00:00:29 +0000515 put_uint32le(&buf[0x614], firmware_size - sizeof(nrv2e_d8) - of_packedsize
Rafaël Carré96165ab2009-05-28 18:27:08 +0000516 - 1);
Rafaël Carréf5c79102010-07-16 00:00:29 +0000517 put_uint32le(&buf[0x618], rb_packedsize);
Dave Chapman757f5112008-10-01 09:15:44 +0000518
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000519 ucl_dest = model_memory_size(model) - 1; /* last byte of memory */
Rafaël Carréf5c79102010-07-16 00:00:29 +0000520 put_uint32le(&buf[0x61c], ucl_dest);
Dave Chapman757f5112008-10-01 09:15:44 +0000521
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000522 /* Update the firmware block checksum */
Rafaël Carré96165ab2009-05-28 18:27:08 +0000523 sum = calc_checksum(buf + 0x400, firmware_size);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000524
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000525 if (fw_revision == 1) {
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000526 put_uint32le(&buf[0x04], sum);
527 put_uint32le(&buf[0x204], sum);
Thomas Martitzf9eeab22009-07-05 02:30:33 +0000528 } else if (fw_revision == 2) {
Dave Chapman1c4bcfa2008-10-11 11:35:59 +0000529 put_uint32le(&buf[0x08], sum);
530 put_uint32le(&buf[0x208], sum);
531
532 /* Update the header checksums */
533 put_uint32le(&buf[0x1fc], calc_checksum(buf, 0x1fc));
534 put_uint32le(&buf[0x3fc], calc_checksum(buf + 0x200, 0x1fc));
535 }
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000536
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000537 /* Update the whole-file checksum */
538 filesum = 0;
Dave Chapman757f5112008-10-01 09:15:44 +0000539 for (i=0;i < (unsigned)len - 4; i+=4)
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000540 filesum += get_uint32le(&buf[i]);
541
Dave Chapman757f5112008-10-01 09:15:44 +0000542 put_uint32le(buf + len - 4, filesum);
Rafaël Carré96165ab2009-05-28 18:27:08 +0000543}
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000544
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000545/* returns != 0 if the firmware can be safely patched */
546int check_sizes(int model, int rb_packed_size, int rb_unpacked_size,
547 int of_packed_size, int of_unpacked_size, int *total_size,
548 char *errstr, int errstrsize)
Rafaël Carréa96f2372009-05-28 21:19:21 +0000549{
Rafaël Carrébff5e3d2010-07-18 17:05:05 +0000550 /* XXX: we keep the first 0x200 bytes block unmodified, we just replace
551 * the ARM vectors */
Dominik Riebeling02dce452011-01-31 20:15:50 +0000552 unsigned int packed_size = ams_identity[model].bootloader_size + sizeof(nrv2e_d8) +
Rafaël Carrébff5e3d2010-07-18 17:05:05 +0000553 of_packed_size + rb_packed_size + 0x200;
Rafaël Carré96165ab2009-05-28 18:27:08 +0000554
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000555 /* how much memory is available */
556 unsigned int memory_size = model_memory_size(model);
557
558 /* the memory used when unpacking the OF */
559 unsigned int ram_of = sizeof(nrv2e_d8) + of_packed_size + of_unpacked_size;
560
561 /* the memory used when unpacking the bootloader */
562 unsigned int ram_rb = sizeof(nrv2e_d8) + rb_packed_size + rb_unpacked_size;
563
564 *total_size = packed_size;
565
566#define ERROR(format, ...) \
567 do { \
568 snprintf(errstr, errstrsize, format, __VA_ARGS__); \
569 return 0; \
570 } while(0)
571
Rafaël Carrébff5e3d2010-07-18 17:05:05 +0000572 /* will packed data fit in the OF file ? */
573 if(packed_size > of_unpacked_size)
Rafaël Carréb6c20c12010-02-19 14:10:26 +0000574 ERROR(
575 "[ERR] Packed data (%d bytes) doesn't fit in the firmware "
576 "(%d bytes)\n", packed_size, of_unpacked_size
577 );
578
579 else if(ram_rb > memory_size)
580 ERROR("[ERR] Rockbox can't be unpacked at runtime, needs %d bytes "
581 "of memory and only %d available\n", ram_rb, memory_size
582 );
583
584 else if(ram_of > memory_size)
585 ERROR("[ERR] OF can't be unpacked at runtime, needs %d bytes "
586 "of memory and only %d available\n", ram_of, memory_size
587 );
588
589 return 1;
590
591#undef ERROR
592}