Refactor mknkboot in preparation for beastpatcher integration.
[kugel-rb.git] / tools / mknkboot.c
blob87372500e5d227044ee67adcea4d9b7c1a9b4535
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Dave Chapman
12 * Based on merge0.cpp by James Espinoza, but completely rewritten.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met:
18 * * Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
21 * * Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials provided
24 * with the distribution.
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ****************************************************************************/
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <inttypes.h>
48 /* New entry point for nk.bin - where our dualboot code is inserted */
49 #define NK_ENTRY_POINT 0x88200000
51 /* Entry point (and load address) for the main Rockbox bootloader */
52 #define BL_ENTRY_POINT 0x8a000000
56 Description of nk.bin from
58 http://www.xs4all.nl/~itsme/projects/xda/wince-flashfile-formats.html
60 these files contain most information, several non-contigouos blocks
61 may be present and an entrypoint in the code.
63 1. a 7 character signature "B000FF\n" ( that is with 3 zeroes, and
64 ending in a linefeed )
65 2. DWORD for image start
66 3. DWORD for image length
67 4. followd by several records of this format:
68 1. DWORD with address where this block is to be flashed to
69 2. DWORD with the length of this block
70 3. DWORD with the 32 bit checksum of this block, in perl:
71 unpack("%32C*", $data);
72 4. followed by <length> bytes of data
73 5. the last record has address ZERO, in the length the entrypoint
74 into the rom, and ZERO as checksum.
77 NOTE: The Gigabeat-S nk.bin contains 171 records, plus the EOF record.
79 mknkboot.c appends two images:
81 1) A "Disable" image which overwrites a word in the EBoot image
82 2) Our bootloader image, which has the same load address as nk.exe
86 /* win32 compatibility */
88 #ifndef O_BINARY
89 #define O_BINARY 0
90 #endif
92 #define DISABLE_ADDR 0x88065A10 /* in EBoot */
93 #define DISABLE_INSN 0xe3a00001
94 #define DISABLE_SUM (0xe3+0xa0+0x00+0x01)
96 /* Code to dual-boot - this is inserted at NK_ENTRY_POINT */
97 static uint32_t dualboot[] =
99 0xe59f900c, /* ldr r9, [pc, #12] -> 0x53fa4000 */
100 0xe5999000, /* ldr r9, [r9] */
101 0xe3190010, /* tst r9, #16 ; 0x10 */
102 #if 0
103 /* Branch to Rockbox if hold is on */
104 0x159ff004, /* ldrne pc, [pc, #4] -> 0x89000000 */
105 #else
106 /* Branch to Rockbox if hold is off */
107 0x059ff004, /* ldreq pc, [pc, #4] -> 0x89000000 */
108 #endif
109 /* Branch to original firmware */
110 0xea0003fa, /* b 0x1000 */
112 0x53fa4000, /* GPIO3_DR */
113 BL_ENTRY_POINT /* RB bootloader load address/entry point */
116 static void put_uint32le(uint32_t x, unsigned char* p)
118 p[0] = x & 0xff;
119 p[1] = (x >> 8) & 0xff;
120 p[2] = (x >> 16) & 0xff;
121 p[3] = (x >> 24) & 0xff;
124 static void usage(void)
126 printf("Usage: mknkboot <firmware file> <boot file> <output file>\n");
128 exit(1);
131 static off_t filesize(int fd) {
132 struct stat buf;
134 if (fstat(fd,&buf) < 0) {
135 perror("[ERR] Checking filesize of input file");
136 return -1;
137 } else {
138 return(buf.st_size);
143 int mknkboot(const char* infile, const char* bootfile, const char* outfile)
145 int fdin, fdboot = -1, fdout = -1;
146 int i,n;
147 int inlength,bootlength,newlength;
148 unsigned char* buf = NULL;
149 unsigned char* boot;
150 unsigned char* boot2;
151 unsigned char* disable;
152 uint32_t sum;
153 int result = 0;
155 fdin = open(infile, O_RDONLY|O_BINARY);
156 if (fdin < 0)
158 perror(infile);
159 result = 1;
160 goto quit;
163 fdboot = open(bootfile, O_RDONLY|O_BINARY);
164 if (fdboot < 0)
166 perror(bootfile);
167 close(fdin);
168 result = 2;
169 goto quit;
172 inlength = filesize(fdin);
173 bootlength = filesize(fdboot);
175 /* Create buffer for original nk.bin, plus our bootloader (with 12
176 byte header), plus the 16-byte "disable record", plus our dual-boot code */
178 newlength = inlength + (bootlength + 12) + 16 + (12 + 28);
179 buf = malloc(newlength);
181 if (buf==NULL)
183 printf("[ERR] Could not allocate memory, aborting\n");
184 result = 3;
185 goto quit;
188 /****** STEP 1 - Read original nk.bin into buffer */
190 n = read(fdin, buf, inlength);
191 if (n != inlength)
193 printf("[ERR] Could not read from %s\n",infile);
194 result = 4;
195 goto quit;
198 /****** STEP 2 - Move EOF record to the new EOF */
199 memcpy(buf + newlength - 12, buf + inlength - 12, 12);
201 /* Overwrite default entry point with NK_ENTRY_POINT */
202 put_uint32le(NK_ENTRY_POINT, buf + newlength - 8);
204 /****** STEP 3 - Create a record to disable the firmware signature
205 check in EBoot */
206 disable = buf + inlength - 12;
208 put_uint32le(DISABLE_ADDR, disable);
209 put_uint32le(4, disable + 4);
210 put_uint32le(DISABLE_SUM, disable + 8);
211 put_uint32le(DISABLE_INSN, disable + 12);
213 /****** STEP 4 - Read the bootloader binary */
214 boot = disable + 16;
215 n = read(fdboot, boot + 12, bootlength);
216 if (n != bootlength)
218 printf("[ERR] Could not read from %s\n",bootfile);
219 result = 5;
220 goto quit;
223 /****** STEP 5 - Create header for bootloader record */
225 /* Calculate checksum */
226 sum = 0;
227 for (i = 0; i < bootlength; i++) {
228 sum += boot[12 + i];
231 put_uint32le(BL_ENTRY_POINT, boot); /* Our entry point */
232 put_uint32le(bootlength, boot + 4);
233 put_uint32le(sum, boot + 8);
235 /****** STEP 6 - Insert our dual-boot code */
236 boot2 = boot + bootlength + 12;
238 /* Copy dual-boot code in an endian-safe way */
239 for (i = 0; i < (signed int)sizeof(dualboot) / 4; i++) {
240 put_uint32le(dualboot[i], boot2 + 12 + i*4);
243 /* Calculate checksum */
244 sum = 0;
245 for (i = 0; i < (signed int)sizeof(dualboot); i++) {
246 sum += boot2[i+12];
249 put_uint32le(NK_ENTRY_POINT, boot2); /* New entry point for our nk.bin */
250 put_uint32le(sizeof(dualboot), boot2 + 4);
251 put_uint32le(sum, boot2 + 8);
253 /****** STEP 7 - Now write the output file */
255 fdout = open(outfile, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
256 if (fdout < 0)
258 perror(outfile);
259 result = 6;
260 goto quit;
263 n = write(fdout, buf, newlength);
264 if (n != newlength)
266 printf("[ERR] Could not write output file %s\n",outfile);
267 result = 7;
268 goto quit;
271 quit:
272 if(buf != NULL)
273 free(buf);
274 close(fdin);
275 close(fdboot);
276 close(fdout);
278 return result;
282 int main(int argc, char* argv[])
284 char *infile, *bootfile, *outfile;
285 if(argc < 4) {
286 usage();
289 infile = argv[1];
290 bootfile = argv[2];
291 outfile = argv[3];
293 return mknkboot(infile, bootfile, outfile);