Make sansapatcher check and report permission denied errors.
[Rockbox.git] / rbutil / sansapatcher / sansapatcher.c
blobee15a16ca9704be44e40f5259e96e654e306775c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006-2007 Dave Chapman
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <inttypes.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
29 #include "sansaio.h"
30 #include "sansapatcher.h"
32 #ifndef RBUTIL
33 #include "bootimg_c200.h"
34 #include "bootimg_e200.h"
35 #endif
36 /* The offset of the MI4 image header in the firmware partition */
37 #define PPMI_OFFSET 0x80000
38 #define NVPARAMS_OFFSET 0x780000
39 #define NVPARAMS_SIZE (0x80000-0x200)
41 extern int verbose;
43 /* Windows requires the buffer for disk I/O to be aligned in memory on a
44 multiple of the disk volume size - so we use a single global variable
45 and initialise it with sansa_alloc_buf() in main().
48 extern unsigned char* sectorbuf;
50 static off_t filesize(int fd) {
51 struct stat buf;
53 if (fstat(fd,&buf) < 0) {
54 perror("[ERR] Checking filesize of input file");
55 return -1;
56 } else {
57 return(buf.st_size);
61 /* Partition table parsing code taken from Rockbox */
63 #define MAX_SECTOR_SIZE 2048
64 #define SECTOR_SIZE 512
66 static inline int32_t le2int(unsigned char* buf)
68 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
70 return res;
73 static inline uint32_t le2uint(unsigned char* buf)
75 uint32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
77 return res;
80 static inline void int2le(unsigned int val, unsigned char* addr)
82 addr[0] = val & 0xFF;
83 addr[1] = (val >> 8) & 0xff;
84 addr[2] = (val >> 16) & 0xff;
85 addr[3] = (val >> 24) & 0xff;
88 #define BYTES2INT32(array,pos)\
89 ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
90 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
92 int sansa_read_partinfo(struct sansa_t* sansa, int silent)
94 int i;
95 unsigned long count;
97 count = sansa_read(sansa,sectorbuf, sansa->sector_size);
99 if (count <= 0) {
100 print_error(" Error reading from disk: ");
101 return -1;
104 if ((sectorbuf[510] == 0x55) && (sectorbuf[511] == 0xaa)) {
105 /* parse partitions */
106 for ( i = 0; i < 4; i++ ) {
107 unsigned char* ptr = sectorbuf + 0x1be + 16*i;
108 sansa->pinfo[i].type = ptr[4];
109 sansa->pinfo[i].start = BYTES2INT32(ptr, 8);
110 sansa->pinfo[i].size = BYTES2INT32(ptr, 12);
112 /* extended? */
113 if ( sansa->pinfo[i].type == 5 ) {
114 /* not handled yet */
117 } else if ((sectorbuf[0] == 'E') && (sectorbuf[1] == 'R')) {
118 if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n");
119 return -1;
122 /* Calculate the starting position of the firmware partition */
123 sansa->start = (loff_t)sansa->pinfo[1].start*(loff_t)sansa->sector_size;
124 return 0;
127 /* NOTE: memmem implementation copied from glibc-2.2.4 - it's a GNU
128 extension and is not universally. In addition, early versions of
129 memmem had a serious bug - the meaning of needle and haystack were
130 reversed. */
132 /* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc.
133 This file is part of the GNU C Library.
135 The GNU C Library is free software; you can redistribute it and/or
136 modify it under the terms of the GNU Lesser General Public
137 License as published by the Free Software Foundation; either
138 version 2.1 of the License, or (at your option) any later version.
140 The GNU C Library is distributed in the hope that it will be useful,
141 but WITHOUT ANY WARRANTY; without even the implied warranty of
142 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
143 Lesser General Public License for more details.
145 You should have received a copy of the GNU Lesser General Public
146 License along with the GNU C Library; if not, write to the Free
147 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
148 02111-1307 USA. */
150 /* Return the first occurrence of NEEDLE in HAYSTACK. */
151 static void *
152 sansa_memmem (haystack, haystack_len, needle, needle_len)
153 const void *haystack;
154 size_t haystack_len;
155 const void *needle;
156 size_t needle_len;
158 const char *begin;
159 const char *const last_possible
160 = (const char *) haystack + haystack_len - needle_len;
162 if (needle_len == 0)
163 /* The first occurrence of the empty string is deemed to occur at
164 the beginning of the string. */
165 return (void *) haystack;
167 /* Sanity check, otherwise the loop might search through the whole
168 memory. */
169 if (__builtin_expect (haystack_len < needle_len, 0))
170 return NULL;
172 for (begin = (const char *) haystack; begin <= last_possible; ++begin)
173 if (begin[0] == ((const char *) needle)[0] &&
174 !memcmp ((const void *) &begin[1],
175 (const void *) ((const char *) needle + 1),
176 needle_len - 1))
177 return (void *) begin;
179 return NULL;
183 * CRC32 implementation taken from:
185 * efone - Distributed internet phone system.
187 * (c) 1999,2000 Krzysztof Dabrowski
188 * (c) 1999,2000 ElysiuM deeZine
190 * This program is free software; you can redistribute it and/or
191 * modify it under the terms of the GNU General Public License
192 * as published by the Free Software Foundation; either version
193 * 2 of the License, or (at your option) any later version.
197 /* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
198 * so make sure, you call it before using the other
199 * functions!
201 static unsigned int crc_tab[256];
203 /* chksum_crc() -- to a given block, this one calculates the
204 * crc32-checksum until the length is
205 * reached. the crc32-checksum will be
206 * the result.
208 static unsigned int chksum_crc32 (unsigned char *block, unsigned int length)
210 register unsigned long crc;
211 unsigned long i;
213 crc = 0;
214 for (i = 0; i < length; i++)
216 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
218 return (crc);
221 /* chksum_crc32gentab() -- to a global crc_tab[256], this one will
222 * calculate the crcTable for crc32-checksums.
223 * it is generated to the polynom [..]
226 static void chksum_crc32gentab (void)
228 unsigned long crc, poly;
229 int i, j;
231 poly = 0xEDB88320L;
232 for (i = 0; i < 256; i++)
234 crc = i;
235 for (j = 8; j > 0; j--)
237 if (crc & 1)
239 crc = (crc >> 1) ^ poly;
241 else
243 crc >>= 1;
246 crc_tab[i] = crc;
250 /* Known keys for Sansa E200 and C200 firmwares: */
251 #define NUM_KEYS ((int)(sizeof(keys)/sizeof(keys[0])))
252 static uint32_t keys[][4] = {
253 { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */
254 { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */
255 { 0x1d29ddc0, 0x2579c2cd, 0xce339e1a, 0x75465dfe }, /* sansa 103 */
257 { 0xbf2d06fa, 0xf0e23d59, 0x29738132, 0xe2d04ca7 }, /* c200 */
258 { 0x2a7968de, 0x15127979, 0x142e60a7, 0xe49c1893 }, /* c200 1.00.03 */
259 { 0xa913d139, 0xf842f398, 0x3e03f1a6, 0x060ee012 }, /* c200 1.00.06 */
264 tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
266 "Following is an adaptation of the reference encryption and decryption
267 routines in C, released into the public domain by David Wheeler and
268 Roger Needham:"
272 /* NOTE: The mi4 version of TEA uses a different initial value to sum compared
273 to the reference implementation and the main loop is 8 iterations, not
277 void tea_decrypt(uint32_t* v0, uint32_t* v1, uint32_t* k) {
278 uint32_t sum=0xF1BBCDC8, i; /* set up */
279 uint32_t delta=0x9E3779B9; /* a key schedule constant */
280 uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
281 for(i=0; i<8; i++) { /* basic cycle start */
282 *v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3);
283 *v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1);
284 sum -= delta; /* end cycle */
288 /* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit
289 integers) and the key is incremented after each block
292 void tea_decrypt_buf(unsigned char* src, unsigned char* dest, size_t n, uint32_t * key)
294 uint32_t v0, v1;
295 unsigned int i;
297 for (i = 0; i < (n / 8); i++) {
298 v0 = le2int(src);
299 v1 = le2int(src+4);
301 tea_decrypt(&v0, &v1, key);
303 int2le(v0, dest);
304 int2le(v1, dest+4);
306 src += 8;
307 dest += 8;
309 /* Now increment the key */
310 key[0]++;
311 if (key[0]==0) {
312 key[1]++;
313 if (key[1]==0) {
314 key[2]++;
315 if (key[2]==0) {
316 key[3]++;
323 static int get_mi4header(unsigned char* buf,struct mi4header_t* mi4header)
325 if (memcmp(buf,"PPOS",4)!=0)
326 return -1;
328 mi4header->version = le2int(buf+0x04);
329 mi4header->length = le2int(buf+0x08);
330 mi4header->crc32 = le2int(buf+0x0c);
331 mi4header->enctype = le2int(buf+0x10);
332 mi4header->mi4size = le2int(buf+0x14);
333 mi4header->plaintext = le2int(buf+0x18);
335 return 0;
338 static int set_mi4header(unsigned char* buf,struct mi4header_t* mi4header)
340 if (memcmp(buf,"PPOS",4)!=0)
341 return -1;
343 int2le(mi4header->version ,buf+0x04);
344 int2le(mi4header->length ,buf+0x08);
345 int2le(mi4header->crc32 ,buf+0x0c);
346 int2le(mi4header->enctype ,buf+0x10);
347 int2le(mi4header->mi4size ,buf+0x14);
348 int2le(mi4header->plaintext ,buf+0x18);
350 /* Add a dummy DSA signature */
351 memset(buf+0x1c,0,40);
352 buf[0x2f] = 1;
354 return 0;
357 static int sansa_seek_and_read(struct sansa_t* sansa, loff_t pos, unsigned char* buf, int nbytes)
359 int n;
361 if (sansa_seek(sansa, pos) < 0) {
362 return -1;
365 if ((n = sansa_read(sansa,buf,nbytes)) < 0) {
366 return -1;
369 if (n < nbytes) {
370 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
371 nbytes,n);
372 return -1;
375 return 0;
379 /* We identify an E200 based on the following criteria:
381 1) Exactly two partitions;
382 2) First partition is type "W95 FAT32" (0x0b or 0x0c);
383 3) Second partition is type "OS/2 hidden C: drive" (0x84);
384 4) The "PPBL" string appears at offset 0 in the 2nd partition;
385 5) The "PPMI" string appears at offset PPMI_OFFSET in the 2nd partition.
388 int is_sansa(struct sansa_t* sansa)
390 struct mi4header_t mi4header;
391 int ppmi_length;
392 int ppbl_length;
394 /* Check partition layout */
396 if (((sansa->pinfo[0].type != 0x06) &&
397 (sansa->pinfo[0].type != 0x0b) &&
398 (sansa->pinfo[0].type != 0x0c) &&
399 (sansa->pinfo[0].type != 0x0e)) ||
400 (sansa->pinfo[1].type != 0x84) ||
401 (sansa->pinfo[2].type != 0x00) ||
402 (sansa->pinfo[3].type != 0x00)) {
403 /* Bad partition layout, abort */
404 return -1;
407 /* Check Bootloader header */
408 if (sansa_seek_and_read(sansa, sansa->start, sectorbuf, 0x200) < 0) {
409 return -2;
411 if (memcmp(sectorbuf,"PPBL",4)!=0) {
412 /* No bootloader header, abort */
413 return -4;
415 ppbl_length = (le2int(sectorbuf+4) + 0x1ff) & ~0x1ff;
417 /* Sanity/safety check - the bootloader can't be larger than PPMI_OFFSET */
418 if (ppbl_length > PPMI_OFFSET)
420 return -5;
423 /* Load Sansa bootloader and check for "Sansa C200" magic string */
424 if (sansa_seek_and_read(sansa, sansa->start + 0x200, sectorbuf, ppbl_length) < 0) {
425 fprintf(stderr,"[ERR] Seek and read to 0x%08llx in is_sansa failed.\n",
426 sansa->start+0x200);
427 return -6;
429 if (sansa_memmem(sectorbuf, ppbl_length, "Sansa C200", 10) != NULL) {
430 /* C200 */
431 sansa->targetname="c200";
432 } else {
433 /* E200 */
434 sansa->targetname="e200";
437 /* Check Main firmware header */
438 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sectorbuf, 0x200) < 0) {
439 fprintf(stderr,"[ERR] Seek to 0x%08llx in is_sansa failed.\n",
440 sansa->start+PPMI_OFFSET);
441 return -5;
443 if (memcmp(sectorbuf,"PPMI",4)!=0) {
444 /* No bootloader header, abort */
445 return -7;
447 ppmi_length = le2int(sectorbuf+4);
449 /* Check main mi4 file header */
450 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sectorbuf, 0x200) < 0) {
451 fprintf(stderr,"[ERR] Seek to 0x%08llx in is_sansa failed.\n",
452 sansa->start+PPMI_OFFSET+0x200);
453 return -5;
456 if (get_mi4header(sectorbuf,&mi4header) < 0) {
457 fprintf(stderr,"[ERR] Invalid mi4header\n");
458 return -6;
461 /* Some sanity checks:
463 1) Main MI4 image without RBBL and < 100000 bytes -> old install
464 2) Main MI4 image with RBBL but no second image -> old install
467 sansa->hasoldbootloader = 0;
468 if (memcmp(sectorbuf+0x1f8,"RBBL",4)==0) {
469 /* Look for an original firmware after the first image */
470 if (sansa_seek_and_read(sansa,
471 sansa->start + PPMI_OFFSET + 0x200 + ppmi_length,
472 sectorbuf, 512) < 0) {
473 return -7;
476 if (get_mi4header(sectorbuf,&mi4header)!=0) {
477 fprintf(stderr,"[ERR] No original firmware found\n");
478 sansa->hasoldbootloader = 1;
480 } else if (mi4header.mi4size < 100000) {
481 fprintf(stderr,"[ERR] Old bootloader found\n");
482 sansa->hasoldbootloader = 1;
485 return 0;
488 int sansa_scan(struct sansa_t* sansa)
490 int i;
491 int n = 0;
492 char last_disk[4096];
493 int denied = 0;
494 int result;
496 printf("[INFO] Scanning disk devices...\n");
498 for (i = 0; i <= 25 ; i++) {
499 #ifdef __WIN32__
500 sprintf(sansa->diskname,"\\\\.\\PhysicalDrive%d",i);
501 #elif defined(linux) || defined (__linux)
502 sprintf(sansa->diskname,"/dev/sd%c",'a'+i);
503 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
504 || defined(__bsdi__) || defined(__DragonFly__)
505 sprintf(sansa->diskname,"/dev/da%d",i);
506 #elif defined(__APPLE__) && defined(__MACH__)
507 sprintf(sansa->diskname,"/dev/disk%d",i);
508 #else
509 #error No disk paths defined for this platform
510 #endif
511 if ((result = sansa_open(sansa, 1)) < 0) {
512 if(result == -2) {
513 denied++;
515 continue;
518 if (sansa_read_partinfo(sansa,1) < 0) {
519 continue;
522 if (is_sansa(sansa) < 0) {
523 continue;
526 #ifdef __WIN32__
527 printf("[INFO] %s found - disk device %d\n",sansa->targetname, i);
528 #else
529 printf("[INFO] %s found - %s\n",sansa->targetname, sansa->diskname);
530 #endif
531 n++;
532 strcpy(last_disk,sansa->diskname);
533 sansa_close(sansa);
536 if (n==1) {
537 /* Remember the disk name */
538 strcpy(sansa->diskname,last_disk);
540 else if (n == 0 && denied) {
541 printf("[ERR] FATAL: Permission denied on %d device(s) and no sansa detected.\n", denied);
542 #ifdef __WIN32__
543 printf("[ERR] You need to run this program with administrator priviledges!\n");
544 #else
545 printf("[ERR] You need permissions for raw disc access for this program to work!\n");
546 #endif
549 return (n == 0 && denied) ? -1 : n;
552 /* Prepare original firmware for writing to the firmware partition by decrypting
553 and updating the header */
554 static int prepare_original_firmware(unsigned char* buf, struct mi4header_t* mi4header)
556 unsigned char* tmpbuf;
557 int i;
558 int key_found;
560 get_mi4header(buf,mi4header);
562 #if 0
563 printf("mi4header->version =0x%08x\n",mi4header->version);
564 printf("mi4header->length =0x%08x\n",mi4header->length);
565 printf("mi4header->crc32 =0x%08x\n",mi4header->crc32);
566 printf("mi4header->enctype =0x%08x\n",mi4header->enctype);
567 printf("mi4header->mi4size =0x%08x\n",mi4header->mi4size);
568 printf("mi4header->plaintext =0x%08x\n",mi4header->plaintext);
569 #endif
571 /* Decrypt anything that needs decrypting. */
572 if (mi4header->plaintext < mi4header->mi4size - 0x200) {
573 /* TODO: Check different keys */
574 tmpbuf=malloc(mi4header->mi4size-(mi4header->plaintext+0x200));
575 if (tmpbuf==NULL) {
576 fprintf(stderr,"[ERR] Can not allocate memory\n");
577 return -1;
580 key_found=0;
581 for (i=0; i < NUM_KEYS && !key_found ; i++) {
582 tea_decrypt_buf(buf+(mi4header->plaintext+0x200),
583 tmpbuf,
584 mi4header->mi4size-(mi4header->plaintext+0x200),
585 keys[i]);
586 key_found = (le2uint(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55);
589 if (key_found) {
590 memcpy(buf+(mi4header->plaintext+0x200),tmpbuf,mi4header->mi4size-(mi4header->plaintext+0x200));
591 free(tmpbuf);
592 } else {
593 fprintf(stderr,"[ERR] Failed to decrypt image, aborting\n");
594 free(tmpbuf);
595 return -1;
599 /* Increase plaintext value to full file */
600 mi4header->plaintext = mi4header->mi4size - 0x200;
602 /* Update CRC checksum */
603 chksum_crc32gentab ();
604 mi4header->crc32 = chksum_crc32(buf+0x200,mi4header->mi4size-0x200);
606 set_mi4header(buf,mi4header);
608 /* Add Rockbox-specific header */
609 memcpy(buf+0x1f8,"RBOFe200",8);
611 return 0;
614 static int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header)
616 int ppmi_length;
617 int n;
619 /* Read 512 bytes from PPMI_OFFSET - the PPMI header plus the mi4 header */
620 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, buf, 512) < 0) {
621 return -1;
624 /* No need to check PPMI magic - it's done during init to confirm
625 this is an E200 */
626 ppmi_length = le2int(buf+4);
628 /* Firstly look for an original firmware after the first image */
629 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, buf, 512) < 0) {
630 return -1;
633 if (get_mi4header(buf,mi4header)==0) {
634 /* We have a valid MI4 file after a bootloader, so we use this. */
635 if ((n = sansa_seek_and_read(sansa,
636 sansa->start + PPMI_OFFSET + 0x200 + ppmi_length,
637 buf, mi4header->mi4size)) < 0) {
638 return -1;
640 } else {
641 /* No valid MI4 file, so read the first image. */
642 if ((n = sansa_seek_and_read(sansa,
643 sansa->start + PPMI_OFFSET + 0x200,
644 buf, ppmi_length)) < 0) {
645 return -1;
648 return prepare_original_firmware(buf, mi4header);
651 int sansa_read_firmware(struct sansa_t* sansa, char* filename)
653 int res;
654 int outfile;
655 struct mi4header_t mi4header;
657 res = load_original_firmware(sansa,sectorbuf,&mi4header);
658 if (res < 0)
659 return res;
661 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
662 if (outfile < 0) {
663 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
664 return -1;
667 res = write(outfile,sectorbuf,mi4header.mi4size);
668 if (res != (int)mi4header.mi4size) {
669 fprintf(stderr,"[ERR] Write error - %d\n", res);
670 return -1;
672 close(outfile);
674 return 0;
678 int sansa_add_bootloader(struct sansa_t* sansa, char* filename, int type)
680 int res;
681 int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */
682 int bl_length = 0; /* Keep gcc happy when building for rbutil */
683 struct mi4header_t mi4header;
684 int n;
685 int length;
687 if (type==FILETYPE_MI4) {
688 /* Step 1 - read bootloader into RAM. */
689 infile=open(filename,O_RDONLY|O_BINARY);
690 if (infile < 0) {
691 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
692 return -1;
695 bl_length = filesize(infile);
696 } else {
697 #ifndef RBUTIL
698 if (strcmp(sansa->targetname,"c200") == 0) {
699 bl_length = LEN_bootimg_c200;
700 } else {
701 bl_length = LEN_bootimg_e200;
703 #endif
706 /* Create PPMI header */
707 memset(sectorbuf,0,0x200);
708 memcpy(sectorbuf,"PPMI",4);
709 int2le(bl_length, sectorbuf+4);
710 int2le(0x00020000, sectorbuf+8);
712 if (type==FILETYPE_MI4) {
713 /* Read bootloader into sectorbuf+0x200 */
714 n = read(infile,sectorbuf+0x200,bl_length);
715 close(infile);
716 if (n < bl_length) {
717 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
718 ,bl_length,n);
719 return -1;
722 if (memcmp(sectorbuf+0x200+0x1f8,"RBBL",4)!=0) {
723 fprintf(stderr,"[ERR] %s is not a Rockbox bootloader, aborting.\n",
724 filename);
725 return -1;
727 } else {
728 #ifndef RBUTIL
729 if (strcmp(sansa->targetname,"c200") == 0) {
730 memcpy(sectorbuf+0x200,bootimg_c200,LEN_bootimg_c200);
731 } else {
732 memcpy(sectorbuf+0x200,bootimg_e200,LEN_bootimg_e200);
734 #endif
737 /* Load original firmware from Sansa to the space after the bootloader */
738 res = load_original_firmware(sansa,sectorbuf+0x200+bl_length,&mi4header);
739 if (res < 0)
740 return res;
742 /* Now write the whole thing back to the Sansa */
744 if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
745 fprintf(stderr,"[ERR] Seek to 0x%08llx in add_bootloader failed.\n",
746 sansa->start+PPMI_OFFSET);
747 return -5;
750 length = 0x200 + bl_length + mi4header.mi4size;
752 n=sansa_write(sansa, sectorbuf, length);
753 if (n < length) {
754 fprintf(stderr,"[ERR] Short write in add_bootloader\n");
755 return -6;
758 return 0;
761 int sansa_delete_bootloader(struct sansa_t* sansa)
763 int res;
764 struct mi4header_t mi4header;
765 int n;
766 int length;
768 /* Load original firmware from Sansa to sectorbuf+0x200 */
769 res = load_original_firmware(sansa,sectorbuf+0x200,&mi4header);
770 if (res < 0)
771 return res;
773 /* Create PPMI header */
774 memset(sectorbuf,0,0x200);
775 memcpy(sectorbuf,"PPMI",4);
776 int2le(mi4header.mi4size, sectorbuf+4);
777 int2le(0x00020000, sectorbuf+8);
779 /* Now write the whole thing back to the Sansa */
781 if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
782 fprintf(stderr,"[ERR] Seek to 0x%08llx in add_bootloader failed.\n",
783 sansa->start+PPMI_OFFSET);
784 return -5;
787 length = 0x200 + mi4header.mi4size;
789 n=sansa_write(sansa, sectorbuf, length);
790 if (n < length) {
791 fprintf(stderr,"[ERR] Short write in delete_bootloader\n");
792 return -6;
795 return 0;
798 void sansa_list_images(struct sansa_t* sansa)
800 struct mi4header_t mi4header;
801 loff_t ppmi_length;
803 /* Check Main firmware header */
804 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sectorbuf, 0x200) < 0) {
805 return;
808 ppmi_length = le2int(sectorbuf+4);
810 printf("[INFO] Image 1 - %llu bytes\n",ppmi_length);
812 /* Look for an original firmware after the first image */
813 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sectorbuf, 512) < 0) {
814 return;
817 if (get_mi4header(sectorbuf,&mi4header)==0) {
818 printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size);
822 int sansa_update_of(struct sansa_t* sansa, char* filename)
824 int n;
825 int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */
826 int of_length = 0; /* Keep gcc happy when building for rbutil */
827 int ppmi_length;
828 struct mi4header_t mi4header;
829 unsigned char buf[512];
831 /* Step 1 - check we have an OF on the Sansa to upgrade. We expect the
832 Rockbox bootloader to be installed and the OF to be after it on disk. */
834 /* Read 512 bytes from PPMI_OFFSET - the PPMI header */
835 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET,
836 buf, 512) < 0) {
837 return -1;
840 /* No need to check PPMI magic - it's done during init to confirm
841 this is an E200 */
842 ppmi_length = le2int(buf+4);
844 /* Look for an original firmware after the first image */
845 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length,
846 buf, 512) < 0) {
847 return -1;
850 if (get_mi4header(buf,&mi4header)!=0) {
851 /* We don't have a valid MI4 file after a bootloader, so do nothing. */
852 fprintf(stderr,"[ERR] No original firmware found at 0x%08llx\n",
853 sansa->start+PPMI_OFFSET+0x200+ppmi_length);
854 return -1;
857 /* Step 2 - read OF into RAM. */
858 infile=open(filename,O_RDONLY|O_BINARY);
859 if (infile < 0) {
860 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
861 return -1;
864 of_length = filesize(infile);
866 /* Load original firmware from file */
867 memset(sectorbuf,0,0x200);
868 n = read(infile,sectorbuf,of_length);
869 close(infile);
870 if (n < of_length) {
871 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
872 , of_length, n);
873 return -1;
876 /* Check we have a valid MI4 file. */
877 if (get_mi4header(sectorbuf,&mi4header)!=0) {
878 fprintf(stderr,"[ERR] %s is not a valid mi4 file\n",filename);
879 return -1;
882 /* Decrypt and build the header */
883 if(prepare_original_firmware(sectorbuf, &mi4header)!=0){
884 fprintf(stderr,"[ERR] Unable to build decrypted mi4 from %s\n"
885 ,filename);
886 return -1;
889 /* Step 3 - write the OF to the Sansa */
890 if (sansa_seek(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length) < 0) {
891 fprintf(stderr,"[ERR] Seek to 0x%08llx in sansa_update_of failed.\n",
892 sansa->start+PPMI_OFFSET+0x200+ppmi_length);
893 return -1;
896 n=sansa_write(sansa, sectorbuf, of_length);
897 if (n < of_length) {
898 fprintf(stderr,"[ERR] Short write in sansa_update_of\n");
899 return -1;
902 /* Step 4 - zero out the nvparams section - we have to do this or we end up
903 with multiple copies of the nvparams data and don't know which one to
904 work with for the database rebuild disabling trick in our bootloader */
905 if (strcmp(sansa->targetname,"e200") == 0) {
906 printf("[INFO] Resetting Original Firmware settings\n");
907 if (sansa_seek(sansa, sansa->start+NVPARAMS_OFFSET+0x200) < 0) {
908 fprintf(stderr,"[ERR] Seek to 0x%08llx in sansa_update_of failed.\n",
909 sansa->start+NVPARAMS_OFFSET+0x200);
910 return -1;
913 memset(sectorbuf,0,NVPARAMS_SIZE);
914 n=sansa_write(sansa, sectorbuf, NVPARAMS_SIZE);
915 if (n < NVPARAMS_SIZE) {
916 fprintf(stderr,"[ERR] Short write in sansa_update_of\n");
917 return -1;
921 return 0;
924 /* Update the PPBL (bootloader) image in the hidden firmware partition */
925 int sansa_update_ppbl(struct sansa_t* sansa, char* filename)
927 int n;
928 int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */
929 int ppbl_length = 0; /* Keep gcc happy when building for rbutil */
931 /* Step 1 - read bootloader into RAM. */
932 infile=open(filename,O_RDONLY|O_BINARY);
933 if (infile < 0) {
934 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
935 return -1;
938 ppbl_length = filesize(infile);
940 n = read(infile,sectorbuf+0x200,ppbl_length);
941 close(infile);
942 if (n < ppbl_length) {
943 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", ppbl_length, n);
944 return -1;
947 /* Step 2 - Build the header */
948 memset(sectorbuf,0,0x200);
949 memcpy(sectorbuf,"PPBL",4);
950 int2le(ppbl_length, sectorbuf+4);
951 int2le(0x00010000, sectorbuf+8);
953 /* Step 3 - write the bootloader to the Sansa */
954 if (sansa_seek(sansa, sansa->start) < 0) {
955 fprintf(stderr,"[ERR] Seek to 0x%08llx in sansa_update_ppbl failed.\n", sansa->start);
956 return -1;
959 n=sansa_write(sansa, sectorbuf, ppbl_length + 0x200);
960 if (n < (ppbl_length+0x200)) {
961 fprintf(stderr,"[ERR] Short write in sansa_update_ppbl\n");
962 return -1;
965 return 0;