/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 by Dave Chapman * * Based on mkboot, Copyright (C) 2005 by Linus Nielsen Feltzing * * 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 #include #include #include #include #include #include #include #include "mktccboot.h" #include "telechips.h" /* Append a Rockbox bootloader to a Telechips original firmware file. The first instruction in a TCC firmware file is always of the form: ldr pc, [pc, #xxx] where [pc, #xxx] is the entry point of the firmware - e.g. 0x20000020 mktccboot appends the Rockbox bootloader to the end of the original firmware image and replaces the contents of [pc, #xxx] with the entry point of our bootloader - i.e. the length of the original firmware plus 0x20000000. It then stores the original entry point from [pc, #xxx] in a fixed offset in the Rockbox boootloader, which is used by the bootloader to dual-boot. Finally, mktccboot corrects the length and CRCs in the main firmware header, creating a new legal firmware file which can be installed on the device. */ /* win32 compatibility */ #ifndef O_BINARY #define O_BINARY 0 #endif static void put_uint32le(uint32_t x, unsigned char* p) { p[0] = x & 0xff; p[1] = (x >> 8) & 0xff; p[2] = (x >> 16) & 0xff; p[3] = (x >> 24) & 0xff; } static uint32_t get_uint32le(unsigned char* p) { return (p[3] << 24) | (p[2] << 16) | (p[1]<<8) | p[0]; } 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); } } #define DRAMORIG 0x20000000 /* Injects a bootloader into a Telechips 77X/78X firmware file */ unsigned char *patch_firmware_tcc(unsigned char *of_buf, int of_size, unsigned char *boot_buf, int boot_size, int *patched_size) { unsigned char *patched_buf; uint32_t ldr, old_ep_offset, new_ep_offset; int of_offset; patched_buf = malloc(of_size + boot_size); if (!patched_buf) return NULL; memcpy(patched_buf, of_buf, of_size); memcpy(patched_buf + of_size, boot_buf, boot_size); ldr = get_uint32le(patched_buf); /* TODO: Verify it's a LDR instruction */ of_offset = (ldr & 0xfff) + 8; old_ep_offset = get_uint32le(patched_buf + of_offset); new_ep_offset = DRAMORIG + of_size; printf("OF entry point: 0x%08x\n", old_ep_offset); printf("New entry point: 0x%08x\n", new_ep_offset + 8); /* Save the OF entry point at the start of the bootloader image */ put_uint32le(old_ep_offset, patched_buf + of_size); put_uint32le(new_ep_offset, patched_buf + of_size + 4); /* Change the OF entry point to the third word in our bootloader */ put_uint32le(new_ep_offset + 8, patched_buf + of_offset); telechips_encode_crc(patched_buf, of_size + boot_size); *patched_size = of_size + boot_size; return patched_buf; } unsigned char *file_read(char *filename, int *size) { unsigned char *buf = NULL; int n, fd = -1; /* Open file for reading */ fd = open(filename, O_RDONLY|O_BINARY); if (fd < 0) { printf("[ERR] Could open file for reading, aborting\n"); perror(filename); goto error; } /* Get file size, and allocate a buffer of that size */ *size = filesize(fd); buf = malloc(*size); if (buf == NULL) { printf("[ERR] Could not allocate memory, aborting\n"); goto error; } /* Read the file's content to the buffer */ n = read(fd, buf, *size); if (n != *size) { printf("[ERR] Could not read from %s\n", filename); goto error; } return buf; error: if (fd >= 0) close(fd); if (buf) free(buf); return NULL; } /* A CRC test in order to reject non OF file */ int test_firmware_tcc(unsigned char* buf, int length) { return telechips_test_crc(buf, length); }