FS#10053 - Sansa AMS >2GB support by Rafaël Carré. Enables bank switching on
[kugel-rb/myfork.git] / tools / mknkboot.c
blobcfa0e427b3d3dfaa71a6416598525f4ec4c41c53
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 * 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 ****************************************************************************/
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <inttypes.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 */
72 #ifndef O_BINARY
73 #define O_BINARY 0
74 #endif
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 */
86 #if 0
87 /* Branch to Rockbox if hold is on */
88 0x159ff004, /* ldrne pc, [pc, #4] -> 0x89000000 */
89 #else
90 /* Branch to Rockbox if hold is off */
91 0x059ff004, /* ldreq pc, [pc, #4] -> 0x89000000 */
92 #endif
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)
102 p[0] = x & 0xff;
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");
112 exit(1);
115 static off_t filesize(int fd) {
116 struct stat buf;
118 if (fstat(fd,&buf) < 0) {
119 perror("[ERR] Checking filesize of input file");
120 return -1;
121 } else {
122 return(buf.st_size);
127 int main(int argc, char *argv[])
129 char *infile, *bootfile, *outfile;
130 int fdin, fdboot,fdout;
131 int i,n;
132 int inlength,bootlength,newlength;
133 unsigned char* buf;
134 unsigned char* boot;
135 unsigned char* boot2;
136 unsigned char* disable;
137 uint32_t sum;
139 if(argc < 3) {
140 usage();
143 infile = argv[1];
144 bootfile = argv[2];
145 outfile = argv[3];
147 fdin = open(infile, O_RDONLY|O_BINARY);
148 if (fdin < 0)
150 perror(infile);
153 fdboot = open(bootfile, O_RDONLY|O_BINARY);
154 if (fdboot < 0)
156 perror(bootfile);
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);
169 if (buf==NULL)
171 printf("[ERR] Could not allocate memory, aborting\n");
172 return 1;
175 /****** STEP 1 - Read original nk.bin into buffer */
177 n = read(fdin, buf, inlength);
178 if (n != inlength)
180 printf("[ERR] Could not read from %s\n",infile);
181 return 2;
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
191 check in EBoot */
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 */
200 boot = disable + 16;
201 n = read(fdboot, boot + 12, bootlength);
202 if (n != bootlength)
204 printf("[ERR] Could not read from %s\n",bootfile);
205 return 3;
208 /****** STEP 5 - Create header for bootloader record */
210 /* Calculate checksum */
211 sum = 0;
212 for (i = 0; i < bootlength; i++) {
213 sum += boot[12 + 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 */
229 sum = 0;
230 for (i = 0; i < (signed int)sizeof(dualboot); i++) {
231 sum += boot2[i+12];
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);
241 if (fdout < 0)
243 perror(outfile);
246 n = write(fdout, buf, newlength);
247 if (n != newlength)
249 printf("[ERR] Could not write output file %s\n",outfile);
250 return 3;
253 close(fdin);
254 close(fdboot);
255 close(fdout);
257 return 0;