summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-05-11 13:25:26 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-05-12 10:35:20 +0000
commitcc22df198d0ccb64dfdfe0c2f247f7d86b7fd750 (patch)
treee6cd8aaa33c161bc0f72a9b35e763372b20f910d
parent3748117ee3623437d63ba2016637fd532dbdd4b9 (diff)
downloadrockbox-cc22df198d0ccb64dfdfe0c2f247f7d86b7fd750.tar.gz
rockbox-cc22df198d0ccb64dfdfe0c2f247f7d86b7fd750.zip
jztool: Support new M3K bootloader
Change-Id: Ia2d96893a9a5c77deb71c1fe32ae5a0585093f5b
-rw-r--r--rbutil/jztool/Makefile8
-rw-r--r--rbutil/jztool/README.md118
-rw-r--r--rbutil/jztool/include/jztool.h37
-rw-r--r--rbutil/jztool/jztool.c162
-rw-r--r--rbutil/jztool/src/context.c10
-rw-r--r--rbutil/jztool/src/device_info.c29
-rw-r--r--rbutil/jztool/src/fiiom3k.c373
-rw-r--r--rbutil/jztool/src/identify_file.c50
-rw-r--r--rbutil/jztool/src/paramlist.c135
-rw-r--r--rbutil/jztool/src/usb.c64
-rw-r--r--rbutil/jztool/src/x1000.c209
-rw-r--r--rbutil/libtools.make6
12 files changed, 433 insertions, 768 deletions
diff --git a/rbutil/jztool/Makefile b/rbutil/jztool/Makefile
index 6ab990d9d8..bc2724ef59 100644
--- a/rbutil/jztool/Makefile
+++ b/rbutil/jztool/Makefile
@@ -5,7 +5,7 @@
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/ 6# \/ \/ \/ \/ \/
7 7
8CFLAGS += -Wall -Wextra -Iinclude 8CFLAGS += -Wall -Wextra -Iinclude -I../../tools/ucl/include -I../../lib/microtar/src
9OUTPUT = jztool 9OUTPUT = jztool
10 10
11ifdef RELEASE 11ifdef RELEASE
@@ -15,10 +15,9 @@ CFLAGS += -O0 -ggdb
15endif 15endif
16 16
17LIBSOURCES := src/buffer.c src/context.c src/device_info.c \ 17LIBSOURCES := src/buffer.c src/context.c src/device_info.c \
18 src/fiiom3k.c src/identify_file.c src/paramlist.c \ 18 src/identify_file.c src/fiiom3k.c src/usb.c
19 src/usb.c src/x1000.c
20SOURCES := $(LIBSOURCES) jztool.c 19SOURCES := $(LIBSOURCES) jztool.c
21EXTRADEPS := 20EXTRADEPS := libucl.a libmicrotar.a
22 21
23CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -) 22CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -)
24 23
@@ -27,6 +26,7 @@ ifeq ($(findstring WIN32,$(CPPDEFINES)),WIN32)
27else 26else
28ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE) 27ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE)
29# OSX -- /opt location is cheesy attempt to support ARM macs 28# OSX -- /opt location is cheesy attempt to support ARM macs
29# COMPLETELY UNTESTED, testing from someone with an actual Mac is appreciated!
30CFLAGS += -I/usr/local/include -I/opt/homebrew/include 30CFLAGS += -I/usr/local/include -I/opt/homebrew/include
31LDOPTS += -L/usr/local/lib -L/opt/homebrew/lib -lusb-1.0 31LDOPTS += -L/usr/local/lib -L/opt/homebrew/lib -lusb-1.0
32else 32else
diff --git a/rbutil/jztool/README.md b/rbutil/jztool/README.md
index 6a9b78f8d7..2c4dd4992c 100644
--- a/rbutil/jztool/README.md
+++ b/rbutil/jztool/README.md
@@ -1,28 +1,90 @@
1# jztool -- Ingenic device utility & bootloader installer 1# jztool -- Ingenic device utility & bootloader installer
2 2
3The `jztool` utility can install, backup, and restore the bootloader on 3The `jztool` utility can help install, backup, and restore the bootloader on
4Rockbox players based on a supported Ingenic SoC. 4Rockbox players based on a supported Ingenic SoC.
5 5
6## FiiO M3K 6## FiiO M3K
7 7
8To use `jztool` on the FiiO M3K you have to connect the player to your 8First, get a copy of the `bootloader.m3k` file, either by downloading it
9computer in USB boot mode. 9from <https://rockbox.org>, or by compiling it yourself (choose 'B'ootloader
10build when configuring your build).
10 11
11The easiest way to do this is by plugging in the microUSB cable to the M3K 12The first time you install Rockbox, you need to load the Rockbox bootloader
12and holding the volume down button while plugging the USB into your computer. 13over USB by entering USB boot mode. The easiest way to do this is by plugging
13If you entered USB boot mode, the button light will turn on but the LCD will 14in the microUSB cable to the M3K and holding the VOL- button while plugging
14turn off. 15the USB into your computer. If you entered USB boot mode, the button light
16will turn on but the LCD will remain black.
15 17
16To install or update the Rockbox bootloader on the M3K, use the command 18Copy the `bootloader.m3k` next to the `jztool` executable and follow the
17`jztool fiiom3k install`. It is recommended that you take a backup of your 19instructions below which are appropriate to your OS.
18current bootloader so you can restore it in case of any problems.
19 20
20After any operation finishes, you will have to force a power off of the M3K 21### Running jztool
21by holding down the power button for at least 10 seconds. This must be done 22
22whether the operation succeeds or fails. Just don't power off or unplug the 23#### Linux/Mac
23device in the middle of an operation -- that might make bad things happen. 24
25Run the following command in a terminal. Note that on Linux, you will need to
26have root access to allow libusb to access the USB device.
27
28```sh
29# Linux / Mac
30# NOTE: root permissions are required on Linux to access the USB device
31# eg. with 'sudo' or 'su -c' depending on your distro.
32$ ./jztool fiiom3k load bootloader.m3k
33```
34
35#### Windows
36
37To allow `jztool` access to the M3K in USB boot mode, you need to install
38the WinUSB driver. The recommended way to install it is using Zadig, which
39may be downloaded from its homepage <https://zadig.akeo.ie>. Please note
40this is 3rd party software not maintained or supported by Rockbox developers.
41(Zadig will require administrator access on the machine you are using.)
42
43When running Zadig you must select the WinUSB driver; the other driver options
44will not work properly with `jztool`. You will have to select the correct USB
45device in Zadig -- the name and USB IDs of the M3K in USB boot mode are listed
46below. NOTE: the device name may show only as "X" and a hollow square in Zadig.
47The IDs will not change, so those are the most reliable way to confirm you have
48selected the correct device.
49
50```
51Name: Ingenic Semiconductor Co.,Ltd X1000
52USB ID: A108 1000
53```
54
55Assuming you installed the WinUSB driver successfully, open a command prompt
56in the folder containing `jztool`. Administrator access is not required for
57this step.
58
59Type the following command to load the Rockbox bootloader:
60
61```sh
62# Windows
63$ jztool.exe fiiom3k load bootloader.m3k
64```
65
66### Further instructions
67
68After running `jztool` successfully your M3K will display the recovery menu
69of the Rockbox bootloader. If you want to permanently install Rockbox to your
70M3K, copy `bootloader.m3k` to the root of an SD card, insert it to your device,
71then choose "Install/update bootloader" from the menu.
72
73It is _highly_ recommended that you take a backup of your existing bootloader
74in case of any trouble -- choose "Backup bootloader" from the recovery menu.
75The backup file is called "fiiom3k-boot.bin" and will be saved to the root of
76the SD card. If you need to restore it, simply place the file at the root of
77your SD card and select "Restore bootloader".
78
79In the future if you want to backup, restore, or update the bootloader, you
80can access the Rockbox bootloader's recovery menu by holding VOL+ when booting.
81
82### Known issues
83
84- When using the bootloader's USB mode, you may get stuck on "Waiting for USB"
85 even though the cable is already plugged in. If this occurs, unplug the USB
86 cable and plug it back in to trigger the connection.
24 87
25See `jztool --help` for info.
26 88
27## TODO list 89## TODO list
28 90
@@ -38,23 +100,11 @@ Some of the error messages could be friendlier too.
38Adding support to the Rockbox utility should be mostly boilerplate since the 100Adding support to the Rockbox utility should be mostly boilerplate since the
39jztool library wraps all the troublesome details. 101jztool library wraps all the troublesome details.
40 102
41Getting appropriate privileges to access the USB device is the main issue. 103Permissions are an issue on Linux because by default only root can access
42Preferably, the Rockbox utility should not run as root/admin/etc. 104"raw" USB devices. If we want to package rbutil for distro we can install
43 105a udev rule to allow access to the specific USB IDs we need, eg. allowing
44- Windows: not sure 106users in the "wheel" group to access the device.
45- Linux: needs udev rules or root privileges
46- Mac: apparently does not need privileges
47
48### Porting to Windows
49
50Windows wants to see a driver installed before we can access the USB device,
51the easiest way to do this is by having the user run Zadig, a 3rd party app
52which can install the WinUSB driver. WinUSB itself is from Microsoft and
53bundled with Windows.
54
55Zadig's homepage: https://zadig.akeo.ie/
56
57### Porting to Mac
58 107
59According to the libusb wiki, libusb works on Mac without any special setup or 108On Windows and Mac, no special permissions are needed to access USB devices
60privileges, presumably porting there is easy. 109assuming the drivers are set up. (Zadig does require administrator access
110to run, but that's external to the Rockbox utility.)
diff --git a/rbutil/jztool/include/jztool.h b/rbutil/jztool/include/jztool.h
index e16bc9765f..df51fe9f44 100644
--- a/rbutil/jztool/include/jztool.h
+++ b/rbutil/jztool/include/jztool.h
@@ -37,7 +37,6 @@ typedef struct jz_context jz_context;
37typedef struct jz_usbdev jz_usbdev; 37typedef struct jz_usbdev jz_usbdev;
38typedef struct jz_device_info jz_device_info; 38typedef struct jz_device_info jz_device_info;
39typedef struct jz_buffer jz_buffer; 39typedef struct jz_buffer jz_buffer;
40typedef struct jz_paramlist jz_paramlist;
41 40
42typedef enum jz_error jz_error; 41typedef enum jz_error jz_error;
43typedef enum jz_identify_error jz_identify_error; 42typedef enum jz_identify_error jz_identify_error;
@@ -46,7 +45,6 @@ typedef enum jz_device_type jz_device_type;
46typedef enum jz_cpu_type jz_cpu_type; 45typedef enum jz_cpu_type jz_cpu_type;
47 46
48typedef void(*jz_log_cb)(jz_log_level, const char*); 47typedef void(*jz_log_cb)(jz_log_level, const char*);
49typedef int(*jz_device_action_fn)(jz_context*, jz_paramlist*);
50 48
51enum jz_error { 49enum jz_error {
52 JZ_SUCCESS = 0, 50 JZ_SUCCESS = 0,
@@ -92,10 +90,6 @@ struct jz_device_info {
92 jz_cpu_type cpu_type; 90 jz_cpu_type cpu_type;
93 uint16_t vendor_id; 91 uint16_t vendor_id;
94 uint16_t product_id; 92 uint16_t product_id;
95 int num_actions;
96 const char* const* action_names;
97 const jz_device_action_fn* action_funcs;
98 const char* const* const* action_params;
99}; 93};
100 94
101struct jz_buffer { 95struct jz_buffer {
@@ -132,7 +126,6 @@ const jz_device_info* jz_get_device_info_indexed(int index);
132 126
133int jz_identify_x1000_spl(const void* data, size_t len); 127int jz_identify_x1000_spl(const void* data, size_t len);
134int jz_identify_scramble_image(const void* data, size_t len); 128int jz_identify_scramble_image(const void* data, size_t len);
135int jz_identify_fiiom3k_bootimage(const void* data, size_t len);
136 129
137/****************************************************************************** 130/******************************************************************************
138 * USB boot ROM protocol 131 * USB boot ROM protocol
@@ -148,27 +141,10 @@ int jz_usb_start2(jz_usbdev* dev, uint32_t addr);
148int jz_usb_flush_caches(jz_usbdev* dev); 141int jz_usb_flush_caches(jz_usbdev* dev);
149 142
150/****************************************************************************** 143/******************************************************************************
151 * X1000 flash protocol 144 * Rockbox loader (all functions are model-specific, see docs)
152 */ 145 */
153 146
154int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data); 147int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename);
155int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data);
156int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data);
157int jz_x1000_boot_rockbox(jz_usbdev* dev);
158
159/******************************************************************************
160 * FiiO M3K bootloader backup/installation
161 */
162
163int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr);
164int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf);
165int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size,
166 const void* spl_buf, size_t spl_size,
167 const void* boot_buf, size_t boot_size);
168
169int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl);
170int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl);
171int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl);
172 148
173/****************************************************************************** 149/******************************************************************************
174 * Simple buffer API 150 * Simple buffer API
@@ -181,15 +157,6 @@ int jz_buffer_load(jz_buffer** buf, const char* filename);
181int jz_buffer_save(jz_buffer* buf, const char* filename); 157int jz_buffer_save(jz_buffer* buf, const char* filename);
182 158
183/****************************************************************************** 159/******************************************************************************
184 * Parameter list
185 */
186
187jz_paramlist* jz_paramlist_new(void);
188void jz_paramlist_free(jz_paramlist* pl);
189int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value);
190const char* jz_paramlist_get(jz_paramlist* pl, const char* param);
191
192/******************************************************************************
193 * END 160 * END
194 */ 161 */
195 162
diff --git a/rbutil/jztool/jztool.c b/rbutil/jztool/jztool.c
index 5aae8d7457..5fb6dc173f 100644
--- a/rbutil/jztool/jztool.c
+++ b/rbutil/jztool/jztool.c
@@ -26,21 +26,68 @@
26#include <stdbool.h> 26#include <stdbool.h>
27 27
28jz_context* jz = NULL; 28jz_context* jz = NULL;
29jz_usbdev* usbdev = NULL;
29const jz_device_info* dev_info = NULL; 30const jz_device_info* dev_info = NULL;
30int dev_action = -1; 31
31jz_paramlist* action_params = NULL; 32void usage_fiiom3k(void)
33{
34 printf("Usage:\n"
35 " jztool fiiom3k load <bootloader.m3k>\n"
36 "\n"
37 "The 'load' command is used to boot the Rockbox bootloader in\n"
38 "recovery mode, which allows you to install the Rockbox bootloader\n"
39 "and backup or restore bootloader images. You need to connect the\n"
40 "M3K in USB boot mode in order to use this tool.\n"
41 "\n"
42 "On Windows, you will need to install the WinUSB driver for the M3K\n"
43 "using a 3rd-party tool such as Zadig <https://zadig.akeo.ie>. For\n"
44 "more details check the jztool README.md file or the Rockbox wiki at\n"
45 "<https://rockbox.org/wiki/FiioM3K>.\n"
46 "\n"
47 "To connect the M3K in USB boot mode, plug the microUSB into the\n"
48 "M3K, and hold the VOL- button while plugging the USB into your\n"
49 "computer. If successful, the button light will turn on and the\n"
50 "LCD will remain black. If you encounter any errors and need to\n"
51 "reconnect the device, you must force a power off by holding POWER\n"
52 "for more than 10 seconds.\n"
53 "\n"
54 "Once the Rockbox bootloader is installed on your M3K, you can\n"
55 "access the recovery menu by holding VOL+ while powering on the\n"
56 "device.\n");
57 exit(4);
58}
59
60int cmdline_fiiom3k(int argc, char** argv)
61{
62 if(argc < 2 || strcmp(argv[0], "load")) {
63 usage_fiiom3k();
64 return 2;
65 }
66
67 int rc = jz_usb_open(jz, &usbdev, dev_info->vendor_id, dev_info->product_id);
68 if(rc < 0) {
69 jz_log(jz, JZ_LOG_ERROR, "Cannot open USB device: %d", rc);
70 return 1;
71 }
72
73 rc = jz_fiiom3k_boot(usbdev, argv[1]);
74 if(rc < 0) {
75 jz_log(jz, JZ_LOG_ERROR, "Boot failed: %d", rc);
76 return 1;
77 }
78
79 return 0;
80}
32 81
33void usage(void) 82void usage(void)
34{ 83{
35 printf("Usage:\n" 84 printf("Usage:\n"
36 " jztool [global options] <device> <action> [action options]\n" 85 " jztool [global options] <device> <command> [command arguments]\n"
37 "\n" 86 "\n"
38 "Global options:\n" 87 "Global options:\n"
39 "\n"
40 " -h, --help Display this help\n" 88 " -h, --help Display this help\n"
41 " -q, --quiet Don't log anything except errors\n" 89 " -q, --quiet Don't log anything except errors\n"
42 " -v, --verbose Display detailed logging output\n" 90 " -v, --verbose Display detailed logging output\n\n");
43 " -l, --loglevel LEVEL Set log level\n");
44 91
45 printf("Supported devices:\n\n"); 92 printf("Supported devices:\n\n");
46 int n = jz_get_num_device_info(); 93 int n = jz_get_num_device_info();
@@ -49,39 +96,17 @@ void usage(void)
49 printf(" %s - %s\n", info->name, info->description); 96 printf(" %s - %s\n", info->name, info->description);
50 } 97 }
51 98
52 printf( 99 printf("\n"
53"\n" 100 "For device-specific help run 'jztool DEVICE' without arguments,\n"
54"Available actions for fiiom3k:\n" 101 "eg. 'jztool fiiom3k' will display help for the FiiO M3K.\n");
55"\n"
56" install --spl <spl.m3k> --bootloader <bootloader.m3k>\n"
57" [--without-backup yes] [--backup IMAGE]\n"
58" Install or update the Rockbox bootloader on a device.\n"
59"\n"
60" If --backup is given, back up the current bootloader to IMAGE before\n"
61" installing the new bootloader. The installer will normally refuse to\n"
62" overwrite your current bootloader; pass '--without-backup yes' if you\n"
63" really want to proceed without taking a backup.\n"
64"\n"
65" WARNING: it is NOT RECOMMENDED to install the Rockbox bootloader\n"
66" without taking a backup of the original firmware bootloader. It may\n"
67" be very difficult or impossible to recover your player without one.\n"
68" At least one M3Ks is known to not to work with the Rockbox bootloader,\n"
69" so it is very important to take a backup.\n"
70"\n"
71" backup --image IMAGE\n"
72" Backup the current bootloader to the file IMAGE\n"
73"\n"
74" restore --image IMAGE\n"
75" Restore a bootloader image backup from the file IMAGE\n"
76"\n");
77 102
78 exit(4); 103 exit(4);
79} 104}
80 105
81void cleanup(void) 106void cleanup(void)
82{ 107{
83 if(action_params) 108 if(usbdev)
84 jz_paramlist_free(action_params); 109 jz_usb_close(usbdev);
85 if(jz) 110 if(jz)
86 jz_context_destroy(jz); 111 jz_context_destroy(jz);
87} 112}
@@ -157,71 +182,14 @@ int main(int argc, char** argv)
157 exit(2); 182 exit(2);
158 } 183 }
159 184
160 /* Read the action */ 185 /* Dispatch to device handler */
161 --argc, ++argv;
162 if(argc == 0) {
163 jz_log(jz, JZ_LOG_ERROR, "No action specified (try jztool --help)");
164 exit(2);
165 }
166
167 for(dev_action = 0; dev_action < dev_info->num_actions; ++dev_action)
168 if(!strcmp(*argv, dev_info->action_names[dev_action]))
169 break;
170
171 if(dev_action == dev_info->num_actions) {
172 jz_log(jz, JZ_LOG_ERROR, "Unknown action '%s' (try jztool --help)", *argv);
173 exit(2);
174 }
175
176 /* Parse the action options */
177 action_params = jz_paramlist_new();
178 if(!action_params) {
179 jz_log(jz, JZ_LOG_ERROR, "Out of memory: can't create paramlist");
180 exit(1);
181 }
182
183 const char* const* allowed_params = dev_info->action_params[dev_action];
184
185 --argc, ++argv; 186 --argc, ++argv;
186 while(argc > 0 && argv[0][0] == '-') { 187 switch(dev_info->device_type) {
187 if(argv[0][1] != '-') { 188 case JZ_DEVICE_FIIOM3K:
188 jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv); 189 return cmdline_fiiom3k(argc, argv);
189 exit(2);
190 }
191 190
192 bool bad_option = true; 191 default:
193 for(int i = 0; allowed_params[i] != NULL; ++i) { 192 jz_log(jz, JZ_LOG_ERROR, "INTERNAL ERROR: unhandled device type");
194 if(!strcmp(&argv[0][2], allowed_params[i])) { 193 return 1;
195 ++argv;
196 if(--argc == 0) {
197 jz_log(jz, JZ_LOG_ERROR, "Missing argument for parameter '%s'", *argv);
198 exit(2);
199 }
200
201 int rc = jz_paramlist_set(action_params, allowed_params[i], *argv);
202 if(rc < 0) {
203 jz_log(jz, JZ_LOG_ERROR, "Out of memory");
204 exit(1);
205 }
206
207 bad_option = false;
208 }
209 }
210
211 if(bad_option) {
212 jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv);
213 exit(2);
214 }
215
216 --argc, ++argv;
217 }
218
219 if(argc != 0) {
220 jz_log(jz, JZ_LOG_ERROR, "Excess arguments on command line");
221 exit(2);
222 } 194 }
223
224 /* Invoke action handler */
225 int rc = dev_info->action_funcs[dev_action](jz, action_params);
226 return (rc < 0) ? 1 : 0;
227} 195}
diff --git a/rbutil/jztool/src/context.c b/rbutil/jztool/src/context.c
index 94b21b5196..d269d1eece 100644
--- a/rbutil/jztool/src/context.c
+++ b/rbutil/jztool/src/context.c
@@ -27,6 +27,10 @@
27#include <stdio.h> 27#include <stdio.h>
28#include <time.h> 28#include <time.h>
29 29
30#ifdef WIN32
31# include <windows.h>
32#endif
33
30/** \brief Allocate a library context 34/** \brief Allocate a library context
31 * \returns New context or NULL if out of memory 35 * \returns New context or NULL if out of memory
32 */ 36 */
@@ -137,13 +141,18 @@ void jz_log_cb_stderr(jz_log_level lev, const char* msg)
137 */ 141 */
138void jz_sleepms(int ms) 142void jz_sleepms(int ms)
139{ 143{
144#ifdef WIN32
145 Sleep(ms);
146#else
140 struct timespec ts; 147 struct timespec ts;
141 long ns = ms % 1000; 148 long ns = ms % 1000;
142 ts.tv_nsec = ns * 1000 * 1000; 149 ts.tv_nsec = ns * 1000 * 1000;
143 ts.tv_sec = ms / 1000; 150 ts.tv_sec = ms / 1000;
144 nanosleep(&ts, NULL); 151 nanosleep(&ts, NULL);
152#endif
145} 153}
146 154
155/** \brief Add reference to libusb context, allocating it if necessary */
147int jz_context_ref_libusb(jz_context* jz) 156int jz_context_ref_libusb(jz_context* jz)
148{ 157{
149 if(jz->usb_ctxref == 0) { 158 if(jz->usb_ctxref == 0) {
@@ -158,6 +167,7 @@ int jz_context_ref_libusb(jz_context* jz)
158 return JZ_SUCCESS; 167 return JZ_SUCCESS;
159} 168}
160 169
170/** \brief Remove reference to libusb context, freeing if it hits zero */
161void jz_context_unref_libusb(jz_context* jz) 171void jz_context_unref_libusb(jz_context* jz)
162{ 172{
163 if(--jz->usb_ctxref == 0) { 173 if(--jz->usb_ctxref == 0) {
diff --git a/rbutil/jztool/src/device_info.c b/rbutil/jztool/src/device_info.c
index bc1477be32..5ce3899262 100644
--- a/rbutil/jztool/src/device_info.c
+++ b/rbutil/jztool/src/device_info.c
@@ -22,30 +22,6 @@
22#include "jztool.h" 22#include "jztool.h"
23#include <string.h> 23#include <string.h>
24 24
25static const char* const fiiom3k_action_names[] = {
26 "install",
27 "backup",
28 "restore",
29};
30
31static const char* const fiiom3k_install_action_params[] =
32 {"spl", "bootloader", "backup", "without-backup", NULL};
33
34static const char* const fiiom3k_backuprestore_action_params[] =
35 {"spl", "image", NULL};
36
37static const char* const* fiiom3k_action_params[] = {
38 fiiom3k_install_action_params,
39 fiiom3k_backuprestore_action_params,
40 fiiom3k_backuprestore_action_params,
41};
42
43static const jz_device_action_fn fiiom3k_action_funcs[] = {
44 jz_fiiom3k_install,
45 jz_fiiom3k_backup,
46 jz_fiiom3k_restore,
47};
48
49static const jz_device_info infotable[] = { 25static const jz_device_info infotable[] = {
50 { 26 {
51 .name = "fiiom3k", 27 .name = "fiiom3k",
@@ -54,10 +30,6 @@ static const jz_device_info infotable[] = {
54 .cpu_type = JZ_CPU_X1000, 30 .cpu_type = JZ_CPU_X1000,
55 .vendor_id = 0xa108, 31 .vendor_id = 0xa108,
56 .product_id = 0x1000, 32 .product_id = 0x1000,
57 .num_actions = sizeof(fiiom3k_action_names)/sizeof(void*),
58 .action_names = fiiom3k_action_names,
59 .action_funcs = fiiom3k_action_funcs,
60 .action_params = fiiom3k_action_params,
61 }, 33 },
62}; 34};
63 35
@@ -69,6 +41,7 @@ int jz_get_num_device_info(void)
69 return infotable_size; 41 return infotable_size;
70} 42}
71 43
44/** \brief Lookup info for a device by type, returns NULL if not found. */
72const jz_device_info* jz_get_device_info(jz_device_type type) 45const jz_device_info* jz_get_device_info(jz_device_type type)
73{ 46{
74 for(int i = 0; i < infotable_size; ++i) 47 for(int i = 0; i < infotable_size; ++i)
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}
diff --git a/rbutil/jztool/src/identify_file.c b/rbutil/jztool/src/identify_file.c
index 3bf4a9ced7..e735075687 100644
--- a/rbutil/jztool/src/identify_file.c
+++ b/rbutil/jztool/src/identify_file.c
@@ -81,6 +81,14 @@ static uint8_t crc7(const uint8_t* buf, size_t len)
81 return crc; 81 return crc;
82} 82}
83 83
84/** \brief Identify a file as an SPL for X1000 CPUs
85 * \param data File data buffer
86 * \param len Length of file
87 * \return JZ_SUCCESS if file looks correct, or one of the following errors
88 * \retval JZ_IDERR_WRONG_SIZE file too small or size doesn't match header
89 * \retval JZ_IDERR_BAD_HEADER missing magic bytes from header
90 * \retval JZ_IDERR_BAD_CHECKSUM CRC7 mismatch
91 */
84int jz_identify_x1000_spl(const void* data, size_t len) 92int jz_identify_x1000_spl(const void* data, size_t len)
85{ 93{
86 /* Use <= check because a header-only file is not really valid, 94 /* Use <= check because a header-only file is not really valid,
@@ -115,6 +123,14 @@ static const struct scramble_model_info {
115 {NULL, 0}, 123 {NULL, 0},
116}; 124};
117 125
126/** \brief Identify a file as a Rockbox `scramble` image
127 * \param data File data buffer
128 * \param len Length of file
129 * \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
132 * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch
133 */
118int jz_identify_scramble_image(const void* data, size_t len) 134int jz_identify_scramble_image(const void* data, size_t len)
119{ 135{
120 /* 4 bytes checksum + 4 bytes player model */ 136 /* 4 bytes checksum + 4 bytes player model */
@@ -143,37 +159,3 @@ int jz_identify_scramble_image(const void* data, size_t len)
143 159
144 return JZ_SUCCESS; 160 return JZ_SUCCESS;
145} 161}
146
147int jz_identify_fiiom3k_bootimage(const void* data, size_t len)
148{
149 /* The bootloader image is simply a dump of the first NAND eraseblock,
150 * so it has a fixed 128 KiB size */
151 if(len != 128*1024)
152 return JZ_IDERR_WRONG_SIZE;
153
154 /* We'll verify the embedded SPL, but we have to drag out the correct
155 * length from the header. Length should be more than 12 KiB, due to
156 * limitations of the hardware */
157 const struct x1000_spl_header* spl_header;
158 spl_header = (const struct x1000_spl_header*)data;
159 if(spl_header->length > 12 * 1024)
160 return JZ_IDERR_BAD_HEADER;
161
162 int rc = jz_identify_x1000_spl(data, spl_header->length);
163 if(rc < 0)
164 return rc;
165
166 const uint8_t* dat = (const uint8_t*)data;
167
168 /* Check the partition table is present */
169 if(memcmp(&dat[0x3c00], "nand", 4))
170 return JZ_IDERR_OTHER;
171
172 /* Check first bytes of PDMA firmware. It doesn't change
173 * between OF versions, and Rockbox doesn't modify it. */
174 static const uint8_t pdma_fw[] = {0x54, 0x25, 0x42, 0xb3, 0x70, 0x25, 0x42, 0xb3};
175 if(memcmp(&dat[0x4000], pdma_fw, sizeof(pdma_fw)))
176 return JZ_IDERR_OTHER;
177
178 return JZ_SUCCESS;
179}
diff --git a/rbutil/jztool/src/paramlist.c b/rbutil/jztool/src/paramlist.c
deleted file mode 100644
index 05bcf97a13..0000000000
--- a/rbutil/jztool/src/paramlist.c
+++ /dev/null
@@ -1,135 +0,0 @@
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 <stdlib.h>
24#include <string.h>
25#include <stdbool.h>
26
27struct jz_paramlist {
28 int size;
29 char** keys;
30 char** values;
31};
32
33static int jz_paramlist_extend(jz_paramlist* pl, int count)
34{
35 int nsize = pl->size + count;
36
37 /* Reallocate key list */
38 char** nkeys = realloc(pl->keys, nsize * sizeof(char*));
39 if(!nkeys)
40 return JZ_ERR_OUT_OF_MEMORY;
41
42 for(int i = pl->size; i < nsize; ++i)
43 nkeys[i] = NULL;
44
45 pl->keys = nkeys;
46
47 /* Reallocate value list */
48 char** nvalues = realloc(pl->values, nsize * sizeof(char*));
49 if(!nvalues)
50 return JZ_ERR_OUT_OF_MEMORY;
51
52 for(int i = pl->size; i < nsize; ++i)
53 nvalues[i] = NULL;
54
55 pl->values = nvalues;
56
57 pl->size = nsize;
58 return JZ_SUCCESS;
59}
60
61jz_paramlist* jz_paramlist_new(void)
62{
63 jz_paramlist* pl = malloc(sizeof(struct jz_paramlist));
64 if(!pl)
65 return NULL;
66
67 pl->size = 0;
68 pl->keys = NULL;
69 pl->values = NULL;
70 return pl;
71}
72
73void jz_paramlist_free(jz_paramlist* pl)
74{
75 for(int i = 0; i < pl->size; ++i) {
76 free(pl->keys[i]);
77 free(pl->values[i]);
78 }
79
80 if(pl->size > 0) {
81 free(pl->keys);
82 free(pl->values);
83 }
84
85 free(pl);
86}
87
88int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value)
89{
90 int pos = -1;
91 for(int i = 0; i < pl->size; ++i) {
92 if(!pl->keys[i] || !strcmp(pl->keys[i], param)) {
93 pos = i;
94 break;
95 }
96 }
97
98 if(pos == -1) {
99 pos = pl->size;
100 int rc = jz_paramlist_extend(pl, 1);
101 if(rc < 0)
102 return rc;
103 }
104
105 bool need_key = (pl->keys[pos] == NULL);
106 if(need_key) {
107 char* newparam = strdup(param);
108 if(!newparam)
109 return JZ_ERR_OUT_OF_MEMORY;
110
111 pl->keys[pos] = newparam;
112 }
113
114 char* newvalue = strdup(value);
115 if(!newvalue) {
116 if(need_key) {
117 free(pl->keys[pos]);
118 pl->keys[pos] = NULL;
119 }
120
121 return JZ_ERR_OUT_OF_MEMORY;
122 }
123
124 pl->values[pos] = newvalue;
125 return JZ_SUCCESS;
126}
127
128const char* jz_paramlist_get(jz_paramlist* pl, const char* param)
129{
130 for(int i = 0; i < pl->size; ++i)
131 if(pl->keys[i] && !strcmp(pl->keys[i], param))
132 return pl->values[i];
133
134 return NULL;
135}
diff --git a/rbutil/jztool/src/usb.c b/rbutil/jztool/src/usb.c
index 7e4a5f3388..c101f2be77 100644
--- a/rbutil/jztool/src/usb.c
+++ b/rbutil/jztool/src/usb.c
@@ -30,6 +30,16 @@
30#define VR_PROGRAM_START1 4 30#define VR_PROGRAM_START1 4
31#define VR_PROGRAM_START2 5 31#define VR_PROGRAM_START2 5
32 32
33/** \brief Open a USB device
34 * \param jz Context
35 * \param devptr Returns pointer to the USB device upon success
36 * \param vend_id USB vendor ID
37 * \param prod_id USB product ID
38 * \return either JZ_SUCCESS if device was opened, or an error below
39 * \retval JZ_ERR_OUT_OF_MEMORY malloc failed
40 * \retval JZ_ERR_USB libusb error (details are logged)
41 * \retval JZ_ERR_NO_DEVICE can't unambiguously find the device
42 */
33int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id) 43int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id)
34{ 44{
35 int rc; 45 int rc;
@@ -80,7 +90,7 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
80 } 90 }
81 91
82 if(dev_index < 0) { 92 if(dev_index < 0) {
83 jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%05x found", 93 jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%04x found",
84 (unsigned int)vend_id, (unsigned int)prod_id); 94 (unsigned int)vend_id, (unsigned int)prod_id);
85 rc = JZ_ERR_NO_DEVICE; 95 rc = JZ_ERR_NO_DEVICE;
86 goto error; 96 goto error;
@@ -100,6 +110,8 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
100 goto error; 110 goto error;
101 } 111 }
102 112
113 jz_log(jz, JZ_LOG_DEBUG, "Opened device (%p, ID %04x:%04x)",
114 dev, (unsigned int)vend_id, (unsigned int)prod_id);
103 dev->jz = jz; 115 dev->jz = jz;
104 dev->handle = usb_handle; 116 dev->handle = usb_handle;
105 *devptr = dev; 117 *devptr = dev;
@@ -119,14 +131,20 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
119 goto exit; 131 goto exit;
120} 132}
121 133
134/** \brief Close a USB device
135 * \param dev Device to close; memory will be freed automatically
136 */
122void jz_usb_close(jz_usbdev* dev) 137void jz_usb_close(jz_usbdev* dev)
123{ 138{
139 jz_log(dev->jz, JZ_LOG_DEBUG, "Closing device (%p)", dev);
124 libusb_release_interface(dev->handle, 0); 140 libusb_release_interface(dev->handle, 0);
125 libusb_close(dev->handle); 141 libusb_close(dev->handle);
126 jz_context_unref_libusb(dev->jz); 142 jz_context_unref_libusb(dev->jz);
127 free(dev); 143 free(dev);
128} 144}
129 145
146// Does an Ingenic-specific vendor request
147// Written with X1000 in mind but other Ingenic CPUs have the same commands
130static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg) 148static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg)
131{ 149{
132 int rc = libusb_control_transfer(dev->handle, 150 int rc = libusb_control_transfer(dev->handle,
@@ -137,12 +155,24 @@ static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg)
137 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc)); 155 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc));
138 rc = JZ_ERR_USB; 156 rc = JZ_ERR_USB;
139 } else { 157 } else {
158 static const char* req_names[] = {
159 "GET_CPU_INFO",
160 "SET_DATA_ADDRESS",
161 "SET_DATA_LENGTH",
162 "FLUSH_CACHES",
163 "PROGRAM_START1",
164 "PROGRAM_START2",
165 };
166
167 jz_log(dev->jz, JZ_LOG_DEBUG, "Issued %s %08lu",
168 req_names[req], (unsigned long)arg);
140 rc = JZ_SUCCESS; 169 rc = JZ_SUCCESS;
141 } 170 }
142 171
143 return rc; 172 return rc;
144} 173}
145 174
175// Bulk transfer wrapper
146static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf) 176static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
147{ 177{
148 int xfered = 0; 178 int xfered = 0;
@@ -156,12 +186,16 @@ static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
156 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered"); 186 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered");
157 rc = JZ_ERR_USB; 187 rc = JZ_ERR_USB;
158 } else { 188 } else {
189 jz_log(dev->jz, JZ_LOG_DEBUG, "Transferred %zu bytes %s",
190 len, write ? "to device" : "from device");
159 rc = JZ_SUCCESS; 191 rc = JZ_SUCCESS;
160 } 192 }
161 193
162 return rc; 194 return rc;
163} 195}
164 196
197// Memory send/receive primitive, performs the necessary vendor requests
198// and then tranfers data using the bulk endpoint
165static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr, 199static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
166 size_t len, void* data) 200 size_t len, void* data)
167{ 201{
@@ -177,26 +211,54 @@ static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
177 return jz_usb_transfer(dev, write, len, data); 211 return jz_usb_transfer(dev, write, len, data);
178} 212}
179 213
214/** \brief Write data to device memory
215 * \param dev USB device
216 * \param addr Address where data should be written
217 * \param len Length of the data, in bytes, should be positive
218 * \param data Data buffer
219 * \return either JZ_SUCCESS on success or a failure code
220 */
180int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data) 221int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
181{ 222{
182 return jz_usb_sendrecv(dev, true, addr, len, (void*)data); 223 return jz_usb_sendrecv(dev, true, addr, len, (void*)data);
183} 224}
184 225
226/** \brief Read data to device memory
227 * \param dev USB device
228 * \param addr Address to read from
229 * \param len Length of the data, in bytes, should be positive
230 * \param data Data buffer
231 * \return either JZ_SUCCESS on success or a failure code
232 */
185int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data) 233int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
186{ 234{
187 return jz_usb_sendrecv(dev, false, addr, len, data); 235 return jz_usb_sendrecv(dev, false, addr, len, data);
188} 236}
189 237
238/** \brief Execute stage1 program jumping to the specified address
239 * \param dev USB device
240 * \param addr Address to begin execution at
241 * \return either JZ_SUCCESS on success or a failure code
242 */
190int jz_usb_start1(jz_usbdev* dev, uint32_t addr) 243int jz_usb_start1(jz_usbdev* dev, uint32_t addr)
191{ 244{
192 return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr); 245 return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr);
193} 246}
194 247
248/** \brief Execute stage2 program jumping to the specified address
249 * \param dev USB device
250 * \param addr Address to begin execution at
251 * \return either JZ_SUCCESS on success or a failure code
252 */
195int jz_usb_start2(jz_usbdev* dev, uint32_t addr) 253int jz_usb_start2(jz_usbdev* dev, uint32_t addr)
196{ 254{
197 return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr); 255 return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr);
198} 256}
199 257
258/** \brief Ask device to flush CPU caches
259 * \param dev USB device
260 * \return either JZ_SUCCESS on success or a failure code
261 */
200int jz_usb_flush_caches(jz_usbdev* dev) 262int jz_usb_flush_caches(jz_usbdev* dev)
201{ 263{
202 return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0); 264 return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0);
diff --git a/rbutil/jztool/src/x1000.c b/rbutil/jztool/src/x1000.c
deleted file mode 100644
index 1a12340316..0000000000
--- a/rbutil/jztool/src/x1000.c
+++ /dev/null
@@ -1,209 +0,0 @@
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_private.h"
23#include "../../../firmware/target/mips/ingenic_x1000/spl-x1000-defs.h"
24#include "../../../firmware/target/mips/ingenic_x1000/nand-x1000-err.h"
25#include <string.h>
26
27static uint32_t to_le32(uint32_t x)
28{
29 union { uint32_t u; uint8_t p[4]; } f;
30 f.p[0] = x & 0xff;
31 f.p[1] = (x >> 8) & 0xff;
32 f.p[2] = (x >> 16) & 0xff;
33 f.p[3] = (x >> 24) & 0xff;
34 return f.u;
35}
36
37static uint32_t from_le32(uint32_t x)
38{
39 union { uint32_t u; uint8_t p[4]; } f;
40 f.u = x;
41 return f.p[0] | (f.p[1] << 8) | (f.p[2] << 16) | (f.p[3] << 24);
42}
43
44static const char* jz_x1000_nand_strerror(int rc)
45{
46 switch(rc) {
47 case NANDERR_CHIP_UNSUPPORTED:
48 return "Chip unsupported";
49 case NANDERR_WRITE_PROTECTED:
50 return "Operation forbidden by write-protect";
51 case NANDERR_UNALIGNED_ADDRESS:
52 return "Improperly aligned address";
53 case NANDERR_UNALIGNED_LENGTH:
54 return "Improperly aligned length";
55 case NANDERR_READ_FAILED:
56 return "Read operation failed";
57 case NANDERR_ECC_FAILED:
58 return "Uncorrectable ECC error on read";
59 case NANDERR_ERASE_FAILED:
60 return "Erase operation failed";
61 case NANDERR_PROGRAM_FAILED:
62 return "Program operation failed";
63 case NANDERR_COMMAND_FAILED:
64 return "NAND command failed";
65 default:
66 return "Unknown NAND error";
67 }
68}
69
70
71static int jz_x1000_send_args(jz_usbdev* dev, struct x1000_spl_arguments* args)
72{
73 args->command = to_le32(args->command);
74 args->param1 = to_le32(args->param1);
75 args->param2 = to_le32(args->param2);
76 args->flags = to_le32(args->flags);
77 return jz_usb_send(dev, SPL_ARGUMENTS_ADDRESS, sizeof(*args), args);
78}
79
80static int jz_x1000_recv_status(jz_usbdev* dev, struct x1000_spl_status* status)
81{
82 int rc = jz_usb_recv(dev, SPL_STATUS_ADDRESS, sizeof(*status), status);
83 if(rc < 0)
84 return rc;
85
86 status->err_code = from_le32(status->err_code);
87 status->reserved = from_le32(status->reserved);
88 return JZ_SUCCESS;
89}
90
91int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data)
92{
93 int rc = jz_identify_x1000_spl(spl_data, spl_len);
94 if(rc < 0)
95 return JZ_ERR_BAD_FILE_FORMAT;
96 if(spl_len > SPL_MAX_SIZE)
97 return JZ_ERR_BAD_FILE_FORMAT;
98
99 rc = jz_usb_send(dev, SPL_LOAD_ADDRESS, spl_len, spl_data);
100 if(rc < 0)
101 return rc;
102
103 struct x1000_spl_arguments args;
104 args.command = SPL_CMD_BOOT;
105 args.param1 = SPL_BOOTOPT_NONE;
106 args.param2 = 0;
107 args.flags = 0;
108 rc = jz_x1000_send_args(dev, &args);
109 if(rc < 0)
110 return rc;
111
112 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
113 if(rc < 0)
114 return rc;
115
116 jz_sleepms(100);
117
118 struct x1000_spl_status status;
119 rc = jz_x1000_recv_status(dev, &status);
120 if(rc < 0)
121 return rc;
122
123 if(status.err_code != 0) {
124 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 device init error: %d", status.err_code);
125 return JZ_ERR_OTHER;
126 }
127
128 return JZ_SUCCESS;
129}
130
131int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
132{
133 struct x1000_spl_arguments args;
134 args.command = SPL_CMD_FLASH_READ;
135 args.param1 = addr;
136 args.param2 = len;
137 args.flags = SPL_FLAG_SKIP_INIT;
138 int rc = jz_x1000_send_args(dev, &args);
139 if(rc < 0)
140 return rc;
141
142 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
143 if(rc < 0)
144 return rc;
145
146 jz_sleepms(500);
147
148 struct x1000_spl_status status;
149 rc = jz_x1000_recv_status(dev, &status);
150 if(rc < 0)
151 return rc;
152
153 if(status.err_code != 0) {
154 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash read error: %s",
155 jz_x1000_nand_strerror(status.err_code));
156 return JZ_ERR_FLASH_ERROR;
157 }
158
159 return jz_usb_recv(dev, SPL_BUFFER_ADDRESS, len, data);
160}
161
162int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
163{
164 int rc = jz_usb_send(dev, SPL_BUFFER_ADDRESS, len, data);
165 if(rc < 0)
166 return rc;
167
168 struct x1000_spl_arguments args;
169 args.command = SPL_CMD_FLASH_WRITE;
170 args.param1 = addr;
171 args.param2 = len;
172 args.flags = SPL_FLAG_SKIP_INIT;
173 rc = jz_x1000_send_args(dev, &args);
174 if(rc < 0)
175 return rc;
176
177 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
178 if(rc < 0)
179 return rc;
180
181 jz_sleepms(500);
182
183 struct x1000_spl_status status;
184 rc = jz_x1000_recv_status(dev, &status);
185 if(rc < 0)
186 return rc;
187
188 if(status.err_code != 0) {
189 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash write error: %s",
190 jz_x1000_nand_strerror(status.err_code));
191 return JZ_ERR_FLASH_ERROR;
192 }
193
194 return JZ_SUCCESS;
195}
196
197int jz_x1000_boot_rockbox(jz_usbdev* dev)
198{
199 struct x1000_spl_arguments args;
200 args.command = SPL_CMD_BOOT;
201 args.param1 = SPL_BOOTOPT_ROCKBOX;
202 args.param2 = 0;
203 args.flags = 0;
204 int rc = jz_x1000_send_args(dev, &args);
205 if(rc < 0)
206 return rc;
207
208 return jz_usb_start1(dev, SPL_EXEC_ADDRESS);
209}
diff --git a/rbutil/libtools.make b/rbutil/libtools.make
index 46244c07a0..3ab150e876 100644
--- a/rbutil/libtools.make
+++ b/rbutil/libtools.make
@@ -118,6 +118,12 @@ $(LIBBZIP2): $(OBJDIR)$(LIBBZIP2)
118$(OBJDIR)$(LIBBZIP2): 118$(OBJDIR)$(LIBBZIP2):
119 $(SILENT)$(MAKE) -C $(TOP)/bzip2 TARGET_DIR=$(OBJDIR) CC=$(CC) $@ 119 $(SILENT)$(MAKE) -C $(TOP)/bzip2 TARGET_DIR=$(OBJDIR) CC=$(CC) $@
120 120
121LIBMICROTAR = libmicrotar.a
122$(LIBMICROTAR): $(OBJDIR)$(LIBMICROTAR)
123
124$(OBJDIR)$(LIBMICROTAR):
125 $(SILENT)$(MAKE) -C $(TOP)/../lib/microtar/src TARGET_DIR=$(OBJDIR) CC=$(CC) $@
126
121# building the standalone executable 127# building the standalone executable
122$(BINARY): $(OBJS) $(EXTRADEPS) $(addprefix $(OBJDIR),$(EXTRALIBOBJS)) $(TARGET_DIR)lib$(OUTPUT).a 128$(BINARY): $(OBJS) $(EXTRADEPS) $(addprefix $(OBJDIR),$(EXTRALIBOBJS)) $(TARGET_DIR)lib$(OUTPUT).a
123 $(info LD $@) 129 $(info LD $@)