1
0
Fork 0
forked from len0rd/rockbox

Move mkamsboot into the rbutil/mkamsboot/ directory - no other changes, so this should still work the same as before.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18706 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Dave Chapman 2008-10-04 08:46:03 +00:00
parent d16fe2d36a
commit c6be818212
8 changed files with 68 additions and 63 deletions

56
rbutil/mkamsboot/Makefile Normal file
View file

@ -0,0 +1,56 @@
# Change INFILE to point to your original firmware file
INFILE=$(HOME)/FW/AMS/CLIP/m300a-1.1.17A.bin
# OUTFILE is the file you copy to your device's root and rename to
# (e.g.) m300a.bin
OUTFILE=patched.bin
# The uclpack command
UCLPACK=../../tools/uclpack
all: $(OUTFILE)
mkamsboot: mkamsboot.c
gcc -o mkamsboot -W -Wall mkamsboot.c
extract_fw: extract_fw.c
gcc -o extract_fw -W -Wall extract_fw.c
# Rules for our test ARM application - assemble, link, then extract
# the binary code
test.o: test.S
arm-elf-as -o test.o test.S
test.elf: test.o
arm-elf-ld -e 0 -Ttext=0 -o test.elf test.o
test.bin: test.elf
arm-elf-objcopy -O binary test.elf test.bin
# Rules for the ucl unpack function - this is inserted in the padding at
# the end of the original firmware block
nrv2e_d8.o: nrv2e_d8.S
arm-elf-gcc -DPURE_THUMB -c -o nrv2e_d8.o nrv2e_d8.S
# NOTE: this function has no absolute references, so the link address (-e)
# is irrelevant. We just link at address 0.
nrv2e_d8.elf: nrv2e_d8.o
arm-elf-ld -e 0 -Ttext=0 -o nrv2e_d8.elf nrv2e_d8.o
nrv2e_d8.bin: nrv2e_d8.elf
arm-elf-objcopy -O binary nrv2e_d8.elf nrv2e_d8.bin
firmware_block.ucl: firmware_block.bin
$(UCLPACK) --best --2e firmware_block.bin firmware_block.ucl
firmware_block.bin: $(INFILE) extract_fw
./extract_fw $(INFILE) firmware_block.bin
$(OUTFILE): mkamsboot firmware_block.ucl test.bin nrv2e_d8.bin $(INFILE)
./mkamsboot $(INFILE) firmware_block.ucl test.bin nrv2e_d8.bin $(OUTFILE)
clean:
rm -fr amsinfo mkamsboot test.o test.elf test.bin extract_fw \
nrv2e_d8.o nrv2e_d8.elf nrv2e_d8.bin firmware_block.bin \
firmware_block.ucl $(OUTFILE) *~

10
rbutil/mkamsboot/README Normal file
View file

@ -0,0 +1,10 @@
mkamsboot
---------
A tool to inject some code (contained in test.S) into a firmware file.
Edit the INFILE variable in the Makefile to point to the original
firmware file you want to patch, edit "test.S" appropriately, and then
type "make".

View file

@ -0,0 +1,129 @@
/*
extract_fw.c - extract the main firmware image from a Sansa V2 (AMS) firmware
file
Copyright (C) Dave Chapman 2008
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 program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
*/
#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>
/* Win32 compatibility */
#ifndef O_BINARY
#define O_BINARY 0
#endif
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);
}
}
static uint32_t get_uint32le(unsigned char* p)
{
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
void usage(void)
{
printf("Usage: extract_fw <firmware file> <output file>\n");
exit(1);
}
int main(int argc, char* argv[])
{
char *infile, *outfile;
int fdin, fdout;
off_t len;
uint32_t n;
unsigned char* buf;
uint32_t firmware_size;
if(argc != 3) {
usage();
}
infile = argv[1];
outfile = argv[2];
/* Open the firmware file */
fdin = open(infile,O_RDONLY|O_BINARY);
if (fdin < 0) {
fprintf(stderr,"[ERR] Could not open %s for reading\n",infile);
return 1;
}
if ((len = filesize(fdin)) < 0)
return 1;
/* We will need no more memory than the total size plus the bootloader size
padded to a boundary */
if ((buf = malloc(len)) == NULL) {
fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len);
return 1;
}
n = read(fdin, buf, len);
if (n != (uint32_t)len) {
fprintf(stderr,"[ERR] Could not read firmware file\n");
return 1;
}
close(fdin);
/* Get the firmware size */
firmware_size = get_uint32le(&buf[0x0c]);
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);
return 1;
}
n = write(fdout, buf + 0x400, firmware_size);
if (n != (uint32_t)firmware_size) {
fprintf(stderr,"[ERR] Could not write firmware block\n");
return 1;
}
/* Clean up */
close(fdout);
free(buf);
return 0;
}

View file

@ -0,0 +1,341 @@
/*
mkamsboot.c - a tool for merging bootloader code into an Sansa V2
(AMS) firmware file
Copyright (C) Dave Chapman 2008
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 program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
*/
/*
Insert a Rockbox bootloader into an AMS original firmware file.
We replace the main firmware block (bytes 0x400..0x400+firmware_size)
as follows:
--------------------- 0x0
| |
| Rockbox bootloader |
| |
|---------------------|
| EMPTY SPACE |
|---------------------|
| ucl unpack function |
|---------------------|
| |
| compressed OF image |
| |
| |
---------------------
This entire block fits into the space previously occupied by the main
firmware block, and gives about 40KB of space to store the Rockbox
bootloader. This could be increased if we also UCL compress the
Rockbox bootloader.
mkamsboot then corrects the checksums and writes a new legal firmware
file which can be installed on the device.
Our bootloader first checks for the "dual-boot" keypress, and then either:
a) Copies the ucl unpack function and compressed OF image to an unused
part of RAM and then branches to the ucl_unpack function, which
will then branch to 0x0 after decompressing the OF to that location.
b) Continues running with our test code
*/
#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>
/* Win32 compatibility */
#ifndef O_BINARY
#define O_BINARY 0
#endif
#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
/* This magic should appear at the start of any UCL file */
static const unsigned char uclmagic[] = {
0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a
};
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);
}
}
static uint32_t get_uint32le(unsigned char* p)
{
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
static uint32_t get_uint32be(unsigned char* p)
{
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
static void put_uint32le(unsigned char* p, uint32_t x)
{
p[0] = x & 0xff;
p[1] = (x >> 8) & 0xff;
p[2] = (x >> 16) & 0xff;
p[3] = (x >> 24) & 0xff;
}
static int calc_checksum(unsigned char* buf, uint32_t n)
{
uint32_t sum = 0;
uint32_t i;
for (i=0;i<n;i+=4)
sum += get_uint32le(buf + i);
return sum;
}
void usage(void)
{
printf("Usage: mkamsboot <firmware file> <ucl image> <boot file> <ucl unpack file> <output file>\n");
exit(1);
}
int main(int argc, char* argv[])
{
char *infile, *uclfile, *bootfile, *uclunpackfile, *outfile;
int fdin, fducl, fdboot, fduclunpack, fdout;
off_t len;
unsigned char uclheader[26];
uint32_t n;
unsigned char* buf;
uint32_t firmware_size;
uint32_t firmware_paddedsize;
uint32_t bootloader_size;
uint32_t ucl_size;
uint32_t ucl_paddedsize;
uint32_t uclunpack_size;
uint32_t sum,filesum;
uint32_t i;
if(argc != 6) {
usage();
}
infile = argv[1];
uclfile = argv[2];
bootfile = argv[3];
uclunpackfile = argv[4];
outfile = argv[5];
/* Open the bootloader file */
fdboot = open(bootfile, O_RDONLY|O_BINARY);
if (fdboot < 0)
{
fprintf(stderr,"[ERR] Could not open %s for reading\n",bootfile);
return 1;
}
bootloader_size = filesize(fdboot);
/* Open the UCL-compressed image of the firmware block */
fduclunpack = open(uclunpackfile, O_RDONLY|O_BINARY);
if (fduclunpack < 0)
{
fprintf(stderr,"[ERR] Could not open %s for reading\n",uclunpackfile);
return 1;
}
uclunpack_size = filesize(fduclunpack);
/* Open the UCL-compressed image of the firmware block */
fducl = open(uclfile, O_RDONLY|O_BINARY);
if (fducl < 0)
{
fprintf(stderr,"[ERR] Could not open %s for reading\n",uclfile);
return 1;
}
/* Some UCL file sanity checks */
n = read(fducl, uclheader, sizeof(uclheader));
if (n != sizeof(uclheader)) {
fprintf(stderr,"[ERR] Could not read header from UCL file\n");
return 1;
}
if (memcmp(uclmagic, uclheader, sizeof(uclmagic))!=0) {
fprintf(stderr,"[ERR] Invalid UCL file\n");
return 1;
}
if (uclheader[12] != 0x2e) {
fprintf(stderr,"[ERR] Unsupported UCL compression format (0x%02x) - only 0x2e supported.\n",uclheader[12]);
return 1;
}
ucl_size = get_uint32be(&uclheader[22]) + 8;
ucl_paddedsize = (ucl_size + 3) & ~0x3;
if (ucl_size + 26 > (unsigned)filesize(fducl)) {
fprintf(stderr, "[ERR] Size mismatch in UCL file\n");
return 1;
}
/* Open the firmware file */
fdin = open(infile,O_RDONLY|O_BINARY);
if (fdin < 0) {
fprintf(stderr,"[ERR] Could not open %s for reading\n",infile);
return 1;
}
if ((len = filesize(fdin)) < 0)
return 1;
/* Allocate memory for the OF image - we don't change the size */
if ((buf = malloc(len)) == NULL) {
fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len);
return 1;
}
n = read(fdin, buf, len);
if (n != (uint32_t)len) {
fprintf(stderr,"[ERR] Could not read firmware file\n");
return 1;
}
close(fdin);
/* Get the firmware size */
firmware_size = get_uint32le(&buf[0x0c]);
/* Round size up to next multiple of 0x200 */
firmware_paddedsize = PAD_TO_BOUNDARY(firmware_size);
fprintf(stderr,"Original firmware size - %d bytes\n",firmware_size);
fprintf(stderr,"Padded firmware size - %d bytes\n",firmware_paddedsize);
fprintf(stderr,"Bootloader size - %d bytes\n",bootloader_size);
fprintf(stderr,"UCL image size - %d bytes (%d bytes padded)\n",ucl_size,ucl_paddedsize);
fprintf(stderr,"UCL unpack function size - %d bytes\n",uclunpack_size);
fprintf(stderr,"Original total size of firmware - %d bytes\n",(int)len);
/* Check we have room for our bootloader - in the future, we could UCL
pack this image as well if we need to. */
if (bootloader_size > (firmware_size - ucl_paddedsize - uclunpack_size)) {
fprintf(stderr,"[ERR] Bootloader too large (%d bytes, %d available)\n",
bootloader_size, firmware_size - ucl_paddedsize - uclunpack_size);
return 1;
}
/* Zero the original firmware area - not needed, but helps debugging */
memset(buf + 0x400, 0, firmware_size);
/* Locate our bootloader code at the start of the firmware block */
n = read(fdboot, buf + 0x400, bootloader_size);
if (n != bootloader_size) {
fprintf(stderr,"[ERR] Could not load bootloader file\n");
return 1;
}
close(fdboot);
/* Locate the compressed image of the original firmware block at the end
of the firmware block */
n = read(fducl, buf + 0x400 + firmware_size - ucl_paddedsize, ucl_size);
if (n != ucl_size) {
fprintf(stderr,"[ERR] Could not load ucl file\n");
return 1;
}
close(fducl);
/* Locate our UCL unpack function before copy of the compressed firmware */
n = read(fduclunpack, buf + 0x400 + firmware_size - ucl_paddedsize - uclunpack_size, uclunpack_size);
if (n != uclunpack_size) {
fprintf(stderr,"[ERR] Could not load uclunpack file\n");
return 1;
}
close(fduclunpack);
put_uint32le(&buf[0x420], 0x40000 - ucl_paddedsize - uclunpack_size + 1); /* UCL unpack entry point */
put_uint32le(&buf[0x424], 0x40000 - ucl_paddedsize); /* Location of OF */
put_uint32le(&buf[0x428], ucl_size); /* Size of UCL image */
put_uint32le(&buf[0x42c], firmware_size - uclunpack_size - ucl_paddedsize); /* Start of data to copy */
put_uint32le(&buf[0x430], uclunpack_size + ucl_paddedsize); /* Size of data to copy */
/* Update checksum */
sum = calc_checksum(buf + 0x400,firmware_size);
put_uint32le(&buf[0x04], sum);
put_uint32le(&buf[0x204], sum);
/* Update the whole-file checksum */
filesum = 0;
for (i=0;i < (unsigned)len - 4; i+=4)
filesum += get_uint32le(&buf[i]);
put_uint32le(buf + len - 4, filesum);
/* 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);
return 1;
}
n = write(fdout, buf, len);
if (n != (unsigned)len) {
fprintf(stderr,"[ERR] Could not write firmware file\n");
return 1;
}
close(fdout);
return 0;
}

194
rbutil/mkamsboot/nrv2e_d8.S Normal file
View file

@ -0,0 +1,194 @@
/* arm_nrv2e_d8.S -- ARM decompressor for NRV2E
This file is part of the UPX executable compressor.
Copyright (C) 1996-2008 Markus Franz Xaver Johannes Oberhumer
Copyright (C) 1996-2008 Laszlo Molnar
Copyright (C) 2000-2008 John F. Reiser
All Rights Reserved.
UPX and the UCL library are free software; you can redistribute them
and/or modify them 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 program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.
If not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Markus F.X.J. Oberhumer Laszlo Molnar
<markus@oberhumer.com> <ml1050@users.sourceforge.net>
John F. Reiser
<jreiser@users.sourceforge.net>
*/
#define SAFE 0 /* 1 for src+dst bounds checking: cost 40 bytes */
#define src r0
#define len r1 /* overlaps 'cnt' */
#define dst r2
#define tmp r3
#define bits r4
#define off r5
#define wrnk r6 /* 0x500 M2_MAX_OFFSET before "wrinkle" */
#define srclim r7
#if 1==SAFE /*{*/
#define dstlim r12
#endif /*}*/
#define cnt r1 /* overlaps 'len' while reading an offset */
#if 1==SAFE /*{*/
#define CHECK_SRC cmp src,srclim; bhs bad_src_n2e
#define CHECK_DST cmp dst,dstlim; bhs bad_dst_n2e
#else /*}{*/
#define CHECK_SRC /*empty*/
#define CHECK_DST /*empty*/
#endif /*}*/
#if 0 /*{ DEBUG only: check newly-decompressed against original dst */
#define CHECK_BYTE \
push {wrnk}; \
ldrb wrnk,[dst]; \
cmp wrnk,tmp; beq 0f; bkpt; \
0: pop {wrnk}
#else /*}{*/
#define CHECK_BYTE /*empty*/
#endif /*}*/
/* "mov lr,pc; bxx ..." implements conditional subroutine call */
#define GETBIT add bits,bits; mov lr,pc; beq get1_n2e
#define getnextb(reg) GETBIT; adc reg,reg
#define jnextb0 GETBIT; bcc
#define jnextb1 GETBIT; bcs
#ifndef PURE_THUMB
ucl_nrv2e_decompress_8: .globl ucl_nrv2e_decompress_8 @ ARM mode
.type ucl_nrv2e_decompress_8, %function
/* error = (*)(char const *src, int len_src, char *dst, int *plen_dst)
Actual decompressed length is stored through plen_dst.
For SAFE mode: at call, *plen_dst must be allowed length of output buffer.
*/
adr r12,1+.thumb_nrv2e_d8; bx r12 @ enter THUMB mode
#endif
.code 16 @ THUMB mode
.thumb_func
.thumb_nrv2e_d8:
#if 0
push {r2,r3, r4,r5,r6,r7, lr}
#define sp_DST0 0 /* stack offset of original dst */
#endif
add srclim,len,src @ srclim= eof_src;
#if 1==SAFE /*{*/
ldr tmp,[r3] @ len_dst
add tmp,dst
mov dstlim,tmp
#endif /*}*/
mov bits,#1; neg off,bits @ off= -1 initial condition
lsl bits,#31 @ 1<<31: refill next time
mov wrnk,#5
lsl wrnk,#8 @ 0x500 @ nrv2e M2_MAX_OFFSET
b top_n2e
#if 1==SAFE /*{*/
bad_dst_n2e: # return value will be 2
add src,srclim,#1
bad_src_n2e: # return value will be 1
add src,#1
#endif /*}*/
eof_n2e:
#if 0
pop {r3,r4} @ r3= orig_dst; r4= plen_dst
sub src,srclim @ 0 if actual src length equals expected length
sub dst,r3 @ actual dst length
str dst,[r4]
pop {r4,r5,r6,r7 /*,pc*/}
pop {r1}; bx r1 @ "pop {,pc}" fails return to ARM mode on ARMv4T
#else
mov r0, #0
bx r0 /* Branch to 0x0, switch to ARM mode */
#endif
get1_n2e: @ In: Carry set [from adding 0x80000000 (1<<31) to itself]
ldrb bits,[src] @ zero-extend next byte
adc bits,bits @ double and insert CarryIn as low bit
CHECK_SRC
add src,#1
lsl bits,#24 @ move to top byte, and set CarryOut from old bit 8
mov pc,lr @ return, stay in current (THUMB) mode
lit_n2e:
CHECK_SRC; ldrb tmp,[src]; add src,#1
CHECK_BYTE
CHECK_DST; strb tmp,[dst]; add dst,#1
top_n2e:
jnextb1 lit_n2e
mov cnt,#1; b getoff_n2e
off_n2e:
sub cnt,#1
getnextb(cnt)
getoff_n2e:
getnextb(cnt)
jnextb0 off_n2e
sub tmp,cnt,#3 @ set Carry
mov len,#0 @ Carry unaffected
blo offprev_n2e @ cnt was 2; tests Carry only
lsl tmp,#8
CHECK_SRC; ldrb off,[src]; add src,#1 @ low 7+1 bits
orr off,tmp
mvn off,off; beq eof_n2e @ off= ~off
asr off,#1; bcs lenlast_n2e
b lenmore_n2e
offprev_n2e:
jnextb1 lenlast_n2e
lenmore_n2e:
mov len,#1
jnextb1 lenlast_n2e
len_n2e:
getnextb(len)
jnextb0 len_n2e
add len,#6-2
b gotlen_n2e
lenlast_n2e:
getnextb(len) @ 0,1,2,3
add len,#2
gotlen_n2e: @ 'cmn': add the inputs, set condition codes, discard the sum
cmn wrnk,off; bcs near_n2e @ within M2_MAX_OFFSET
add len,#1 @ too far away, so minimum match length is 3
near_n2e:
#if 1==SAFE /*{*/
ldr tmp,[sp,#sp_DST0]
sub tmp,dst
sub tmp,off; bhi bad_dst_n2e @ reaching back too far
add tmp,dst,cnt
cmp tmp,dstlim; bhi bad_dst_n2e @ too much output
#endif /*}*/
ldrb tmp,[dst] @ force cacheline allocate
copy_n2e:
ldrb tmp,[dst,off]
CHECK_BYTE
strb tmp,[dst]; add dst,#1
sub len,#1; bne copy_n2e
b top_n2e
#ifndef PURE_THUMB
.size ucl_nrv2e_decompress_8, .-ucl_nrv2e_decompress_8
#endif
/*
vi:ts=8:et:nowrap
*/

55
rbutil/mkamsboot/test.S Normal file
View file

@ -0,0 +1,55 @@
/* int ucl_nrv2e_decompress_8(const unsigned char *src, unsigned char *dst,
unsigned long *dst_len) */
.text
.global ucl_nrv2e_decompress_8
/* Vectors */
ldr pc, =start
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
/* These values are filled in by mkamsboot - don't move them from offset 0x20 */
ucl_unpack: .word 0 /* Entry point (plus 1 - for thumb) of ucl_unpack after copy*/
ucl_start: .word 0 /* Start of the ucl-compressed OF image after copy */
ucl_size: .word 0 /* Length in bytes of the compressed OF image */
copy_start: .word 0 /* Start of the copy of the ucl_unpack function */
copy_size: .word 0 /* uclunpack_size + ucl_paddedsize */
start:
/* A delay loop - just to prove we're running */
mov r1, #0x500000 /* Approximately 5 seconds */
loop: subs r1, r1, #1
bne loop
/* First copy the compressed firmware to unused RAM */
ldr r0, copy_start /* Source */
ldr r1, copy_size /* Source length */
mov r2, #0x40000 /* Destination end */
sub r2, r2, r1
memcpy:
ldrb r3, [r0], #1
strb r3, [r2], #1
cmp r2, #0x40000 /* Stop when we reached dest_end */
bne memcpy
/* Call the ucl decompress function, which will branch to 0x0 */
/* on completion */
ldr r0, ucl_start /* Address of compressed image */
ldr r1, ucl_size /* Compressed size */
mov r2, #0 /* Destination */
ldr r3, ucl_unpack
bx r3
/* never reached */