blob: 7103f23895901e1edd89aa62642fd29897d4b6a4 [file] [log] [blame]
Dave Chapman28f6ae42007-10-28 11:08:10 +00001/***************************************************************************
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 Stenberg2acc0ac2008-06-28 18:10:04 +000014 * 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 Chapman28f6ae42007-10-28 11:08:10 +000018 *
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 Stenbergafecfe42008-04-10 21:16:00 +000026#include <unistd.h>
Dave Chapman28f6ae42007-10-28 11:08:10 +000027#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <inttypes.h>
Tomer Shalevd0bc5252009-10-29 22:21:30 +000031#include "mktccboot.h"
Dave Chapman28f6ae42007-10-28 11:08:10 +000032#include "telechips.h"
33
34/*
35
36Append a Rockbox bootloader to a Telechips original firmware file.
37
38The first instruction in a TCC firmware file is always of the form:
39
40 ldr pc, [pc, #xxx]
41
42where [pc, #xxx] is the entry point of the firmware - e.g. 0x20000020
43
44mktccboot appends the Rockbox bootloader to the end of the original
45firmware image and replaces the contents of [pc, #xxx] with the entry
46point of our bootloader - i.e. the length of the original firmware plus
470x20000000.
48
49It then stores the original entry point from [pc, #xxx] in a fixed
50offset in the Rockbox boootloader, which is used by the bootloader to
51dual-boot.
52
53Finally, mktccboot corrects the length and CRCs in the main firmware
54header, creating a new legal firmware file which can be installed on
55the device.
56
57*/
58
59/* win32 compatibility */
60
61#ifndef O_BINARY
62#define O_BINARY 0
63#endif
64
65static 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
73static uint32_t get_uint32le(unsigned char* p)
74{
75 return (p[3] << 24) | (p[2] << 16) | (p[1]<<8) | p[0];
76}
77
78void usage(void)
79{
80 printf("Usage: mktccboot <firmware file> <boot file> <output file>\n");
81
82 exit(1);
83}
84
Tomer Shalevad785512009-10-29 21:31:50 +000085static off_t filesize(int fd) {
Dave Chapman28f6ae42007-10-28 11:08:10 +000086 struct stat buf;
87
88 if (fstat(fd,&buf) < 0) {
89 perror("[ERR] Checking filesize of input file");
90 return -1;
91 } else {
92 return(buf.st_size);
93 }
94}
95
Tomer Shalevad785512009-10-29 21:31:50 +000096#define DRAMORIG 0x20000000
97/* Injects a bootloader into a Telechips 77X/78X firmware file */
98unsigned char *patch_firmware_tcc(unsigned char *of_buf, int of_size,
99 unsigned char *boot_buf, int boot_size, int *patched_size)
100{
101 unsigned char *patched_buf;
102 uint32_t ldr, old_ep_offset, new_ep_offset;
103 int of_offset;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000104
Tomer Shalevad785512009-10-29 21:31:50 +0000105 patched_buf = malloc(of_size + boot_size);
106 if (!patched_buf)
107 return NULL;
108
109 memcpy(patched_buf, of_buf, of_size);
110 memcpy(patched_buf + of_size, boot_buf, boot_size);
111
112 ldr = get_uint32le(patched_buf);
113
114 /* TODO: Verify it's a LDR instruction */
115 of_offset = (ldr & 0xfff) + 8;
116 old_ep_offset = get_uint32le(patched_buf + of_offset);
117 new_ep_offset = DRAMORIG + of_size;
118
119 printf("OF entry point: 0x%08x\n", old_ep_offset);
120 printf("New entry point: 0x%08x\n", new_ep_offset + 8);
121
122 /* Save the OF entry point at the start of the bootloader image */
123 put_uint32le(old_ep_offset, patched_buf + of_size);
124 put_uint32le(new_ep_offset, patched_buf + of_size + 4);
125
126 /* Change the OF entry point to the third word in our bootloader */
127 put_uint32le(new_ep_offset + 8, patched_buf + of_offset);
128
129 telechips_encode_crc(patched_buf, of_size + boot_size);
130 *patched_size = of_size + boot_size;
131
132 return patched_buf;
133}
134
135unsigned char *file_read(char *filename, int *size)
136{
137 unsigned char *buf = NULL;
138 int n, fd = -1;
139
140 /* Open file for reading */
141 fd = open(filename, O_RDONLY|O_BINARY);
142 if (fd < 0)
143 {
144 printf("[ERR] Could open file for reading, aborting\n");
145 perror(filename);
146 goto error;
147 }
148
149 /* Get file size, and allocate a buffer of that size */
150 *size = filesize(fd);
151 buf = malloc(*size);
152 if (buf == NULL)
153 {
154 printf("[ERR] Could not allocate memory, aborting\n");
155 goto error;
156 }
157
158 /* Read the file's content to the buffer */
159 n = read(fd, buf, *size);
160 if (n != *size)
161 {
162 printf("[ERR] Could not read from %s\n", filename);
163 goto error;
164 }
165
166 return buf;
167
168error:
169 if (fd >= 0)
170 close(fd);
171
172 if (buf)
173 free(buf);
174
175 return NULL;
176}
177
Tomer Shalevbcadf962009-11-01 18:26:00 +0000178/* A CRC test in order to reject non OF file */
179int test_firmware_tcc(unsigned char* buf, int length)
180{
181 return telechips_test_crc(buf, length);
182}
183
Tomer Shalevad785512009-10-29 21:31:50 +0000184#ifndef LIB
Dave Chapman28f6ae42007-10-28 11:08:10 +0000185int main(int argc, char *argv[])
186{
187 char *infile, *bootfile, *outfile;
Tomer Shalevad785512009-10-29 21:31:50 +0000188 int fdout = -1;
189 int n, of_size, boot_size, patched_size;
190 unsigned char *of_buf;
191 unsigned char *boot_buf = NULL;
192 unsigned char* image = NULL;
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000193 int ret = 0;
Tomer Shalevbcadf962009-11-01 18:26:00 +0000194
Dave Chapman28f6ae42007-10-28 11:08:10 +0000195 if(argc < 3) {
196 usage();
197 }
198
199 infile = argv[1];
200 bootfile = argv[2];
201 outfile = argv[3];
202
Tomer Shalevad785512009-10-29 21:31:50 +0000203 /* Read OF and boot files */
204 of_buf = file_read(infile, &of_size);
205 if (!of_buf)
Dave Chapman28f6ae42007-10-28 11:08:10 +0000206 {
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000207 ret = 1;
208 goto error_exit;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000209 }
210
Tomer Shalevbcadf962009-11-01 18:26:00 +0000211 /* Validate input file */
212 if (test_firmware_tcc(of_buf, of_size))
213 {
214 printf("[ERR] Unknown OF file used, aborting\n");
215 ret = 2;
216 goto error_exit;
217 }
218
Tomer Shalevad785512009-10-29 21:31:50 +0000219 boot_buf = file_read(bootfile, &boot_size);
220 if (!boot_buf)
Dave Chapman28f6ae42007-10-28 11:08:10 +0000221 {
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000222 ret = 3;
223 goto error_exit;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000224 }
225
Tomer Shalevad785512009-10-29 21:31:50 +0000226 /* Allocate buffer for patched firmware */
227 image = malloc(of_size + boot_size);
228 if (image == NULL)
Dave Chapman28f6ae42007-10-28 11:08:10 +0000229 {
Tomer Shalevad785512009-10-29 21:31:50 +0000230 printf("[ERR] Could not allocate memory, aborting\n");
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000231 ret = 4;
232 goto error_exit;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000233 }
234
Tomer Shalevad785512009-10-29 21:31:50 +0000235 /* Create the patched firmware */
236 image = patch_firmware_tcc(of_buf, of_size, boot_buf, boot_size,
237 &patched_size);
238 if (!image)
Dave Chapman28f6ae42007-10-28 11:08:10 +0000239 {
Tomer Shalevad785512009-10-29 21:31:50 +0000240 printf("[ERR] Error creating patched firmware, aborting\n");
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000241 ret = 5;
242 goto error_exit;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000243 }
244
Dave Chapman28f6ae42007-10-28 11:08:10 +0000245 fdout = open(outfile, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
246 if (fdout < 0)
247 {
Tomer Shalevad785512009-10-29 21:31:50 +0000248 perror(outfile);
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000249 ret = 6;
250 goto error_exit;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000251 }
252
Tomer Shalevad785512009-10-29 21:31:50 +0000253 n = write(fdout, image, patched_size);
254 if (n != patched_size)
Dave Chapman28f6ae42007-10-28 11:08:10 +0000255 {
256 printf("[ERR] Could not write output file %s\n",outfile);
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000257 ret = 7;
258 goto error_exit;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000259 }
260
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000261error_exit:
262
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000263 if (fdout >= 0)
264 close(fdout);
265
Tomer Shalevad785512009-10-29 21:31:50 +0000266 if (of_buf)
267 free(of_buf);
268
269 if (boot_buf)
270 free(boot_buf);
271
272 if (image)
273 free(image);
274
Rob Purchase3bc86fd2009-07-19 21:53:11 +0000275 return ret;
Dave Chapman28f6ae42007-10-28 11:08:10 +0000276}
Tomer Shalevad785512009-10-29 21:31:50 +0000277#endif