Dave Chapman | 28f6ae4 | 2007-10-28 11:08:10 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2007 by Dave Chapman |
| 11 | * |
| 12 | * Based on mkboot, Copyright (C) 2005 by Linus Nielsen Feltzing |
| 13 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame] | 14 | * This program is free software; you can redistribute it and/or |
| 15 | * modify it under the terms of the GNU General Public License |
| 16 | * as published by the Free Software Foundation; either version 2 |
| 17 | * of the License, or (at your option) any later version. |
Dave Chapman | 28f6ae4 | 2007-10-28 11:08:10 +0000 | [diff] [blame] | 18 | * |
| 19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 20 | * KIND, either express or implied. |
| 21 | * |
| 22 | ****************************************************************************/ |
| 23 | #include <stdio.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
Daniel Stenberg | afecfe4 | 2008-04-10 21:16:00 +0000 | [diff] [blame] | 26 | #include <unistd.h> |
Dave Chapman | 28f6ae4 | 2007-10-28 11:08:10 +0000 | [diff] [blame] | 27 | #include <sys/types.h> |
| 28 | #include <sys/stat.h> |
| 29 | #include <fcntl.h> |
| 30 | #include <inttypes.h> |
Tomer Shalev | d0bc525 | 2009-10-29 22:21:30 +0000 | [diff] [blame] | 31 | #include "mktccboot.h" |
Dave Chapman | 28f6ae4 | 2007-10-28 11:08:10 +0000 | [diff] [blame] | 32 | #include "telechips.h" |
| 33 | |
| 34 | /* |
| 35 | |
| 36 | Append a Rockbox bootloader to a Telechips original firmware file. |
| 37 | |
| 38 | The first instruction in a TCC firmware file is always of the form: |
| 39 | |
| 40 | ldr pc, [pc, #xxx] |
| 41 | |
| 42 | where [pc, #xxx] is the entry point of the firmware - e.g. 0x20000020 |
| 43 | |
| 44 | mktccboot appends the Rockbox bootloader to the end of the original |
| 45 | firmware image and replaces the contents of [pc, #xxx] with the entry |
| 46 | point of our bootloader - i.e. the length of the original firmware plus |
| 47 | 0x20000000. |
| 48 | |
| 49 | It then stores the original entry point from [pc, #xxx] in a fixed |
| 50 | offset in the Rockbox boootloader, which is used by the bootloader to |
| 51 | dual-boot. |
| 52 | |
| 53 | Finally, mktccboot corrects the length and CRCs in the main firmware |
| 54 | header, creating a new legal firmware file which can be installed on |
| 55 | the device. |
| 56 | |
| 57 | */ |
| 58 | |
| 59 | /* win32 compatibility */ |
| 60 | |
| 61 | #ifndef O_BINARY |
| 62 | #define O_BINARY 0 |
| 63 | #endif |
| 64 | |
| 65 | static void put_uint32le(uint32_t x, unsigned char* p) |
| 66 | { |
| 67 | p[0] = x & 0xff; |
| 68 | p[1] = (x >> 8) & 0xff; |
| 69 | p[2] = (x >> 16) & 0xff; |
| 70 | p[3] = (x >> 24) & 0xff; |
| 71 | } |
| 72 | |
| 73 | static uint32_t get_uint32le(unsigned char* p) |
| 74 | { |
| 75 | return (p[3] << 24) | (p[2] << 16) | (p[1]<<8) | p[0]; |
| 76 | } |
| 77 | |
Tomer Shalev | ad78551 | 2009-10-29 21:31:50 +0000 | [diff] [blame] | 78 | static off_t filesize(int fd) { |
Dave Chapman | 28f6ae4 | 2007-10-28 11:08:10 +0000 | [diff] [blame] | 79 | struct stat buf; |
| 80 | |
| 81 | if (fstat(fd,&buf) < 0) { |
| 82 | perror("[ERR] Checking filesize of input file"); |
| 83 | return -1; |
| 84 | } else { |
| 85 | return(buf.st_size); |
| 86 | } |
| 87 | } |
| 88 | |
Tomer Shalev | ad78551 | 2009-10-29 21:31:50 +0000 | [diff] [blame] | 89 | #define DRAMORIG 0x20000000 |
| 90 | /* Injects a bootloader into a Telechips 77X/78X firmware file */ |
| 91 | unsigned char *patch_firmware_tcc(unsigned char *of_buf, int of_size, |
| 92 | unsigned char *boot_buf, int boot_size, int *patched_size) |
| 93 | { |
| 94 | unsigned char *patched_buf; |
| 95 | uint32_t ldr, old_ep_offset, new_ep_offset; |
| 96 | int of_offset; |
Dave Chapman | 28f6ae4 | 2007-10-28 11:08:10 +0000 | [diff] [blame] | 97 | |
Tomer Shalev | ad78551 | 2009-10-29 21:31:50 +0000 | [diff] [blame] | 98 | patched_buf = malloc(of_size + boot_size); |
| 99 | if (!patched_buf) |
| 100 | return NULL; |
| 101 | |
| 102 | memcpy(patched_buf, of_buf, of_size); |
| 103 | memcpy(patched_buf + of_size, boot_buf, boot_size); |
| 104 | |
| 105 | ldr = get_uint32le(patched_buf); |
| 106 | |
| 107 | /* TODO: Verify it's a LDR instruction */ |
| 108 | of_offset = (ldr & 0xfff) + 8; |
| 109 | old_ep_offset = get_uint32le(patched_buf + of_offset); |
| 110 | new_ep_offset = DRAMORIG + of_size; |
| 111 | |
| 112 | printf("OF entry point: 0x%08x\n", old_ep_offset); |
| 113 | printf("New entry point: 0x%08x\n", new_ep_offset + 8); |
| 114 | |
| 115 | /* Save the OF entry point at the start of the bootloader image */ |
| 116 | put_uint32le(old_ep_offset, patched_buf + of_size); |
| 117 | put_uint32le(new_ep_offset, patched_buf + of_size + 4); |
| 118 | |
| 119 | /* Change the OF entry point to the third word in our bootloader */ |
| 120 | put_uint32le(new_ep_offset + 8, patched_buf + of_offset); |
| 121 | |
| 122 | telechips_encode_crc(patched_buf, of_size + boot_size); |
| 123 | *patched_size = of_size + boot_size; |
| 124 | |
| 125 | return patched_buf; |
| 126 | } |
| 127 | |
| 128 | unsigned char *file_read(char *filename, int *size) |
| 129 | { |
| 130 | unsigned char *buf = NULL; |
| 131 | int n, fd = -1; |
| 132 | |
| 133 | /* Open file for reading */ |
| 134 | fd = open(filename, O_RDONLY|O_BINARY); |
| 135 | if (fd < 0) |
| 136 | { |
| 137 | printf("[ERR] Could open file for reading, aborting\n"); |
| 138 | perror(filename); |
| 139 | goto error; |
| 140 | } |
| 141 | |
| 142 | /* Get file size, and allocate a buffer of that size */ |
| 143 | *size = filesize(fd); |
| 144 | buf = malloc(*size); |
| 145 | if (buf == NULL) |
| 146 | { |
| 147 | printf("[ERR] Could not allocate memory, aborting\n"); |
| 148 | goto error; |
| 149 | } |
| 150 | |
| 151 | /* Read the file's content to the buffer */ |
| 152 | n = read(fd, buf, *size); |
| 153 | if (n != *size) |
| 154 | { |
| 155 | printf("[ERR] Could not read from %s\n", filename); |
| 156 | goto error; |
| 157 | } |
| 158 | |
| 159 | return buf; |
| 160 | |
| 161 | error: |
| 162 | if (fd >= 0) |
| 163 | close(fd); |
| 164 | |
| 165 | if (buf) |
| 166 | free(buf); |
| 167 | |
| 168 | return NULL; |
| 169 | } |
| 170 | |
Tomer Shalev | bcadf96 | 2009-11-01 18:26:00 +0000 | [diff] [blame] | 171 | /* A CRC test in order to reject non OF file */ |
| 172 | int test_firmware_tcc(unsigned char* buf, int length) |
| 173 | { |
| 174 | return telechips_test_crc(buf, length); |
| 175 | } |
| 176 | |