summaryrefslogtreecommitdiff
path: root/rbutil/jztool/src/fiiom3k.c
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/jztool/src/fiiom3k.c')
-rw-r--r--rbutil/jztool/src/fiiom3k.c373
1 files changed, 182 insertions, 191 deletions
diff --git a/rbutil/jztool/src/fiiom3k.c b/rbutil/jztool/src/fiiom3k.c
index a43863c2f7..72e25a1220 100644
--- a/rbutil/jztool/src/fiiom3k.c
+++ b/rbutil/jztool/src/fiiom3k.c
@@ -20,264 +20,255 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "jztool.h" 22#include "jztool.h"
23#include "jztool_private.h"
24#include "microtar.h"
25#include "ucl/ucl.h"
26#include <stdbool.h>
23#include <string.h> 27#include <string.h>
24 28
25#define IMAGE_ADDR 0 29static uint32_t xread32(const uint8_t* d)
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
32int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr)
33{ 30{
34 jz_buffer* buf = jz_buffer_alloc(IMAGE_SIZE, NULL); 31 uint32_t r = 0;
35 if(!buf) 32 r |= d[0] << 24;
36 return JZ_ERR_OUT_OF_MEMORY; 33 r |= d[1] << 16;
37 34 r |= d[2] << 8;
38 int rc = jz_x1000_read_flash(dev, IMAGE_ADDR, buf->size, buf->data); 35 r |= d[3] << 0;
39 if(rc < 0) { 36 return r;
40 jz_buffer_free(buf);
41 return rc;
42 }
43
44 *bufptr = buf;
45 return JZ_SUCCESS;
46} 37}
47 38
48int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf) 39/* adapted from firmware/common/ucl_decompress.c */
40static jz_buffer* ucl_unpack(const uint8_t* src, uint32_t src_len,
41 uint32_t* dst_len)
49{ 42{
50 int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size); 43 static const uint8_t magic[8] =
51 if(rc < 0 || image_size != IMAGE_SIZE) 44 {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a};
52 return JZ_ERR_BAD_FILE_FORMAT;
53 45
54 rc = jz_x1000_write_flash(dev, IMAGE_ADDR, image_size, image_buf); 46 jz_buffer* buffer = NULL;
55 if(rc < 0)
56 return rc;
57 47
58 return JZ_SUCCESS; 48 /* make sure there are enough bytes for the header */
59} 49 if(src_len < 18)
50 goto error;
60 51
61int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size, 52 /* avoid memcmp for reasons of code size */
62 const void* spl_buf, size_t spl_size, 53 for(size_t i = 0; i < sizeof(magic); ++i)
63 const void* boot_buf, size_t boot_size) 54 if(src[i] != magic[i])
64{ 55 goto error;
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 56
71 rc = jz_identify_x1000_spl(spl_buf, spl_size); 57 /* read the other header fields */
72 if(rc < 0) { 58 /* uint32_t flags = xread32(&src[8]); */
73 jz_log(jz, JZ_LOG_ERROR, "SPL image is invalid: %d", rc); 59 uint8_t method = src[12];
74 return JZ_ERR_BAD_FILE_FORMAT; 60 /* uint8_t level = src[13]; */
75 } 61 uint32_t block_size = xread32(&src[14]);
76 62
77 if(spl_size > SPL_SIZE) { 63 /* check supported compression method */
78 jz_log(jz, JZ_LOG_ERROR, "SPL is too big"); 64 if(method != 0x2e)
79 return JZ_ERR_BAD_FILE_FORMAT; 65 goto error;
80 }
81 66
82 rc = jz_identify_scramble_image(boot_buf, boot_size); 67 /* validate */
83 if(rc < 0) { 68 if(block_size < 1024 || block_size > 8*1024*1024)
84 jz_log(jz, JZ_LOG_ERROR, "Bootloader image is invalid: %d", rc); 69 goto error;
85 return JZ_ERR_BAD_FILE_FORMAT;
86 }
87 70
88 if(boot_size > BOOT_SIZE) { 71 src += 18;
89 jz_log(jz, JZ_LOG_ERROR, "Bootloader is too big"); 72 src_len -= 18;
90 return JZ_ERR_BAD_FILE_FORMAT; 73
91 } 74 /* Calculate amount of space that we might need & allocate a buffer:
75 * - subtract 4 to account for end of file marker
76 * - each block is block_size bytes + 8 bytes of header
77 * - add one to nr_blocks to account for case where file size < block size
78 * - total size = max uncompressed size of block * nr_blocks
79 */
80 uint32_t nr_blocks = (src_len - 4) / (8 + block_size) + 1;
81 uint32_t max_size = nr_blocks * (block_size + block_size/8 + 256);
82 buffer = jz_buffer_alloc(max_size, NULL);
83 if(!buffer)
84 goto error;
92 85
93 uint8_t* imgdat = (uint8_t*)image_buf; 86 /* perform the decompression */
94 memset(&imgdat[SPL_OFFSET], 0xff, SPL_SIZE); 87 uint32_t dst_ilen = buffer->size;
95 memcpy(&imgdat[SPL_OFFSET], spl_buf, spl_size); 88 uint8_t* dst = buffer->data;
96 memset(&imgdat[BOOT_OFFSET], 0xff, BOOT_SIZE); 89 while(1) {
97 memcpy(&imgdat[BOOT_OFFSET], boot_buf, boot_size); 90 if(src_len < 4)
98 return JZ_SUCCESS; 91 goto error;
99}
100 92
101#define IMGBUF 0 93 uint32_t out_len = xread32(src); src += 4, src_len -= 4;
102#define SPLBUF 1 94 if(out_len == 0)
103#define BOOTBUF 2 95 break;
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
110struct fiiom3k_workstate {
111 jz_usbdev* dev;
112 jz_buffer* bufs[NUMBUFS];
113};
114
115static 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 96
121 if(state->dev) 97 if(src_len < 4)
122 jz_usb_close(state->dev); 98 goto error;
123}
124 99
125static int fiiom3k_action_loadbuf(jz_context* jz, jz_paramlist* pl, 100 uint32_t in_len = xread32(src); src += 4, src_len -= 4;
126 struct fiiom3k_workstate* state, int idx) 101 if(in_len > block_size || out_len > block_size ||
127{ 102 in_len == 0 || in_len > out_len)
128 const char* const paramnames[] = {IMGBUF_NAME, SPLBUF_NAME, BOOTBUF_NAME}; 103 goto error;
129 104
130 if(state->bufs[idx]) 105 if(src_len < in_len)
131 return JZ_SUCCESS; 106 goto error;
132 107
133 const char* filename = jz_paramlist_get(pl, paramnames[idx]); 108 if(in_len < out_len) {
134 if(!filename) { 109 uint32_t actual_out_len = dst_ilen;
135 jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", paramnames[idx]); 110 int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL);
136 return JZ_ERR_OTHER; 111 if(rc != UCL_E_OK)
137 } 112 goto error;
113 if(actual_out_len != out_len)
114 goto error;
115 } else {
116 for(size_t i = 0; i < in_len; ++i)
117 dst[i] = src[i];
118 }
138 119
139 int rc = jz_buffer_load(&state->bufs[idx], filename); 120 src += in_len;
140 if(rc < 0) { 121 src_len -= in_len;
141 jz_log(jz, JZ_LOG_ERROR, "Error reading '%s' file (%d): %s", paramnames[idx], rc, filename); 122 dst += out_len;
142 return rc; 123 dst_ilen -= out_len;
143 } 124 }
144 125
145 return JZ_SUCCESS; 126 /* subtract leftover number of bytes to get size of compressed output */
127 *dst_len = buffer->size - dst_ilen;
128 return buffer;
129
130 error:
131 jz_buffer_free(buffer);
132 return NULL;
146} 133}
147 134
148static int fiiom3k_action_setup(jz_context* jz, jz_paramlist* pl, 135static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf)
149 struct fiiom3k_workstate* state)
150{ 136{
151 const jz_device_info* info = jz_get_device_info(JZ_DEVICE_FIIOM3K); 137 int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data);
152 if(!info) 138 if(rc < 0)
153 return JZ_ERR_OTHER; 139 return rc;
154 140
155 int rc = fiiom3k_action_loadbuf(jz, pl, state, SPLBUF); 141 return jz_usb_start1(dev, 0xf4001800);
142}
143
144static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf)
145{
146 int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data);
156 if(rc < 0) 147 if(rc < 0)
157 return rc; 148 return rc;
158 149
159 jz_log(jz, JZ_LOG_DETAIL, "Open USB device %04x:%04x", 150 rc = jz_usb_flush_caches(dev);
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) 151 if(rc < 0)
163 return rc; 152 return rc;
164 153
165 jz_log(jz, JZ_LOG_DETAIL, "Setup device for flash access"); 154 return jz_usb_start2(dev, 0x80004000);
166 jz_buffer* splbuf = state->bufs[SPLBUF];
167 return jz_x1000_setup(state->dev, splbuf->size, splbuf->data);
168} 155}
169 156
170int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl) 157static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file,
158 bool decompress, jz_buffer** buf)
171{ 159{
172 struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; 160 jz_buffer* buffer = NULL;
161 mtar_header_t h;
173 int rc; 162 int rc;
174 163
175 rc = fiiom3k_action_loadbuf(jz, pl, &state, BOOTBUF); 164 rc = mtar_find(tar, file, &h);
176 if(rc < 0) 165 if(rc != MTAR_ESUCCESS) {
177 goto error; 166 jz_log(jz, JZ_LOG_ERROR, "can't find %s in boot file, tar error %d", file, rc);
167 return JZ_ERR_BAD_FILE_FORMAT;
168 }
178 169
179 rc = fiiom3k_action_setup(jz, pl, &state); 170 buffer = jz_buffer_alloc(h.size, NULL);
180 if(rc < 0) 171 if(!buffer)
181 goto error; 172 return JZ_ERR_OUT_OF_MEMORY;
182 173
183 jz_log(jz, JZ_LOG_DETAIL, "Reading boot image from device"); 174 rc = mtar_read_data(tar, buffer->data, buffer->size);
184 rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); 175 if(rc != MTAR_ESUCCESS) {
185 if(rc < 0) 176 jz_buffer_free(buffer);
186 goto error; 177 jz_log(jz, JZ_LOG_ERROR, "can't read %s in boot file, tar error %d", file, rc);
178 return JZ_ERR_BAD_FILE_FORMAT;
179 }
187 180
188 jz_buffer* img_buf = state.bufs[IMGBUF]; 181 if(decompress) {
189 const char* backupfile = jz_paramlist_get(pl, "backup"); 182 uint32_t dst_len;
190 const char* without_backup = jz_paramlist_get(pl, "without-backup"); 183 jz_buffer* nbuf = ucl_unpack(buffer->data, buffer->size, &dst_len);
191 if(backupfile) { 184 jz_buffer_free(buffer);
192 jz_log(jz, JZ_LOG_DETAIL, "Backup original boot image to file: %s", backupfile); 185 if(!nbuf) {
193 rc = jz_buffer_save(img_buf, backupfile); 186 jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file);
194 if(rc < 0) { 187 return JZ_ERR_BAD_FILE_FORMAT;
195 jz_log(jz, JZ_LOG_ERROR, "Error saving backup image file (%d): %s", rc, backupfile);
196 goto error;
197 } 188 }
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 189
204 jz_log(jz, JZ_LOG_DETAIL, "Patching image with new SPL/bootloader"); 190 /* for simplicity just forget original size of buffer */
205 jz_buffer* boot_buf = state.bufs[BOOTBUF]; 191 nbuf->size = dst_len;
206 jz_buffer* spl_buf = state.bufs[SPLBUF]; 192 buffer = nbuf;
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 } 193 }
214 194
215 jz_log(jz, JZ_LOG_DETAIL, "Writing patched image to device"); 195 *buf = buffer;
216 rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); 196 return JZ_SUCCESS;
217 if(rc < 0) 197}
218 goto error;
219 198
220 rc = JZ_SUCCESS; 199static int m3k_show_version(jz_context* jz, jz_buffer* info_file)
200{
201 /* Extract the version string and log it for informational purposes */
202 char* boot_version = (char*)info_file->data;
203 char* endpos = memchr(boot_version, '\n', info_file->size);
204 if(!endpos) {
205 jz_log(jz, JZ_LOG_ERROR, "invalid metadata in boot file");
206 return JZ_ERR_BAD_FILE_FORMAT;
207 }
221 208
222 error: 209 *endpos = 0;
223 fiiom3k_action_cleanup(&state); 210 jz_log(jz, JZ_LOG_NOTICE, "Rockbox bootloader version: %s", boot_version);
224 return rc; 211 return JZ_SUCCESS;
225} 212}
226 213
227int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl) 214/** \brief Load the Rockbox bootloader on the FiiO M3K
215 * \param dev USB device freshly returned by jz_usb_open()
216 * \param filename Path to the "bootloader.m3k" file
217 * \return either JZ_SUCCESS or an error code
218 */
219int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename)
228{ 220{
229 struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; 221 jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL;
222 mtar_t tar;
230 int rc; 223 int rc;
231 224
232 const char* outfile_path = jz_paramlist_get(pl, IMGBUF_NAME); 225 rc = mtar_open(&tar, filename, "r");
233 if(!outfile_path) { 226 if(rc != MTAR_ESUCCESS) {
234 jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", IMGBUF_NAME); 227 jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc);
235 rc = JZ_ERR_OTHER; 228 return JZ_ERR_OPEN_FILE;
236 goto error;
237 } 229 }
238 230
239 rc = fiiom3k_action_setup(jz, pl, &state); 231 /* Extract all necessary files */
240 if(rc < 0) 232 rc = m3k_get_file(dev->jz, &tar, "spl.m3k", false, &spl);
233 if(rc != JZ_SUCCESS)
241 goto error; 234 goto error;
242 235
243 rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); 236 rc = m3k_get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader);
244 if(rc < 0) 237 if(rc != JZ_SUCCESS)
245 goto error; 238 goto error;
246 239
247 rc = jz_buffer_save(state.bufs[IMGBUF], outfile_path); 240 rc = m3k_get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file);
248 if(rc < 0) { 241 if(rc != JZ_SUCCESS)
249 jz_log(jz, JZ_LOG_ERROR, "Error writing '%s' file (%d): %s", IMGBUF_NAME, rc, outfile_path);
250 goto error; 242 goto error;
251 }
252
253 rc = JZ_SUCCESS;
254 243
255 error: 244 /* Display the version string */
256 fiiom3k_action_cleanup(&state); 245 rc = m3k_show_version(dev->jz, info_file);
257 return rc; 246 if(rc != JZ_SUCCESS)
258}
259
260int 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; 247 goto error;
268 248
269 rc = fiiom3k_action_setup(jz, pl, &state); 249 /* Stage1 boot of SPL to set up hardware */
270 if(rc < 0) 250 rc = m3k_stage1(dev, spl);
251 if(rc != JZ_SUCCESS)
271 goto error; 252 goto error;
272 253
273 jz_buffer* img_buf = state.bufs[IMGBUF]; 254 /* Need a bit of time for SPL to handle init */
274 rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); 255 jz_sleepms(500);
275 if(rc < 0) 256
257 /* Stage2 boot into the bootloader's recovery menu
258 * User has to take manual action from there */
259 rc = m3k_stage2(dev, bootloader);
260 if(rc != JZ_SUCCESS)
276 goto error; 261 goto error;
277 262
278 rc = JZ_SUCCESS; 263 rc = JZ_SUCCESS;
279 264
280 error: 265 error:
281 fiiom3k_action_cleanup(&state); 266 if(spl)
267 jz_buffer_free(spl);
268 if(bootloader)
269 jz_buffer_free(bootloader);
270 if(info_file)
271 jz_buffer_free(info_file);
272 mtar_close(&tar);
282 return rc; 273 return rc;
283} 274}