diff options
Diffstat (limited to 'rbutil/jztool/src')
-rw-r--r-- | rbutil/jztool/src/device_info.c | 72 | ||||
-rw-r--r-- | rbutil/jztool/src/identify_file.c | 37 | ||||
-rw-r--r-- | rbutil/jztool/src/ucl_unpack.c | 128 | ||||
-rw-r--r-- | rbutil/jztool/src/usb.c | 40 | ||||
-rw-r--r-- | rbutil/jztool/src/x1000.c (renamed from rbutil/jztool/src/fiiom3k.c) | 149 |
5 files changed, 266 insertions, 160 deletions
diff --git a/rbutil/jztool/src/device_info.c b/rbutil/jztool/src/device_info.c index 5ce3899262..cc431959ca 100644 --- a/rbutil/jztool/src/device_info.c +++ b/rbutil/jztool/src/device_info.c | |||
@@ -22,39 +22,58 @@ | |||
22 | #include "jztool.h" | 22 | #include "jztool.h" |
23 | #include <string.h> | 23 | #include <string.h> |
24 | 24 | ||
25 | static const jz_device_info infotable[] = { | 25 | static const jz_device_info infotable[JZ_NUM_DEVICES] = { |
26 | { | 26 | [JZ_DEVICE_FIIOM3K] = { |
27 | .name = "fiiom3k", | 27 | .name = "fiiom3k", |
28 | .file_ext = "m3k", | ||
28 | .description = "FiiO M3K", | 29 | .description = "FiiO M3K", |
29 | .device_type = JZ_DEVICE_FIIOM3K, | 30 | .device_type = JZ_DEVICE_FIIOM3K, |
30 | .cpu_type = JZ_CPU_X1000, | 31 | .cpu_type = JZ_CPU_X1000, |
32 | .vendor_id = 0x2972, | ||
33 | .product_id = 0x0003, | ||
34 | }, | ||
35 | [JZ_DEVICE_SHANLINGQ1] = { | ||
36 | .name = "shanlingq1", | ||
37 | .file_ext = "q1", | ||
38 | .description = "Shanling Q1", | ||
39 | .device_type = JZ_DEVICE_SHANLINGQ1, | ||
40 | .cpu_type = JZ_CPU_X1000, | ||
41 | .vendor_id = 0x0525, | ||
42 | .product_id = 0xa4a5, | ||
43 | }, | ||
44 | [JZ_DEVICE_EROSQ] = { | ||
45 | .name = "erosq", | ||
46 | .file_ext = "erosq", | ||
47 | .description = "AIGO Eros Q", | ||
48 | .device_type = JZ_DEVICE_EROSQ, | ||
49 | .cpu_type = JZ_CPU_X1000, | ||
50 | .vendor_id = 0xc502, | ||
51 | .product_id = 0x0023, | ||
52 | }, | ||
53 | }; | ||
54 | |||
55 | static const jz_cpu_info cputable[JZ_NUM_CPUS] = { | ||
56 | [JZ_CPU_X1000] = { | ||
57 | .info_str = "X1000_v1", | ||
31 | .vendor_id = 0xa108, | 58 | .vendor_id = 0xa108, |
32 | .product_id = 0x1000, | 59 | .product_id = 0x1000, |
60 | .stage1_load_addr = 0xf4001000, | ||
61 | .stage1_exec_addr = 0xf4001800, | ||
62 | .stage2_load_addr = 0x80004000, | ||
63 | .stage2_exec_addr = 0x80004000, | ||
33 | }, | 64 | }, |
34 | }; | 65 | }; |
35 | 66 | ||
36 | static const int infotable_size = sizeof(infotable)/sizeof(struct jz_device_info); | ||
37 | |||
38 | /** \brief Get the number of entries in the device info list */ | ||
39 | int jz_get_num_device_info(void) | ||
40 | { | ||
41 | return infotable_size; | ||
42 | } | ||
43 | |||
44 | /** \brief Lookup info for a device by type, returns NULL if not found. */ | 67 | /** \brief Lookup info for a device by type, returns NULL if not found. */ |
45 | const jz_device_info* jz_get_device_info(jz_device_type type) | 68 | const jz_device_info* jz_get_device_info(jz_device_type type) |
46 | { | 69 | { |
47 | for(int i = 0; i < infotable_size; ++i) | 70 | return jz_get_device_info_indexed(type); |
48 | if(infotable[i].device_type == type) | ||
49 | return &infotable[i]; | ||
50 | |||
51 | return NULL; | ||
52 | } | 71 | } |
53 | 72 | ||
54 | /** \brief Lookup info for a device by name, returns NULL if not found. */ | 73 | /** \brief Lookup info for a device by name, returns NULL if not found. */ |
55 | const jz_device_info* jz_get_device_info_named(const char* name) | 74 | const jz_device_info* jz_get_device_info_named(const char* name) |
56 | { | 75 | { |
57 | for(int i = 0; i < infotable_size; ++i) | 76 | for(int i = 0; i < JZ_NUM_DEVICES; ++i) |
58 | if(!strcmp(infotable[i].name, name)) | 77 | if(!strcmp(infotable[i].name, name)) |
59 | return &infotable[i]; | 78 | return &infotable[i]; |
60 | 79 | ||
@@ -64,8 +83,27 @@ const jz_device_info* jz_get_device_info_named(const char* name) | |||
64 | /** \brief Get a device info entry by index, returns NULL if out of range. */ | 83 | /** \brief Get a device info entry by index, returns NULL if out of range. */ |
65 | const jz_device_info* jz_get_device_info_indexed(int index) | 84 | const jz_device_info* jz_get_device_info_indexed(int index) |
66 | { | 85 | { |
67 | if(index < infotable_size) | 86 | if(index < JZ_NUM_DEVICES) |
68 | return &infotable[index]; | 87 | return &infotable[index]; |
69 | else | 88 | else |
70 | return NULL; | 89 | return NULL; |
71 | } | 90 | } |
91 | |||
92 | /** \brief Lookup info for a CPU, returns NULL if not found. */ | ||
93 | const jz_cpu_info* jz_get_cpu_info(jz_cpu_type type) | ||
94 | { | ||
95 | if(type < JZ_NUM_CPUS) | ||
96 | return &cputable[type]; | ||
97 | else | ||
98 | return NULL; | ||
99 | } | ||
100 | |||
101 | /** \brief Lookup info for a CPU by info string, returns NULL if not found. */ | ||
102 | const jz_cpu_info* jz_get_cpu_info_named(const char* info_str) | ||
103 | { | ||
104 | for(int i = 0; i < JZ_NUM_CPUS; ++i) | ||
105 | if(!strcmp(cputable[i].info_str, info_str)) | ||
106 | return &cputable[i]; | ||
107 | |||
108 | return NULL; | ||
109 | } | ||
diff --git a/rbutil/jztool/src/identify_file.c b/rbutil/jztool/src/identify_file.c index e735075687..e475d98a3b 100644 --- a/rbutil/jztool/src/identify_file.c +++ b/rbutil/jztool/src/identify_file.c | |||
@@ -118,43 +118,52 @@ int jz_identify_x1000_spl(const void* data, size_t len) | |||
118 | static const struct scramble_model_info { | 118 | static const struct scramble_model_info { |
119 | const char* name; | 119 | const char* name; |
120 | int model_num; | 120 | int model_num; |
121 | size_t offset_crc; | ||
122 | size_t offset_name; | ||
123 | size_t offset_data; | ||
121 | } scramble_models[] = { | 124 | } scramble_models[] = { |
122 | {"fiio", 114}, | 125 | {"fiio", 114, 0, 4, 8}, |
123 | {NULL, 0}, | 126 | {"shq1", 115, 0, 4, 8}, |
127 | {"eros", 116, 0, 4, 8}, | ||
128 | {NULL, 0, 0, 0, 0}, | ||
124 | }; | 129 | }; |
125 | 130 | ||
126 | /** \brief Identify a file as a Rockbox `scramble` image | 131 | /** \brief Identify a file as a Rockbox `scramble` image |
127 | * \param data File data buffer | 132 | * \param data File data buffer |
128 | * \param len Length of file | 133 | * \param len Length of file |
129 | * \return JZ_SUCCESS if file looks correct, or one of the following errors | 134 | * \return JZ_SUCCESS if file looks correct, or one of the following errors |
130 | * \retval JZ_IDERR_WRONG_SIZE file too small to be valid | ||
131 | * \retval JZ_IDERR_UNRECOGNIZED_MODEL unsupported/unknown model type | 135 | * \retval JZ_IDERR_UNRECOGNIZED_MODEL unsupported/unknown model type |
132 | * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch | 136 | * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch |
133 | */ | 137 | */ |
134 | int jz_identify_scramble_image(const void* data, size_t len) | 138 | int jz_identify_scramble_image(const void* data, size_t len) |
135 | { | 139 | { |
136 | /* 4 bytes checksum + 4 bytes player model */ | 140 | const uint8_t* dat; |
137 | if(len < 8) | 141 | const struct scramble_model_info* model_info; |
138 | return JZ_IDERR_WRONG_SIZE; | 142 | uint32_t sum, file_sum; |
143 | |||
144 | dat = (const uint8_t*)data; | ||
145 | model_info = &scramble_models[0]; | ||
139 | 146 | ||
140 | /* Look up the model number */ | 147 | /* Look up the model number */ |
141 | const uint8_t* dat = (const uint8_t*)data; | 148 | for(; model_info->name != NULL; ++model_info) { |
142 | const struct scramble_model_info* model_info = &scramble_models[0]; | 149 | if(model_info->offset_name + 4 > len) |
143 | for(; model_info->name != NULL; ++model_info) | 150 | continue; |
144 | if(!memcmp(&dat[4], model_info->name, 4)) | 151 | if(!memcmp(&dat[model_info->offset_name], model_info->name, 4)) |
145 | break; | 152 | break; |
153 | } | ||
146 | 154 | ||
147 | if(model_info->name == NULL) | 155 | if(model_info->name == NULL) |
148 | return JZ_IDERR_UNRECOGNIZED_MODEL; | 156 | return JZ_IDERR_UNRECOGNIZED_MODEL; |
149 | 157 | ||
150 | /* Compute the checksum */ | 158 | /* Compute the checksum */ |
151 | uint32_t sum = model_info->model_num; | 159 | sum = model_info->model_num; |
152 | for(size_t i = 8; i < len; ++i) | 160 | for(size_t i = model_info->offset_data; i < len; ++i) |
153 | sum += dat[i]; | 161 | sum += dat[i]; |
154 | 162 | ||
155 | /* Compare with file's checksum, it's stored in big-endian form */ | 163 | /* Compare with file's checksum, it's stored in big-endian form */ |
156 | uint32_t fsum = (dat[0] << 24) | (dat[1] << 16) | (dat[2] << 8) | dat[3]; | 164 | dat += model_info->offset_crc; |
157 | if(sum != fsum) | 165 | file_sum = (dat[0] << 24) | (dat[1] << 16) | (dat[2] << 8) | dat[3]; |
166 | if(sum != file_sum) | ||
158 | return JZ_IDERR_BAD_CHECKSUM; | 167 | return JZ_IDERR_BAD_CHECKSUM; |
159 | 168 | ||
160 | return JZ_SUCCESS; | 169 | return JZ_SUCCESS; |
diff --git a/rbutil/jztool/src/ucl_unpack.c b/rbutil/jztool/src/ucl_unpack.c new file mode 100644 index 0000000000..3b199c7008 --- /dev/null +++ b/rbutil/jztool/src/ucl_unpack.c | |||
@@ -0,0 +1,128 @@ | |||
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 "ucl/ucl.h" | ||
24 | |||
25 | static uint32_t xread32(const uint8_t* d) | ||
26 | { | ||
27 | uint32_t r = 0; | ||
28 | r |= d[0] << 24; | ||
29 | r |= d[1] << 16; | ||
30 | r |= d[2] << 8; | ||
31 | r |= d[3] << 0; | ||
32 | return r; | ||
33 | } | ||
34 | |||
35 | /* adapted from firmware/common/ucl_decompress.c */ | ||
36 | jz_buffer* jz_ucl_unpack(const uint8_t* src, uint32_t src_len, uint32_t* dst_len) | ||
37 | { | ||
38 | static const uint8_t magic[8] = | ||
39 | {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a}; | ||
40 | |||
41 | jz_buffer* buffer = NULL; | ||
42 | |||
43 | /* make sure there are enough bytes for the header */ | ||
44 | if(src_len < 18) | ||
45 | goto error; | ||
46 | |||
47 | /* avoid memcmp for reasons of code size */ | ||
48 | for(size_t i = 0; i < sizeof(magic); ++i) | ||
49 | if(src[i] != magic[i]) | ||
50 | goto error; | ||
51 | |||
52 | /* read the other header fields */ | ||
53 | /* uint32_t flags = xread32(&src[8]); */ | ||
54 | uint8_t method = src[12]; | ||
55 | /* uint8_t level = src[13]; */ | ||
56 | uint32_t block_size = xread32(&src[14]); | ||
57 | |||
58 | /* check supported compression method */ | ||
59 | if(method != 0x2e) | ||
60 | goto error; | ||
61 | |||
62 | /* validate */ | ||
63 | if(block_size < 1024 || block_size > 8*1024*1024) | ||
64 | goto error; | ||
65 | |||
66 | src += 18; | ||
67 | src_len -= 18; | ||
68 | |||
69 | /* Calculate amount of space that we might need & allocate a buffer: | ||
70 | * - subtract 4 to account for end of file marker | ||
71 | * - each block is block_size bytes + 8 bytes of header | ||
72 | * - add one to nr_blocks to account for case where file size < block size | ||
73 | * - total size = max uncompressed size of block * nr_blocks | ||
74 | */ | ||
75 | uint32_t nr_blocks = (src_len - 4) / (8 + block_size) + 1; | ||
76 | uint32_t max_size = nr_blocks * (block_size + block_size/8 + 256); | ||
77 | buffer = jz_buffer_alloc(max_size, NULL); | ||
78 | if(!buffer) | ||
79 | goto error; | ||
80 | |||
81 | /* perform the decompression */ | ||
82 | uint32_t dst_ilen = buffer->size; | ||
83 | uint8_t* dst = buffer->data; | ||
84 | while(1) { | ||
85 | if(src_len < 4) | ||
86 | goto error; | ||
87 | |||
88 | uint32_t out_len = xread32(src); src += 4, src_len -= 4; | ||
89 | if(out_len == 0) | ||
90 | break; | ||
91 | |||
92 | if(src_len < 4) | ||
93 | goto error; | ||
94 | |||
95 | uint32_t in_len = xread32(src); src += 4, src_len -= 4; | ||
96 | if(in_len > block_size || out_len > block_size || | ||
97 | in_len == 0 || in_len > out_len) | ||
98 | goto error; | ||
99 | |||
100 | if(src_len < in_len) | ||
101 | goto error; | ||
102 | |||
103 | if(in_len < out_len) { | ||
104 | uint32_t actual_out_len = dst_ilen; | ||
105 | int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL); | ||
106 | if(rc != UCL_E_OK) | ||
107 | goto error; | ||
108 | if(actual_out_len != out_len) | ||
109 | goto error; | ||
110 | } else { | ||
111 | for(size_t i = 0; i < in_len; ++i) | ||
112 | dst[i] = src[i]; | ||
113 | } | ||
114 | |||
115 | src += in_len; | ||
116 | src_len -= in_len; | ||
117 | dst += out_len; | ||
118 | dst_ilen -= out_len; | ||
119 | } | ||
120 | |||
121 | /* subtract leftover number of bytes to get size of compressed output */ | ||
122 | *dst_len = buffer->size - dst_ilen; | ||
123 | return buffer; | ||
124 | |||
125 | error: | ||
126 | jz_buffer_free(buffer); | ||
127 | return NULL; | ||
128 | } | ||
diff --git a/rbutil/jztool/src/usb.c b/rbutil/jztool/src/usb.c index c101f2be77..cfc3ba60cb 100644 --- a/rbutil/jztool/src/usb.c +++ b/rbutil/jztool/src/usb.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "jztool_private.h" | 22 | #include "jztool_private.h" |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <stdbool.h> | 24 | #include <stdbool.h> |
25 | #include <string.h> | ||
25 | 26 | ||
26 | #define VR_GET_CPU_INFO 0 | 27 | #define VR_GET_CPU_INFO 0 |
27 | #define VR_SET_DATA_ADDRESS 1 | 28 | #define VR_SET_DATA_ADDRESS 1 |
@@ -145,11 +146,12 @@ void jz_usb_close(jz_usbdev* dev) | |||
145 | 146 | ||
146 | // Does an Ingenic-specific vendor request | 147 | // Does an Ingenic-specific vendor request |
147 | // Written with X1000 in mind but other Ingenic CPUs have the same commands | 148 | // Written with X1000 in mind but other Ingenic CPUs have the same commands |
148 | static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg) | 149 | static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg, |
150 | void* buffer, int buflen) | ||
149 | { | 151 | { |
150 | int rc = libusb_control_transfer(dev->handle, | 152 | int rc = libusb_control_transfer(dev->handle, |
151 | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, | 153 | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, |
152 | req, arg >> 16, arg & 0xffff, NULL, 0, 1000); | 154 | req, arg >> 16, arg & 0xffff, buffer, buflen, 1000); |
153 | 155 | ||
154 | if(rc < 0) { | 156 | if(rc < 0) { |
155 | jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc)); | 157 | jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc)); |
@@ -200,11 +202,11 @@ static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr, | |||
200 | size_t len, void* data) | 202 | size_t len, void* data) |
201 | { | 203 | { |
202 | int rc; | 204 | int rc; |
203 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_ADDRESS, addr); | 205 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_ADDRESS, addr, NULL, 0); |
204 | if(rc < 0) | 206 | if(rc < 0) |
205 | return rc; | 207 | return rc; |
206 | 208 | ||
207 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_LENGTH, len); | 209 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_LENGTH, len, NULL, 0); |
208 | if(rc < 0) | 210 | if(rc < 0) |
209 | return rc; | 211 | return rc; |
210 | 212 | ||
@@ -242,7 +244,7 @@ int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data) | |||
242 | */ | 244 | */ |
243 | int jz_usb_start1(jz_usbdev* dev, uint32_t addr) | 245 | int jz_usb_start1(jz_usbdev* dev, uint32_t addr) |
244 | { | 246 | { |
245 | return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr); | 247 | return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr, NULL, 0); |
246 | } | 248 | } |
247 | 249 | ||
248 | /** \brief Execute stage2 program jumping to the specified address | 250 | /** \brief Execute stage2 program jumping to the specified address |
@@ -252,7 +254,7 @@ int jz_usb_start1(jz_usbdev* dev, uint32_t addr) | |||
252 | */ | 254 | */ |
253 | int jz_usb_start2(jz_usbdev* dev, uint32_t addr) | 255 | int jz_usb_start2(jz_usbdev* dev, uint32_t addr) |
254 | { | 256 | { |
255 | return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr); | 257 | return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr, NULL, 0); |
256 | } | 258 | } |
257 | 259 | ||
258 | /** \brief Ask device to flush CPU caches | 260 | /** \brief Ask device to flush CPU caches |
@@ -261,5 +263,29 @@ int jz_usb_start2(jz_usbdev* dev, uint32_t addr) | |||
261 | */ | 263 | */ |
262 | int jz_usb_flush_caches(jz_usbdev* dev) | 264 | int jz_usb_flush_caches(jz_usbdev* dev) |
263 | { | 265 | { |
264 | return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0); | 266 | return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0, NULL, 0); |
267 | } | ||
268 | |||
269 | /** \brief Ask device for CPU info string | ||
270 | * \param dev USB device | ||
271 | * \param buffer Buffer to hold the info string | ||
272 | * \param buflen Size of the buffer, in bytes | ||
273 | * \return either JZ_SUCCESS on success or a failure code | ||
274 | * | ||
275 | * The buffer will always be null terminated, but to ensure the info string is | ||
276 | * not truncated the buffer needs to be at least `JZ_CPUINFO_BUFLEN` byes long. | ||
277 | */ | ||
278 | int jz_usb_get_cpu_info(jz_usbdev* dev, char* buffer, size_t buflen) | ||
279 | { | ||
280 | char tmpbuf[JZ_CPUINFO_BUFLEN]; | ||
281 | int rc = jz_usb_vendor_req(dev, VR_GET_CPU_INFO, 0, tmpbuf, 8); | ||
282 | if(rc != JZ_SUCCESS) | ||
283 | return rc; | ||
284 | |||
285 | if(buflen > 0) { | ||
286 | strncpy(buffer, tmpbuf, buflen); | ||
287 | buffer[buflen - 1] = 0; | ||
288 | } | ||
289 | |||
290 | return JZ_SUCCESS; | ||
265 | } | 291 | } |
diff --git a/rbutil/jztool/src/fiiom3k.c b/rbutil/jztool/src/x1000.c index 72e25a1220..aacad0ef01 100644 --- a/rbutil/jztool/src/fiiom3k.c +++ b/rbutil/jztool/src/x1000.c | |||
@@ -22,117 +22,11 @@ | |||
22 | #include "jztool.h" | 22 | #include "jztool.h" |
23 | #include "jztool_private.h" | 23 | #include "jztool_private.h" |
24 | #include "microtar.h" | 24 | #include "microtar.h" |
25 | #include "ucl/ucl.h" | ||
26 | #include <stdbool.h> | 25 | #include <stdbool.h> |
27 | #include <string.h> | 26 | #include <string.h> |
28 | 27 | ||
29 | static uint32_t xread32(const uint8_t* d) | 28 | /* TODO: these functions could be refactored to be CPU-agnostic */ |
30 | { | 29 | static int run_stage1(jz_usbdev* dev, jz_buffer* buf) |
31 | uint32_t r = 0; | ||
32 | r |= d[0] << 24; | ||
33 | r |= d[1] << 16; | ||
34 | r |= d[2] << 8; | ||
35 | r |= d[3] << 0; | ||
36 | return r; | ||
37 | } | ||
38 | |||
39 | /* adapted from firmware/common/ucl_decompress.c */ | ||
40 | static jz_buffer* ucl_unpack(const uint8_t* src, uint32_t src_len, | ||
41 | uint32_t* dst_len) | ||
42 | { | ||
43 | static const uint8_t magic[8] = | ||
44 | {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a}; | ||
45 | |||
46 | jz_buffer* buffer = NULL; | ||
47 | |||
48 | /* make sure there are enough bytes for the header */ | ||
49 | if(src_len < 18) | ||
50 | goto error; | ||
51 | |||
52 | /* avoid memcmp for reasons of code size */ | ||
53 | for(size_t i = 0; i < sizeof(magic); ++i) | ||
54 | if(src[i] != magic[i]) | ||
55 | goto error; | ||
56 | |||
57 | /* read the other header fields */ | ||
58 | /* uint32_t flags = xread32(&src[8]); */ | ||
59 | uint8_t method = src[12]; | ||
60 | /* uint8_t level = src[13]; */ | ||
61 | uint32_t block_size = xread32(&src[14]); | ||
62 | |||
63 | /* check supported compression method */ | ||
64 | if(method != 0x2e) | ||
65 | goto error; | ||
66 | |||
67 | /* validate */ | ||
68 | if(block_size < 1024 || block_size > 8*1024*1024) | ||
69 | goto error; | ||
70 | |||
71 | src += 18; | ||
72 | src_len -= 18; | ||
73 | |||
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; | ||
85 | |||
86 | /* perform the decompression */ | ||
87 | uint32_t dst_ilen = buffer->size; | ||
88 | uint8_t* dst = buffer->data; | ||
89 | while(1) { | ||
90 | if(src_len < 4) | ||
91 | goto error; | ||
92 | |||
93 | uint32_t out_len = xread32(src); src += 4, src_len -= 4; | ||
94 | if(out_len == 0) | ||
95 | break; | ||
96 | |||
97 | if(src_len < 4) | ||
98 | goto error; | ||
99 | |||
100 | uint32_t in_len = xread32(src); src += 4, src_len -= 4; | ||
101 | if(in_len > block_size || out_len > block_size || | ||
102 | in_len == 0 || in_len > out_len) | ||
103 | goto error; | ||
104 | |||
105 | if(src_len < in_len) | ||
106 | goto error; | ||
107 | |||
108 | if(in_len < out_len) { | ||
109 | uint32_t actual_out_len = dst_ilen; | ||
110 | int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL); | ||
111 | if(rc != UCL_E_OK) | ||
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 | } | ||
119 | |||
120 | src += in_len; | ||
121 | src_len -= in_len; | ||
122 | dst += out_len; | ||
123 | dst_ilen -= out_len; | ||
124 | } | ||
125 | |||
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; | ||
133 | } | ||
134 | |||
135 | static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf) | ||
136 | { | 30 | { |
137 | int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data); | 31 | int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data); |
138 | if(rc < 0) | 32 | if(rc < 0) |
@@ -141,7 +35,7 @@ static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf) | |||
141 | return jz_usb_start1(dev, 0xf4001800); | 35 | return jz_usb_start1(dev, 0xf4001800); |
142 | } | 36 | } |
143 | 37 | ||
144 | static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf) | 38 | static int run_stage2(jz_usbdev* dev, jz_buffer* buf) |
145 | { | 39 | { |
146 | int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data); | 40 | int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data); |
147 | if(rc < 0) | 41 | if(rc < 0) |
@@ -154,8 +48,8 @@ static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf) | |||
154 | return jz_usb_start2(dev, 0x80004000); | 48 | return jz_usb_start2(dev, 0x80004000); |
155 | } | 49 | } |
156 | 50 | ||
157 | static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file, | 51 | static int get_file(jz_context* jz, mtar_t* tar, const char* file, |
158 | bool decompress, jz_buffer** buf) | 52 | bool decompress, jz_buffer** buf) |
159 | { | 53 | { |
160 | jz_buffer* buffer = NULL; | 54 | jz_buffer* buffer = NULL; |
161 | mtar_header_t h; | 55 | mtar_header_t h; |
@@ -180,7 +74,7 @@ static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file, | |||
180 | 74 | ||
181 | if(decompress) { | 75 | if(decompress) { |
182 | uint32_t dst_len; | 76 | uint32_t dst_len; |
183 | jz_buffer* nbuf = ucl_unpack(buffer->data, buffer->size, &dst_len); | 77 | jz_buffer* nbuf = jz_ucl_unpack(buffer->data, buffer->size, &dst_len); |
184 | jz_buffer_free(buffer); | 78 | jz_buffer_free(buffer); |
185 | if(!nbuf) { | 79 | if(!nbuf) { |
186 | jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file); | 80 | jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file); |
@@ -196,7 +90,7 @@ static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file, | |||
196 | return JZ_SUCCESS; | 90 | return JZ_SUCCESS; |
197 | } | 91 | } |
198 | 92 | ||
199 | static int m3k_show_version(jz_context* jz, jz_buffer* info_file) | 93 | static int show_version(jz_context* jz, jz_buffer* info_file) |
200 | { | 94 | { |
201 | /* Extract the version string and log it for informational purposes */ | 95 | /* Extract the version string and log it for informational purposes */ |
202 | char* boot_version = (char*)info_file->data; | 96 | char* boot_version = (char*)info_file->data; |
@@ -211,17 +105,28 @@ static int m3k_show_version(jz_context* jz, jz_buffer* info_file) | |||
211 | return JZ_SUCCESS; | 105 | return JZ_SUCCESS; |
212 | } | 106 | } |
213 | 107 | ||
214 | /** \brief Load the Rockbox bootloader on the FiiO M3K | 108 | /** \brief Load the Rockbox bootloader on an X1000 device |
215 | * \param dev USB device freshly returned by jz_usb_open() | 109 | * \param dev USB device freshly returned by jz_usb_open() |
216 | * \param filename Path to the "bootloader.m3k" file | 110 | * \param filename Path to the "bootloader.target" file |
217 | * \return either JZ_SUCCESS or an error code | 111 | * \return either JZ_SUCCESS or an error code |
218 | */ | 112 | */ |
219 | int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename) | 113 | int jz_x1000_boot(jz_usbdev* dev, jz_device_type type, const char* filename) |
220 | { | 114 | { |
115 | const jz_device_info* dev_info; | ||
116 | char spl_filename[32]; | ||
221 | jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL; | 117 | jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL; |
222 | mtar_t tar; | 118 | mtar_t tar; |
223 | int rc; | 119 | int rc; |
224 | 120 | ||
121 | /* In retrospect using a model-dependent archive format was not a good | ||
122 | * idea, but it's not worth fixing just yet... */ | ||
123 | dev_info = jz_get_device_info(type); | ||
124 | if(!dev_info) | ||
125 | return JZ_ERR_OTHER; | ||
126 | /* use of sprintf is safe since file_ext is short */ | ||
127 | sprintf(spl_filename, "spl.%s", dev_info->file_ext); | ||
128 | |||
129 | /* Now open the archive */ | ||
225 | rc = mtar_open(&tar, filename, "r"); | 130 | rc = mtar_open(&tar, filename, "r"); |
226 | if(rc != MTAR_ESUCCESS) { | 131 | if(rc != MTAR_ESUCCESS) { |
227 | jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc); | 132 | jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc); |
@@ -229,25 +134,25 @@ int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename) | |||
229 | } | 134 | } |
230 | 135 | ||
231 | /* Extract all necessary files */ | 136 | /* Extract all necessary files */ |
232 | rc = m3k_get_file(dev->jz, &tar, "spl.m3k", false, &spl); | 137 | rc = get_file(dev->jz, &tar, spl_filename, false, &spl); |
233 | if(rc != JZ_SUCCESS) | 138 | if(rc != JZ_SUCCESS) |
234 | goto error; | 139 | goto error; |
235 | 140 | ||
236 | rc = m3k_get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader); | 141 | rc = get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader); |
237 | if(rc != JZ_SUCCESS) | 142 | if(rc != JZ_SUCCESS) |
238 | goto error; | 143 | goto error; |
239 | 144 | ||
240 | rc = m3k_get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file); | 145 | rc = get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file); |
241 | if(rc != JZ_SUCCESS) | 146 | if(rc != JZ_SUCCESS) |
242 | goto error; | 147 | goto error; |
243 | 148 | ||
244 | /* Display the version string */ | 149 | /* Display the version string */ |
245 | rc = m3k_show_version(dev->jz, info_file); | 150 | rc = show_version(dev->jz, info_file); |
246 | if(rc != JZ_SUCCESS) | 151 | if(rc != JZ_SUCCESS) |
247 | goto error; | 152 | goto error; |
248 | 153 | ||
249 | /* Stage1 boot of SPL to set up hardware */ | 154 | /* Stage1 boot of SPL to set up hardware */ |
250 | rc = m3k_stage1(dev, spl); | 155 | rc = run_stage1(dev, spl); |
251 | if(rc != JZ_SUCCESS) | 156 | if(rc != JZ_SUCCESS) |
252 | goto error; | 157 | goto error; |
253 | 158 | ||
@@ -256,7 +161,7 @@ int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename) | |||
256 | 161 | ||
257 | /* Stage2 boot into the bootloader's recovery menu | 162 | /* Stage2 boot into the bootloader's recovery menu |
258 | * User has to take manual action from there */ | 163 | * User has to take manual action from there */ |
259 | rc = m3k_stage2(dev, bootloader); | 164 | rc = run_stage2(dev, bootloader); |
260 | if(rc != JZ_SUCCESS) | 165 | if(rc != JZ_SUCCESS) |
261 | goto error; | 166 | goto error; |
262 | 167 | ||