rockbox/rbutil/mkamsboot/main.c

175 lines
5.1 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* mkamsboot - a tool for merging bootloader code into an Sansa V2
* (AMS) firmware file
*
* Copyright (C) 2008 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 <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <ucl/ucl.h>
#include "mkamsboot.h"
/* Header for ARM code binaries */
#include "dualboot.h"
/* Win32 compatibility */
#ifndef O_BINARY
#define O_BINARY 0
#endif
/* standalone executable */
int main(int argc, char* argv[])
{
char *infile, *bootfile, *outfile;
int fdout;
off_t len;
uint32_t n;
unsigned char* buf;
int firmware_size;
int bootloader_size;
unsigned char* of_packed;
int of_packedsize;
unsigned char* rb_packed;
int rb_packedsize;
mkamsboot: prevents 2 potential problems We checked if the new firmware block (bootloader+ucl function+packed bootloader & OF) fit in the OF file, but not if it would run properly. For example the Clipv2 OF is bigger than 0x50000 bytes uncompressed, but it fitted in this space when packed and concatenated to a packed bootloader + ucl function and dualboot code (but we use 1MB of RAM and not 0x50000 anyway). Now we check that both bootloader and OF are small enough to be unpacked at runtime: the unpacked data must be smaller than available memory and not overlap with ucl function and packed data (although the unpacked and packed data could probably overlap a bit, I don't know how to calculate this and this could be quite complex). total_size() is replaced by check_sizes() which will perform all the checks and set an error string if the firmware can't be patched. (both mkamsboot and rbutilqt modified accordingly) The second problem is that dualboot.S assumed r3 and r5 were left untouched in the device specific checks. This was undocumented and very error prone when modifying these checks. r3 is the last byte of packed copy (bootloader or OF) r5 is the entry point of uclunpack function derived from r3, so move r5 calculation after the device specific code. Even if r3 is currently unused in the device specific code, we store it in memory after copying the ucl function, when it points to the last byte of packed data (not yet copied at this point since we didn't chose if we boot the OF or the bootloader), and restore it just before using it so no restriction is placed on registers usage in device specific code. Add a new variable ucl_dest in dualboot.S set by mkamsboot.c, which represents the last bound of buffer where we copy the ucl function, and then the packed data (bootloader or OF). RAM_SIZE definition is moved from dualboot.S to mkamsboot.c new model_memory_size(), where it is a bit better documented. Tested on e200v2 and Clip+ git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24772 a1c6a512-1295-4272-9138-f99709370657
2010-02-19 14:10:26 +00:00
int patchable;
int totalsize;
int model;
char errstr[200];
struct md5sums sum;
char md5sum[33]; /* 32 digits + \0 */
sum.md5 = md5sum;
/* VERSION comes frome the Makefile */
fprintf(stderr,
"mkamsboot Version " VERSION "\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
"\n");
if(argc != 4) {
printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n");
return 1;
}
infile = argv[1];
bootfile = argv[2];
outfile = argv[3];
/* Load bootloader file */
rb_packed = load_rockbox_file(bootfile, &model, &bootloader_size,
&rb_packedsize, errstr, sizeof(errstr));
if (rb_packed == NULL) {
fprintf(stderr, "%s", errstr);
fprintf(stderr, "[ERR] Could not load %s\n", bootfile);
return 1;
}
/* Load original firmware file */
buf = load_of_file(infile, model, &len, &sum,
&firmware_size, &of_packed, &of_packedsize, errstr, sizeof(errstr));
if (buf == NULL) {
free(rb_packed);
fprintf(stderr, "%s", errstr);
fprintf(stderr, "[ERR] Could not load %s\n", infile);
return 1;
}
fprintf(stderr, "[INFO] Original firmware MD5 checksum match\n");
fprintf(stderr, "[INFO] Model: Sansa %s v%d - Firmware version: %s\n",
ams_identity[sum.model].model_name,
ams_identity[sum.model].hw_revision, sum.version);
printf("[INFO] Firmware patching has begun !\n\n");
fprintf(stderr, "[INFO] Original firmware size: %8d bytes\n",
firmware_size);
fprintf(stderr, "[INFO] Packed OF size: %8d bytes\n",
of_packedsize);
fprintf(stderr, "[INFO] Bootloader size: %8d bytes\n",
bootloader_size);
fprintf(stderr, "[INFO] Packed bootloader size: %8d bytes\n",
rb_packedsize);
fprintf(stderr, "[INFO] Dual-boot function size: %8d bytes\n",
ams_identity[sum.model].bootloader_size);
fprintf(stderr, "[INFO] UCL unpack function size: %8zu bytes\n",
sizeof(nrv2e_d8));
fprintf(stderr, "[INFO] Original firmware version: %8u bytes\n",
0x200);
mkamsboot: prevents 2 potential problems We checked if the new firmware block (bootloader+ucl function+packed bootloader & OF) fit in the OF file, but not if it would run properly. For example the Clipv2 OF is bigger than 0x50000 bytes uncompressed, but it fitted in this space when packed and concatenated to a packed bootloader + ucl function and dualboot code (but we use 1MB of RAM and not 0x50000 anyway). Now we check that both bootloader and OF are small enough to be unpacked at runtime: the unpacked data must be smaller than available memory and not overlap with ucl function and packed data (although the unpacked and packed data could probably overlap a bit, I don't know how to calculate this and this could be quite complex). total_size() is replaced by check_sizes() which will perform all the checks and set an error string if the firmware can't be patched. (both mkamsboot and rbutilqt modified accordingly) The second problem is that dualboot.S assumed r3 and r5 were left untouched in the device specific checks. This was undocumented and very error prone when modifying these checks. r3 is the last byte of packed copy (bootloader or OF) r5 is the entry point of uclunpack function derived from r3, so move r5 calculation after the device specific code. Even if r3 is currently unused in the device specific code, we store it in memory after copying the ucl function, when it points to the last byte of packed data (not yet copied at this point since we didn't chose if we boot the OF or the bootloader), and restore it just before using it so no restriction is placed on registers usage in device specific code. Add a new variable ucl_dest in dualboot.S set by mkamsboot.c, which represents the last bound of buffer where we copy the ucl function, and then the packed data (bootloader or OF). RAM_SIZE definition is moved from dualboot.S to mkamsboot.c new model_memory_size(), where it is a bit better documented. Tested on e200v2 and Clip+ git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24772 a1c6a512-1295-4272-9138-f99709370657
2010-02-19 14:10:26 +00:00
patchable = check_sizes(sum.model, rb_packedsize, bootloader_size,
of_packedsize, firmware_size, &totalsize, errstr, sizeof(errstr));
fprintf(stderr, "[INFO] Total size of new image: %8d bytes\n", totalsize);
mkamsboot: prevents 2 potential problems We checked if the new firmware block (bootloader+ucl function+packed bootloader & OF) fit in the OF file, but not if it would run properly. For example the Clipv2 OF is bigger than 0x50000 bytes uncompressed, but it fitted in this space when packed and concatenated to a packed bootloader + ucl function and dualboot code (but we use 1MB of RAM and not 0x50000 anyway). Now we check that both bootloader and OF are small enough to be unpacked at runtime: the unpacked data must be smaller than available memory and not overlap with ucl function and packed data (although the unpacked and packed data could probably overlap a bit, I don't know how to calculate this and this could be quite complex). total_size() is replaced by check_sizes() which will perform all the checks and set an error string if the firmware can't be patched. (both mkamsboot and rbutilqt modified accordingly) The second problem is that dualboot.S assumed r3 and r5 were left untouched in the device specific checks. This was undocumented and very error prone when modifying these checks. r3 is the last byte of packed copy (bootloader or OF) r5 is the entry point of uclunpack function derived from r3, so move r5 calculation after the device specific code. Even if r3 is currently unused in the device specific code, we store it in memory after copying the ucl function, when it points to the last byte of packed data (not yet copied at this point since we didn't chose if we boot the OF or the bootloader), and restore it just before using it so no restriction is placed on registers usage in device specific code. Add a new variable ucl_dest in dualboot.S set by mkamsboot.c, which represents the last bound of buffer where we copy the ucl function, and then the packed data (bootloader or OF). RAM_SIZE definition is moved from dualboot.S to mkamsboot.c new model_memory_size(), where it is a bit better documented. Tested on e200v2 and Clip+ git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24772 a1c6a512-1295-4272-9138-f99709370657
2010-02-19 14:10:26 +00:00
if (!patchable) {
fprintf(stderr, "%s", errstr);
free(buf);
free(of_packed);
free(rb_packed);
return 1;
}
patch_firmware(sum.model, ams_identity[sum.model].fw_revision,
firmware_size, buf, len, of_packed, of_packedsize, rb_packed,
rb_packedsize);
/* Write the new firmware */
fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666);
if (fdout < 0) {
fprintf(stderr, "[ERR] Could not open %s for writing\n", outfile);
free(buf);
free(of_packed);
free(rb_packed);
return 1;
}
n = write(fdout, buf, len);
if (n != (unsigned)len) {
fprintf(stderr, "[ERR] Could not write firmware file\n");
free(buf);
free(of_packed);
free(rb_packed);
return 1;
}
close(fdout);
free(buf);
free(of_packed);
free(rb_packed);
fprintf(stderr, "\n[INFO] Patching succeeded!\n");
return 0;
}