summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-02-27 22:04:03 +0000
committerSolomon Peachy <pizza@shaftnet.org>2021-02-28 02:52:56 +0000
commit13c7f482ce9614012312e8189b91be0fee65f2d9 (patch)
treefcf747c5cad7ed658095b551a477c53ebc4e1760
parenta3d41098c7078bcb039535bfbbcc3f273cb5df61 (diff)
downloadrockbox-13c7f482ce9614012312e8189b91be0fee65f2d9.tar.gz
rockbox-13c7f482ce9614012312e8189b91be0fee65f2d9.zip
Add ingenic_tools/usbboot utility
This is essentially an expanded version of jz4760_tools/usbboot, able to support both X1000 and JZ4760 CPUs and easily extended to handle other Ingenic CPUs using the same boot protocol. Change-Id: I70ce3acc3531d65390c6bbae4d2b3352140acf0a
-rw-r--r--.gitignore3
-rw-r--r--utils/ingenic_tools/Makefile14
-rw-r--r--utils/ingenic_tools/usbboot.c420
3 files changed, 437 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index a3ce328029..35cde028b5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -165,6 +165,9 @@ __pycache__
165/utils/jz4760_tools/packtools 165/utils/jz4760_tools/packtools
166/utils/jz4760_tools/usbboot 166/utils/jz4760_tools/usbboot
167 167
168# /utils/ingenic_tools
169/utils/ingenic_tools/usbboot
170
168# /utils/nwztools 171# /utils/nwztools
169/utils/nwztools/scsitools/scsitool 172/utils/nwztools/scsitools/scsitool
170/utils/nwztools/upgtools/upgtool 173/utils/nwztools/upgtools/upgtool
diff --git a/utils/ingenic_tools/Makefile b/utils/ingenic_tools/Makefile
new file mode 100644
index 0000000000..78b538ea4b
--- /dev/null
+++ b/utils/ingenic_tools/Makefile
@@ -0,0 +1,14 @@
1DEFINES=
2CC?=gcc
3CFLAGS=-g -std=c99 -Wall $(DEFINES) `pkg-config --cflags libusb-1.0`
4LDFLAGS=`pkg-config --libs libusb-1.0`
5SRC=$(wildcard *.c)
6EXEC=$(SRC:.c=)
7
8all: $(EXEC)
9
10%: %.c
11 $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
12
13clean:
14 rm -fr $(EXEC)
diff --git a/utils/ingenic_tools/usbboot.c b/utils/ingenic_tools/usbboot.c
new file mode 100644
index 0000000000..0001711605
--- /dev/null
+++ b/utils/ingenic_tools/usbboot.c
@@ -0,0 +1,420 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * Directly adapted from jz4760_tools/usbboot.c,
13 * Copyright (C) 2015 by Amaury Pouly
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24
25#include <libusb.h>
26#include <getopt.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <stdbool.h>
30#include <string.h>
31#include <stdio.h>
32#include <stdarg.h>
33
34#define VR_GET_CPU_INFO 0
35#define VR_SET_DATA_ADDRESS 1
36#define VR_SET_DATA_LENGTH 2
37#define VR_FLUSH_CACHES 3
38#define VR_PROGRAM_START1 4
39#define VR_PROGRAM_START2 5
40
41/* Global variables */
42bool g_verbose = false;
43libusb_device_handle* g_usb_dev = NULL;
44int g_vid = 0, g_pid = 0;
45
46/* Utility functions */
47void die(const char* msg, ...)
48{
49 va_list ap;
50 va_start(ap, msg);
51 vfprintf(stderr, msg, ap);
52 fprintf(stderr, "\n");
53 va_end(ap);
54 exit(1);
55}
56
57void verbose(const char* msg, ...)
58{
59 if(!g_verbose)
60 return;
61
62 va_list ap;
63 va_start(ap, msg);
64 vprintf(msg, ap);
65 printf("\n");
66 va_end(ap);
67}
68
69void open_usb(void)
70{
71 if(g_usb_dev) {
72 verbose("Closing USB device");
73 libusb_close(g_usb_dev);
74 }
75
76 if(g_vid == 0 || g_pid == 0)
77 die("Can't open USB device: vendor/product ID not specified");
78
79 verbose("Opening USB device %04x:%04x", g_vid, g_pid);
80 g_usb_dev = libusb_open_device_with_vid_pid(NULL, g_vid, g_pid);
81 if(!g_usb_dev)
82 die("Could not open USB device");
83
84 int ret = libusb_claim_interface(g_usb_dev, 0);
85 if(ret != 0) {
86 libusb_close(g_usb_dev);
87 die("Could not claim interface: %d", ret);
88 }
89}
90
91void ensure_usb(void)
92{
93 if(!g_usb_dev)
94 open_usb();
95}
96
97/* USB communication functions */
98void jz_get_cpu_info(void)
99{
100 ensure_usb();
101 verbose("Issue GET_CPU_INFO");
102
103 uint8_t buf[9];
104 int ret = libusb_control_transfer(g_usb_dev,
105 LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
106 VR_GET_CPU_INFO, 0, 0, buf, 8, 1000);
107 if(ret != 0)
108 die("Can't get CPU info: %d", ret);
109
110 buf[8] = 0;
111 printf("CPU info: %s\n", buf);
112}
113
114void jz_upload(const char* filename, int length)
115{
116 if(length < 0)
117 die("invalid upload length: %d", length);
118
119 ensure_usb();
120 verbose("Transfer %d bytes from device to host", length);
121
122 void* data = malloc(length);
123 int xfered = 0;
124 int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | 1,
125 data, length, &xfered, 10000);
126 if(ret != 0)
127 die("Transfer failed: %d", ret);
128 if(xfered != length)
129 die("Transfer error: got %d bytes, expected %d", xfered, length);
130
131 FILE* f = fopen(filename, "wb");
132 if(f == NULL)
133 die("Can't open file '%s' for writing", filename);
134
135 if(fwrite(data, length, 1, f) != 1)
136 die("Error writing transfered data to file");
137
138 fclose(f);
139 free(data);
140}
141
142void jz_download(const char* filename)
143{
144 FILE* f = fopen(filename, "rb");
145 if(f == NULL)
146 die("Can't open file '%s' for reading", filename);
147
148 fseek(f, 0, SEEK_END);
149 int length = ftell(f);
150 fseek(f, 0, SEEK_SET);
151
152 void* data = malloc(length);
153 if(fread(data, length, 1, f) != 1)
154 die("Error reading data from file");
155 fclose(f);
156
157 verbose("Transfer %d bytes from host to device", length);
158 int xfered = 0;
159 int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_OUT | 1,
160 data, length, &xfered, 10000);
161 if(ret != 0)
162 die("Transfer failed: %d", ret);
163 if(xfered != length)
164 die("Transfer error: %d bytes recieved, expected %d", xfered, length);
165
166 free(data);
167}
168
169#define jz_vendor_out_func(name, type, fmt) \
170 void name(unsigned long param) { \
171 ensure_usb(); \
172 verbose("Issue " #type fmt, param); \
173 int ret = libusb_control_transfer(g_usb_dev, \
174 LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, \
175 VR_##type, param >> 16, param & 0xffff, NULL, 0, 1000); \
176 if(ret != 0) \
177 die("Request " #type " failed: %d", ret); \
178 }
179
180jz_vendor_out_func(jz_set_data_address, SET_DATA_ADDRESS, " 0x%08lx")
181jz_vendor_out_func(jz_set_data_length, SET_DATA_LENGTH, " 0x%0lx")
182jz_vendor_out_func(_jz_flush_caches, FLUSH_CACHES, "")
183jz_vendor_out_func(jz_program_start1, PROGRAM_START1, " 0x%08lx")
184jz_vendor_out_func(jz_program_start2, PROGRAM_START2, " 0x%08lx")
185#define jz_flush_caches() _jz_flush_caches(0)
186
187/* Default settings */
188struct cpu_profile {
189 const char* name;
190 int vid, pid;
191 unsigned long s1_load_addr, s1_exec_addr;
192 unsigned long s2_load_addr, s2_exec_addr;
193};
194
195static const struct cpu_profile cpu_profiles[] = {
196 {"x1000",
197 0xa108, 0x1000,
198 0xf4001000, 0xf4001800,
199 0x80004000, 0x80004000},
200 {"jz4760",
201 0x601a, 0x4760,
202 0x80000000, 0x80000000,
203 0x80000000, 0x80000000},
204 {NULL}
205};
206
207/* Simple "download and run" functions for dev purposes */
208unsigned long s1_load_addr = 0, s1_exec_addr = 0;
209unsigned long s2_load_addr = 0, s2_exec_addr = 0;
210
211void apply_cpu_profile(const char* name)
212{
213 const struct cpu_profile* p = &cpu_profiles[0];
214 for(p = &cpu_profiles[0]; p->name != NULL; ++p) {
215 if(strcmp(p->name, name) != 0)
216 continue;
217
218 g_vid = p->vid;
219 g_pid = p->pid;
220 s1_load_addr = p->s1_load_addr;
221 s1_exec_addr = p->s1_exec_addr;
222 s2_load_addr = p->s2_load_addr;
223 s2_exec_addr = p->s2_exec_addr;
224 return;
225 }
226
227 die("CPU '%s' not known", name);
228}
229
230void run_stage1(const char* filename)
231{
232 if(s1_load_addr == 0 || s1_exec_addr == 0)
233 die("No stage1 binary settings -- did you specify --cpu?");
234 jz_set_data_address(s1_load_addr);
235 jz_download(filename);
236 jz_program_start1(s1_exec_addr);
237}
238
239void run_stage2(const char* filename)
240{
241 if(s2_load_addr == 0 || s2_exec_addr == 0)
242 die("No stage2 binary settings -- did you specify --cpu?");
243 jz_set_data_address(s2_load_addr);
244 jz_download(filename);
245 jz_flush_caches();
246 jz_program_start2(s2_exec_addr);
247}
248
249/* Main functions */
250void usage()
251{
252 printf("\
253Usage: usbboot [options]\n\
254\n\
255Basic options:\n\
256 --cpu <cpu> Select device CPU type\n\
257 --stage1 <file> Download and execute stage1 binary\n\
258 --stage2 <file> Download and execute stage2 binary\n\
259\n\
260Advanced options:\n\
261 --vid <vid> Specify USB vendor ID\n\
262 --pid <pid> Specify USB product ID\n\
263 --cpuinfo Ask device for CPU info\n\
264 --addr <addr> Set data address\n\
265 --length <len> Set data length\n\
266 --upload <file> Transfer data from device (needs prior --length)\n\
267 --download <file> Transfer data to device\n\
268 --start1 <addr> Execute stage1 code at address\n\
269 --start2 <addr> Execute stage2 code at address\n\
270 --flush-caches Flush device CPU caches\n\
271 --renumerate Close and re-open the USB device\n\
272 --wait <time> Wait for <time> seconds\n\
273 -v, --verbose Be verbose\n\
274\n\
275Known CPU types and default stage1/stage2 binary settings:\n");
276 const struct cpu_profile* p = &cpu_profiles[0];
277 for(p = &cpu_profiles[0]; p->name != NULL; ++p) {
278 printf("* %s\n", p->name);
279 printf(" - USB ID: %04x:%04x\n", p->vid, p->pid);
280 printf(" - Stage1: load %#08lx, exec %#08lx\n",
281 p->s1_load_addr, p->s1_exec_addr);
282 printf(" - Stage2: load %#08lx, exec %#08lx\n",
283 p->s2_load_addr, p->s2_exec_addr);
284 }
285
286 exit(1);
287}
288
289void cleanup()
290{
291 if(g_usb_dev == NULL)
292 libusb_close(g_usb_dev);
293 libusb_exit(NULL);
294}
295
296int main(int argc, char* argv[])
297{
298 if(argc <= 1)
299 usage();
300
301 libusb_init(NULL);
302 atexit(cleanup);
303
304 enum {
305 OPT_VID = 0x100, OPT_PID,
306 OPT_CPUINFO,
307 OPT_START1, OPT_START2, OPT_FLUSH_CACHES,
308 OPT_RENUMERATE, OPT_WAIT,
309 };
310
311 static const struct option long_options[] = {
312 {"cpu", required_argument, 0, 'c'},
313 {"stage1", required_argument, 0, '1'},
314 {"stage2", required_argument, 0, '2'},
315 {"vid", required_argument, 0, OPT_VID},
316 {"pid", required_argument, 0, OPT_PID},
317 {"cpuinfo", no_argument, 0, OPT_CPUINFO},
318 {"addr", required_argument, 0, 'a'},
319 {"length", required_argument, 0, 'l'},
320 {"upload", required_argument, 0, 'u'},
321 {"download", required_argument, 0, 'd'},
322 {"start1", required_argument, 0, OPT_START1},
323 {"start2", required_argument, 0, OPT_START2},
324 {"flush-caches", no_argument, 0, OPT_FLUSH_CACHES},
325 {"renumerate", no_argument, 0, OPT_RENUMERATE},
326 {"wait", required_argument, 0, OPT_WAIT},
327 {"help", no_argument, 0, 'h'},
328 {"verbose", no_argument, 0, 'v'},
329 {0, 0, 0, 0}
330 };
331
332 int opt;
333 int data_length = -1;
334 while((opt = getopt_long(argc, argv, "hvc:1:2:a:l:u:d:", long_options, NULL)) != -1) {
335 unsigned long param;
336 char* end;
337 switch(opt) {
338 case OPT_VID:
339 case OPT_PID:
340 case 'a':
341 case 'l':
342 case OPT_START1:
343 case OPT_START2:
344 case OPT_WAIT:
345 param = strtoul(optarg, &end, 0);
346 if(*end)
347 die("Invalid argument '%s'", optarg);
348 break;
349 default:
350 break;
351 }
352
353 switch(opt) {
354 case 'h':
355 usage();
356 break;
357 case 'v':
358 g_verbose = true;
359 break;
360 case 'c':
361 apply_cpu_profile(optarg);
362 break;
363 case '1':
364 run_stage1(optarg);
365 break;
366 case '2':
367 run_stage2(optarg);
368 break;
369 case OPT_VID:
370 g_vid = param & 0xffff;
371 break;
372 case OPT_PID:
373 g_pid = param & 0xffff;
374 break;
375 case OPT_CPUINFO:
376 jz_get_cpu_info();
377 break;
378 case 'a':
379 jz_set_data_address(param);
380 break;
381 case 'l':
382 data_length = param;
383 jz_set_data_length(param);
384 break;
385 case 'u':
386 if(data_length < 0)
387 die("Need to specify --length before --upload");
388 jz_upload(optarg, data_length);
389 break;
390 case 'd':
391 jz_download(optarg);
392 break;
393 case OPT_START1:
394 jz_program_start1(param);
395 break;
396 case OPT_START2:
397 jz_program_start2(param);
398 break;
399 case OPT_FLUSH_CACHES:
400 jz_flush_caches();
401 break;
402 case OPT_RENUMERATE:
403 open_usb();
404 break;
405 case OPT_WAIT:
406 verbose("Wait %lu seconds", param);
407 sleep(param);
408 break;
409 default:
410 /* should only happen due to a bug */
411 die("Bad option");
412 break;
413 }
414 }
415
416 if(optind != argc)
417 die("Extra arguments on command line");
418
419 return 0;
420}