diff options
author | Cástor Muñoz <cmvidal@gmail.com> | 2016-02-04 23:05:17 +0100 |
---|---|---|
committer | Cástor Muñoz <cmvidal@gmail.com> | 2017-04-14 00:03:42 +0200 |
commit | 346423c040fe4ac31dae7c1afcb1d853cc80635c (patch) | |
tree | bd8dd4c55f083a1162f7019188977213a20dc41c /rbutil/mks5lboot/mkdfu.c | |
parent | eefc7c73e2495decdc6f242515696fe0e3f85609 (diff) | |
download | rockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.tar.gz rockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.zip |
mks5lboot v1.0 - dualboot installer for s5l8702 targetsmks5lboot_1.0
A tool to install/uninstall a bootloader into a s5l8702 based device:
- iPod Classic 6G
- iPod Nano 3G (TODO)
See mks5lboot/README for detailed info.
Change-Id: I451d2aaff34509ebd356e4660647e5222c5d3409
Diffstat (limited to 'rbutil/mks5lboot/mkdfu.c')
-rw-r--r-- | rbutil/mks5lboot/mkdfu.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/rbutil/mks5lboot/mkdfu.c b/rbutil/mks5lboot/mkdfu.c new file mode 100644 index 0000000000..6ac0daf1ac --- /dev/null +++ b/rbutil/mks5lboot/mkdfu.c | |||
@@ -0,0 +1,318 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2015 by Cástor Muñoz | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <stdint.h> | ||
24 | #include <stdbool.h> | ||
25 | |||
26 | #include <sys/types.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <unistd.h> | ||
30 | #include <string.h> | ||
31 | |||
32 | #include "mks5lboot.h" | ||
33 | |||
34 | /* Header for ARM code binaries */ | ||
35 | #include "dualboot.h" | ||
36 | |||
37 | /* Win32 compatibility */ | ||
38 | #ifndef O_BINARY | ||
39 | #define O_BINARY 0 | ||
40 | #endif | ||
41 | |||
42 | /* | ||
43 | * This code is based on emCORE, adds a couple of improvements thanks to | ||
44 | * some extra features in Apple's ROM: | ||
45 | * - Generic Im3Info header valid for all payloads. It is done by moving | ||
46 | * the certificate to a fixed position (before data), and using a 'magic' | ||
47 | * value (0x300) for signature offset. | ||
48 | * - Some integer overflows in ROM make it possible to use the free space | ||
49 | * available in Im3Hdr, resulting a maximum payload size of: | ||
50 | * 128 KiB - 0x50 bytes (IM3INFO_SZ) - 700 bytes (CERT_SIZE). | ||
51 | */ | ||
52 | |||
53 | const struct ipod_models ipod_identity[] = | ||
54 | { | ||
55 | [MODEL_IPOD6G] = { | ||
56 | "Classic 6G", "ipod6g", "ip6g", 71, | ||
57 | dualboot_install_ipod6g, sizeof(dualboot_install_ipod6g), | ||
58 | dualboot_uninstall_ipod6g, sizeof(dualboot_uninstall_ipod6g) }, | ||
59 | }; | ||
60 | |||
61 | struct Im3Info s5l8702hdr = | ||
62 | { | ||
63 | .ident = IM3_IDENT, | ||
64 | .version = IM3_VERSION, | ||
65 | .enc_type = 3, | ||
66 | .u.enc34 = { | ||
67 | .sign_off = "\x00\x03\x00\x00", | ||
68 | .cert_off = "\x50\xF8\xFF\xFF", /* -0x800 + CERT_OFFSET */ | ||
69 | .cert_sz = "\xBA\x02\x00\x00", /* CERT_SIZE */ | ||
70 | }, | ||
71 | .info_sign = "\xC2\x54\x51\x31\xDC\xC0\x84\xA4" | ||
72 | "\x7F\xD1\x45\x08\xE8\xFF\xE8\x1D", | ||
73 | }; | ||
74 | |||
75 | unsigned char s5l8702pwnage[/*CERT_SIZE*/] = | ||
76 | { | ||
77 | "\x30\x82\x00\x7A\x30\x66\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86" | ||
78 | "\xF7\x0D\x01\x01\x05\x05\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55" | ||
79 | "\x04\x03\x13\x00\x30\x1E\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00" | ||
80 | "\x00\x00\x00\x00\x00\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
81 | "\x00\x00\x00\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55\x04\x03\x13" | ||
82 | "\x00\x30\x19\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01" | ||
83 | "\x05\x00\x03\x08\x00\x30\x05\x02\x01\x00\x02\x00\x30\x0D\x06\x09" | ||
84 | "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x01\x00\x30\x82" | ||
85 | "\x00\x7A\x30\x66\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D" | ||
86 | "\x01\x01\x05\x05\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55\x04\x03" | ||
87 | "\x13\x00\x30\x1E\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
88 | "\x00\x00\x00\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | ||
89 | "\x00\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55\x04\x03\x13\x00\x30" | ||
90 | "\x19\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00" | ||
91 | "\x03\x08\x00\x30\x05\x02\x01\x00\x02\x00\x30\x0D\x06\x09\x2A\x86" | ||
92 | "\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x01\x00\x30\x82\x01\xBA" | ||
93 | "\x30\x50\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01" | ||
94 | "\x05\x05\x00\x30\x00\x30\x1E\x17\x0D\x00\x00\x00\x00\x00\x00\x00" | ||
95 | "\x00\x00\x00\x00\x00\x00\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00" | ||
96 | "\x00\x00\x00\x00\x00\x30\x00\x30\x19\x30\x0D\x06\x09\x2A\x86\x48" | ||
97 | "\x86\xF7\x0D\x01\x01\x01\x05\x00\x03\x08\x00\x30\x05\x02\x01\x00" | ||
98 | "\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05" | ||
99 | "\x00\x03\x82\x01\x55" | ||
100 | }; | ||
101 | |||
102 | static uint32_t get_uint32le(unsigned char* p) | ||
103 | { | ||
104 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
105 | } | ||
106 | |||
107 | static uint32_t get_uint32be(unsigned char* p) | ||
108 | { | ||
109 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; | ||
110 | } | ||
111 | |||
112 | static void put_uint32le(unsigned char* p, uint32_t x) | ||
113 | { | ||
114 | p[0] = x & 0xff; | ||
115 | p[1] = (x >> 8) & 0xff; | ||
116 | p[2] = (x >> 16) & 0xff; | ||
117 | p[3] = (x >> 24) & 0xff; | ||
118 | } | ||
119 | |||
120 | #define ERROR(format, ...) \ | ||
121 | do { \ | ||
122 | snprintf(errstr, errstrsize, "[ERR] "format, __VA_ARGS__); \ | ||
123 | goto error; \ | ||
124 | } while(0) | ||
125 | |||
126 | static unsigned char *load_file(char *filename, int *bufsize, | ||
127 | const struct ipod_models** model, | ||
128 | char* errstr, int errstrsize) | ||
129 | { | ||
130 | int fd, i; | ||
131 | struct stat s; | ||
132 | unsigned char header[8]; | ||
133 | unsigned char *buf = NULL; | ||
134 | bool is_rbbl = (model); | ||
135 | |||
136 | fd = open(filename, O_RDONLY|O_BINARY); | ||
137 | if (fd < 0) | ||
138 | ERROR("Could not open %s for reading", filename); | ||
139 | |||
140 | if (fstat(fd, &s) < 0) | ||
141 | ERROR("Checking filesize of input file %s", filename); | ||
142 | *bufsize = s.st_size; | ||
143 | |||
144 | if (is_rbbl) { | ||
145 | /* Read Rockbox header */ | ||
146 | if (read(fd, header, sizeof(header)) != sizeof(header)) | ||
147 | ERROR("Could not read file %s", filename); | ||
148 | *bufsize -= sizeof(header); | ||
149 | |||
150 | for (i = 0; i < NUM_MODELS; i++) | ||
151 | if (memcmp(ipod_identity[i].rb_model_name, header + 4, 4) == 0) | ||
152 | break; | ||
153 | |||
154 | if (i == NUM_MODELS) | ||
155 | ERROR("Model name \"%4.4s\" unknown. " | ||
156 | "Is this really a rockbox bootloader?", header + 4); | ||
157 | |||
158 | *model = &ipod_identity[i]; | ||
159 | } | ||
160 | |||
161 | buf = malloc(*bufsize); | ||
162 | if (buf == NULL) | ||
163 | ERROR("Could not allocate memory for %s", filename); | ||
164 | |||
165 | if (read(fd, buf, *bufsize) != *bufsize) | ||
166 | ERROR("Could not read file %s", filename); | ||
167 | |||
168 | if (is_rbbl) { | ||
169 | /* Check checksum */ | ||
170 | uint32_t sum = (*model)->rb_model_num; | ||
171 | for (i = 0; i < *bufsize; i++) { | ||
172 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
173 | sum += buf[i]; | ||
174 | } | ||
175 | if (sum != get_uint32be(header)) | ||
176 | ERROR("Checksum mismatch in %s", filename); | ||
177 | } | ||
178 | |||
179 | close(fd); | ||
180 | return buf; | ||
181 | |||
182 | error: | ||
183 | if (fd >= 0) | ||
184 | close(fd); | ||
185 | if (buf) | ||
186 | free(buf); | ||
187 | return NULL; | ||
188 | } | ||
189 | |||
190 | unsigned char *mkdfu(int dfu_type, char *dfu_arg, int* dfu_size, | ||
191 | char* errstr, int errstrsize) | ||
192 | { | ||
193 | const struct ipod_models *model = NULL; | ||
194 | unsigned char *dfu_buf = NULL; | ||
195 | unsigned char *f_buf; | ||
196 | int f_size; | ||
197 | uint32_t padded_bl_size; | ||
198 | uint32_t cert_off, cert_sz; | ||
199 | off_t cur_off; | ||
200 | char *dfu_desc; | ||
201 | int i; | ||
202 | |||
203 | switch (dfu_type) | ||
204 | { | ||
205 | case DFU_INST: | ||
206 | case DFU_INST_SINGLE: | ||
207 | f_buf = load_file(dfu_arg, &f_size, &model, errstr, errstrsize); | ||
208 | if (f_buf == NULL) | ||
209 | goto error; | ||
210 | /* IM3 data size should be padded to 16 */ | ||
211 | padded_bl_size = ((f_size + 0xf) & ~0xf); | ||
212 | *dfu_size = BIN_OFFSET + (model->dualboot_install_size + | ||
213 | (IM3HDR_SZ - (int)IM3INFO_SZ) + (int)padded_bl_size); | ||
214 | dfu_desc = (dfu_type == DFU_INST_SINGLE) ? "BL installer (single)" | ||
215 | : "BL installer"; | ||
216 | break; | ||
217 | |||
218 | case DFU_UNINST: | ||
219 | for (i = 0; i < NUM_MODELS; i++) { | ||
220 | if (!strcmp(ipod_identity[i].platform_name, dfu_arg)) { | ||
221 | model = &ipod_identity[i]; | ||
222 | break; | ||
223 | } | ||
224 | } | ||
225 | if (!model) | ||
226 | ERROR("Platform name \"%s\" unknown", dfu_arg); | ||
227 | |||
228 | *dfu_size = BIN_OFFSET + model->dualboot_uninstall_size; | ||
229 | dfu_desc = "BL uninstaller"; | ||
230 | break; | ||
231 | |||
232 | case DFU_RAW: | ||
233 | default: | ||
234 | f_buf = load_file(dfu_arg, &f_size, NULL, errstr, errstrsize); | ||
235 | if (f_buf == NULL) | ||
236 | goto error; | ||
237 | /* IM3 data size should be padded to 16 */ | ||
238 | *dfu_size = BIN_OFFSET + ((f_size + 0xf) & ~0xf); | ||
239 | dfu_desc = "raw binary"; | ||
240 | break; | ||
241 | } | ||
242 | |||
243 | printf("[INFO] Building DFU:\n"); | ||
244 | printf("[INFO] type: %s\n", dfu_desc); | ||
245 | if ((dfu_type == DFU_INST) || (dfu_type == DFU_INST_SINGLE)) | ||
246 | printf("[INFO] BL size: %d\n", f_size); | ||
247 | if (dfu_type == DFU_RAW) | ||
248 | printf("[INFO] BIN size: %d\n", f_size); | ||
249 | printf("[INFO] DFU size: %d\n", *dfu_size); | ||
250 | if (model) { | ||
251 | printf("[INFO] model name: %s\n", model->model_name); | ||
252 | printf("[INFO] platform: %s\n", model->platform_name); | ||
253 | printf("[INFO] RB name: %s\n", model->rb_model_name); | ||
254 | printf("[INFO] RB num: %d\n", model->rb_model_num); | ||
255 | } | ||
256 | |||
257 | if (*dfu_size > DFU_MAXSIZE) | ||
258 | ERROR("DFU image (%d bytes) too big", *dfu_size); | ||
259 | |||
260 | dfu_buf = calloc(*dfu_size, 1); | ||
261 | if (!dfu_buf) | ||
262 | ERROR("Could not allocate %d bytes for DFU image", *dfu_size); | ||
263 | |||
264 | cert_off = get_uint32le(s5l8702hdr.u.enc34.cert_off); | ||
265 | cert_sz = get_uint32le(s5l8702hdr.u.enc34.cert_sz); | ||
266 | |||
267 | memcpy(dfu_buf, &s5l8702hdr, sizeof(s5l8702hdr)); | ||
268 | |||
269 | cur_off = IM3HDR_SZ + cert_off; | ||
270 | |||
271 | memcpy(dfu_buf + cur_off, s5l8702pwnage, sizeof(s5l8702pwnage)); | ||
272 | |||
273 | /* set entry point */ | ||
274 | cur_off += cert_sz - 4; | ||
275 | put_uint32le(dfu_buf + cur_off, DFU_LOADADDR + BIN_OFFSET); | ||
276 | |||
277 | cur_off = BIN_OFFSET; | ||
278 | |||
279 | switch (dfu_type) | ||
280 | { | ||
281 | case DFU_INST: | ||
282 | case DFU_INST_SINGLE: | ||
283 | /* copy the dualboot installer binary */ | ||
284 | memcpy(dfu_buf + cur_off, model->dualboot_install, | ||
285 | model->dualboot_install_size); | ||
286 | /* point to the start of the included IM3 header info */ | ||
287 | cur_off += model->dualboot_install_size - IM3INFO_SZ; | ||
288 | /* set bootloader data size */ | ||
289 | struct Im3Info *bl_info = (struct Im3Info*)(dfu_buf + cur_off); | ||
290 | put_uint32le(bl_info->data_sz, padded_bl_size); | ||
291 | /* use info_sign to pass params to the dualboot installer */ | ||
292 | if (dfu_type == DFU_INST_SINGLE) | ||
293 | bl_info->info_sign[0] = 0x1; | ||
294 | /* add bootloader binary */ | ||
295 | cur_off += IM3HDR_SZ; | ||
296 | memcpy(dfu_buf + cur_off, f_buf, f_size); | ||
297 | break; | ||
298 | |||
299 | case DFU_UNINST: | ||
300 | /* copy the dualboot uninstaller binary */ | ||
301 | memcpy(dfu_buf + cur_off, model->dualboot_uninstall, | ||
302 | model->dualboot_uninstall_size); | ||
303 | break; | ||
304 | |||
305 | case DFU_RAW: | ||
306 | default: | ||
307 | /* add raw binary */ | ||
308 | memcpy(dfu_buf + cur_off, f_buf, f_size); | ||
309 | break; | ||
310 | } | ||
311 | |||
312 | return dfu_buf; | ||
313 | |||
314 | error: | ||
315 | if (dfu_buf) | ||
316 | free(dfu_buf); | ||
317 | return NULL; | ||
318 | } | ||