rbutil: Merge rbutil with utils folder.

rbutil uses several components from the utils folder, and can be
considered part of utils too. Having it in a separate folder is an
arbitrary split that doesn't help anymore these days, so merge them.

This also allows other utils to easily use libtools.make without the
need to navigate to a different folder.

Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21
This commit is contained in:
Dominik Riebeling 2021-12-15 21:04:28 +01:00
parent 6c6f0757d7
commit c876d3bbef
494 changed files with 13 additions and 13 deletions

View file

@ -0,0 +1,52 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
TARGET_DIR ?= $(shell pwd)/
CFLAGS += -Wall -W -D_LARGEFILE64_SOURCE
OUTPUT = sansapatcher
# inputs
LIBSOURCES := sansapatcher.c sansaio-posix.c sansaio-win32.c
SOURCES := main.c
# additional link dependencies for the standalone executable
EXTRADEPS :=
# Releases of sansapatcher are created with "make RELEASE=1". This
# enables BOOTOBJS and uses the VERSION string defined in main.c
ifdef RELEASE
CFLAGS += -DRELEASE
BOOTOBJS=1
endif
ifdef WITH_BOOTOBJS
BOOTSRC = bootimg_c200.c bootimg_e200.c
SOURCES += $(BOOTSRC)
CFLAGS += -DWITH_BOOTOBJS
endif
include ../libtools.make
# find out if we need to link the manifest resource.
# Since libtools.make sets up BINARY we check it for the file extension .exe.
ifeq ($(findstring exe,$(BINARY)),exe)
$(BINARY): $(OBJDIR)sansapatcher-rc.o
endif
$(OBJDIR)main.o: $(BOOTSRC)
$(OBJDIR)sansapatcher-rc.o: sansapatcher.rc sansapatcher.manifest
@echo WINDRES $(notdir $<)
$(SILENT)$(CROSS)$(WINDRES) -i sansapatcher.rc -o $@
bootimg_c200.c: firmware.mi4 $(BIN2C)
@echo BIN2C $<
$(SILENT)$(BIN2C) $< $*
bootimg_e200.c: PP5022.mi4 $(BIN2C)
@echo BIN2C $< $*
$(SILENT)$(BIN2C) $< $*

36
utils/sansapatcher/README Normal file
View file

@ -0,0 +1,36 @@
sansapatcher
------------
To compile sansapatcher, you need both the C200 and E200 Rockbox
bootloaders. The latest bootloaders can always be found here:
http://download.rockbox.org/bootloader/sandisk-sansa/e200/PP5022.mi4
http://download.rockbox.org/bootloader/sandisk-sansa/c200/firmware.mi4
Place both these files in the sansapatcher source directory, and type "make".
Building your own bootloaders
-----------------------------
If you would like to compile the bootloaders yourself, they are the output of
running the "Bootloader" build for the E200 and C200 targets.
NOTE: Unless you know what you are doing, it is recommended that you
use the official pre-built binary bootloaders linked to above.
Bootloaders compiled from current Rockbox SVN are untested and
may contain bugs preventing you from starting the device (or
worse...).
In the Rockbox source directory, do:
mkdir build-e200-bootloader
cd build-e200-bootloader
../tools/configure
[Select E200, then B for bootloader]
make
This will create PP5022.mi4 which you should copy to the sansapatcher
build directory.
A similar process for the C200 will create firmware.mi4.

420
utils/sansapatcher/main.c Normal file
View file

@ -0,0 +1,420 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Dave Chapman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sansapatcher.h"
#include "sansaio.h"
#include "parttypes.h"
#ifdef WITH_BOOTOBJS
#include "bootimg_c200.h"
#include "bootimg_e200.h"
#endif
#ifndef VERSION
#define VERSION "0.8 with v6.0 bootloaders"
#endif
enum {
NONE,
INSTALL,
INTERACTIVE,
SHOW_INFO,
LIST_IMAGES,
DELETE_BOOTLOADER,
ADD_BOOTLOADER,
READ_FIRMWARE,
WRITE_FIRMWARE,
READ_PARTITION,
WRITE_PARTITION,
UPDATE_OF,
UPDATE_PPBL
};
static void print_usage(void)
{
fprintf(stderr,"Usage: sansapatcher --scan\n");
#ifdef __WIN32__
fprintf(stderr," or sansapatcher [DISKNO] [action]\n");
#else
fprintf(stderr," or sansapatcher [device] [action]\n");
#endif
fprintf(stderr,"\n");
fprintf(stderr,"Where [action] is one of the following options:\n");
fprintf(stderr," --install\n");
fprintf(stderr," -l, --list\n");
fprintf(stderr," -rf, --read-firmware filename.mi4\n");
fprintf(stderr," -a, --add-bootloader filename.mi4\n");
fprintf(stderr," -d, --delete-bootloader\n");
fprintf(stderr," -of --update-original-firmware filename.mi4\n");
fprintf(stderr," -bl --update-ppbl filename.bin\n");
fprintf(stderr,"\n");
#ifdef __WIN32__
fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your sansa's hard disk.\n");
fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk 0, the next disk\n");
fprintf(stderr,"will be disk 1 etc. sansapatcher will refuse to access a disk unless it\n");
fprintf(stderr,"can identify it as being an E200 or C200.\n");
fprintf(stderr,"\n");
#else
#if defined(linux) || defined (__linux)
fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your sansa.\n");
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
fprintf(stderr,"\"device\" is the device node (e.g. /dev/da1) assigned to your sansa.\n");
#elif defined(__APPLE__) && defined(__MACH__)
fprintf(stderr,"\"device\" is the device node (e.g. /dev/disk1) assigned to your sansa.\n");
#endif
fprintf(stderr,"sansapatcher will refuse to access a disk unless it can identify it as being\n");
fprintf(stderr,"an E200 or C200.\n");
#endif
}
static const char* get_parttype(int pt)
{
int i;
static const char unknown[]="Unknown";
if (pt == -1) {
return "HFS/HFS+";
}
i=0;
while (parttypes[i].name != NULL) {
if (parttypes[i].type == pt) {
return (parttypes[i].name);
}
i++;
}
return unknown;
}
static void display_partinfo(struct sansa_t* sansa)
{
int i;
double sectors_per_MB = (1024.0*1024.0)/sansa->sector_size;
printf("[INFO] Part Start Sector End Sector Size (MB) Type\n");
for ( i = 0; i < 4; i++ ) {
if (sansa->pinfo[i].start != 0) {
printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n",
i,
sansa->pinfo[i].start,
sansa->pinfo[i].start+sansa->pinfo[i].size-1,
sansa->pinfo[i].size/sectors_per_MB,
get_parttype(sansa->pinfo[i].type),
sansa->pinfo[i].type);
}
}
}
int main(int argc, char* argv[])
{
char yesno[4];
int i;
int n;
char* filename;
int action = SHOW_INFO;
struct sansa_t sansa;
int res = 0;
unsigned char* buf = NULL;
unsigned int len;
fprintf(stderr,"sansapatcher v" VERSION " - (C) Dave Chapman 2006-2007\n");
fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n");
fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
if ((argc > 1) && ((strcmp(argv[1],"-h")==0) || (strcmp(argv[1],"--help")==0))) {
print_usage();
return SANSA_OK;
}
if (sansa_alloc_buffer(&sansa, BUFFER_SIZE) < 0) {
fprintf(stderr,"Failed to allocate memory buffer\n");
return SANSA_INTERNAL_ERROR;
}
if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) {
if (sansa_scan(&sansa) == 0)
fprintf(stderr,"[ERR] No E200s or C200s found.\n");
return SANSA_NOT_FOUND;
}
/* If the first parameter doesn't start with -, then we interpret it as a device */
if ((argc > 1) && (argv[1][0] != '-')) {
sansa.diskname[0]=0;
#ifdef __WIN32__
snprintf(sansa.diskname,sizeof(sansa.diskname),"\\\\.\\PhysicalDrive%s",argv[1]);
#else
strncpy(sansa.diskname,argv[1],sizeof(sansa.diskname));
#endif
i = 2;
} else {
/* Autoscan for C200/E200s */
n = sansa_scan(&sansa);
if (n==0) {
fprintf(stderr,"[ERR] No E200s or C200s found, aborting\n");
fprintf(stderr,"[ERR] Please connect your sansa and ensure it is in UMS mode\n");
#if defined(__APPLE__) && defined(__MACH__)
fprintf(stderr,"[ERR] Also ensure that your Sansa's main partition is not mounted.\n");
#elif !defined(__WIN32__)
if (geteuid()!=0) {
fprintf(stderr,"[ERR] You may also need to run sansapatcher as root.\n");
}
#endif
fprintf(stderr,"[ERR] Please refer to the Rockbox manual if you continue to have problems.\n");
} else if (n > 1) {
fprintf(stderr,"[ERR] %d Sansas found, aborting\n",n);
fprintf(stderr,"[ERR] Please connect only one Sansa and re-run sansapatcher.\n");
}
if (n != 1) {
#ifdef WITH_BOOTOBJS
if (argc==1) {
printf("\nPress ENTER to exit sansapatcher :");
fgets(yesno,4,stdin);
}
#endif
return n > 1 ? SANSA_MULTIPLE_DEVICES : SANSA_NOT_FOUND;
}
i = 1;
}
#ifdef WITH_BOOTOBJS
action = INTERACTIVE;
#endif
while (i < argc) {
if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) {
action = LIST_IMAGES;
i++;
} else if (strcmp(argv[i],"--install")==0) {
action = INSTALL;
i++;
} else if ((strcmp(argv[i],"-d")==0) ||
(strcmp(argv[i],"--delete-bootloader")==0)) {
action = DELETE_BOOTLOADER;
i++;
} else if ((strcmp(argv[i],"-a")==0) ||
(strcmp(argv[i],"--add-bootloader")==0)) {
action = ADD_BOOTLOADER;
i++;
if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
filename=argv[i];
i++;
} else if ((strcmp(argv[i],"-of")==0) ||
(strcmp(argv[i],"--update-original-firmware")==0)) {
action = UPDATE_OF;
i++;
if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
filename=argv[i];
i++;
} else if ((strcmp(argv[i],"-bl")==0) ||
(strcmp(argv[i],"--update-ppbl")==0)) {
action = UPDATE_PPBL;
i++;
if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
filename=argv[i];
i++;
} else if ((strcmp(argv[i],"-rf")==0) ||
(strcmp(argv[i],"--read-firmware")==0)) {
action = READ_FIRMWARE;
i++;
if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
filename=argv[i];
i++;
}
}
if (sansa.diskname[0]==0) {
print_usage();
return SANSA_WRONG_ARGUMENTS;
}
if (sansa_open(&sansa, 0) < 0) {
return SANSA_ACCESS_DENIED;
}
fprintf(stderr,"[INFO] Reading partition table from %s\n",sansa.diskname);
fprintf(stderr,"[INFO] Sector size is %d bytes\n",sansa.sector_size);
if (sansa_read_partinfo(&sansa,0) < 0) {
return SANSA_PARTITION_ERROR;
}
display_partinfo(&sansa);
i = is_sansa(&sansa);
if (i < 0) {
fprintf(stderr,"[ERR] Disk is not an E200 or C200 (%d), aborting.\n",i);
return SANSA_WRONG_TYPE;
}
if (sansa.hasoldbootloader) {
printf("[ERR] ************************************************************************\n");
printf("[ERR] *** OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n");
printf("[ERR] *** You must reinstall the original Sansa firmware before running\n");
printf("[ERR] *** sansapatcher for the first time.\n");
printf("[ERR] *** See http://www.rockbox.org/wiki/SansaE200Install\n");
printf("[ERR] ************************************************************************\n");
res = SANSA_OLD_INSTALL;
} else {
if (action==LIST_IMAGES) {
sansa_list_images(&sansa);
#ifdef WITH_BOOTOBJS
} else if (action==INTERACTIVE) {
printf("Enter i to install the Rockbox bootloader, u to uninstall\n or c to cancel and do nothing (i/u/c) :");
if (fgets(yesno,4,stdin)) {
if (yesno[0]=='i') {
if (sansa_reopen_rw(&sansa) < 0) {
res = SANSA_CANNOT_REOPEN;
}
if (strcmp(sansa.targetname,"c200") == 0) {
len = LEN_bootimg_c200;
buf = bootimg_c200;
} else {
len = LEN_bootimg_e200;
buf = bootimg_e200;
}
if (sansa_add_bootloader(&sansa, buf, len)==0) {
fprintf(stderr,"[INFO] Bootloader installed successfully.\n");
} else {
fprintf(stderr,"[ERR] --install failed.\n");
res = SANSA_INSTALL_FAILED;
}
} else if (yesno[0]=='u') {
if (sansa_reopen_rw(&sansa) < 0) {
res = SANSA_CANNOT_REOPEN;
}
if (sansa_delete_bootloader(&sansa)==0) {
fprintf(stderr,"[INFO] Bootloader removed.\n");
} else {
fprintf(stderr,"[ERR] Bootloader removal failed.\n");
res = SANSA_UNINSTALL_FAILED;
}
}
}
#endif
} else if (action==READ_FIRMWARE) {
if (sansa_read_firmware(&sansa, filename)==0) {
fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename);
} else {
fprintf(stderr,"[ERR] --read-firmware failed.\n");
}
#ifdef WITH_BOOTOBJS
} else if (action==INSTALL) {
if (sansa_reopen_rw(&sansa) < 0) {
return SANSA_CANNOT_REOPEN;
}
if (strcmp(sansa.targetname,"c200") == 0) {
len = LEN_bootimg_c200;
buf = bootimg_c200;
} else {
len = LEN_bootimg_e200;
buf = bootimg_e200;
}
if (sansa_add_bootloader(&sansa, buf, len)==0) {
fprintf(stderr,"[INFO] Bootloader installed successfully.\n");
} else {
fprintf(stderr,"[ERR] --install failed.\n");
}
#endif
} else if (action==ADD_BOOTLOADER) {
if (sansa_reopen_rw(&sansa) < 0) {
return SANSA_CANNOT_REOPEN;
}
len = sansa_read_bootloader(&sansa, filename, &buf);
if (len > 0) {
if (sansa_add_bootloader(&sansa, buf, len)==0) {
fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename);
} else {
fprintf(stderr,"[ERR] --add-bootloader failed.\n");
}
}
} else if (action==DELETE_BOOTLOADER) {
if (sansa_reopen_rw(&sansa) < 0) {
return SANSA_CANNOT_REOPEN;
}
if (sansa_delete_bootloader(&sansa)==0) {
fprintf(stderr,"[INFO] Bootloader removed successfully.\n");
} else {
fprintf(stderr,"[ERR] --delete-bootloader failed.\n");
}
} else if (action==UPDATE_OF) {
if (sansa_reopen_rw(&sansa) < 0) {
return SANSA_CANNOT_REOPEN;
}
if (sansa_update_of(&sansa, filename)==0) {
fprintf(stderr,"[INFO] OF updated successfully.\n");
} else {
fprintf(stderr,"[ERR] --update-original-firmware failed.\n");
}
} else if (action==UPDATE_PPBL) {
printf("[WARN] PPBL installation will overwrite your bootloader. This will lead to a\n");
printf(" Sansa that won't boot if the bootloader file is invalid. Only continue if\n");
printf(" you're sure you know what you're doing.\n");
printf(" Continue (y/n)? ");
if (fgets(yesno,4,stdin)) {
if (yesno[0]=='y') {
if (sansa_reopen_rw(&sansa) < 0) {
return SANSA_CANNOT_REOPEN;
}
if (sansa_update_ppbl(&sansa, filename)==0) {
fprintf(stderr,"[INFO] PPBL updated successfully.\n");
} else {
fprintf(stderr,"[ERR] --update-ppbl failed.\n");
}
}
}
}
}
sansa_close(&sansa);
sansa_dealloc_buffer(&sansa);
#ifdef WITH_BOOTOBJS
if (action==INTERACTIVE) {
printf("Press ENTER to exit sansapatcher :");
fgets(yesno,4,stdin);
}
#endif
return res;
}

View file

@ -0,0 +1,109 @@
/* DOS partition types - taken from fdisk */
struct parttype {
unsigned char type;
char *name;
};
struct parttype parttypes[] = {
{0x00, "Empty"},
{0x01, "FAT12"},
{0x02, "XENIX root"},
{0x03, "XENIX usr"},
{0x04, "FAT16 <32M"},
{0x05, "Extended"}, /* DOS 3.3+ extended partition */
{0x06, "FAT16"}, /* DOS 16-bit >=32M */
{0x07, "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
{0x08, "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
{0x09, "AIX bootable"}, /* AIX data or Coherent */
{0x0a, "OS/2 Boot Manager"},/* OS/2 Boot Manager */
{0x0b, "W95 FAT32"},
{0x0c, "W95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */
{0x0e, "W95 FAT16 (LBA)"},
{0x0f, "W95 Ext'd (LBA)"},
{0x10, "OPUS"},
{0x11, "Hidden FAT12"},
{0x12, "Compaq diagnostics"},
{0x14, "Hidden FAT16 <32M"},
{0x16, "Hidden FAT16"},
{0x17, "Hidden HPFS/NTFS"},
{0x18, "AST SmartSleep"},
{0x1b, "Hidden W95 FAT32"},
{0x1c, "Hidden W95 FAT32 (LBA)"},
{0x1e, "Hidden W95 FAT16 (LBA)"},
{0x24, "NEC DOS"},
{0x39, "Plan 9"},
{0x3c, "PartitionMagic recovery"},
{0x40, "Venix 80286"},
{0x41, "PPC PReP Boot"},
{0x42, "SFS"},
{0x4d, "QNX4.x"},
{0x4e, "QNX4.x 2nd part"},
{0x4f, "QNX4.x 3rd part"},
{0x50, "OnTrack DM"},
{0x51, "OnTrack DM6 Aux1"}, /* (or Novell) */
{0x52, "CP/M"}, /* CP/M or Microport SysV/AT */
{0x53, "OnTrack DM6 Aux3"},
{0x54, "OnTrackDM6"},
{0x55, "EZ-Drive"},
{0x56, "Golden Bow"},
{0x5c, "Priam Edisk"},
{0x61, "SpeedStor"},
{0x63, "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
{0x64, "Novell Netware 286"},
{0x65, "Novell Netware 386"},
{0x70, "DiskSecure Multi-Boot"},
{0x75, "PC/IX"},
{0x80, "Old Minix"}, /* Minix 1.4a and earlier */
{0x81, "Minix / old Linux"},/* Minix 1.4b and later */
{0x82, "Linux swap / Solaris"},
{0x83, "Linux"},
{0x84, "OS/2 hidden C: drive"},
{0x85, "Linux extended"},
{0x86, "NTFS volume set"},
{0x87, "NTFS volume set"},
{0x88, "Linux plaintext"},
{0x8e, "Linux LVM"},
{0x93, "Amoeba"},
{0x94, "Amoeba BBT"}, /* (bad block table) */
{0x9f, "BSD/OS"}, /* BSDI */
{0xa0, "IBM Thinkpad hibernation"},
{0xa5, "FreeBSD"}, /* various BSD flavours */
{0xa6, "OpenBSD"},
{0xa7, "NeXTSTEP"},
{0xa8, "Darwin UFS"},
{0xa9, "NetBSD"},
{0xab, "Darwin boot"},
{0xb7, "BSDI fs"},
{0xb8, "BSDI swap"},
{0xbb, "Boot Wizard hidden"},
{0xbe, "Solaris boot"},
{0xbf, "Solaris"},
{0xc1, "DRDOS/sec (FAT-12)"},
{0xc4, "DRDOS/sec (FAT-16 < 32M)"},
{0xc6, "DRDOS/sec (FAT-16)"},
{0xc7, "Syrinx"},
{0xda, "Non-FS data"},
{0xdb, "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or
Concurrent DOS or CTOS */
{0xde, "Dell Utility"}, /* Dell PowerEdge Server utilities */
{0xdf, "BootIt"}, /* BootIt EMBRM */
{0xe1, "DOS access"}, /* DOS access or SpeedStor 12-bit FAT
extended partition */
{0xe3, "DOS R/O"}, /* DOS R/O or SpeedStor */
{0xe4, "SpeedStor"}, /* SpeedStor 16-bit FAT extended
partition < 1024 cyl. */
{0xeb, "BeOS fs"},
{0xee, "EFI GPT"}, /* Intel EFI GUID Partition Table */
{0xef, "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */
{0xf0, "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */
{0xf1, "SpeedStor"},
{0xf4, "SpeedStor"}, /* SpeedStor large partition */
{0xf2, "DOS secondary"}, /* DOS 3.3+ secondary */
{0xfd, "Linux raid autodetect"},/* New (2.2.x) raid partition with
autodetect using persistent
superblock */
{0xfe, "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */
{0xff, "BBT"}, /* Xenix Bad Block Table */
{ 0, 0 }
};

View file

@ -0,0 +1,157 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Dave Chapman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#if !defined(_WIN32) /* all non-Windows platforms supported are POSIX. */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#if defined(linux) || defined (__linux)
#include <sys/mount.h>
#define SANSA_SECTORSIZE_IOCTL BLKSSZGET
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
|| defined(__bsdi__) || defined(__DragonFly__)
#include <sys/disk.h>
#define SANSA_SECTORSIZE_IOCTL DIOCGSECTORSIZE
#elif defined(__APPLE__) && defined(__MACH__)
#include <sys/disk.h>
#define SANSA_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE
#else
#error No sector-size detection implemented for this platform
#endif
#include "sansaio.h"
#if defined(__APPLE__) && defined(__MACH__)
static int sansa_unmount(struct sansa_t* sansa)
{
char cmd[4096];
int res;
sprintf(cmd, "/usr/sbin/diskutil unmount \"%ss1\"",sansa->diskname);
fprintf(stderr,"[INFO] ");
res = system(cmd);
if (res==0) {
return 0;
} else {
perror("Unmount failed");
return -1;
}
}
#endif
void sansa_print_error(char* msg)
{
perror(msg);
}
int sansa_open(struct sansa_t* sansa, int silent)
{
sansa->dh=open(sansa->diskname,O_RDONLY);
if (sansa->dh < 0) {
if (!silent) perror(sansa->diskname);
if(errno == EACCES) return -2;
else return -1;
}
if(ioctl(sansa->dh,SANSA_SECTORSIZE_IOCTL,&sansa->sector_size) < 0) {
sansa->sector_size=512;
if (!silent) {
fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n"
,sansa->sector_size);
}
}
return 0;
}
int sansa_reopen_rw(struct sansa_t* sansa)
{
#if defined(__APPLE__) && defined(__MACH__)
if (sansa_unmount(sansa) < 0)
return -1;
#endif
close(sansa->dh);
sansa->dh=open(sansa->diskname,O_RDWR);
if (sansa->dh < 0) {
perror(sansa->diskname);
return -1;
}
return 0;
}
int sansa_close(struct sansa_t* sansa)
{
close(sansa->dh);
return 0;
}
int sansa_alloc_buffer(struct sansa_t *sansa, int bufsize)
{
sansa->sectorbuf=malloc(bufsize);
if (sansa->sectorbuf == NULL) {
return -1;
}
return 0;
}
int sansa_dealloc_buffer(struct sansa_t* sansa)
{
if (sansa->sectorbuf == NULL) {
return -1;
}
free(sansa->sectorbuf);
sansa->sectorbuf = NULL;
return 0;
}
int sansa_seek(struct sansa_t* sansa, loff_t pos)
{
off_t res;
res = lseek64(sansa->dh, pos, SEEK_SET);
if (res == -1) {
return -1;
}
return 0;
}
int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes)
{
return read(sansa->dh, buf, nbytes);
}
int sansa_write(struct sansa_t* sansa, int nbytes)
{
return write(sansa->dh, sansa->sectorbuf, nbytes);
}
#endif

View file

@ -0,0 +1,217 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Dave Chapman
*
* error(), lock_volume() and unlock_volume() functions and inspiration taken
* from:
* RawDisk - Direct Disk Read/Write Access for NT/2000/XP
* Copyright (c) 2003 Jan Kiszka
* http://www.stud.uni-hannover.de/user/73174/RawDisk/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#if defined(_WIN32)
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef __WIN32__
#include <windows.h>
#include <winioctl.h>
#endif
#include "sansaio.h"
static int lock_volume(HANDLE hDisk)
{
DWORD dummy;
return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0,
&dummy, NULL);
}
static int unlock_volume(HANDLE hDisk)
{
DWORD dummy;
return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0,
&dummy, NULL);
}
void sansa_print_error(char* msg)
{
LPSTR pMsgBuf = NULL;
printf(msg);
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), pMsgBuf,
0, NULL);
printf(pMsgBuf);
LocalFree(pMsgBuf);
}
int sansa_open(struct sansa_t* sansa, int silent)
{
DISK_GEOMETRY_EX diskgeometry_ex;
DISK_GEOMETRY diskgeometry;
unsigned long n;
sansa->dh = CreateFileA(sansa->diskname, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
if (sansa->dh == INVALID_HANDLE_VALUE) {
if (!silent) sansa_print_error(" Error opening disk: ");
if(GetLastError() == ERROR_ACCESS_DENIED)
return -2;
else
return -1;
}
if (!lock_volume(sansa->dh)) {
if (!silent) sansa_print_error(" Error locking disk: ");
return -1;
}
if (!DeviceIoControl(sansa->dh,
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
NULL,
0,
&diskgeometry_ex,
sizeof(diskgeometry_ex),
&n,
NULL)) {
if (!DeviceIoControl(sansa->dh,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
&diskgeometry,
sizeof(diskgeometry),
&n,
NULL)) {
if (!silent) sansa_print_error(" Error reading disk geometry: ");
return -1;
} else {
sansa->sector_size=diskgeometry.BytesPerSector;
}
} else {
sansa->sector_size=diskgeometry_ex.Geometry.BytesPerSector;
}
return 0;
}
int sansa_reopen_rw(struct sansa_t* sansa)
{
/* Close existing file and re-open for writing */
unlock_volume(sansa->dh);
CloseHandle(sansa->dh);
sansa->dh = CreateFileA(sansa->diskname, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
if (sansa->dh == INVALID_HANDLE_VALUE) {
sansa_print_error(" Error opening disk: ");
return -1;
}
if (!lock_volume(sansa->dh)) {
sansa_print_error(" Error locking disk: ");
return -1;
}
return 0;
}
int sansa_close(struct sansa_t* sansa)
{
unlock_volume(sansa->dh);
CloseHandle(sansa->dh);
return 0;
}
int sansa_alloc_buffer(struct sansa_t* sansa, int bufsize)
{
/* The ReadFile function requires a memory buffer aligned to a multiple of
the disk sector size. */
sansa->sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE);
if (sansa->sectorbuf == NULL) {
sansa_print_error(" Error allocating a buffer: ");
return -1;
}
return 0;
}
int sansa_dealloc_buffer(struct sansa_t* sansa)
{
if (sansa->sectorbuf == NULL) {
return -1;
}
if(!VirtualFree(sansa->sectorbuf, 0, MEM_RELEASE)) {
sansa_print_error(" Error releasing buffer ");
return -1;
}
sansa->sectorbuf = NULL;
return 0;
}
int sansa_seek(struct sansa_t* sansa, loff_t pos)
{
LARGE_INTEGER li;
li.QuadPart = pos;
li.LowPart = SetFilePointer (sansa->dh, li.LowPart, &li.HighPart, FILE_BEGIN);
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
sansa_print_error(" Seek error ");
return -1;
}
return 0;
}
int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes)
{
unsigned long count;
if (!ReadFile(sansa->dh, buf, nbytes, &count, NULL)) {
sansa_print_error(" Error reading from disk: ");
return -1;
}
return count;
}
int sansa_write(struct sansa_t* sansa, int nbytes)
{
unsigned long count;
if (!WriteFile(sansa->dh, sansa->sectorbuf, nbytes, &count, NULL)) {
sansa_print_error(" Error writing to disk: ");
return -1;
}
return count;
}
#endif

View file

@ -0,0 +1,88 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Dave Chapman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __SANSAIO_H
#define __SANSAIO_H
#include <stdint.h>
#if !defined(_MSC_VER)
#include <unistd.h> /* not available on MSVC */
#endif
#if defined(__WIN32__) || defined(_WIN32)
#include <windows.h>
#define loff_t int64_t
#else
#define HANDLE int
#define O_BINARY 0
/* Only Linux seems to need lseek64 and loff_t */
#if !defined(linux) && !defined (__linux)
#define loff_t off_t
#define lseek64 lseek
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct sansa_partinfo_t {
unsigned long start; /* first sector (LBA) */
unsigned long size; /* number of sectors */
int type;
};
struct mi4header_t {
uint32_t version;
uint32_t length;
uint32_t crc32;
uint32_t enctype;
uint32_t mi4size;
uint32_t plaintext;
};
struct sansa_t {
HANDLE dh;
unsigned char* sectorbuf;
char diskname[4096];
int sector_size;
struct sansa_partinfo_t pinfo[4];
int hasoldbootloader;
char* targetname; /* "e200" or "c200" */
loff_t start; /* Offset in bytes of firmware partition from start of disk */
};
void sansa_print_error(char* msg);
int sansa_open(struct sansa_t* sansa, int silent);
int sansa_reopen_rw(struct sansa_t* sansa);
int sansa_close(struct sansa_t* sansa);
int sansa_seek(struct sansa_t* sansa, loff_t pos);
int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes);
int sansa_write(struct sansa_t* sansa, int nbytes);
int sansa_alloc_buffer(struct sansa_t* sansa, int bufsize);
int sansa_dealloc_buffer(struct sansa_t* sansa);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,975 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Dave Chapman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sansaio.h"
#include "sansapatcher.h"
/* The offset of the MI4 image header in the firmware partition */
#define PPMI_OFFSET 0x80000
#define NVPARAMS_OFFSET 0x780000
#define NVPARAMS_SIZE (0x80000-0x200)
int sansa_verbose = 0;
/* Windows requires the buffer for disk I/O to be aligned in memory on a
multiple of the disk volume size - so we use a single global variable
and initialise it with sansa_alloc_buf() in main().
*/
static off_t filesize(int fd) {
struct stat buf;
if (fstat(fd,&buf) < 0) {
perror("[ERR] Checking filesize of input file");
return -1;
} else {
return(buf.st_size);
}
}
/* Partition table parsing code taken from Rockbox */
#define MAX_SECTOR_SIZE 2048
#define SECTOR_SIZE 512
static inline int32_t le2int(const unsigned char* buf)
{
int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
return res;
}
static inline uint32_t le2uint(const unsigned char* buf)
{
uint32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
return res;
}
static inline void int2le(unsigned int val, unsigned char* addr)
{
addr[0] = val & 0xFF;
addr[1] = (val >> 8) & 0xff;
addr[2] = (val >> 16) & 0xff;
addr[3] = (val >> 24) & 0xff;
}
#define BYTES2INT32(array,pos)\
((long)array[pos] | ((long)array[pos+1] << 8 ) |\
((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
int sansa_read_partinfo(struct sansa_t* sansa, int silent)
{
int i;
unsigned long count;
count = sansa_read(sansa,sansa->sectorbuf, sansa->sector_size);
if (count <= 0) {
sansa_print_error(" Error reading from disk: ");
return -1;
}
if ((sansa->sectorbuf[510] == 0x55) && (sansa->sectorbuf[511] == 0xaa)) {
/* parse partitions */
for ( i = 0; i < 4; i++ ) {
unsigned char* ptr = sansa->sectorbuf + 0x1be + 16*i;
sansa->pinfo[i].type = ptr[4];
sansa->pinfo[i].start = BYTES2INT32(ptr, 8);
sansa->pinfo[i].size = BYTES2INT32(ptr, 12);
/* extended? */
if ( sansa->pinfo[i].type == 5 ) {
/* not handled yet */
}
}
} else if ((sansa->sectorbuf[0] == 'E') && (sansa->sectorbuf[1] == 'R')) {
if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n");
return -1;
}
/* Calculate the starting position of the firmware partition */
sansa->start = (loff_t)sansa->pinfo[1].start*(loff_t)sansa->sector_size;
return 0;
}
/* NOTE: memmem implementation copied from glibc-2.2.4 - it's a GNU
extension and is not universally. In addition, early versions of
memmem had a serious bug - the meaning of needle and haystack were
reversed. */
/* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
/* Return the first occurrence of NEEDLE in HAYSTACK. */
static void *
sansa_memmem (haystack, haystack_len, needle, needle_len)
const void *haystack;
size_t haystack_len;
const void *needle;
size_t needle_len;
{
const char *begin;
const char *const last_possible
= (const char *) haystack + haystack_len - needle_len;
if (needle_len == 0)
/* The first occurrence of the empty string is deemed to occur at
the beginning of the string. */
return (void *) haystack;
/* Sanity check, otherwise the loop might search through the whole
memory. */
if (__builtin_expect (haystack_len < needle_len, 0))
return NULL;
for (begin = (const char *) haystack; begin <= last_possible; ++begin)
if (begin[0] == ((const char *) needle)[0] &&
!memcmp ((const void *) &begin[1],
(const void *) ((const char *) needle + 1),
needle_len - 1))
return (void *) begin;
return NULL;
}
/*
* CRC32 implementation taken from:
*
* efone - Distributed internet phone system.
*
* (c) 1999,2000 Krzysztof Dabrowski
* (c) 1999,2000 ElysiuM deeZine
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
* so make sure, you call it before using the other
* functions!
*/
static unsigned int crc_tab[256];
/* chksum_crc() -- to a given block, this one calculates the
* crc32-checksum until the length is
* reached. the crc32-checksum will be
* the result.
*/
static unsigned int chksum_crc32 (const unsigned char *block, unsigned int length)
{
register unsigned long crc;
unsigned long i;
crc = 0;
for (i = 0; i < length; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
}
return (crc);
}
/* chksum_crc32gentab() -- to a global crc_tab[256], this one will
* calculate the crcTable for crc32-checksums.
* it is generated to the polynom [..]
*/
static void chksum_crc32gentab (void)
{
unsigned long crc, poly;
int i, j;
poly = 0xEDB88320L;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 8; j > 0; j--)
{
if (crc & 1)
{
crc = (crc >> 1) ^ poly;
}
else
{
crc >>= 1;
}
}
crc_tab[i] = crc;
}
}
/* Known keys for Sansa E200 and C200 firmwares: */
#define NUM_KEYS ((int)(sizeof(keys)/sizeof(keys[0])))
static const uint32_t keys[][4] = {
{ 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */
{ 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */
{ 0x1d29ddc0, 0x2579c2cd, 0xce339e1a, 0x75465dfe }, /* sansa 103 */
{ 0x2a7968de, 0x15127979, 0x142e60a7, 0xe49c1893 }, /* c200 1.00.03 */
{ 0xbf2d06fa, 0xf0e23d59, 0x29738132, 0xe2d04ca7 }, /* c200 1.00.04 and up*/
{ 0xa913d139, 0xf842f398, 0x3e03f1a6, 0x060ee012 }, /* c200 1.01.05 and up*/
{ 0x0fe92902, 0xe8cc0f89, 0x6ff568ba, 0x1eff5161 }, /* c200 1.01.07 */
};
/*
tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
"Following is an adaptation of the reference encryption and decryption
routines in C, released into the public domain by David Wheeler and
Roger Needham:"
*/
/* NOTE: The mi4 version of TEA uses a different initial value to sum compared
to the reference implementation and the main loop is 8 iterations, not
32.
*/
static void tea_decrypt(uint32_t* v0, uint32_t* v1, const uint32_t* k) {
uint32_t sum=0xF1BBCDC8, i; /* set up */
uint32_t delta=0x9E3779B9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for(i=0; i<8; i++) { /* basic cycle start */
*v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3);
*v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1);
sum -= delta; /* end cycle */
}
}
/* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit
integers) and the key is incremented after each block
*/
static void tea_decrypt_buf(const unsigned char* src, unsigned char* dest,
size_t n, const uint32_t * initial_key)
{
uint32_t v0, v1;
unsigned int i;
uint32_t key[4];
memcpy(key, initial_key, sizeof(key));
for (i = 0; i < (n / 8); i++) {
v0 = le2int(src);
v1 = le2int(src+4);
tea_decrypt(&v0, &v1, key);
int2le(v0, dest);
int2le(v1, dest+4);
src += 8;
dest += 8;
/* Now increment the key */
key[0]++;
if (key[0]==0) {
key[1]++;
if (key[1]==0) {
key[2]++;
if (key[2]==0) {
key[3]++;
}
}
}
}
}
static int get_mi4header(const unsigned char* buf,struct mi4header_t* mi4header)
{
if (memcmp(buf,"PPOS",4)!=0)
return -1;
mi4header->version = le2int(buf+0x04);
mi4header->length = le2int(buf+0x08);
mi4header->crc32 = le2int(buf+0x0c);
mi4header->enctype = le2int(buf+0x10);
mi4header->mi4size = le2int(buf+0x14);
mi4header->plaintext = le2int(buf+0x18);
return 0;
}
static int set_mi4header(unsigned char* buf,const struct mi4header_t* mi4header)
{
if (memcmp(buf,"PPOS",4)!=0)
return -1;
int2le(mi4header->version ,buf+0x04);
int2le(mi4header->length ,buf+0x08);
int2le(mi4header->crc32 ,buf+0x0c);
int2le(mi4header->enctype ,buf+0x10);
int2le(mi4header->mi4size ,buf+0x14);
int2le(mi4header->plaintext ,buf+0x18);
/* Add a dummy DSA signature */
memset(buf+0x1c,0,40);
buf[0x2f] = 1;
return 0;
}
static int sansa_seek_and_read(struct sansa_t* sansa, loff_t pos, unsigned char* buf, int nbytes)
{
int n;
if (sansa_seek(sansa, pos) < 0) {
return -1;
}
if ((n = sansa_read(sansa,buf,nbytes)) < 0) {
return -1;
}
if (n < nbytes) {
fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
nbytes,n);
return -1;
}
return 0;
}
/* We identify an E200 based on the following criteria:
1) Exactly two partitions;
2) First partition is type "W95 FAT32" (0x0b or 0x0c);
3) Second partition is type "OS/2 hidden C: drive" (0x84);
4) The "PPBL" string appears at offset 0 in the 2nd partition;
5) The "PPMI" string appears at offset PPMI_OFFSET in the 2nd partition.
*/
int is_sansa(struct sansa_t* sansa)
{
struct mi4header_t mi4header;
int ppmi_length;
int ppbl_length;
/* Check partition layout */
if (((sansa->pinfo[0].type != 0x06) &&
(sansa->pinfo[0].type != 0x0b) &&
(sansa->pinfo[0].type != 0x0c) &&
(sansa->pinfo[0].type != 0x0e)) ||
(sansa->pinfo[1].type != 0x84) ||
(sansa->pinfo[2].type != 0x00) ||
(sansa->pinfo[3].type != 0x00)) {
/* Bad partition layout, abort */
return -1;
}
/* Check Bootloader header */
if (sansa_seek_and_read(sansa, sansa->start, sansa->sectorbuf, 0x200) < 0) {
return -2;
}
if (memcmp(sansa->sectorbuf,"PPBL",4)!=0) {
/* No bootloader header, abort */
return -4;
}
ppbl_length = (le2int(sansa->sectorbuf+4) + 0x1ff) & ~0x1ff;
/* Sanity/safety check - the bootloader can't be larger than PPMI_OFFSET */
if (ppbl_length > PPMI_OFFSET)
{
return -5;
}
/* Load Sansa bootloader and check for "Sansa C200" magic string */
if (sansa_seek_and_read(sansa, sansa->start + 0x200, sansa->sectorbuf, ppbl_length) < 0) {
fprintf(stderr,"[ERR] Seek and read to 0x%08"PRIx64" in is_sansa failed.\n",
sansa->start+0x200);
return -6;
}
if (sansa_memmem(sansa->sectorbuf, ppbl_length, "Sansa C200", 10) != NULL) {
/* C200 */
sansa->targetname="c200";
} else {
/* E200 */
sansa->targetname="e200";
}
/* Check Main firmware header */
if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) {
fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n",
sansa->start+PPMI_OFFSET);
return -5;
}
if (memcmp(sansa->sectorbuf,"PPMI",4)!=0) {
/* No bootloader header, abort */
return -7;
}
ppmi_length = le2int(sansa->sectorbuf+4);
/* Check main mi4 file header */
if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sansa->sectorbuf, 0x200) < 0) {
fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n",
sansa->start+PPMI_OFFSET+0x200);
return -5;
}
if (get_mi4header(sansa->sectorbuf,&mi4header) < 0) {
fprintf(stderr,"[ERR] Invalid mi4header\n");
return -6;
}
/* Some sanity checks:
1) Main MI4 image without RBBL and < 100000 bytes -> old install
2) Main MI4 image with RBBL but no second image -> old install
*/
sansa->hasoldbootloader = 0;
if (memcmp(sansa->sectorbuf+0x1f8,"RBBL",4)==0) {
/* Look for an original firmware after the first image */
if (sansa_seek_and_read(sansa,
sansa->start + PPMI_OFFSET + 0x200 + ppmi_length,
sansa->sectorbuf, 512) < 0) {
return -7;
}
if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) {
fprintf(stderr,"[ERR] No original firmware found\n");
sansa->hasoldbootloader = 1;
}
} else if (mi4header.mi4size < 100000) {
fprintf(stderr,"[ERR] Old bootloader found\n");
sansa->hasoldbootloader = 1;
}
return 0;
}
int sansa_scan(struct sansa_t* sansa)
{
int i;
int n = 0;
char last_disk[4096];
int denied = 0;
int result;
printf("[INFO] Scanning disk devices...\n");
for (i = 0; i <= 25 ; i++) {
#ifdef __WIN32__
sprintf(sansa->diskname,"\\\\.\\PhysicalDrive%d",i);
#elif defined(linux) || defined (__linux)
sprintf(sansa->diskname,"/dev/sd%c",'a'+i);
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
|| defined(__bsdi__) || defined(__DragonFly__)
sprintf(sansa->diskname,"/dev/da%d",i);
#elif defined(__APPLE__) && defined(__MACH__)
sprintf(sansa->diskname,"/dev/disk%d",i);
#else
#error No disk paths defined for this platform
#endif
if ((result = sansa_open(sansa, 1)) < 0) {
if(result == -2) {
denied++;
}
sansa_close(sansa);
continue;
}
if (sansa_read_partinfo(sansa,1) < 0) {
sansa_close(sansa);
continue;
}
if (is_sansa(sansa) < 0) {
sansa_close(sansa);
continue;
}
#ifdef __WIN32__
printf("[INFO] %s found - disk device %d\n",sansa->targetname, i);
#else
printf("[INFO] %s found - %s\n",sansa->targetname, sansa->diskname);
#endif
n++;
strcpy(last_disk,sansa->diskname);
sansa_close(sansa);
}
if (n==1) {
/* Remember the disk name */
strcpy(sansa->diskname,last_disk);
}
else if (n == 0 && denied) {
printf("[ERR] FATAL: Permission denied on %d device(s) and no sansa detected.\n", denied);
#ifdef __WIN32__
printf("[ERR] You need to run this program with administrator priviledges!\n");
#else
printf("[ERR] You need permissions for raw disc access for this program to work!\n");
#endif
}
return (n == 0 && denied) ? -1 : n;
}
/* Prepare original firmware for writing to the firmware partition by decrypting
and updating the header */
static int prepare_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header)
{
unsigned char* tmpbuf;
int i;
int key_found;
get_mi4header(buf,mi4header);
#if 0
printf("mi4header->version =0x%08x\n",mi4header->version);
printf("mi4header->length =0x%08x\n",mi4header->length);
printf("mi4header->crc32 =0x%08x\n",mi4header->crc32);
printf("mi4header->enctype =0x%08x\n",mi4header->enctype);
printf("mi4header->mi4size =0x%08x\n",mi4header->mi4size);
printf("mi4header->plaintext =0x%08x\n",mi4header->plaintext);
#endif
/* Decrypt anything that needs decrypting. */
if (mi4header->plaintext < mi4header->mi4size - 0x200) {
/* TODO: Check different keys */
tmpbuf=malloc(mi4header->mi4size-(mi4header->plaintext+0x200));
if (tmpbuf==NULL) {
fprintf(stderr,"[ERR] Can not allocate memory\n");
return -1;
}
key_found=0;
for (i=0; i < NUM_KEYS && !key_found ; i++) {
tea_decrypt_buf(buf+(mi4header->plaintext+0x200),
tmpbuf,
mi4header->mi4size-(mi4header->plaintext+0x200),
keys[i]);
key_found = (le2uint(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55);
}
if (key_found) {
memcpy(buf+(mi4header->plaintext+0x200),tmpbuf,mi4header->mi4size-(mi4header->plaintext+0x200));
free(tmpbuf);
} else {
fprintf(stderr,"[ERR] Failed to decrypt image, aborting\n");
free(tmpbuf);
return -1;
}
}
/* Increase plaintext value to full file */
mi4header->plaintext = mi4header->mi4size - 0x200;
/* Update CRC checksum */
chksum_crc32gentab ();
mi4header->crc32 = chksum_crc32(buf+0x200,mi4header->mi4size-0x200);
set_mi4header(buf,mi4header);
/* Add Rockbox-specific header */
memcpy(buf+0x1f8,"RBOF",4);
memcpy(buf+0x1fc,sansa->targetname,4);
return 0;
}
static int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header)
{
int ppmi_length;
int n;
/* Read 512 bytes from PPMI_OFFSET - the PPMI header plus the mi4 header */
if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, buf, 512) < 0) {
return -1;
}
/* No need to check PPMI magic - it's done during init to confirm
this is an E200 */
ppmi_length = le2int(buf+4);
/* Firstly look for an original firmware after the first image */
if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, buf, 512) < 0) {
return -1;
}
if (get_mi4header(buf,mi4header)==0) {
/* We have a valid MI4 file after a bootloader, so we use this. */
if ((n = sansa_seek_and_read(sansa,
sansa->start + PPMI_OFFSET + 0x200 + ppmi_length,
buf, mi4header->mi4size)) < 0) {
return -1;
}
} else {
/* No valid MI4 file, so read the first image. */
if ((n = sansa_seek_and_read(sansa,
sansa->start + PPMI_OFFSET + 0x200,
buf, ppmi_length)) < 0) {
return -1;
}
}
return prepare_original_firmware(sansa, buf, mi4header);
}
int sansa_read_firmware(struct sansa_t* sansa, const char* filename)
{
int res;
int outfile;
struct mi4header_t mi4header;
res = load_original_firmware(sansa,sansa->sectorbuf,&mi4header);
if (res < 0)
return res;
outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
if (outfile < 0) {
fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
return -1;
}
res = write(outfile,sansa->sectorbuf,mi4header.mi4size);
if (res != (int)mi4header.mi4size) {
fprintf(stderr,"[ERR] Write error - %d\n", res);
return -1;
}
close(outfile);
return 0;
}
unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer)
{
/* Step 1 - read bootloader into RAM. */
int infile;
unsigned int n;
unsigned int len;
infile=open(filename,O_RDONLY|O_BINARY);
if (infile < 0) {
fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
return 0;
}
len = filesize(infile);
unsigned char* b = malloc(len);
if (b == NULL) {
fprintf(stderr,"[ERR] Could not allocate memory for bootloader\n");
close(infile);
return 0;
}
n = read(infile,b,len);
close(infile);
if (n < len) {
fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
,len,n);
return 0;
}
if (memcmp(b+0x1f8,"RBBL",4)!=0) {
fprintf(stderr,"[ERR] %s is not a Rockbox bootloader, aborting.\n",
filename);
return 0;
}
if (memcmp(b+0x1fc,sansa->targetname,4)!=0) {
fprintf(stderr,"[ERR] %s is not a Rockbox bootloader for %s, aborting.\n",
filename, sansa->targetname);
return 0;
}
*bl_buffer = b;
return len;
}
int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* bootloader, const unsigned int bl_length)
{
int res;
struct mi4header_t mi4header;
int length;
int n;
/* Create PPMI header */
memset(sansa->sectorbuf,0,0x200);
memcpy(sansa->sectorbuf,"PPMI",4);
int2le(bl_length, sansa->sectorbuf+4);
int2le(0x00020000, sansa->sectorbuf+8);
/* copy bootloader to sansa->sectorbuf+0x200 */
memcpy(sansa->sectorbuf+0x200,bootloader,bl_length);
/* Load original firmware from Sansa to the space after the bootloader */
res = load_original_firmware(sansa,sansa->sectorbuf+0x200+bl_length,&mi4header);
if (res < 0)
return res;
/* Now write the whole thing back to the Sansa */
if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n",
sansa->start+PPMI_OFFSET);
return -5;
}
length = 0x200 + bl_length + mi4header.mi4size;
n=sansa_write(sansa, length);
if (n < length) {
fprintf(stderr,"[ERR] Short write in add_bootloader\n");
return -6;
}
return 0;
}
int sansa_delete_bootloader(struct sansa_t* sansa)
{
int res;
struct mi4header_t mi4header;
int n;
int length;
/* Load original firmware from Sansa to sansa->sectorbuf+0x200 */
res = load_original_firmware(sansa,sansa->sectorbuf+0x200,&mi4header);
if (res < 0)
return res;
/* Create PPMI header */
memset(sansa->sectorbuf,0,0x200);
memcpy(sansa->sectorbuf,"PPMI",4);
int2le(mi4header.mi4size, sansa->sectorbuf+4);
int2le(0x00020000, sansa->sectorbuf+8);
/* Now write the whole thing back to the Sansa */
if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n",
sansa->start+PPMI_OFFSET);
return -5;
}
length = 0x200 + mi4header.mi4size;
n=sansa_write(sansa, length);
if (n < length) {
fprintf(stderr,"[ERR] Short write in delete_bootloader\n");
return -6;
}
return 0;
}
/** List number of MI4 images on the player, return number.
*/
int sansa_list_images(struct sansa_t* sansa)
{
struct mi4header_t mi4header;
loff_t ppmi_length;
int num = 0;
/* Check Main firmware header */
if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) {
return 0;
}
ppmi_length = le2int(sansa->sectorbuf+4);
printf("[INFO] Image 1 - %"PRIu64" bytes\n",ppmi_length);
num = 1;
/* Look for an original firmware after the first image */
if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sansa->sectorbuf, 512) < 0) {
return 0;
}
if (get_mi4header(sansa->sectorbuf,&mi4header)==0) {
printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size);
num = 2;
}
return num;
}
int sansa_update_of(struct sansa_t* sansa, const char* filename)
{
int n;
int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */
int of_length = 0; /* Keep gcc happy when building for rbutil */
int ppmi_length;
struct mi4header_t mi4header;
unsigned char buf[512];
/* Step 1 - check we have an OF on the Sansa to upgrade. We expect the
Rockbox bootloader to be installed and the OF to be after it on disk. */
/* Read 512 bytes from PPMI_OFFSET - the PPMI header */
if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET,
buf, 512) < 0) {
return -1;
}
/* No need to check PPMI magic - it's done during init to confirm
this is an E200 */
ppmi_length = le2int(buf+4);
/* Look for an original firmware after the first image */
if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length,
buf, 512) < 0) {
return -1;
}
if (get_mi4header(buf,&mi4header)!=0) {
/* We don't have a valid MI4 file after a bootloader, so do nothing. */
fprintf(stderr,"[ERR] No original firmware found at 0x%08"PRIx64"\n",
sansa->start+PPMI_OFFSET+0x200+ppmi_length);
return -1;
}
/* Step 2 - read OF into RAM. */
infile=open(filename,O_RDONLY|O_BINARY);
if (infile < 0) {
fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
return -1;
}
of_length = filesize(infile);
/* Load original firmware from file */
memset(sansa->sectorbuf,0,0x200);
n = read(infile,sansa->sectorbuf,of_length);
close(infile);
if (n < of_length) {
fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
, of_length, n);
return -1;
}
/* Check we have a valid MI4 file. */
if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) {
fprintf(stderr,"[ERR] %s is not a valid mi4 file\n",filename);
return -1;
}
/* Decrypt and build the header */
if(prepare_original_firmware(sansa, sansa->sectorbuf, &mi4header)!=0){
fprintf(stderr,"[ERR] Unable to build decrypted mi4 from %s\n"
,filename);
return -1;
}
/* Step 3 - write the OF to the Sansa */
if (sansa_seek(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length) < 0) {
fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n",
sansa->start+PPMI_OFFSET+0x200+ppmi_length);
return -1;
}
n=sansa_write(sansa, of_length);
if (n < of_length) {
fprintf(stderr,"[ERR] Short write in sansa_update_of\n");
return -1;
}
/* Step 4 - zero out the nvparams section - we have to do this or we end up
with multiple copies of the nvparams data and don't know which one to
work with for the database rebuild disabling trick in our bootloader */
if (strcmp(sansa->targetname,"e200") == 0) {
printf("[INFO] Resetting Original Firmware settings\n");
if (sansa_seek(sansa, sansa->start+NVPARAMS_OFFSET+0x200) < 0) {
fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n",
sansa->start+NVPARAMS_OFFSET+0x200);
return -1;
}
memset(sansa->sectorbuf,0,NVPARAMS_SIZE);
n=sansa_write(sansa, NVPARAMS_SIZE);
if (n < NVPARAMS_SIZE) {
fprintf(stderr,"[ERR] Short write in sansa_update_of\n");
return -1;
}
}
return 0;
}
/* Update the PPBL (bootloader) image in the hidden firmware partition */
int sansa_update_ppbl(struct sansa_t* sansa, const char* filename)
{
int n;
int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */
int ppbl_length = 0; /* Keep gcc happy when building for rbutil */
/* Step 1 - read bootloader into RAM. */
infile=open(filename,O_RDONLY|O_BINARY);
if (infile < 0) {
fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
return -1;
}
ppbl_length = filesize(infile);
n = read(infile,sansa->sectorbuf+0x200,ppbl_length);
close(infile);
if (n < ppbl_length) {
fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", ppbl_length, n);
return -1;
}
/* Step 2 - Build the header */
memset(sansa->sectorbuf,0,0x200);
memcpy(sansa->sectorbuf,"PPBL",4);
int2le(ppbl_length, sansa->sectorbuf+4);
int2le(0x00010000, sansa->sectorbuf+8);
/* Step 3 - write the bootloader to the Sansa */
if (sansa_seek(sansa, sansa->start) < 0) {
fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_ppbl failed.\n", sansa->start);
return -1;
}
n=sansa_write(sansa, ppbl_length + 0x200);
if (n < (ppbl_length+0x200)) {
fprintf(stderr,"[ERR] Short write in sansa_update_ppbl\n");
return -1;
}
return 0;
}

View file

@ -0,0 +1,67 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006-2007 Dave Chapman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _SANSAPATCHER_H
#define _SANSAPATCHER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "sansaio.h"
/* exit codes */
#define SANSA_OK 0
#define SANSA_WRONG_ARGUMENTS 1
#define SANSA_OPEN_INFILE_FAILED 2
#define SANSA_PARTITION_ERROR 3
#define SANSA_CANNOT_REOPEN 5
#define SANSA_INSTALL_FAILED 6
#define SANSA_UNINSTALL_FAILED 7
#define SANSA_ACCESS_DENIED 10
#define SANSA_NOT_FOUND 11
#define SANSA_WRONG_DEVICE_COUNT 12
#define SANSA_MULTIPLE_DEVICES 15
#define SANSA_WRONG_TYPE 16
#define SANSA_OLD_INSTALL 17
#define SANSA_INTERNAL_ERROR 20
extern int sansa_verbose;
/* Size of buffer for disk I/O - 8MB is large enough for any version
of the Apple firmware, but not the Nano's RSRC image. */
#define BUFFER_SIZE 8*1024*1024
int sansa_read_partinfo(struct sansa_t* sansa, int silent);
int is_sansa(struct sansa_t* sansa);
int sansa_scan(struct sansa_t* sansa);
int sansa_read_firmware(struct sansa_t* sansa, const char* filename);
unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer);
int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* buf, unsigned int len);
int sansa_delete_bootloader(struct sansa_t* sansa);
int sansa_update_of(struct sansa_t* sansa,const char* filename);
int sansa_update_ppbl(struct sansa_t* sansa,const char* filename);
int sansa_list_images(struct sansa_t* sansa);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="sansapatcher.exe" type="win32"/>
<!-- Identify the application security requirements. -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View file

@ -0,0 +1,38 @@
#
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
#
# All files in this archive are subject to the GNU General Public License.
# See the file COPYING in the source tree root for full license agreement.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
TEMPLATE = app
TARGET = sansapatcher
QT -= core
SOURCES += \
sansaio-posix.c \
sansaio-win32.c \
sansapatcher.c \
main.c
HEADERS += \
parttypes.h \
sansaio.h \
sansapatcher.h \
RC_FILE = sansapatcher.rc
DEFINES += _LARGEFILE64_SOURCE
unix {
target.path = /usr/local/bin
INSTALLS += target
}

View file

@ -0,0 +1 @@
1 24 MOVEABLE PURE "sansapatcher.manifest"