1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 by Dave Chapman
12 * Based on merge0.cpp by James Espinoza, but completely rewritten.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
26 #include <sys/types.h>
32 /* New entry point for nk.bin - where our dualboot code is inserted */
33 #define NK_ENTRY_POINT 0x88200000
35 /* Entry point (and load address) for the main Rockbox bootloader */
36 #define BL_ENTRY_POINT 0x8a000000
40 Description of nk.bin from
42 http://www.xs4all.nl/~itsme/projects/xda/wince-flashfile-formats.html
44 these files contain most information, several non-contigouos blocks
45 may be present and an entrypoint in the code.
47 1. a 7 character signature "B000FF\n" ( that is with 3 zeroes, and
48 ending in a linefeed )
49 2. DWORD for image start
50 3. DWORD for image length
51 4. followd by several records of this format:
52 1. DWORD with address where this block is to be flashed to
53 2. DWORD with the length of this block
54 3. DWORD with the 32 bit checksum of this block, in perl:
55 unpack("%32C*", $data);
56 4. followed by <length> bytes of data
57 5. the last record has address ZERO, in the length the entrypoint
58 into the rom, and ZERO as checksum.
61 NOTE: The Gigabeat-S nk.bin contains 171 records, plus the EOF record.
63 mknkboot.c appends two images:
65 1) A "Disable" image which overwrites a word in the EBoot image
66 2) Our bootloader image, which has the same load address as nk.exe
70 /* win32 compatibility */
76 #define DISABLE_ADDR 0x88065A10 /* in EBoot */
77 #define DISABLE_INSN 0xe3a00001
78 #define DISABLE_SUM (0xe3+0xa0+0x00+0x01)
80 /* Code to dual-boot - this is inserted at NK_ENTRY_POINT */
81 static uint32_t dualboot
[] =
83 0xe59f900c, /* ldr r9, [pc, #12] -> 0x53fa4000 */
84 0xe5999000, /* ldr r9, [r9] */
85 0xe3190010, /* tst r9, #16 ; 0x10 */
87 /* Branch to Rockbox if hold is on */
88 0x159ff004, /* ldrne pc, [pc, #4] -> 0x89000000 */
90 /* Branch to Rockbox if hold is off */
91 0x059ff004, /* ldreq pc, [pc, #4] -> 0x89000000 */
93 /* Branch to original firmware */
94 0xea0003fa, /* b 0x1000 */
96 0x53fa4000, /* GPIO3_DR */
97 BL_ENTRY_POINT
/* RB bootloader load address/entry point */
100 static void put_uint32le(uint32_t x
, unsigned char* p
)
103 p
[1] = (x
>> 8) & 0xff;
104 p
[2] = (x
>> 16) & 0xff;
105 p
[3] = (x
>> 24) & 0xff;
108 static void usage(void)
110 printf("Usage: mknkboot <firmware file> <boot file> <output file>\n");
115 static off_t
filesize(int fd
) {
118 if (fstat(fd
,&buf
) < 0) {
119 perror("[ERR] Checking filesize of input file");
127 int main(int argc
, char *argv
[])
129 char *infile
, *bootfile
, *outfile
;
130 int fdin
, fdboot
,fdout
;
132 int inlength
,bootlength
,newlength
;
135 unsigned char* boot2
;
136 unsigned char* disable
;
147 fdin
= open(infile
, O_RDONLY
|O_BINARY
);
153 fdboot
= open(bootfile
, O_RDONLY
|O_BINARY
);
159 inlength
= filesize(fdin
);
161 bootlength
= filesize(fdboot
);
163 /* Create buffer for original nk.bin, plus our bootloader (with 12
164 byte header), plus the 16-byte "disable record", plus our dual-boot code */
166 newlength
= inlength
+ (bootlength
+ 12) + 16 + (12 + 28);
167 buf
= malloc(newlength
);
171 printf("[ERR] Could not allocate memory, aborting\n");
175 /****** STEP 1 - Read original nk.bin into buffer */
177 n
= read(fdin
, buf
, inlength
);
180 printf("[ERR] Could not read from %s\n",infile
);
184 /****** STEP 2 - Move EOF record to the new EOF */
185 memcpy(buf
+ newlength
- 12, buf
+ inlength
- 12, 12);
187 /* Overwrite default entry point with NK_ENTRY_POINT */
188 put_uint32le(NK_ENTRY_POINT
, buf
+ newlength
- 8);
190 /****** STEP 3 - Create a record to disable the firmware signature
192 disable
= buf
+ inlength
- 12;
194 put_uint32le(DISABLE_ADDR
, disable
);
195 put_uint32le(4, disable
+ 4);
196 put_uint32le(DISABLE_SUM
, disable
+ 8);
197 put_uint32le(DISABLE_INSN
, disable
+ 12);
199 /****** STEP 4 - Read the bootloader binary */
201 n
= read(fdboot
, boot
+ 12, bootlength
);
204 printf("[ERR] Could not read from %s\n",bootfile
);
208 /****** STEP 5 - Create header for bootloader record */
210 /* Calculate checksum */
212 for (i
= 0; i
< bootlength
; i
++) {
216 put_uint32le(BL_ENTRY_POINT
, boot
); /* Our entry point */
217 put_uint32le(bootlength
, boot
+ 4);
218 put_uint32le(sum
, boot
+ 8);
220 /****** STEP 6 - Insert our dual-boot code */
221 boot2
= boot
+ bootlength
+ 12;
223 /* Copy dual-boot code in an endian-safe way */
224 for (i
= 0; i
< (signed int)sizeof(dualboot
) / 4; i
++) {
225 put_uint32le(dualboot
[i
], boot2
+ 12 + i
*4);
228 /* Calculate checksum */
230 for (i
= 0; i
< (signed int)sizeof(dualboot
); i
++) {
234 put_uint32le(NK_ENTRY_POINT
, boot2
); /* New entry point for our nk.bin */
235 put_uint32le(sizeof(dualboot
), boot2
+ 4);
236 put_uint32le(sum
, boot2
+ 8);
238 /****** STEP 7 - Now write the output file */
240 fdout
= open(outfile
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_BINARY
, 0644);
246 n
= write(fdout
, buf
, newlength
);
249 printf("[ERR] Could not write output file %s\n",outfile
);