diff options
Diffstat (limited to 'rbutil')
-rw-r--r-- | rbutil/jztool/Makefile | 8 | ||||
-rw-r--r-- | rbutil/jztool/README.md | 118 | ||||
-rw-r--r-- | rbutil/jztool/include/jztool.h | 37 | ||||
-rw-r--r-- | rbutil/jztool/jztool.c | 162 | ||||
-rw-r--r-- | rbutil/jztool/src/context.c | 10 | ||||
-rw-r--r-- | rbutil/jztool/src/device_info.c | 29 | ||||
-rw-r--r-- | rbutil/jztool/src/fiiom3k.c | 373 | ||||
-rw-r--r-- | rbutil/jztool/src/identify_file.c | 50 | ||||
-rw-r--r-- | rbutil/jztool/src/paramlist.c | 135 | ||||
-rw-r--r-- | rbutil/jztool/src/usb.c | 64 | ||||
-rw-r--r-- | rbutil/jztool/src/x1000.c | 209 | ||||
-rw-r--r-- | rbutil/libtools.make | 6 |
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 | ||
8 | CFLAGS += -Wall -Wextra -Iinclude | 8 | CFLAGS += -Wall -Wextra -Iinclude -I../../tools/ucl/include -I../../lib/microtar/src |
9 | OUTPUT = jztool | 9 | OUTPUT = jztool |
10 | 10 | ||
11 | ifdef RELEASE | 11 | ifdef RELEASE |
@@ -15,10 +15,9 @@ CFLAGS += -O0 -ggdb | |||
15 | endif | 15 | endif |
16 | 16 | ||
17 | LIBSOURCES := src/buffer.c src/context.c src/device_info.c \ | 17 | LIBSOURCES := 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 | ||
20 | SOURCES := $(LIBSOURCES) jztool.c | 19 | SOURCES := $(LIBSOURCES) jztool.c |
21 | EXTRADEPS := | 20 | EXTRADEPS := libucl.a libmicrotar.a |
22 | 21 | ||
23 | CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -) | 22 | CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -) |
24 | 23 | ||
@@ -27,6 +26,7 @@ ifeq ($(findstring WIN32,$(CPPDEFINES)),WIN32) | |||
27 | else | 26 | else |
28 | ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE) | 27 | ifeq ($(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! | ||
30 | CFLAGS += -I/usr/local/include -I/opt/homebrew/include | 30 | CFLAGS += -I/usr/local/include -I/opt/homebrew/include |
31 | LDOPTS += -L/usr/local/lib -L/opt/homebrew/lib -lusb-1.0 | 31 | LDOPTS += -L/usr/local/lib -L/opt/homebrew/lib -lusb-1.0 |
32 | else | 32 | else |
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 | ||
3 | The `jztool` utility can install, backup, and restore the bootloader on | 3 | The `jztool` utility can help install, backup, and restore the bootloader on |
4 | Rockbox players based on a supported Ingenic SoC. | 4 | Rockbox players based on a supported Ingenic SoC. |
5 | 5 | ||
6 | ## FiiO M3K | 6 | ## FiiO M3K |
7 | 7 | ||
8 | To use `jztool` on the FiiO M3K you have to connect the player to your | 8 | First, get a copy of the `bootloader.m3k` file, either by downloading it |
9 | computer in USB boot mode. | 9 | from <https://rockbox.org>, or by compiling it yourself (choose 'B'ootloader |
10 | build when configuring your build). | ||
10 | 11 | ||
11 | The easiest way to do this is by plugging in the microUSB cable to the M3K | 12 | The first time you install Rockbox, you need to load the Rockbox bootloader |
12 | and holding the volume down button while plugging the USB into your computer. | 13 | over USB by entering USB boot mode. The easiest way to do this is by plugging |
13 | If you entered USB boot mode, the button light will turn on but the LCD will | 14 | in the microUSB cable to the M3K and holding the VOL- button while plugging |
14 | turn off. | 15 | the USB into your computer. If you entered USB boot mode, the button light |
16 | will turn on but the LCD will remain black. | ||
15 | 17 | ||
16 | To install or update the Rockbox bootloader on the M3K, use the command | 18 | Copy 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 | 19 | instructions below which are appropriate to your OS. |
18 | current bootloader so you can restore it in case of any problems. | ||
19 | 20 | ||
20 | After any operation finishes, you will have to force a power off of the M3K | 21 | ### Running jztool |
21 | by holding down the power button for at least 10 seconds. This must be done | 22 | |
22 | whether the operation succeeds or fails. Just don't power off or unplug the | 23 | #### Linux/Mac |
23 | device in the middle of an operation -- that might make bad things happen. | 24 | |
25 | Run the following command in a terminal. Note that on Linux, you will need to | ||
26 | have 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 | |||
37 | To allow `jztool` access to the M3K in USB boot mode, you need to install | ||
38 | the WinUSB driver. The recommended way to install it is using Zadig, which | ||
39 | may be downloaded from its homepage <https://zadig.akeo.ie>. Please note | ||
40 | this is 3rd party software not maintained or supported by Rockbox developers. | ||
41 | (Zadig will require administrator access on the machine you are using.) | ||
42 | |||
43 | When running Zadig you must select the WinUSB driver; the other driver options | ||
44 | will not work properly with `jztool`. You will have to select the correct USB | ||
45 | device in Zadig -- the name and USB IDs of the M3K in USB boot mode are listed | ||
46 | below. NOTE: the device name may show only as "X" and a hollow square in Zadig. | ||
47 | The IDs will not change, so those are the most reliable way to confirm you have | ||
48 | selected the correct device. | ||
49 | |||
50 | ``` | ||
51 | Name: Ingenic Semiconductor Co.,Ltd X1000 | ||
52 | USB ID: A108 1000 | ||
53 | ``` | ||
54 | |||
55 | Assuming you installed the WinUSB driver successfully, open a command prompt | ||
56 | in the folder containing `jztool`. Administrator access is not required for | ||
57 | this step. | ||
58 | |||
59 | Type 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 | |||
68 | After running `jztool` successfully your M3K will display the recovery menu | ||
69 | of the Rockbox bootloader. If you want to permanently install Rockbox to your | ||
70 | M3K, copy `bootloader.m3k` to the root of an SD card, insert it to your device, | ||
71 | then choose "Install/update bootloader" from the menu. | ||
72 | |||
73 | It is _highly_ recommended that you take a backup of your existing bootloader | ||
74 | in case of any trouble -- choose "Backup bootloader" from the recovery menu. | ||
75 | The backup file is called "fiiom3k-boot.bin" and will be saved to the root of | ||
76 | the SD card. If you need to restore it, simply place the file at the root of | ||
77 | your SD card and select "Restore bootloader". | ||
78 | |||
79 | In the future if you want to backup, restore, or update the bootloader, you | ||
80 | can 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 | ||
25 | See `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. | |||
38 | Adding support to the Rockbox utility should be mostly boilerplate since the | 100 | Adding support to the Rockbox utility should be mostly boilerplate since the |
39 | jztool library wraps all the troublesome details. | 101 | jztool library wraps all the troublesome details. |
40 | 102 | ||
41 | Getting appropriate privileges to access the USB device is the main issue. | 103 | Permissions are an issue on Linux because by default only root can access |
42 | Preferably, 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 | 105 | a udev rule to allow access to the specific USB IDs we need, eg. allowing | |
44 | - Windows: not sure | 106 | users 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 | |||
50 | Windows wants to see a driver installed before we can access the USB device, | ||
51 | the easiest way to do this is by having the user run Zadig, a 3rd party app | ||
52 | which can install the WinUSB driver. WinUSB itself is from Microsoft and | ||
53 | bundled with Windows. | ||
54 | |||
55 | Zadig's homepage: https://zadig.akeo.ie/ | ||
56 | |||
57 | ### Porting to Mac | ||
58 | 107 | ||
59 | According to the libusb wiki, libusb works on Mac without any special setup or | 108 | On Windows and Mac, no special permissions are needed to access USB devices |
60 | privileges, presumably porting there is easy. | 109 | assuming the drivers are set up. (Zadig does require administrator access |
110 | to 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; | |||
37 | typedef struct jz_usbdev jz_usbdev; | 37 | typedef struct jz_usbdev jz_usbdev; |
38 | typedef struct jz_device_info jz_device_info; | 38 | typedef struct jz_device_info jz_device_info; |
39 | typedef struct jz_buffer jz_buffer; | 39 | typedef struct jz_buffer jz_buffer; |
40 | typedef struct jz_paramlist jz_paramlist; | ||
41 | 40 | ||
42 | typedef enum jz_error jz_error; | 41 | typedef enum jz_error jz_error; |
43 | typedef enum jz_identify_error jz_identify_error; | 42 | typedef enum jz_identify_error jz_identify_error; |
@@ -46,7 +45,6 @@ typedef enum jz_device_type jz_device_type; | |||
46 | typedef enum jz_cpu_type jz_cpu_type; | 45 | typedef enum jz_cpu_type jz_cpu_type; |
47 | 46 | ||
48 | typedef void(*jz_log_cb)(jz_log_level, const char*); | 47 | typedef void(*jz_log_cb)(jz_log_level, const char*); |
49 | typedef int(*jz_device_action_fn)(jz_context*, jz_paramlist*); | ||
50 | 48 | ||
51 | enum jz_error { | 49 | enum 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 | ||
101 | struct jz_buffer { | 95 | struct jz_buffer { |
@@ -132,7 +126,6 @@ const jz_device_info* jz_get_device_info_indexed(int index); | |||
132 | 126 | ||
133 | int jz_identify_x1000_spl(const void* data, size_t len); | 127 | int jz_identify_x1000_spl(const void* data, size_t len); |
134 | int jz_identify_scramble_image(const void* data, size_t len); | 128 | int jz_identify_scramble_image(const void* data, size_t len); |
135 | int 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); | |||
148 | int jz_usb_flush_caches(jz_usbdev* dev); | 141 | int 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 | ||
154 | int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data); | 147 | int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename); |
155 | int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data); | ||
156 | int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data); | ||
157 | int jz_x1000_boot_rockbox(jz_usbdev* dev); | ||
158 | |||
159 | /****************************************************************************** | ||
160 | * FiiO M3K bootloader backup/installation | ||
161 | */ | ||
162 | |||
163 | int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr); | ||
164 | int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf); | ||
165 | int 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 | |||
169 | int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl); | ||
170 | int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl); | ||
171 | int 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); | |||
181 | int jz_buffer_save(jz_buffer* buf, const char* filename); | 157 | int jz_buffer_save(jz_buffer* buf, const char* filename); |
182 | 158 | ||
183 | /****************************************************************************** | 159 | /****************************************************************************** |
184 | * Parameter list | ||
185 | */ | ||
186 | |||
187 | jz_paramlist* jz_paramlist_new(void); | ||
188 | void jz_paramlist_free(jz_paramlist* pl); | ||
189 | int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value); | ||
190 | const 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 | ||
28 | jz_context* jz = NULL; | 28 | jz_context* jz = NULL; |
29 | jz_usbdev* usbdev = NULL; | ||
29 | const jz_device_info* dev_info = NULL; | 30 | const jz_device_info* dev_info = NULL; |
30 | int dev_action = -1; | 31 | |
31 | jz_paramlist* action_params = NULL; | 32 | void 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 | |||
60 | int 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 | ||
33 | void usage(void) | 82 | void 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 | ||
81 | void cleanup(void) | 106 | void 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 | */ |
138 | void jz_sleepms(int ms) | 142 | void 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 */ | ||
147 | int jz_context_ref_libusb(jz_context* jz) | 156 | int 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 */ | ||
161 | void jz_context_unref_libusb(jz_context* jz) | 171 | void 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 | ||
25 | static const char* const fiiom3k_action_names[] = { | ||
26 | "install", | ||
27 | "backup", | ||
28 | "restore", | ||
29 | }; | ||
30 | |||
31 | static const char* const fiiom3k_install_action_params[] = | ||
32 | {"spl", "bootloader", "backup", "without-backup", NULL}; | ||
33 | |||
34 | static const char* const fiiom3k_backuprestore_action_params[] = | ||
35 | {"spl", "image", NULL}; | ||
36 | |||
37 | static const char* const* fiiom3k_action_params[] = { | ||
38 | fiiom3k_install_action_params, | ||
39 | fiiom3k_backuprestore_action_params, | ||
40 | fiiom3k_backuprestore_action_params, | ||
41 | }; | ||
42 | |||
43 | static const jz_device_action_fn fiiom3k_action_funcs[] = { | ||
44 | jz_fiiom3k_install, | ||
45 | jz_fiiom3k_backup, | ||
46 | jz_fiiom3k_restore, | ||
47 | }; | ||
48 | |||
49 | static const jz_device_info infotable[] = { | 25 | static 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. */ | ||
72 | const jz_device_info* jz_get_device_info(jz_device_type type) | 45 | const 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 | 29 | static 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 | |||
32 | int 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 | ||
48 | int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf) | 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) | ||
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 | ||
61 | int 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 | |||
110 | struct fiiom3k_workstate { | ||
111 | jz_usbdev* dev; | ||
112 | jz_buffer* bufs[NUMBUFS]; | ||
113 | }; | ||
114 | |||
115 | static void fiiom3k_action_cleanup(struct fiiom3k_workstate* state) | ||
116 | { | ||
117 | for(int i = 0; i < NUMBUFS; ++i) | ||
118 | if(state->bufs[i]) | ||
119 | jz_buffer_free(state->bufs[i]); | ||
120 | 96 | ||
121 | if(state->dev) | 97 | if(src_len < 4) |
122 | jz_usb_close(state->dev); | 98 | goto error; |
123 | } | ||
124 | 99 | ||
125 | static 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 | ||
148 | static int fiiom3k_action_setup(jz_context* jz, jz_paramlist* pl, | 135 | static 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 | |||
144 | static 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 | ||
170 | int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl) | 157 | static 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; | 199 | static 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 | ||
227 | int 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 | */ | ||
219 | int 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 | |||
260 | int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl) | ||
261 | { | ||
262 | struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; | ||
263 | int rc; | ||
264 | |||
265 | rc = fiiom3k_action_loadbuf(jz, pl, &state, IMGBUF); | ||
266 | if(rc < 0) | ||
267 | goto error; | 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 | */ | ||
84 | int jz_identify_x1000_spl(const void* data, size_t len) | 92 | int 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 | */ | ||
118 | int jz_identify_scramble_image(const void* data, size_t len) | 134 | int 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 | |||
147 | int 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 | |||
27 | struct jz_paramlist { | ||
28 | int size; | ||
29 | char** keys; | ||
30 | char** values; | ||
31 | }; | ||
32 | |||
33 | static 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 | |||
61 | jz_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 | |||
73 | void 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 | |||
88 | int 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 | |||
128 | const 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 | */ | ||
33 | int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id) | 43 | int 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 | */ | ||
122 | void jz_usb_close(jz_usbdev* dev) | 137 | void 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 | ||
130 | static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg) | 148 | static 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 | ||
146 | static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf) | 176 | static 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 | ||
165 | static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr, | 199 | static 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 | */ | ||
180 | int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data) | 221 | int 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 | */ | ||
185 | int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data) | 233 | int 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 | */ | ||
190 | int jz_usb_start1(jz_usbdev* dev, uint32_t addr) | 243 | int 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 | */ | ||
195 | int jz_usb_start2(jz_usbdev* dev, uint32_t addr) | 253 | int 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 | */ | ||
200 | int jz_usb_flush_caches(jz_usbdev* dev) | 262 | int 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 | |||
27 | static 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 | |||
37 | static 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 | |||
44 | static 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 | |||
71 | static 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 | |||
80 | static 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 | |||
91 | int 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 | |||
131 | int 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 | |||
162 | int 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 | |||
197 | int 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 | ||
121 | LIBMICROTAR = 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 $@) |