diff options
Diffstat (limited to 'utils/mknwzboot/mknwzboot.c')
-rw-r--r-- | utils/mknwzboot/mknwzboot.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/utils/mknwzboot/mknwzboot.c b/utils/mknwzboot/mknwzboot.c new file mode 100644 index 0000000000..22885674d8 --- /dev/null +++ b/utils/mknwzboot/mknwzboot.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by Amaury Pouly | ||
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 <stdarg.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | #include "mknwzboot.h" | ||
27 | #include "upg.h" | ||
28 | |||
29 | #include "install_script.h" | ||
30 | #include "uninstall_script.h" | ||
31 | |||
32 | struct nwz_model_desc_t | ||
33 | { | ||
34 | /* Descriptive name of this model */ | ||
35 | const char *model_name; | ||
36 | /* Model name used in the Rockbox header in ".sansa" files - these match the | ||
37 | -add parameter to the "scramble" tool */ | ||
38 | const char *rb_model_name; | ||
39 | /* Model number used to initialise the checksum in the Rockbox header in | ||
40 | ".sansa" files - these are the same as MODEL_NUMBER in config-target.h */ | ||
41 | const int rb_model_num; | ||
42 | /* Codename used in upgtool */ | ||
43 | const char *codename; | ||
44 | }; | ||
45 | |||
46 | static const struct nwz_model_desc_t nwz_models[] = | ||
47 | { | ||
48 | { "Sony NWZ-E350 Series", "e350", 109, "nwz-e350" }, | ||
49 | { "Sony NWZ-E450 Series", "e450", 100, "nwz-e450" }, | ||
50 | { "Sony NWZ-E460 Series", "e460", 101, "nwz-e460" }, | ||
51 | { "Sony NWZ-E470 Series", "e470", 103, "nwz-e470" }, | ||
52 | { "Sony NWZ-E580 Series", "e580", 102, "nwz-e580" }, | ||
53 | { "Sony NWZ-A10 Series", "a10", 104, "nwz-a10" }, | ||
54 | { "Sony NW-A20 Series", "a20", 106, "nw-a20" }, | ||
55 | { "Sony NWZ-A860 Series", "a860", 107, "nwz-a860" }, | ||
56 | { "Sony NWZ-S750 Series", "s750", 108, "nwz-s750" }, | ||
57 | }; | ||
58 | |||
59 | #define NR_NWZ_MODELS (sizeof(nwz_models) / sizeof(nwz_models[0])) | ||
60 | |||
61 | void dump_nwz_dev_info(const char *prefix) | ||
62 | { | ||
63 | printf("%smknwzboot models:\n", prefix); | ||
64 | for(int i = 0; i < NR_NWZ_MODELS; i++) | ||
65 | { | ||
66 | printf("%s %s: rb_model=%s rb_num=%d codename=%s\n", prefix, | ||
67 | nwz_models[i].model_name, nwz_models[i].rb_model_name, | ||
68 | nwz_models[i].rb_model_num, nwz_models[i].codename); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /* read a file to a buffer */ | ||
73 | static void *read_file(const char *file, size_t *size) | ||
74 | { | ||
75 | FILE *f = fopen(file, "rb"); | ||
76 | if(f == NULL) | ||
77 | { | ||
78 | printf("[ERR] Cannot open file '%s' for reading: %m\n", file); | ||
79 | return NULL; | ||
80 | } | ||
81 | fseek(f, 0, SEEK_END); | ||
82 | *size = ftell(f); | ||
83 | fseek(f, 0, SEEK_SET); | ||
84 | void *buffer = malloc(*size); | ||
85 | if(fread(buffer, *size, 1, f) != 1) | ||
86 | { | ||
87 | free(buffer); | ||
88 | fclose(f); | ||
89 | printf("[ERR] Cannot read file '%s': %m\n", file); | ||
90 | return NULL; | ||
91 | } | ||
92 | fclose(f); | ||
93 | return buffer; | ||
94 | } | ||
95 | |||
96 | /* write a file from a buffer */ | ||
97 | static bool write_file(const char *file, void *buffer, size_t size) | ||
98 | { | ||
99 | FILE *f = fopen(file, "wb"); | ||
100 | if(f == NULL) | ||
101 | { | ||
102 | printf("[ERR] Cannot open file '%s' for writing: %m\n", file); | ||
103 | return false; | ||
104 | } | ||
105 | if(fwrite(buffer, size, 1, f) != 1) | ||
106 | { | ||
107 | fclose(f); | ||
108 | printf("[ERR] Cannot write file '%s': %m\n", file); | ||
109 | return false; | ||
110 | } | ||
111 | fclose(f); | ||
112 | return true; | ||
113 | } | ||
114 | |||
115 | static unsigned int be2int(unsigned char* buf) | ||
116 | { | ||
117 | return ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]); | ||
118 | } | ||
119 | |||
120 | static int find_model(uint8_t *boot, size_t boot_size) | ||
121 | { | ||
122 | if(boot_size < 8) | ||
123 | { | ||
124 | printf("[ERR] Boot file is too small to be valid\n"); | ||
125 | return -1; | ||
126 | } | ||
127 | /* find model by comparing magic scramble value */ | ||
128 | int model = 0; | ||
129 | for(; model < NR_NWZ_MODELS; model++) | ||
130 | if(memcmp(boot + 4, nwz_models[model].rb_model_name, 4) == 0) | ||
131 | break; | ||
132 | if(model == NR_NWZ_MODELS) | ||
133 | { | ||
134 | printf("[ERR] This player is not supported: %.4s\n", boot + 4); | ||
135 | return -1; | ||
136 | } | ||
137 | printf("[INFO] Bootloader file for %s\n", nwz_models[model].model_name); | ||
138 | /* verify checksum */ | ||
139 | uint32_t sum = nwz_models[model].rb_model_num; | ||
140 | for(int i = 8; i < boot_size; i++) | ||
141 | sum += boot[i]; | ||
142 | if(sum != be2int(boot)) | ||
143 | { | ||
144 | printf("[ERR] Checksum mismatch\n"); | ||
145 | return -1; | ||
146 | } | ||
147 | return model; | ||
148 | } | ||
149 | |||
150 | static int find_model2(const char *model_str) | ||
151 | { | ||
152 | /* since it can be confusing for the user, we accept both rbmodel and codename */ | ||
153 | /* find model by comparing magic scramble value */ | ||
154 | int model = 0; | ||
155 | for(; model < NR_NWZ_MODELS; model++) | ||
156 | if(strcmp(nwz_models[model].rb_model_name, model_str) == 0 || | ||
157 | strcmp(nwz_models[model].codename, model_str) == 0) | ||
158 | break; | ||
159 | if(model == NR_NWZ_MODELS) | ||
160 | { | ||
161 | printf("[ERR] Unknown model: %s\n", model_str); | ||
162 | return -1; | ||
163 | } | ||
164 | printf("[INFO] Bootloader file for %s\n", nwz_models[model].model_name); | ||
165 | return model; | ||
166 | } | ||
167 | |||
168 | static bool get_model_keysig(int model, char key[NWZ_KEY_SIZE], char sig[NWZ_SIG_SIZE]) | ||
169 | { | ||
170 | const char *codename = nwz_models[model].codename; | ||
171 | for(int i = 0; g_model_list[i].model; i++) | ||
172 | if(strcmp(g_model_list[i].model, codename) == 0) | ||
173 | { | ||
174 | if(decrypt_keysig(g_model_list[i].kas, key, sig) == 0) | ||
175 | return true; | ||
176 | printf("[ERR] Cannot decrypt kas '%s'\n", g_model_list[i].kas); | ||
177 | return false; | ||
178 | } | ||
179 | printf("[ERR] Codename '%s' matches to entry in upg database\n", codename); | ||
180 | return false; | ||
181 | } | ||
182 | |||
183 | void nwz_printf(void *u, bool err, color_t c, const char *f, ...) | ||
184 | { | ||
185 | (void)err; | ||
186 | (void)c; | ||
187 | bool *debug = u; | ||
188 | va_list args; | ||
189 | va_start(args, f); | ||
190 | if(err || *debug) | ||
191 | vprintf(f, args); | ||
192 | va_end(args); | ||
193 | } | ||
194 | |||
195 | static void *memdup(void *data, size_t size) | ||
196 | { | ||
197 | void *buf = malloc(size); | ||
198 | memcpy(buf, data, size); | ||
199 | return buf; | ||
200 | } | ||
201 | |||
202 | int mknwzboot(const char *bootfile, const char *outfile, bool debug) | ||
203 | { | ||
204 | size_t boot_size; | ||
205 | uint8_t *boot = read_file(bootfile, &boot_size); | ||
206 | if(boot == NULL) | ||
207 | { | ||
208 | printf("[ERR] Cannot open boot file\n"); | ||
209 | return 1; | ||
210 | } | ||
211 | /* check that it is a valid scrambled file */ | ||
212 | int model = find_model(boot, boot_size); | ||
213 | if(model < 0) | ||
214 | { | ||
215 | free(boot); | ||
216 | printf("[ERR] Invalid boot file\n"); | ||
217 | return 2; | ||
218 | } | ||
219 | /* find keys */ | ||
220 | char key[NWZ_KEY_SIZE]; | ||
221 | char sig[NWZ_SIG_SIZE]; | ||
222 | if(!get_model_keysig(model, key, sig)) | ||
223 | { | ||
224 | printf("[ERR][INTERNAL] Cannot get keys for model\n"); | ||
225 | return 3; | ||
226 | } | ||
227 | /* create the upg file */ | ||
228 | struct upg_file_t *upg = upg_new(); | ||
229 | /* first file is the install script: we have to copy data because upg_free() | ||
230 | * will free it */ | ||
231 | upg_append(upg, memdup(install_script, LEN_install_script), LEN_install_script); | ||
232 | /* second file is the bootloader content (expected to be a tar file): we have | ||
233 | * to copy data because upg_free() will free it */ | ||
234 | upg_append(upg, memdup(boot + 8, boot_size - 8), boot_size - 8); | ||
235 | free(boot); | ||
236 | /* write file to buffer */ | ||
237 | size_t upg_size; | ||
238 | void *upg_buf = upg_write_memory(upg, key, sig, &upg_size, &debug, nwz_printf); | ||
239 | upg_free(upg); | ||
240 | if(upg_buf == NULL) | ||
241 | { | ||
242 | printf("[ERR] Cannot create UPG file\n"); | ||
243 | return 4; | ||
244 | } | ||
245 | if(!write_file(outfile, upg_buf, upg_size)) | ||
246 | { | ||
247 | free(upg_buf); | ||
248 | printf("[ERR] Cannpt write UPG file\n"); | ||
249 | return 5; | ||
250 | } | ||
251 | free(upg_buf); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | int mknwzboot_uninst(const char *model_string, const char *outfile, bool debug) | ||
256 | { | ||
257 | /* check that it is a valid scrambled file */ | ||
258 | int model = find_model2(model_string); | ||
259 | if(model < 0) | ||
260 | { | ||
261 | printf("[ERR] Invalid model\n"); | ||
262 | return 2; | ||
263 | } | ||
264 | /* find keys */ | ||
265 | char key[NWZ_KEY_SIZE]; | ||
266 | char sig[NWZ_SIG_SIZE]; | ||
267 | if(!get_model_keysig(model, key, sig)) | ||
268 | { | ||
269 | printf("[ERR][INTERNAL] Cannot get keys for model\n"); | ||
270 | return 3; | ||
271 | } | ||
272 | /* create the upg file */ | ||
273 | struct upg_file_t *upg = upg_new(); | ||
274 | /* first file is the uninstall script: we have to copy data because upg_free() | ||
275 | * will free it */ | ||
276 | upg_append(upg, memdup(uninstall_script, LEN_uninstall_script), LEN_uninstall_script); | ||
277 | /* write file to buffer */ | ||
278 | size_t upg_size; | ||
279 | void *upg_buf = upg_write_memory(upg, key, sig, &upg_size, &debug, nwz_printf); | ||
280 | upg_free(upg); | ||
281 | if(upg_buf == NULL) | ||
282 | { | ||
283 | printf("[ERR] Cannot create UPG file\n"); | ||
284 | return 4; | ||
285 | } | ||
286 | if(!write_file(outfile, upg_buf, upg_size)) | ||
287 | { | ||
288 | free(upg_buf); | ||
289 | printf("[ERR] Cannpt write UPG file\n"); | ||
290 | return 5; | ||
291 | } | ||
292 | free(upg_buf); | ||
293 | return 0; | ||
294 | } | ||