diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-04-13 16:58:15 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-04-17 20:23:19 +0000 |
commit | b41d53792c4c4e4abd6d810b2765d865775f5104 (patch) | |
tree | ae3e02b961c655cd49b3c13bc895a18547ceeec1 /rbutil/jztool/src/fiiom3k.c | |
parent | 1b8542490da3283dfa0ce0f3363f16eab0609815 (diff) | |
download | rockbox-b41d53792c4c4e4abd6d810b2765d865775f5104.tar.gz rockbox-b41d53792c4c4e4abd6d810b2765d865775f5104.zip |
jztool: New utility for installing a bootloader on FiiO M3K
At present, this is just a command line tool for Linux only.
Integrating this with the Rockbox utility and porting to other
platforms should be straightforward; the README contains more
information.
Change-Id: Ie66fc837a02ab13c878925360cabc9805597548a
Diffstat (limited to 'rbutil/jztool/src/fiiom3k.c')
-rw-r--r-- | rbutil/jztool/src/fiiom3k.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/rbutil/jztool/src/fiiom3k.c b/rbutil/jztool/src/fiiom3k.c new file mode 100644 index 0000000000..a43863c2f7 --- /dev/null +++ b/rbutil/jztool/src/fiiom3k.c | |||
@@ -0,0 +1,283 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
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 | |||
22 | #include "jztool.h" | ||
23 | #include <string.h> | ||
24 | |||
25 | #define IMAGE_ADDR 0 | ||
26 | #define IMAGE_SIZE (128 * 1024) | ||
27 | #define SPL_OFFSET 0 | ||
28 | #define SPL_SIZE (12 * 1024) | ||
29 | #define BOOT_OFFSET (26 * 1024) | ||
30 | #define BOOT_SIZE (102 * 1024) | ||
31 | |||
32 | int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr) | ||
33 | { | ||
34 | jz_buffer* buf = jz_buffer_alloc(IMAGE_SIZE, NULL); | ||
35 | if(!buf) | ||
36 | return JZ_ERR_OUT_OF_MEMORY; | ||
37 | |||
38 | int rc = jz_x1000_read_flash(dev, IMAGE_ADDR, buf->size, buf->data); | ||
39 | if(rc < 0) { | ||
40 | jz_buffer_free(buf); | ||
41 | return rc; | ||
42 | } | ||
43 | |||
44 | *bufptr = buf; | ||
45 | return JZ_SUCCESS; | ||
46 | } | ||
47 | |||
48 | int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf) | ||
49 | { | ||
50 | int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size); | ||
51 | if(rc < 0 || image_size != IMAGE_SIZE) | ||
52 | return JZ_ERR_BAD_FILE_FORMAT; | ||
53 | |||
54 | rc = jz_x1000_write_flash(dev, IMAGE_ADDR, image_size, image_buf); | ||
55 | if(rc < 0) | ||
56 | return rc; | ||
57 | |||
58 | return JZ_SUCCESS; | ||
59 | } | ||
60 | |||
61 | int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size, | ||
62 | const void* spl_buf, size_t spl_size, | ||
63 | const void* boot_buf, size_t boot_size) | ||
64 | { | ||
65 | int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size); | ||
66 | if(rc < 0) { | ||
67 | jz_log(jz, JZ_LOG_ERROR, "Boot image is invalid: %d", rc); | ||
68 | return JZ_ERR_BAD_FILE_FORMAT; | ||
69 | } | ||
70 | |||
71 | rc = jz_identify_x1000_spl(spl_buf, spl_size); | ||
72 | if(rc < 0) { | ||
73 | jz_log(jz, JZ_LOG_ERROR, "SPL image is invalid: %d", rc); | ||
74 | return JZ_ERR_BAD_FILE_FORMAT; | ||
75 | } | ||
76 | |||
77 | if(spl_size > SPL_SIZE) { | ||
78 | jz_log(jz, JZ_LOG_ERROR, "SPL is too big"); | ||
79 | return JZ_ERR_BAD_FILE_FORMAT; | ||
80 | } | ||
81 | |||
82 | rc = jz_identify_scramble_image(boot_buf, boot_size); | ||
83 | if(rc < 0) { | ||
84 | jz_log(jz, JZ_LOG_ERROR, "Bootloader image is invalid: %d", rc); | ||
85 | return JZ_ERR_BAD_FILE_FORMAT; | ||
86 | } | ||
87 | |||
88 | if(boot_size > BOOT_SIZE) { | ||
89 | jz_log(jz, JZ_LOG_ERROR, "Bootloader is too big"); | ||
90 | return JZ_ERR_BAD_FILE_FORMAT; | ||
91 | } | ||
92 | |||
93 | uint8_t* imgdat = (uint8_t*)image_buf; | ||
94 | memset(&imgdat[SPL_OFFSET], 0xff, SPL_SIZE); | ||
95 | memcpy(&imgdat[SPL_OFFSET], spl_buf, spl_size); | ||
96 | memset(&imgdat[BOOT_OFFSET], 0xff, BOOT_SIZE); | ||
97 | memcpy(&imgdat[BOOT_OFFSET], boot_buf, boot_size); | ||
98 | return JZ_SUCCESS; | ||
99 | } | ||
100 | |||
101 | #define IMGBUF 0 | ||
102 | #define SPLBUF 1 | ||
103 | #define BOOTBUF 2 | ||
104 | #define NUMBUFS 3 | ||
105 | #define IMGBUF_NAME "image" | ||
106 | #define SPLBUF_NAME "spl" | ||
107 | #define BOOTBUF_NAME "bootloader" | ||
108 | #define FIIOM3K_INIT_WORKSTATE {0} | ||
109 | |||
110 | struct fiiom3k_workstate { | ||
111 | jz_usbdev* dev; | ||
112 | jz_buffer* bufs[NUMBUFS]; | ||
113 | }; | ||
114 | |||
115 | static void fiiom3k_action_cleanup(struct fiiom3k_workstate* state) | ||
116 | { | ||
117 | for(int i = 0; i < NUMBUFS; ++i) | ||
118 | if(state->bufs[i]) | ||
119 | jz_buffer_free(state->bufs[i]); | ||
120 | |||
121 | if(state->dev) | ||
122 | jz_usb_close(state->dev); | ||
123 | } | ||
124 | |||
125 | static int fiiom3k_action_loadbuf(jz_context* jz, jz_paramlist* pl, | ||
126 | struct fiiom3k_workstate* state, int idx) | ||
127 | { | ||
128 | const char* const paramnames[] = {IMGBUF_NAME, SPLBUF_NAME, BOOTBUF_NAME}; | ||
129 | |||
130 | if(state->bufs[idx]) | ||
131 | return JZ_SUCCESS; | ||
132 | |||
133 | const char* filename = jz_paramlist_get(pl, paramnames[idx]); | ||
134 | if(!filename) { | ||
135 | jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", paramnames[idx]); | ||
136 | return JZ_ERR_OTHER; | ||
137 | } | ||
138 | |||
139 | int rc = jz_buffer_load(&state->bufs[idx], filename); | ||
140 | if(rc < 0) { | ||
141 | jz_log(jz, JZ_LOG_ERROR, "Error reading '%s' file (%d): %s", paramnames[idx], rc, filename); | ||
142 | return rc; | ||
143 | } | ||
144 | |||
145 | return JZ_SUCCESS; | ||
146 | } | ||
147 | |||
148 | static int fiiom3k_action_setup(jz_context* jz, jz_paramlist* pl, | ||
149 | struct fiiom3k_workstate* state) | ||
150 | { | ||
151 | const jz_device_info* info = jz_get_device_info(JZ_DEVICE_FIIOM3K); | ||
152 | if(!info) | ||
153 | return JZ_ERR_OTHER; | ||
154 | |||
155 | int rc = fiiom3k_action_loadbuf(jz, pl, state, SPLBUF); | ||
156 | if(rc < 0) | ||
157 | return rc; | ||
158 | |||
159 | jz_log(jz, JZ_LOG_DETAIL, "Open USB device %04x:%04x", | ||
160 | (unsigned int)info->vendor_id, (unsigned int)info->product_id); | ||
161 | rc = jz_usb_open(jz, &state->dev, info->vendor_id, info->product_id); | ||
162 | if(rc < 0) | ||
163 | return rc; | ||
164 | |||
165 | jz_log(jz, JZ_LOG_DETAIL, "Setup device for flash access"); | ||
166 | jz_buffer* splbuf = state->bufs[SPLBUF]; | ||
167 | return jz_x1000_setup(state->dev, splbuf->size, splbuf->data); | ||
168 | } | ||
169 | |||
170 | int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl) | ||
171 | { | ||
172 | struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; | ||
173 | int rc; | ||
174 | |||
175 | rc = fiiom3k_action_loadbuf(jz, pl, &state, BOOTBUF); | ||
176 | if(rc < 0) | ||
177 | goto error; | ||
178 | |||
179 | rc = fiiom3k_action_setup(jz, pl, &state); | ||
180 | if(rc < 0) | ||
181 | goto error; | ||
182 | |||
183 | jz_log(jz, JZ_LOG_DETAIL, "Reading boot image from device"); | ||
184 | rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); | ||
185 | if(rc < 0) | ||
186 | goto error; | ||
187 | |||
188 | jz_buffer* img_buf = state.bufs[IMGBUF]; | ||
189 | const char* backupfile = jz_paramlist_get(pl, "backup"); | ||
190 | const char* without_backup = jz_paramlist_get(pl, "without-backup"); | ||
191 | if(backupfile) { | ||
192 | jz_log(jz, JZ_LOG_DETAIL, "Backup original boot image to file: %s", backupfile); | ||
193 | rc = jz_buffer_save(img_buf, backupfile); | ||
194 | if(rc < 0) { | ||
195 | jz_log(jz, JZ_LOG_ERROR, "Error saving backup image file (%d): %s", rc, backupfile); | ||
196 | goto error; | ||
197 | } | ||
198 | } else if(!without_backup || strcmp(without_backup, "yes")) { | ||
199 | jz_log(jz, JZ_LOG_ERROR, "No --backup option given and --without-backup yes not specified"); | ||
200 | jz_log(jz, JZ_LOG_ERROR, "Refusing to flash a new bootloader without taking a backup"); | ||
201 | goto error; | ||
202 | } | ||
203 | |||
204 | jz_log(jz, JZ_LOG_DETAIL, "Patching image with new SPL/bootloader"); | ||
205 | jz_buffer* boot_buf = state.bufs[BOOTBUF]; | ||
206 | jz_buffer* spl_buf = state.bufs[SPLBUF]; | ||
207 | rc = jz_fiiom3k_patchboot(jz, img_buf->data, img_buf->size, | ||
208 | spl_buf->data, spl_buf->size, | ||
209 | boot_buf->data, boot_buf->size); | ||
210 | if(rc < 0) { | ||
211 | jz_log(jz, JZ_LOG_ERROR, "Error patching image: %d", rc); | ||
212 | goto error; | ||
213 | } | ||
214 | |||
215 | jz_log(jz, JZ_LOG_DETAIL, "Writing patched image to device"); | ||
216 | rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); | ||
217 | if(rc < 0) | ||
218 | goto error; | ||
219 | |||
220 | rc = JZ_SUCCESS; | ||
221 | |||
222 | error: | ||
223 | fiiom3k_action_cleanup(&state); | ||
224 | return rc; | ||
225 | } | ||
226 | |||
227 | int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl) | ||
228 | { | ||
229 | struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; | ||
230 | int rc; | ||
231 | |||
232 | const char* outfile_path = jz_paramlist_get(pl, IMGBUF_NAME); | ||
233 | if(!outfile_path) { | ||
234 | jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", IMGBUF_NAME); | ||
235 | rc = JZ_ERR_OTHER; | ||
236 | goto error; | ||
237 | } | ||
238 | |||
239 | rc = fiiom3k_action_setup(jz, pl, &state); | ||
240 | if(rc < 0) | ||
241 | goto error; | ||
242 | |||
243 | rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); | ||
244 | if(rc < 0) | ||
245 | goto error; | ||
246 | |||
247 | rc = jz_buffer_save(state.bufs[IMGBUF], outfile_path); | ||
248 | if(rc < 0) { | ||
249 | jz_log(jz, JZ_LOG_ERROR, "Error writing '%s' file (%d): %s", IMGBUF_NAME, rc, outfile_path); | ||
250 | goto error; | ||
251 | } | ||
252 | |||
253 | rc = JZ_SUCCESS; | ||
254 | |||
255 | error: | ||
256 | fiiom3k_action_cleanup(&state); | ||
257 | return rc; | ||
258 | } | ||
259 | |||
260 | int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl) | ||
261 | { | ||
262 | struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; | ||
263 | int rc; | ||
264 | |||
265 | rc = fiiom3k_action_loadbuf(jz, pl, &state, IMGBUF); | ||
266 | if(rc < 0) | ||
267 | goto error; | ||
268 | |||
269 | rc = fiiom3k_action_setup(jz, pl, &state); | ||
270 | if(rc < 0) | ||
271 | goto error; | ||
272 | |||
273 | jz_buffer* img_buf = state.bufs[IMGBUF]; | ||
274 | rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); | ||
275 | if(rc < 0) | ||
276 | goto error; | ||
277 | |||
278 | rc = JZ_SUCCESS; | ||
279 | |||
280 | error: | ||
281 | fiiom3k_action_cleanup(&state); | ||
282 | return rc; | ||
283 | } | ||