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 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
24 #include <sys/types.h>
30 /* New entry point for nk.bin - where our dualboot code is inserted */
31 #define NK_ENTRY_POINT 0x88200000
33 /* Entry point (and load address) for the main Rockbox bootloader */
34 #define BL_ENTRY_POINT 0x8a000000
38 Description of nk.bin from
40 http://www.xs4all.nl/~itsme/projects/xda/wince-flashfile-formats.html
42 these files contain most information, several non-contigouos blocks
43 may be present and an entrypoint in the code.
45 1. a 7 character signature "B000FF\n" ( that is with 3 zeroes, and
46 ending in a linefeed )
47 2. DWORD for image start
48 3. DWORD for image length
49 4. followd by several records of this format:
50 1. DWORD with address where this block is to be flashed to
51 2. DWORD with the length of this block
52 3. DWORD with the 32 bit checksum of this block, in perl:
53 unpack("%32C*", $data);
54 4. followed by <length> bytes of data
55 5. the last record has address ZERO, in the length the entrypoint
56 into the rom, and ZERO as checksum.
59 NOTE: The Gigabeat-S nk.bin contains 171 records, plus the EOF record.
61 mknkboot.c appends two images:
63 1) A "Disable" image which overwrites a word in the EBoot image
64 2) Our bootloader image, which has the same load address as nk.exe
68 /* win32 compatibility */
74 #define DISABLE_ADDR 0x88065A10 /* in EBoot */
75 #define DISABLE_INSN 0xe3a00001
76 #define DISABLE_SUM (0xe3+0xa0+0x00+0x01)
78 /* Code to dual-boot - this is inserted at NK_ENTRY_POINT */
79 static uint32_t dualboot
[] =
81 0xe59f900c, /* ldr r9, [pc, #12] -> 0x53fa4000 */
82 0xe5999000, /* ldr r9, [r9] */
83 0xe3190010, /* tst r9, #16 ; 0x10 */
85 /* Branch to Rockbox if hold is on */
86 0x159ff004, /* ldrne pc, [pc, #4] -> 0x89000000 */
88 /* Branch to Rockbox if hold is off */
89 0x059ff004, /* ldreq pc, [pc, #4] -> 0x89000000 */
91 /* Branch to original firmware */
92 0xea0003fa, /* b 0x1000 */
94 0x53fa4000, /* GPIO3_DR */
95 BL_ENTRY_POINT
/* RB bootloader load address/entry point */
98 static void put_uint32le(uint32_t x
, unsigned char* p
)
101 p
[1] = (x
>> 8) & 0xff;
102 p
[2] = (x
>> 16) & 0xff;
103 p
[3] = (x
>> 24) & 0xff;
106 static void usage(void)
108 printf("Usage: mknkboot <firmware file> <boot file> <output file>\n");
113 static off_t
filesize(int fd
) {
116 if (fstat(fd
,&buf
) < 0) {
117 perror("[ERR] Checking filesize of input file");
125 int main(int argc
, char *argv
[])
127 char *infile
, *bootfile
, *outfile
;
128 int fdin
, fdboot
,fdout
;
130 int inlength
,bootlength
,newlength
;
133 unsigned char* boot2
;
134 unsigned char* disable
;
145 fdin
= open(infile
, O_RDONLY
|O_BINARY
);
151 fdboot
= open(bootfile
, O_RDONLY
|O_BINARY
);
157 inlength
= filesize(fdin
);
159 bootlength
= filesize(fdboot
);
161 /* Create buffer for original nk.bin, plus our bootloader (with 12
162 byte header), plus the 16-byte "disable record", plus our dual-boot code */
164 newlength
= inlength
+ (bootlength
+ 12) + 16 + (12 + 28);
165 buf
= malloc(newlength
);
169 printf("[ERR] Could not allocate memory, aborting\n");
173 /****** STEP 1 - Read original nk.bin into buffer */
175 n
= read(fdin
, buf
, inlength
);
178 printf("[ERR] Could not read from %s\n",infile
);
182 /****** STEP 2 - Move EOF record to the new EOF */
183 memcpy(buf
+ newlength
- 12, buf
+ inlength
- 12, 12);
185 /* Overwrite default entry point with NK_ENTRY_POINT */
186 put_uint32le(NK_ENTRY_POINT
, buf
+ newlength
- 8);
188 /****** STEP 3 - Create a record to disable the firmware signature
190 disable
= buf
+ inlength
- 12;
192 put_uint32le(DISABLE_ADDR
, disable
);
193 put_uint32le(4, disable
+ 4);
194 put_uint32le(DISABLE_SUM
, disable
+ 8);
195 put_uint32le(DISABLE_INSN
, disable
+ 12);
197 /****** STEP 4 - Read the bootloader binary */
199 n
= read(fdboot
, boot
+ 12, bootlength
);
202 printf("[ERR] Could not read from %s\n",bootfile
);
206 /****** STEP 5 - Create header for bootloader record */
208 /* Calculate checksum */
210 for (i
= 0; i
< bootlength
; i
++) {
214 put_uint32le(BL_ENTRY_POINT
, boot
); /* Our entry point */
215 put_uint32le(bootlength
, boot
+ 4);
216 put_uint32le(sum
, boot
+ 8);
218 /****** STEP 6 - Insert our dual-boot code */
219 boot2
= boot
+ bootlength
+ 12;
221 /* Copy dual-boot code in an endian-safe way */
222 for (i
= 0; i
< sizeof(dualboot
) / 4; i
++) {
223 put_uint32le(dualboot
[i
], boot2
+ 12 + i
*4);
226 /* Calculate checksum */
228 for (i
= 0; i
< sizeof(dualboot
); i
++) {
232 put_uint32le(NK_ENTRY_POINT
, boot2
); /* New entry point for our nk.bin */
233 put_uint32le(sizeof(dualboot
), boot2
+ 4);
234 put_uint32le(sum
, boot2
+ 8);
236 /****** STEP 7 - Now write the output file */
238 fdout
= open(outfile
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_BINARY
, 0644);
244 n
= write(fdout
, buf
, newlength
);
247 printf("[ERR] Could not write output file %s\n",outfile
);