diff options
Diffstat (limited to 'utils/AMS/hacking/mkamsboot.c')
-rw-r--r-- | utils/AMS/hacking/mkamsboot.c | 195 |
1 files changed, 110 insertions, 85 deletions
diff --git a/utils/AMS/hacking/mkamsboot.c b/utils/AMS/hacking/mkamsboot.c index 30ca66e43b..ea434bc893 100644 --- a/utils/AMS/hacking/mkamsboot.c +++ b/utils/AMS/hacking/mkamsboot.c | |||
@@ -26,26 +26,33 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA | |||
26 | 26 | ||
27 | Insert a Rockbox bootloader into an AMS original firmware file. | 27 | Insert a Rockbox bootloader into an AMS original firmware file. |
28 | 28 | ||
29 | The first instruction in an AMS firmware file is always of the form: | 29 | We replace the main firmware block (bytes 0x400..padded_firmware_size+0x400) |
30 | with the following: | ||
30 | 31 | ||
31 | ldr pc, [pc, #xxx] | 32 | Bytes 0..(firmware_size-ucl_size) - Our bootloader code |
33 | Bytes (firmware_size-ucl_size)..firmware_size - UCL compressed OF image | ||
34 | Bytes firmware_size..padded_firmware_size - UCL decompress function | ||
32 | 35 | ||
33 | where [pc, #xxx] contains the entry point of the firmware - e.g. 0x00000138 | 36 | mkamsboot writes the following values at offsets into our bootloader code: |
34 | 37 | ||
35 | mkamsboot appends the Rockbox bootloader to the end of the original | 38 | 0x20 - Entry point (plus 1 - for thumb mode) of the ucl_unpack function |
36 | firmware block in the firmware file and shifts the remaining contents of the firmware file to make space for it. | 39 | 0x24 - Location of the UCL compressed version of the original firmware block |
37 | 40 | ||
38 | It also replaces the contents of [pc, #xxx] with the entry point of | 41 | mkamsboot then corrects the length (to include the UCL decompress |
39 | our bootloader - i.e. the length of the original firmware block plus 4 | 42 | function) and checksum in the main firmware headers (both copies), |
40 | bytes. | 43 | creating a new legal firmware file which can be installed on the |
44 | device. | ||
41 | 45 | ||
42 | It then stores the original entry point from [pc, #xxx] in the first | 46 | Our bootloader first checks for the "dual-boot" keypress, and then either: |
43 | four bytes of the Rockbox bootloader image, which is used by the | ||
44 | bootloader to dual-boot. | ||
45 | 47 | ||
46 | Finally, mkamsboot corrects the length and checksum in the main | 48 | a) Branches to the ucl_unpack function, which will then branch to 0x0 after |
47 | firmware headers (both copies), creating a new legal firmware file | 49 | decompressing the OF. |
48 | which can be installed on the device. | 50 | |
51 | b) Continues running with our test code | ||
52 | |||
53 | This method uses no RAM outside the padded area of the original | ||
54 | firmware block - the UCL compression can happen in-place when the | ||
55 | compressed image is stored at the end of the destination buffer. | ||
49 | 56 | ||
50 | */ | 57 | */ |
51 | 58 | ||
@@ -106,35 +113,35 @@ static int calc_checksum(unsigned char* buf, uint32_t n) | |||
106 | 113 | ||
107 | void usage(void) | 114 | void usage(void) |
108 | { | 115 | { |
109 | printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n"); | 116 | printf("Usage: mkamsboot <firmware file> <ucl image> <boot file> <ucl unpack file> <output file>\n"); |
110 | 117 | ||
111 | exit(1); | 118 | exit(1); |
112 | } | 119 | } |
113 | 120 | ||
114 | int main(int argc, char* argv[]) | 121 | int main(int argc, char* argv[]) |
115 | { | 122 | { |
116 | char *infile, *bootfile, *outfile; | 123 | char *infile, *uclfile, *bootfile, *uclunpackfile, *outfile; |
117 | int fdin, fdboot,fdout; | 124 | int fdin, fducl, fdboot, fduclunpack, fdout; |
118 | off_t len; | 125 | off_t len; |
119 | uint32_t n; | 126 | uint32_t n; |
120 | unsigned char* buf; | 127 | unsigned char* buf; |
121 | uint32_t ldr; | ||
122 | uint32_t origoffset; | ||
123 | uint32_t firmware_size; | 128 | uint32_t firmware_size; |
124 | uint32_t firmware_paddedsize; | 129 | uint32_t firmware_paddedsize; |
125 | uint32_t bootloader_size; | 130 | uint32_t bootloader_size; |
126 | uint32_t new_paddedsize; | 131 | uint32_t ucl_size; |
132 | uint32_t uclunpack_size; | ||
127 | uint32_t sum,filesum; | 133 | uint32_t sum,filesum; |
128 | uint32_t new_length; | ||
129 | uint32_t i; | 134 | uint32_t i; |
130 | 135 | ||
131 | if(argc != 4) { | 136 | if(argc != 6) { |
132 | usage(); | 137 | usage(); |
133 | } | 138 | } |
134 | 139 | ||
135 | infile = argv[1]; | 140 | infile = argv[1]; |
136 | bootfile = argv[2]; | 141 | uclfile = argv[2]; |
137 | outfile = argv[3]; | 142 | bootfile = argv[3]; |
143 | uclunpackfile = argv[4]; | ||
144 | outfile = argv[5]; | ||
138 | 145 | ||
139 | /* Open the bootloader file */ | 146 | /* Open the bootloader file */ |
140 | fdboot = open(bootfile, O_RDONLY|O_BINARY); | 147 | fdboot = open(bootfile, O_RDONLY|O_BINARY); |
@@ -147,6 +154,28 @@ int main(int argc, char* argv[]) | |||
147 | bootloader_size = filesize(fdboot); | 154 | bootloader_size = filesize(fdboot); |
148 | 155 | ||
149 | 156 | ||
157 | /* Open the UCL-compressed image of the firmware block */ | ||
158 | fduclunpack = open(uclunpackfile, O_RDONLY|O_BINARY); | ||
159 | if (fduclunpack < 0) | ||
160 | { | ||
161 | fprintf(stderr,"[ERR] Could not open %s for reading\n",uclunpackfile); | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | uclunpack_size = filesize(fduclunpack); | ||
166 | |||
167 | |||
168 | /* Open the UCL-compressed image of the firmware block */ | ||
169 | fducl = open(uclfile, O_RDONLY|O_BINARY); | ||
170 | if (fducl < 0) | ||
171 | { | ||
172 | fprintf(stderr,"[ERR] Could not open %s for reading\n",uclfile); | ||
173 | return 1; | ||
174 | } | ||
175 | |||
176 | ucl_size = filesize(fducl); | ||
177 | |||
178 | |||
150 | /* Open the firmware file */ | 179 | /* Open the firmware file */ |
151 | fdin = open(infile,O_RDONLY|O_BINARY); | 180 | fdin = open(infile,O_RDONLY|O_BINARY); |
152 | 181 | ||
@@ -158,9 +187,8 @@ int main(int argc, char* argv[]) | |||
158 | if ((len = filesize(fdin)) < 0) | 187 | if ((len = filesize(fdin)) < 0) |
159 | return 1; | 188 | return 1; |
160 | 189 | ||
161 | /* We will need no more memory than the total size plus the bootloader size | 190 | /* Allocate memory for the OF image - we don't change the size */ |
162 | padded to a boundary */ | 191 | if ((buf = malloc(len)) == NULL) { |
163 | if ((buf = malloc(len + PAD_TO_BOUNDARY(bootloader_size))) == NULL) { | ||
164 | fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len); | 192 | fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len); |
165 | return 1; | 193 | return 1; |
166 | } | 194 | } |
@@ -181,91 +209,83 @@ int main(int argc, char* argv[]) | |||
181 | 209 | ||
182 | firmware_paddedsize = PAD_TO_BOUNDARY(firmware_size); | 210 | firmware_paddedsize = PAD_TO_BOUNDARY(firmware_size); |
183 | 211 | ||
184 | /* Total new size */ | 212 | fprintf(stderr,"Original firmware size - %d bytes\n",firmware_size); |
185 | new_paddedsize = PAD_TO_BOUNDARY(firmware_size + bootloader_size); | 213 | fprintf(stderr,"Padded firmware size - %d bytes\n",firmware_paddedsize); |
186 | 214 | fprintf(stderr,"Bootloader size - %d bytes\n",bootloader_size); | |
187 | /* Total new size of firmware file */ | 215 | fprintf(stderr,"UCL image size - %d bytes\n",ucl_size); |
188 | new_length = len + (new_paddedsize - firmware_paddedsize); | 216 | fprintf(stderr,"UCL unpack function size - %d bytes\n",uclunpack_size); |
217 | fprintf(stderr,"Original total size of firmware - %d bytes\n",(int)len); | ||
218 | |||
219 | /* Check we have room for our bootloader - in the future, we could UCL | ||
220 | pack this image as well if we need to. */ | ||
221 | if (bootloader_size > (firmware_size - ucl_size)) { | ||
222 | fprintf(stderr,"[ERR] Bootloader too large (%d bytes, %d available)\n", | ||
223 | bootloader_size, firmware_size - ucl_size); | ||
224 | return 1; | ||
225 | } | ||
189 | 226 | ||
190 | fprintf(stderr,"Original firmware size - 0x%08x\n",firmware_size); | 227 | /* Check we have enough room for the UCL unpack function. This |
191 | fprintf(stderr,"Padded firmware size - 0x%08x\n",firmware_paddedsize); | 228 | needs to be outside the firmware block, so if we wanted to |
192 | fprintf(stderr,"Bootloader size - 0x%08x\n",bootloader_size); | 229 | support every firmware version, we could store this function in |
193 | fprintf(stderr,"New padded size - 0x%08x\n",new_paddedsize); | 230 | the main firmware block, and then copy it to an unused part of |
194 | fprintf(stderr,"Original total size of firmware - 0x%08x\n",(int)len); | 231 | RAM. */ |
195 | fprintf(stderr,"New total size of firmware - 0x%08x\n",new_length); | 232 | if (uclunpack_size > (firmware_paddedsize - firmware_size)) { |
233 | fprintf(stderr,"[ERR] UCL unpack function too large (%d bytes, %d available)\n", | ||
234 | uclunpack_size, firmware_paddedsize - firmware_size); | ||
235 | return 1; | ||
236 | } | ||
196 | 237 | ||
197 | if (firmware_paddedsize != new_paddedsize) { | 238 | /* Zero the original firmware area - not needed, but helps debugging */ |
198 | /* We don't know how to safely increase the firmware size, so abort */ | 239 | memset(buf + 0x400, 0, firmware_size); |
199 | 240 | ||
200 | fprintf(stderr, | 241 | /* Locate our bootloader code at the start of the firmware block */ |
201 | "[ERR] Bootloader too large (%d bytes - %d bytes available), aborting.\n", | 242 | n = read(fdboot, buf + 0x400, bootloader_size); |
202 | bootloader_size, firmware_paddedsize - firmware_size); | ||
203 | 243 | ||
244 | if (n != bootloader_size) { | ||
245 | fprintf(stderr,"[ERR] Could not load bootloader file\n"); | ||
204 | return 1; | 246 | return 1; |
205 | } | 247 | } |
248 | close(fdboot); | ||
206 | 249 | ||
207 | ldr = get_uint32le(&buf[0x400]); | 250 | /* Locate the compressed image of the original firmware block at the end |
251 | of the firmware block */ | ||
252 | n = read(fducl, buf + 0x400 + firmware_size - ucl_size, ucl_size); | ||
208 | 253 | ||
209 | if ((ldr & 0xfffff000) != 0xe59ff000) { | 254 | if (n != ucl_size) { |
210 | fprintf(stderr,"[ERR] Firmware file doesn't start with an \"ldr pc, [pc, #xx]\" instruction.\n"); | 255 | fprintf(stderr,"[ERR] Could not load ucl file\n"); |
211 | return 1; | 256 | return 1; |
212 | } | 257 | } |
213 | origoffset = (ldr&0xfff) + 8; | 258 | close(fducl); |
214 | |||
215 | printf("original firmware entry point: 0x%08x\n",get_uint32le(buf + 0x400 + origoffset)); | ||
216 | printf("New entry point: 0x%08x\n", firmware_size + 4); | ||
217 | |||
218 | #if 0 | ||
219 | /* Replace the "Product: Express" string with "Rockbox" */ | ||
220 | i = 0x400 + firmware_size - 7; | ||
221 | while ((i > 0x400) && (memcmp(&buf[i],"Express",7)!=0)) | ||
222 | i--; | ||
223 | 259 | ||
224 | i = (i + 3) & ~0x3; | ||
225 | |||
226 | if (i >= 0x400) { | ||
227 | printf("Replacing \"Express\" string at offset 0x%08x\n",i); | ||
228 | memcpy(&buf[i],"Rockbox",7); | ||
229 | } else { | ||
230 | printf("Could not find \"Express\" string to replace\n"); | ||
231 | } | ||
232 | #endif | ||
233 | 260 | ||
234 | n = read(fdboot, buf + 0x400 + firmware_size, bootloader_size); | 261 | /* Locate our UCL unpack function in the padding after the firmware block */ |
262 | n = read(fduclunpack, buf + 0x400 + firmware_size, uclunpack_size); | ||
235 | 263 | ||
236 | if (n != bootloader_size) { | 264 | if (n != uclunpack_size) { |
237 | fprintf(stderr,"[ERR] Could not bootloader file\n"); | 265 | fprintf(stderr,"[ERR] Could not load uclunpack file\n"); |
238 | return 1; | 266 | return 1; |
239 | } | 267 | } |
240 | close(fdboot); | 268 | close(fduclunpack); |
241 | 269 | ||
242 | /* Replace first word of the bootloader with the original entry point */ | 270 | put_uint32le(&buf[0x420], firmware_size + 1); /* UCL unpack entry point */ |
243 | put_uint32le(buf + 0x400 + firmware_size, get_uint32le(buf + 0x400 + origoffset)); | 271 | put_uint32le(&buf[0x424], firmware_size - ucl_size); /* Location of OF */ |
244 | |||
245 | #if 1 | ||
246 | put_uint32le(buf + 0x400 + origoffset, firmware_size + 4); | ||
247 | #endif | ||
248 | 272 | ||
249 | /* Update checksum */ | 273 | /* Update checksum */ |
250 | sum = calc_checksum(buf + 0x400,firmware_size + bootloader_size); | 274 | sum = calc_checksum(buf + 0x400,firmware_size + uclunpack_size); |
251 | 275 | ||
252 | put_uint32le(&buf[0x04], sum); | 276 | put_uint32le(&buf[0x04], sum); |
253 | put_uint32le(&buf[0x204], sum); | 277 | put_uint32le(&buf[0x204], sum); |
254 | 278 | ||
255 | /* Update firmware block count */ | ||
256 | put_uint32le(&buf[0x08], new_paddedsize / 0x200); | ||
257 | put_uint32le(&buf[0x208], new_paddedsize / 0x200); | ||
258 | |||
259 | /* Update firmware size */ | 279 | /* Update firmware size */ |
260 | put_uint32le(&buf[0x0c], firmware_size + bootloader_size); | 280 | put_uint32le(&buf[0x0c], firmware_size + uclunpack_size); |
261 | put_uint32le(&buf[0x20c], firmware_size + bootloader_size); | 281 | put_uint32le(&buf[0x20c], firmware_size + uclunpack_size); |
262 | 282 | ||
263 | /* Update the whole-file checksum */ | 283 | /* Update the whole-file checksum */ |
264 | filesum = 0; | 284 | filesum = 0; |
265 | for (i=0;i < new_length - 4; i+=4) | 285 | for (i=0;i < (unsigned)len - 4; i+=4) |
266 | filesum += get_uint32le(&buf[i]); | 286 | filesum += get_uint32le(&buf[i]); |
267 | 287 | ||
268 | put_uint32le(buf + new_length - 4, filesum); | 288 | put_uint32le(buf + len - 4, filesum); |
269 | 289 | ||
270 | 290 | ||
271 | /* Write the new firmware */ | 291 | /* Write the new firmware */ |
@@ -276,7 +296,12 @@ int main(int argc, char* argv[]) | |||
276 | return 1; | 296 | return 1; |
277 | } | 297 | } |
278 | 298 | ||
279 | write(fdout, buf, new_length); | 299 | n = write(fdout, buf, len); |
300 | |||
301 | if (n != (unsigned)len) { | ||
302 | fprintf(stderr,"[ERR] Could not write firmware file\n"); | ||
303 | return 1; | ||
304 | } | ||
280 | 305 | ||
281 | close(fdout); | 306 | close(fdout); |
282 | 307 | ||