summaryrefslogtreecommitdiffstats
path: root/utils
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 /utils
parenta3d41098c7078bcb039535bfbbcc3f273cb5df61 (diff)
downloadrockbox-13c7f482ce9614012312e8189b91be0fee65f2d9.tar.gz
rockbox-13c7f482ce9614012312e8189b91be0fee65f2d9.tar.bz2
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
Diffstat (limited to 'utils')
-rw-r--r--utils/ingenic_tools/Makefile14
-rw-r--r--utils/ingenic_tools/usbboot.c420
2 files changed, 434 insertions, 0 deletions
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 @@
+DEFINES=
+CC?=gcc
+CFLAGS=-g -std=c99 -Wall $(DEFINES) `pkg-config --cflags libusb-1.0`
+LDFLAGS=`pkg-config --libs libusb-1.0`
+SRC=$(wildcard *.c)
+EXEC=$(SRC:.c=)
+
+all: $(EXEC)
+
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+clean:
+ 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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * Directly adapted from jz4760_tools/usbboot.c,
+ * Copyright (C) 2015 by Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <libusb.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#define VR_GET_CPU_INFO 0
+#define VR_SET_DATA_ADDRESS 1
+#define VR_SET_DATA_LENGTH 2
+#define VR_FLUSH_CACHES 3
+#define VR_PROGRAM_START1 4
+#define VR_PROGRAM_START2 5
+
+/* Global variables */
+bool g_verbose = false;
+libusb_device_handle* g_usb_dev = NULL;
+int g_vid = 0, g_pid = 0;
+
+/* Utility functions */
+void die(const char* msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+void verbose(const char* msg, ...)
+{
+ if(!g_verbose)
+ return;
+
+ va_list ap;
+ va_start(ap, msg);
+ vprintf(msg, ap);
+ printf("\n");
+ va_end(ap);
+}
+
+void open_usb(void)
+{
+ if(g_usb_dev) {
+ verbose("Closing USB device");
+ libusb_close(g_usb_dev);
+ }
+
+ if(g_vid == 0 || g_pid == 0)
+ die("Can't open USB device: vendor/product ID not specified");
+
+ verbose("Opening USB device %04x:%04x", g_vid, g_pid);
+ g_usb_dev = libusb_open_device_with_vid_pid(NULL, g_vid, g_pid);
+ if(!g_usb_dev)
+ die("Could not open USB device");
+
+ int ret = libusb_claim_interface(g_usb_dev, 0);
+ if(ret != 0) {
+ libusb_close(g_usb_dev);
+ die("Could not claim interface: %d", ret);
+ }
+}
+
+void ensure_usb(void)
+{
+ if(!g_usb_dev)
+ open_usb();
+}
+
+/* USB communication functions */
+void jz_get_cpu_info(void)
+{
+ ensure_usb();
+ verbose("Issue GET_CPU_INFO");
+
+ uint8_t buf[9];
+ int ret = libusb_control_transfer(g_usb_dev,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
+ VR_GET_CPU_INFO, 0, 0, buf, 8, 1000);
+ if(ret != 0)
+ die("Can't get CPU info: %d", ret);
+
+ buf[8] = 0;
+ printf("CPU info: %s\n", buf);
+}
+
+void jz_upload(const char* filename, int length)
+{
+ if(length < 0)
+ die("invalid upload length: %d", length);
+
+ ensure_usb();
+ verbose("Transfer %d bytes from device to host", length);
+
+ void* data = malloc(length);
+ int xfered = 0;
+ int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | 1,
+ data, length, &xfered, 10000);
+ if(ret != 0)
+ die("Transfer failed: %d", ret);
+ if(xfered != length)
+ die("Transfer error: got %d bytes, expected %d", xfered, length);
+
+ FILE* f = fopen(filename, "wb");
+ if(f == NULL)
+ die("Can't open file '%s' for writing", filename);
+
+ if(fwrite(data, length, 1, f) != 1)
+ die("Error writing transfered data to file");
+
+ fclose(f);
+ free(data);
+}
+
+void jz_download(const char* filename)
+{
+ FILE* f = fopen(filename, "rb");
+ if(f == NULL)
+ die("Can't open file '%s' for reading", filename);
+
+ fseek(f, 0, SEEK_END);
+ int length = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ void* data = malloc(length);
+ if(fread(data, length, 1, f) != 1)
+ die("Error reading data from file");
+ fclose(f);
+
+ verbose("Transfer %d bytes from host to device", length);
+ int xfered = 0;
+ int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_OUT | 1,
+ data, length, &xfered, 10000);
+ if(ret != 0)
+ die("Transfer failed: %d", ret);
+ if(xfered != length)
+ die("Transfer error: %d bytes recieved, expected %d", xfered, length);
+
+ free(data);
+}
+
+#define jz_vendor_out_func(name, type, fmt) \
+ void name(unsigned long param) { \
+ ensure_usb(); \
+ verbose("Issue " #type fmt, param); \
+ int ret = libusb_control_transfer(g_usb_dev, \
+ LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, \
+ VR_##type, param >> 16, param & 0xffff, NULL, 0, 1000); \
+ if(ret != 0) \
+ die("Request " #type " failed: %d", ret); \
+ }
+
+jz_vendor_out_func(jz_set_data_address, SET_DATA_ADDRESS, " 0x%08lx")
+jz_vendor_out_func(jz_set_data_length, SET_DATA_LENGTH, " 0x%0lx")
+jz_vendor_out_func(_jz_flush_caches, FLUSH_CACHES, "")
+jz_vendor_out_func(jz_program_start1, PROGRAM_START1, " 0x%08lx")
+jz_vendor_out_func(jz_program_start2, PROGRAM_START2, " 0x%08lx")
+#define jz_flush_caches() _jz_flush_caches(0)
+
+/* Default settings */
+struct cpu_profile {
+ const char* name;
+ int vid, pid;
+ unsigned long s1_load_addr, s1_exec_addr;
+ unsigned long s2_load_addr, s2_exec_addr;
+};
+
+static const struct cpu_profile cpu_profiles[] = {
+ {"x1000",
+ 0xa108, 0x1000,
+ 0xf4001000, 0xf4001800,
+ 0x80004000, 0x80004000},
+ {"jz4760",
+ 0x601a, 0x4760,
+ 0x80000000, 0x80000000,
+ 0x80000000, 0x80000000},
+ {NULL}
+};
+
+/* Simple "download and run" functions for dev purposes */
+unsigned long s1_load_addr = 0, s1_exec_addr = 0;
+unsigned long s2_load_addr = 0, s2_exec_addr = 0;
+
+void apply_cpu_profile(const char* name)
+{
+ const struct cpu_profile* p = &cpu_profiles[0];
+ for(p = &cpu_profiles[0]; p->name != NULL; ++p) {
+ if(strcmp(p->name, name) != 0)
+ continue;
+
+ g_vid = p->vid;
+ g_pid = p->pid;
+ s1_load_addr = p->s1_load_addr;
+ s1_exec_addr = p->s1_exec_addr;
+ s2_load_addr = p->s2_load_addr;
+ s2_exec_addr = p->s2_exec_addr;
+ return;
+ }
+
+ die("CPU '%s' not known", name);
+}
+
+void run_stage1(const char* filename)
+{
+ if(s1_load_addr == 0 || s1_exec_addr == 0)
+ die("No stage1 binary settings -- did you specify --cpu?");
+ jz_set_data_address(s1_load_addr);
+ jz_download(filename);
+ jz_program_start1(s1_exec_addr);
+}
+
+void run_stage2(const char* filename)
+{
+ if(s2_load_addr == 0 || s2_exec_addr == 0)
+ die("No stage2 binary settings -- did you specify --cpu?");
+ jz_set_data_address(s2_load_addr);
+ jz_download(filename);
+ jz_flush_caches();
+ jz_program_start2(s2_exec_addr);
+}
+
+/* Main functions */
+void usage()
+{
+ printf("\
+Usage: usbboot [options]\n\
+\n\
+Basic options:\n\
+ --cpu <cpu> Select device CPU type\n\
+ --stage1 <file> Download and execute stage1 binary\n\
+ --stage2 <file> Download and execute stage2 binary\n\
+\n\
+Advanced options:\n\
+ --vid <vid> Specify USB vendor ID\n\
+ --pid <pid> Specify USB product ID\n\
+ --cpuinfo Ask device for CPU info\n\
+ --addr <addr> Set data address\n\
+ --length <len> Set data length\n\
+ --upload <file> Transfer data from device (needs prior --length)\n\
+ --download <file> Transfer data to device\n\
+ --start1 <addr> Execute stage1 code at address\n\
+ --start2 <addr> Execute stage2 code at address\n\
+ --flush-caches Flush device CPU caches\n\
+ --renumerate Close and re-open the USB device\n\
+ --wait <time> Wait for <time> seconds\n\
+ -v, --verbose Be verbose\n\
+\n\
+Known CPU types and default stage1/stage2 binary settings:\n");
+ const struct cpu_profile* p = &cpu_profiles[0];
+ for(p = &cpu_profiles[0]; p->name != NULL; ++p) {
+ printf("* %s\n", p->name);
+ printf(" - USB ID: %04x:%04x\n", p->vid, p->pid);
+ printf(" - Stage1: load %#08lx, exec %#08lx\n",
+ p->s1_load_addr, p->s1_exec_addr);
+ printf(" - Stage2: load %#08lx, exec %#08lx\n",
+ p->s2_load_addr, p->s2_exec_addr);
+ }
+
+ exit(1);
+}
+
+void cleanup()
+{
+ if(g_usb_dev == NULL)
+ libusb_close(g_usb_dev);
+ libusb_exit(NULL);
+}
+
+int main(int argc, char* argv[])
+{
+ if(argc <= 1)
+ usage();
+
+ libusb_init(NULL);
+ atexit(cleanup);
+
+ enum {
+ OPT_VID = 0x100, OPT_PID,
+ OPT_CPUINFO,
+ OPT_START1, OPT_START2, OPT_FLUSH_CACHES,
+ OPT_RENUMERATE, OPT_WAIT,
+ };
+
+ static const struct option long_options[] = {
+ {"cpu", required_argument, 0, 'c'},
+ {"stage1", required_argument, 0, '1'},
+ {"stage2", required_argument, 0, '2'},
+ {"vid", required_argument, 0, OPT_VID},
+ {"pid", required_argument, 0, OPT_PID},
+ {"cpuinfo", no_argument, 0, OPT_CPUINFO},
+ {"addr", required_argument, 0, 'a'},
+ {"length", required_argument, 0, 'l'},
+ {"upload", required_argument, 0, 'u'},
+ {"download", required_argument, 0, 'd'},
+ {"start1", required_argument, 0, OPT_START1},
+ {"start2", required_argument, 0, OPT_START2},
+ {"flush-caches", no_argument, 0, OPT_FLUSH_CACHES},
+ {"renumerate", no_argument, 0, OPT_RENUMERATE},
+ {"wait", required_argument, 0, OPT_WAIT},
+ {"help", no_argument, 0, 'h'},
+ {"verbose", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+ };
+
+ int opt;
+ int data_length = -1;
+ while((opt = getopt_long(argc, argv, "hvc:1:2:a:l:u:d:", long_options, NULL)) != -1) {
+ unsigned long param;
+ char* end;
+ switch(opt) {
+ case OPT_VID:
+ case OPT_PID:
+ case 'a':
+ case 'l':
+ case OPT_START1:
+ case OPT_START2:
+ case OPT_WAIT:
+ param = strtoul(optarg, &end, 0);
+ if(*end)
+ die("Invalid argument '%s'", optarg);
+ break;
+ default:
+ break;
+ }
+
+ switch(opt) {
+ case 'h':
+ usage();
+ break;
+ case 'v':
+ g_verbose = true;
+ break;
+ case 'c':
+ apply_cpu_profile(optarg);
+ break;
+ case '1':
+ run_stage1(optarg);
+ break;
+ case '2':
+ run_stage2(optarg);
+ break;
+ case OPT_VID:
+ g_vid = param & 0xffff;
+ break;
+ case OPT_PID:
+ g_pid = param & 0xffff;
+ break;
+ case OPT_CPUINFO:
+ jz_get_cpu_info();
+ break;
+ case 'a':
+ jz_set_data_address(param);
+ break;
+ case 'l':
+ data_length = param;
+ jz_set_data_length(param);
+ break;
+ case 'u':
+ if(data_length < 0)
+ die("Need to specify --length before --upload");
+ jz_upload(optarg, data_length);
+ break;
+ case 'd':
+ jz_download(optarg);
+ break;
+ case OPT_START1:
+ jz_program_start1(param);
+ break;
+ case OPT_START2:
+ jz_program_start2(param);
+ break;
+ case OPT_FLUSH_CACHES:
+ jz_flush_caches();
+ break;
+ case OPT_RENUMERATE:
+ open_usb();
+ break;
+ case OPT_WAIT:
+ verbose("Wait %lu seconds", param);
+ sleep(param);
+ break;
+ default:
+ /* should only happen due to a bug */
+ die("Bad option");
+ break;
+ }
+ }
+
+ if(optind != argc)
+ die("Extra arguments on command line");
+
+ return 0;
+}