From cc22df198d0ccb64dfdfe0c2f247f7d86b7fd750 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Tue, 11 May 2021 13:25:26 +0100 Subject: [PATCH] jztool: Support new M3K bootloader Change-Id: Ia2d96893a9a5c77deb71c1fe32ae5a0585093f5b --- rbutil/jztool/Makefile | 8 +- rbutil/jztool/README.md | 118 +++++--- rbutil/jztool/include/jztool.h | 37 +-- rbutil/jztool/jztool.c | 164 +++++------ rbutil/jztool/src/context.c | 10 + rbutil/jztool/src/device_info.c | 29 +- rbutil/jztool/src/fiiom3k.c | 447 +++++++++++++++--------------- rbutil/jztool/src/identify_file.c | 50 ++-- rbutil/jztool/src/paramlist.c | 135 --------- rbutil/jztool/src/usb.c | 64 ++++- rbutil/jztool/src/x1000.c | 209 -------------- rbutil/libtools.make | 6 + 12 files changed, 471 insertions(+), 806 deletions(-) delete mode 100644 rbutil/jztool/src/paramlist.c delete mode 100644 rbutil/jztool/src/x1000.c 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 @@ # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ # \/ \/ \/ \/ \/ -CFLAGS += -Wall -Wextra -Iinclude +CFLAGS += -Wall -Wextra -Iinclude -I../../tools/ucl/include -I../../lib/microtar/src OUTPUT = jztool ifdef RELEASE @@ -15,10 +15,9 @@ CFLAGS += -O0 -ggdb endif LIBSOURCES := src/buffer.c src/context.c src/device_info.c \ - src/fiiom3k.c src/identify_file.c src/paramlist.c \ - src/usb.c src/x1000.c + src/identify_file.c src/fiiom3k.c src/usb.c SOURCES := $(LIBSOURCES) jztool.c -EXTRADEPS := +EXTRADEPS := libucl.a libmicrotar.a CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -) @@ -27,6 +26,7 @@ ifeq ($(findstring WIN32,$(CPPDEFINES)),WIN32) else ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE) # OSX -- /opt location is cheesy attempt to support ARM macs +# COMPLETELY UNTESTED, testing from someone with an actual Mac is appreciated! CFLAGS += -I/usr/local/include -I/opt/homebrew/include LDOPTS += -L/usr/local/lib -L/opt/homebrew/lib -lusb-1.0 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 @@ # jztool -- Ingenic device utility & bootloader installer -The `jztool` utility can install, backup, and restore the bootloader on +The `jztool` utility can help install, backup, and restore the bootloader on Rockbox players based on a supported Ingenic SoC. ## FiiO M3K -To use `jztool` on the FiiO M3K you have to connect the player to your -computer in USB boot mode. +First, get a copy of the `bootloader.m3k` file, either by downloading it +from , or by compiling it yourself (choose 'B'ootloader +build when configuring your build). -The easiest way to do this is by plugging in the microUSB cable to the M3K -and holding the volume down button while plugging the USB into your computer. -If you entered USB boot mode, the button light will turn on but the LCD will -turn off. +The first time you install Rockbox, you need to load the Rockbox bootloader +over USB by entering USB boot mode. The easiest way to do this is by plugging +in the microUSB cable to the M3K and holding the VOL- button while plugging +the USB into your computer. If you entered USB boot mode, the button light +will turn on but the LCD will remain black. -To install or update the Rockbox bootloader on the M3K, use the command -`jztool fiiom3k install`. It is recommended that you take a backup of your -current bootloader so you can restore it in case of any problems. +Copy the `bootloader.m3k` next to the `jztool` executable and follow the +instructions below which are appropriate to your OS. -After any operation finishes, you will have to force a power off of the M3K -by holding down the power button for at least 10 seconds. This must be done -whether the operation succeeds or fails. Just don't power off or unplug the -device in the middle of an operation -- that might make bad things happen. +### Running jztool + +#### Linux/Mac + +Run the following command in a terminal. Note that on Linux, you will need to +have root access to allow libusb to access the USB device. + +```sh +# Linux / Mac +# NOTE: root permissions are required on Linux to access the USB device +# eg. with 'sudo' or 'su -c' depending on your distro. +$ ./jztool fiiom3k load bootloader.m3k +``` + +#### Windows + +To allow `jztool` access to the M3K in USB boot mode, you need to install +the WinUSB driver. The recommended way to install it is using Zadig, which +may be downloaded from its homepage . Please note +this is 3rd party software not maintained or supported by Rockbox developers. +(Zadig will require administrator access on the machine you are using.) + +When running Zadig you must select the WinUSB driver; the other driver options +will not work properly with `jztool`. You will have to select the correct USB +device in Zadig -- the name and USB IDs of the M3K in USB boot mode are listed +below. NOTE: the device name may show only as "X" and a hollow square in Zadig. +The IDs will not change, so those are the most reliable way to confirm you have +selected the correct device. + +``` +Name: Ingenic Semiconductor Co.,Ltd X1000 +USB ID: A108 1000 +``` + +Assuming you installed the WinUSB driver successfully, open a command prompt +in the folder containing `jztool`. Administrator access is not required for +this step. + +Type the following command to load the Rockbox bootloader: + +```sh +# Windows +$ jztool.exe fiiom3k load bootloader.m3k +``` + +### Further instructions + +After running `jztool` successfully your M3K will display the recovery menu +of the Rockbox bootloader. If you want to permanently install Rockbox to your +M3K, copy `bootloader.m3k` to the root of an SD card, insert it to your device, +then choose "Install/update bootloader" from the menu. + +It is _highly_ recommended that you take a backup of your existing bootloader +in case of any trouble -- choose "Backup bootloader" from the recovery menu. +The backup file is called "fiiom3k-boot.bin" and will be saved to the root of +the SD card. If you need to restore it, simply place the file at the root of +your SD card and select "Restore bootloader". + +In the future if you want to backup, restore, or update the bootloader, you +can access the Rockbox bootloader's recovery menu by holding VOL+ when booting. + +### Known issues + +- When using the bootloader's USB mode, you may get stuck on "Waiting for USB" + even though the cable is already plugged in. If this occurs, unplug the USB + cable and plug it back in to trigger the connection. -See `jztool --help` for info. ## TODO list @@ -38,23 +100,11 @@ Some of the error messages could be friendlier too. Adding support to the Rockbox utility should be mostly boilerplate since the jztool library wraps all the troublesome details. -Getting appropriate privileges to access the USB device is the main issue. -Preferably, the Rockbox utility should not run as root/admin/etc. +Permissions are an issue on Linux because by default only root can access +"raw" USB devices. If we want to package rbutil for distro we can install +a udev rule to allow access to the specific USB IDs we need, eg. allowing +users in the "wheel" group to access the device. -- Windows: not sure -- Linux: needs udev rules or root privileges -- Mac: apparently does not need privileges - -### Porting to Windows - -Windows wants to see a driver installed before we can access the USB device, -the easiest way to do this is by having the user run Zadig, a 3rd party app -which can install the WinUSB driver. WinUSB itself is from Microsoft and -bundled with Windows. - -Zadig's homepage: https://zadig.akeo.ie/ - -### Porting to Mac - -According to the libusb wiki, libusb works on Mac without any special setup or -privileges, presumably porting there is easy. +On Windows and Mac, no special permissions are needed to access USB devices +assuming the drivers are set up. (Zadig does require administrator access +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; typedef struct jz_usbdev jz_usbdev; typedef struct jz_device_info jz_device_info; typedef struct jz_buffer jz_buffer; -typedef struct jz_paramlist jz_paramlist; typedef enum jz_error jz_error; typedef enum jz_identify_error jz_identify_error; @@ -46,7 +45,6 @@ typedef enum jz_device_type jz_device_type; typedef enum jz_cpu_type jz_cpu_type; typedef void(*jz_log_cb)(jz_log_level, const char*); -typedef int(*jz_device_action_fn)(jz_context*, jz_paramlist*); enum jz_error { JZ_SUCCESS = 0, @@ -92,10 +90,6 @@ struct jz_device_info { jz_cpu_type cpu_type; uint16_t vendor_id; uint16_t product_id; - int num_actions; - const char* const* action_names; - const jz_device_action_fn* action_funcs; - const char* const* const* action_params; }; struct jz_buffer { @@ -132,7 +126,6 @@ const jz_device_info* jz_get_device_info_indexed(int index); int jz_identify_x1000_spl(const void* data, size_t len); int jz_identify_scramble_image(const void* data, size_t len); -int jz_identify_fiiom3k_bootimage(const void* data, size_t len); /****************************************************************************** * USB boot ROM protocol @@ -148,27 +141,10 @@ int jz_usb_start2(jz_usbdev* dev, uint32_t addr); int jz_usb_flush_caches(jz_usbdev* dev); /****************************************************************************** - * X1000 flash protocol + * Rockbox loader (all functions are model-specific, see docs) */ -int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data); -int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data); -int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data); -int jz_x1000_boot_rockbox(jz_usbdev* dev); - -/****************************************************************************** - * FiiO M3K bootloader backup/installation - */ - -int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr); -int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf); -int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size, - const void* spl_buf, size_t spl_size, - const void* boot_buf, size_t boot_size); - -int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl); -int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl); -int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl); +int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename); /****************************************************************************** * Simple buffer API @@ -180,15 +156,6 @@ void jz_buffer_free(jz_buffer* buf); int jz_buffer_load(jz_buffer** buf, const char* filename); int jz_buffer_save(jz_buffer* buf, const char* filename); -/****************************************************************************** - * Parameter list - */ - -jz_paramlist* jz_paramlist_new(void); -void jz_paramlist_free(jz_paramlist* pl); -int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value); -const char* jz_paramlist_get(jz_paramlist* pl, const char* param); - /****************************************************************************** * END */ 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 @@ #include jz_context* jz = NULL; +jz_usbdev* usbdev = NULL; const jz_device_info* dev_info = NULL; -int dev_action = -1; -jz_paramlist* action_params = NULL; + +void usage_fiiom3k(void) +{ + printf("Usage:\n" + " jztool fiiom3k load \n" + "\n" + "The 'load' command is used to boot the Rockbox bootloader in\n" + "recovery mode, which allows you to install the Rockbox bootloader\n" + "and backup or restore bootloader images. You need to connect the\n" + "M3K in USB boot mode in order to use this tool.\n" + "\n" + "On Windows, you will need to install the WinUSB driver for the M3K\n" + "using a 3rd-party tool such as Zadig . For\n" + "more details check the jztool README.md file or the Rockbox wiki at\n" + ".\n" + "\n" + "To connect the M3K in USB boot mode, plug the microUSB into the\n" + "M3K, and hold the VOL- button while plugging the USB into your\n" + "computer. If successful, the button light will turn on and the\n" + "LCD will remain black. If you encounter any errors and need to\n" + "reconnect the device, you must force a power off by holding POWER\n" + "for more than 10 seconds.\n" + "\n" + "Once the Rockbox bootloader is installed on your M3K, you can\n" + "access the recovery menu by holding VOL+ while powering on the\n" + "device.\n"); + exit(4); +} + +int cmdline_fiiom3k(int argc, char** argv) +{ + if(argc < 2 || strcmp(argv[0], "load")) { + usage_fiiom3k(); + return 2; + } + + int rc = jz_usb_open(jz, &usbdev, dev_info->vendor_id, dev_info->product_id); + if(rc < 0) { + jz_log(jz, JZ_LOG_ERROR, "Cannot open USB device: %d", rc); + return 1; + } + + rc = jz_fiiom3k_boot(usbdev, argv[1]); + if(rc < 0) { + jz_log(jz, JZ_LOG_ERROR, "Boot failed: %d", rc); + return 1; + } + + return 0; +} void usage(void) { printf("Usage:\n" - " jztool [global options] [action options]\n" + " jztool [global options] [command arguments]\n" "\n" "Global options:\n" - "\n" " -h, --help Display this help\n" " -q, --quiet Don't log anything except errors\n" - " -v, --verbose Display detailed logging output\n" - " -l, --loglevel LEVEL Set log level\n"); + " -v, --verbose Display detailed logging output\n\n"); printf("Supported devices:\n\n"); int n = jz_get_num_device_info(); @@ -49,39 +96,17 @@ void usage(void) printf(" %s - %s\n", info->name, info->description); } - printf( -"\n" -"Available actions for fiiom3k:\n" -"\n" -" install --spl --bootloader \n" -" [--without-backup yes] [--backup IMAGE]\n" -" Install or update the Rockbox bootloader on a device.\n" -"\n" -" If --backup is given, back up the current bootloader to IMAGE before\n" -" installing the new bootloader. The installer will normally refuse to\n" -" overwrite your current bootloader; pass '--without-backup yes' if you\n" -" really want to proceed without taking a backup.\n" -"\n" -" WARNING: it is NOT RECOMMENDED to install the Rockbox bootloader\n" -" without taking a backup of the original firmware bootloader. It may\n" -" be very difficult or impossible to recover your player without one.\n" -" At least one M3Ks is known to not to work with the Rockbox bootloader,\n" -" so it is very important to take a backup.\n" -"\n" -" backup --image IMAGE\n" -" Backup the current bootloader to the file IMAGE\n" -"\n" -" restore --image IMAGE\n" -" Restore a bootloader image backup from the file IMAGE\n" -"\n"); + printf("\n" + "For device-specific help run 'jztool DEVICE' without arguments,\n" + "eg. 'jztool fiiom3k' will display help for the FiiO M3K.\n"); exit(4); } void cleanup(void) { - if(action_params) - jz_paramlist_free(action_params); + if(usbdev) + jz_usb_close(usbdev); if(jz) jz_context_destroy(jz); } @@ -157,71 +182,14 @@ int main(int argc, char** argv) exit(2); } - /* Read the action */ + /* Dispatch to device handler */ --argc, ++argv; - if(argc == 0) { - jz_log(jz, JZ_LOG_ERROR, "No action specified (try jztool --help)"); - exit(2); + switch(dev_info->device_type) { + case JZ_DEVICE_FIIOM3K: + return cmdline_fiiom3k(argc, argv); + + default: + jz_log(jz, JZ_LOG_ERROR, "INTERNAL ERROR: unhandled device type"); + return 1; } - - for(dev_action = 0; dev_action < dev_info->num_actions; ++dev_action) - if(!strcmp(*argv, dev_info->action_names[dev_action])) - break; - - if(dev_action == dev_info->num_actions) { - jz_log(jz, JZ_LOG_ERROR, "Unknown action '%s' (try jztool --help)", *argv); - exit(2); - } - - /* Parse the action options */ - action_params = jz_paramlist_new(); - if(!action_params) { - jz_log(jz, JZ_LOG_ERROR, "Out of memory: can't create paramlist"); - exit(1); - } - - const char* const* allowed_params = dev_info->action_params[dev_action]; - - --argc, ++argv; - while(argc > 0 && argv[0][0] == '-') { - if(argv[0][1] != '-') { - jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv); - exit(2); - } - - bool bad_option = true; - for(int i = 0; allowed_params[i] != NULL; ++i) { - if(!strcmp(&argv[0][2], allowed_params[i])) { - ++argv; - if(--argc == 0) { - jz_log(jz, JZ_LOG_ERROR, "Missing argument for parameter '%s'", *argv); - exit(2); - } - - int rc = jz_paramlist_set(action_params, allowed_params[i], *argv); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "Out of memory"); - exit(1); - } - - bad_option = false; - } - } - - if(bad_option) { - jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv); - exit(2); - } - - --argc, ++argv; - } - - if(argc != 0) { - jz_log(jz, JZ_LOG_ERROR, "Excess arguments on command line"); - exit(2); - } - - /* Invoke action handler */ - int rc = dev_info->action_funcs[dev_action](jz, action_params); - return (rc < 0) ? 1 : 0; } 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 @@ #include #include +#ifdef WIN32 +# include +#endif + /** \brief Allocate a library context * \returns New context or NULL if out of memory */ @@ -137,13 +141,18 @@ void jz_log_cb_stderr(jz_log_level lev, const char* msg) */ void jz_sleepms(int ms) { +#ifdef WIN32 + Sleep(ms); +#else struct timespec ts; long ns = ms % 1000; ts.tv_nsec = ns * 1000 * 1000; ts.tv_sec = ms / 1000; nanosleep(&ts, NULL); +#endif } +/** \brief Add reference to libusb context, allocating it if necessary */ int jz_context_ref_libusb(jz_context* jz) { if(jz->usb_ctxref == 0) { @@ -158,6 +167,7 @@ int jz_context_ref_libusb(jz_context* jz) return JZ_SUCCESS; } +/** \brief Remove reference to libusb context, freeing if it hits zero */ void jz_context_unref_libusb(jz_context* jz) { 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 @@ #include "jztool.h" #include -static const char* const fiiom3k_action_names[] = { - "install", - "backup", - "restore", -}; - -static const char* const fiiom3k_install_action_params[] = - {"spl", "bootloader", "backup", "without-backup", NULL}; - -static const char* const fiiom3k_backuprestore_action_params[] = - {"spl", "image", NULL}; - -static const char* const* fiiom3k_action_params[] = { - fiiom3k_install_action_params, - fiiom3k_backuprestore_action_params, - fiiom3k_backuprestore_action_params, -}; - -static const jz_device_action_fn fiiom3k_action_funcs[] = { - jz_fiiom3k_install, - jz_fiiom3k_backup, - jz_fiiom3k_restore, -}; - static const jz_device_info infotable[] = { { .name = "fiiom3k", @@ -54,10 +30,6 @@ static const jz_device_info infotable[] = { .cpu_type = JZ_CPU_X1000, .vendor_id = 0xa108, .product_id = 0x1000, - .num_actions = sizeof(fiiom3k_action_names)/sizeof(void*), - .action_names = fiiom3k_action_names, - .action_funcs = fiiom3k_action_funcs, - .action_params = fiiom3k_action_params, }, }; @@ -69,6 +41,7 @@ int jz_get_num_device_info(void) return infotable_size; } +/** \brief Lookup info for a device by type, returns NULL if not found. */ const jz_device_info* jz_get_device_info(jz_device_type type) { 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 @@ ****************************************************************************/ #include "jztool.h" +#include "jztool_private.h" +#include "microtar.h" +#include "ucl/ucl.h" +#include #include -#define IMAGE_ADDR 0 -#define IMAGE_SIZE (128 * 1024) -#define SPL_OFFSET 0 -#define SPL_SIZE (12 * 1024) -#define BOOT_OFFSET (26 * 1024) -#define BOOT_SIZE (102 * 1024) - -int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr) +static uint32_t xread32(const uint8_t* d) { - jz_buffer* buf = jz_buffer_alloc(IMAGE_SIZE, NULL); - if(!buf) + uint32_t r = 0; + r |= d[0] << 24; + r |= d[1] << 16; + r |= d[2] << 8; + r |= d[3] << 0; + return r; +} + +/* adapted from firmware/common/ucl_decompress.c */ +static jz_buffer* ucl_unpack(const uint8_t* src, uint32_t src_len, + uint32_t* dst_len) +{ + static const uint8_t magic[8] = + {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a}; + + jz_buffer* buffer = NULL; + + /* make sure there are enough bytes for the header */ + if(src_len < 18) + goto error; + + /* avoid memcmp for reasons of code size */ + for(size_t i = 0; i < sizeof(magic); ++i) + if(src[i] != magic[i]) + goto error; + + /* read the other header fields */ + /* uint32_t flags = xread32(&src[8]); */ + uint8_t method = src[12]; + /* uint8_t level = src[13]; */ + uint32_t block_size = xread32(&src[14]); + + /* check supported compression method */ + if(method != 0x2e) + goto error; + + /* validate */ + if(block_size < 1024 || block_size > 8*1024*1024) + goto error; + + src += 18; + src_len -= 18; + + /* Calculate amount of space that we might need & allocate a buffer: + * - subtract 4 to account for end of file marker + * - each block is block_size bytes + 8 bytes of header + * - add one to nr_blocks to account for case where file size < block size + * - total size = max uncompressed size of block * nr_blocks + */ + uint32_t nr_blocks = (src_len - 4) / (8 + block_size) + 1; + uint32_t max_size = nr_blocks * (block_size + block_size/8 + 256); + buffer = jz_buffer_alloc(max_size, NULL); + if(!buffer) + goto error; + + /* perform the decompression */ + uint32_t dst_ilen = buffer->size; + uint8_t* dst = buffer->data; + while(1) { + if(src_len < 4) + goto error; + + uint32_t out_len = xread32(src); src += 4, src_len -= 4; + if(out_len == 0) + break; + + if(src_len < 4) + goto error; + + uint32_t in_len = xread32(src); src += 4, src_len -= 4; + if(in_len > block_size || out_len > block_size || + in_len == 0 || in_len > out_len) + goto error; + + if(src_len < in_len) + goto error; + + if(in_len < out_len) { + uint32_t actual_out_len = dst_ilen; + int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL); + if(rc != UCL_E_OK) + goto error; + if(actual_out_len != out_len) + goto error; + } else { + for(size_t i = 0; i < in_len; ++i) + dst[i] = src[i]; + } + + src += in_len; + src_len -= in_len; + dst += out_len; + dst_ilen -= out_len; + } + + /* subtract leftover number of bytes to get size of compressed output */ + *dst_len = buffer->size - dst_ilen; + return buffer; + + error: + jz_buffer_free(buffer); + return NULL; +} + +static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf) +{ + int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data); + if(rc < 0) + return rc; + + return jz_usb_start1(dev, 0xf4001800); +} + +static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf) +{ + int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data); + if(rc < 0) + return rc; + + rc = jz_usb_flush_caches(dev); + if(rc < 0) + return rc; + + return jz_usb_start2(dev, 0x80004000); +} + +static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file, + bool decompress, jz_buffer** buf) +{ + jz_buffer* buffer = NULL; + mtar_header_t h; + int rc; + + rc = mtar_find(tar, file, &h); + if(rc != MTAR_ESUCCESS) { + jz_log(jz, JZ_LOG_ERROR, "can't find %s in boot file, tar error %d", file, rc); + return JZ_ERR_BAD_FILE_FORMAT; + } + + buffer = jz_buffer_alloc(h.size, NULL); + if(!buffer) return JZ_ERR_OUT_OF_MEMORY; - int rc = jz_x1000_read_flash(dev, IMAGE_ADDR, buf->size, buf->data); - if(rc < 0) { - jz_buffer_free(buf); - return rc; - } - - *bufptr = buf; - return JZ_SUCCESS; -} - -int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf) -{ - int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size); - if(rc < 0 || image_size != IMAGE_SIZE) - return JZ_ERR_BAD_FILE_FORMAT; - - rc = jz_x1000_write_flash(dev, IMAGE_ADDR, image_size, image_buf); - if(rc < 0) - return rc; - - return JZ_SUCCESS; -} - -int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size, - const void* spl_buf, size_t spl_size, - const void* boot_buf, size_t boot_size) -{ - int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "Boot image is invalid: %d", rc); + rc = mtar_read_data(tar, buffer->data, buffer->size); + if(rc != MTAR_ESUCCESS) { + jz_buffer_free(buffer); + jz_log(jz, JZ_LOG_ERROR, "can't read %s in boot file, tar error %d", file, rc); return JZ_ERR_BAD_FILE_FORMAT; } - rc = jz_identify_x1000_spl(spl_buf, spl_size); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "SPL image is invalid: %d", rc); - return JZ_ERR_BAD_FILE_FORMAT; - } - - if(spl_size > SPL_SIZE) { - jz_log(jz, JZ_LOG_ERROR, "SPL is too big"); - return JZ_ERR_BAD_FILE_FORMAT; - } - - rc = jz_identify_scramble_image(boot_buf, boot_size); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "Bootloader image is invalid: %d", rc); - return JZ_ERR_BAD_FILE_FORMAT; - } - - if(boot_size > BOOT_SIZE) { - jz_log(jz, JZ_LOG_ERROR, "Bootloader is too big"); - return JZ_ERR_BAD_FILE_FORMAT; - } - - uint8_t* imgdat = (uint8_t*)image_buf; - memset(&imgdat[SPL_OFFSET], 0xff, SPL_SIZE); - memcpy(&imgdat[SPL_OFFSET], spl_buf, spl_size); - memset(&imgdat[BOOT_OFFSET], 0xff, BOOT_SIZE); - memcpy(&imgdat[BOOT_OFFSET], boot_buf, boot_size); - return JZ_SUCCESS; -} - -#define IMGBUF 0 -#define SPLBUF 1 -#define BOOTBUF 2 -#define NUMBUFS 3 -#define IMGBUF_NAME "image" -#define SPLBUF_NAME "spl" -#define BOOTBUF_NAME "bootloader" -#define FIIOM3K_INIT_WORKSTATE {0} - -struct fiiom3k_workstate { - jz_usbdev* dev; - jz_buffer* bufs[NUMBUFS]; -}; - -static void fiiom3k_action_cleanup(struct fiiom3k_workstate* state) -{ - for(int i = 0; i < NUMBUFS; ++i) - if(state->bufs[i]) - jz_buffer_free(state->bufs[i]); - - if(state->dev) - jz_usb_close(state->dev); -} - -static int fiiom3k_action_loadbuf(jz_context* jz, jz_paramlist* pl, - struct fiiom3k_workstate* state, int idx) -{ - const char* const paramnames[] = {IMGBUF_NAME, SPLBUF_NAME, BOOTBUF_NAME}; - - if(state->bufs[idx]) - return JZ_SUCCESS; - - const char* filename = jz_paramlist_get(pl, paramnames[idx]); - if(!filename) { - jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", paramnames[idx]); - return JZ_ERR_OTHER; - } - - int rc = jz_buffer_load(&state->bufs[idx], filename); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "Error reading '%s' file (%d): %s", paramnames[idx], rc, filename); - return rc; - } - - return JZ_SUCCESS; -} - -static int fiiom3k_action_setup(jz_context* jz, jz_paramlist* pl, - struct fiiom3k_workstate* state) -{ - const jz_device_info* info = jz_get_device_info(JZ_DEVICE_FIIOM3K); - if(!info) - return JZ_ERR_OTHER; - - int rc = fiiom3k_action_loadbuf(jz, pl, state, SPLBUF); - if(rc < 0) - return rc; - - jz_log(jz, JZ_LOG_DETAIL, "Open USB device %04x:%04x", - (unsigned int)info->vendor_id, (unsigned int)info->product_id); - rc = jz_usb_open(jz, &state->dev, info->vendor_id, info->product_id); - if(rc < 0) - return rc; - - jz_log(jz, JZ_LOG_DETAIL, "Setup device for flash access"); - jz_buffer* splbuf = state->bufs[SPLBUF]; - return jz_x1000_setup(state->dev, splbuf->size, splbuf->data); -} - -int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl) -{ - struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; - int rc; - - rc = fiiom3k_action_loadbuf(jz, pl, &state, BOOTBUF); - if(rc < 0) - goto error; - - rc = fiiom3k_action_setup(jz, pl, &state); - if(rc < 0) - goto error; - - jz_log(jz, JZ_LOG_DETAIL, "Reading boot image from device"); - rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); - if(rc < 0) - goto error; - - jz_buffer* img_buf = state.bufs[IMGBUF]; - const char* backupfile = jz_paramlist_get(pl, "backup"); - const char* without_backup = jz_paramlist_get(pl, "without-backup"); - if(backupfile) { - jz_log(jz, JZ_LOG_DETAIL, "Backup original boot image to file: %s", backupfile); - rc = jz_buffer_save(img_buf, backupfile); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "Error saving backup image file (%d): %s", rc, backupfile); - goto error; + if(decompress) { + uint32_t dst_len; + jz_buffer* nbuf = ucl_unpack(buffer->data, buffer->size, &dst_len); + jz_buffer_free(buffer); + if(!nbuf) { + jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file); + return JZ_ERR_BAD_FILE_FORMAT; } - } else if(!without_backup || strcmp(without_backup, "yes")) { - jz_log(jz, JZ_LOG_ERROR, "No --backup option given and --without-backup yes not specified"); - jz_log(jz, JZ_LOG_ERROR, "Refusing to flash a new bootloader without taking a backup"); - goto error; + + /* for simplicity just forget original size of buffer */ + nbuf->size = dst_len; + buffer = nbuf; } - jz_log(jz, JZ_LOG_DETAIL, "Patching image with new SPL/bootloader"); - jz_buffer* boot_buf = state.bufs[BOOTBUF]; - jz_buffer* spl_buf = state.bufs[SPLBUF]; - rc = jz_fiiom3k_patchboot(jz, img_buf->data, img_buf->size, - spl_buf->data, spl_buf->size, - boot_buf->data, boot_buf->size); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "Error patching image: %d", rc); - goto error; - } - - jz_log(jz, JZ_LOG_DETAIL, "Writing patched image to device"); - rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); - if(rc < 0) - goto error; - - rc = JZ_SUCCESS; - - error: - fiiom3k_action_cleanup(&state); - return rc; + *buf = buffer; + return JZ_SUCCESS; } -int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl) +static int m3k_show_version(jz_context* jz, jz_buffer* info_file) { - struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; + /* Extract the version string and log it for informational purposes */ + char* boot_version = (char*)info_file->data; + char* endpos = memchr(boot_version, '\n', info_file->size); + if(!endpos) { + jz_log(jz, JZ_LOG_ERROR, "invalid metadata in boot file"); + return JZ_ERR_BAD_FILE_FORMAT; + } + + *endpos = 0; + jz_log(jz, JZ_LOG_NOTICE, "Rockbox bootloader version: %s", boot_version); + return JZ_SUCCESS; +} + +/** \brief Load the Rockbox bootloader on the FiiO M3K + * \param dev USB device freshly returned by jz_usb_open() + * \param filename Path to the "bootloader.m3k" file + * \return either JZ_SUCCESS or an error code + */ +int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename) +{ + jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL; + mtar_t tar; int rc; - const char* outfile_path = jz_paramlist_get(pl, IMGBUF_NAME); - if(!outfile_path) { - jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", IMGBUF_NAME); - rc = JZ_ERR_OTHER; - goto error; + rc = mtar_open(&tar, filename, "r"); + if(rc != MTAR_ESUCCESS) { + jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc); + return JZ_ERR_OPEN_FILE; } - rc = fiiom3k_action_setup(jz, pl, &state); - if(rc < 0) + /* Extract all necessary files */ + rc = m3k_get_file(dev->jz, &tar, "spl.m3k", false, &spl); + if(rc != JZ_SUCCESS) goto error; - rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); - if(rc < 0) + rc = m3k_get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader); + if(rc != JZ_SUCCESS) goto error; - rc = jz_buffer_save(state.bufs[IMGBUF], outfile_path); - if(rc < 0) { - jz_log(jz, JZ_LOG_ERROR, "Error writing '%s' file (%d): %s", IMGBUF_NAME, rc, outfile_path); - goto error; - } - - rc = JZ_SUCCESS; - - error: - fiiom3k_action_cleanup(&state); - return rc; -} - -int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl) -{ - struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; - int rc; - - rc = fiiom3k_action_loadbuf(jz, pl, &state, IMGBUF); - if(rc < 0) + rc = m3k_get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file); + if(rc != JZ_SUCCESS) goto error; - rc = fiiom3k_action_setup(jz, pl, &state); - if(rc < 0) + /* Display the version string */ + rc = m3k_show_version(dev->jz, info_file); + if(rc != JZ_SUCCESS) goto error; - jz_buffer* img_buf = state.bufs[IMGBUF]; - rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); - if(rc < 0) + /* Stage1 boot of SPL to set up hardware */ + rc = m3k_stage1(dev, spl); + if(rc != JZ_SUCCESS) + goto error; + + /* Need a bit of time for SPL to handle init */ + jz_sleepms(500); + + /* Stage2 boot into the bootloader's recovery menu + * User has to take manual action from there */ + rc = m3k_stage2(dev, bootloader); + if(rc != JZ_SUCCESS) goto error; rc = JZ_SUCCESS; error: - fiiom3k_action_cleanup(&state); + if(spl) + jz_buffer_free(spl); + if(bootloader) + jz_buffer_free(bootloader); + if(info_file) + jz_buffer_free(info_file); + mtar_close(&tar); return rc; } 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) return crc; } +/** \brief Identify a file as an SPL for X1000 CPUs + * \param data File data buffer + * \param len Length of file + * \return JZ_SUCCESS if file looks correct, or one of the following errors + * \retval JZ_IDERR_WRONG_SIZE file too small or size doesn't match header + * \retval JZ_IDERR_BAD_HEADER missing magic bytes from header + * \retval JZ_IDERR_BAD_CHECKSUM CRC7 mismatch + */ int jz_identify_x1000_spl(const void* data, size_t len) { /* Use <= check because a header-only file is not really valid, @@ -115,6 +123,14 @@ static const struct scramble_model_info { {NULL, 0}, }; +/** \brief Identify a file as a Rockbox `scramble` image + * \param data File data buffer + * \param len Length of file + * \return JZ_SUCCESS if file looks correct, or one of the following errors + * \retval JZ_IDERR_WRONG_SIZE file too small to be valid + * \retval JZ_IDERR_UNRECOGNIZED_MODEL unsupported/unknown model type + * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch + */ int jz_identify_scramble_image(const void* data, size_t len) { /* 4 bytes checksum + 4 bytes player model */ @@ -143,37 +159,3 @@ int jz_identify_scramble_image(const void* data, size_t len) return JZ_SUCCESS; } - -int jz_identify_fiiom3k_bootimage(const void* data, size_t len) -{ - /* The bootloader image is simply a dump of the first NAND eraseblock, - * so it has a fixed 128 KiB size */ - if(len != 128*1024) - return JZ_IDERR_WRONG_SIZE; - - /* We'll verify the embedded SPL, but we have to drag out the correct - * length from the header. Length should be more than 12 KiB, due to - * limitations of the hardware */ - const struct x1000_spl_header* spl_header; - spl_header = (const struct x1000_spl_header*)data; - if(spl_header->length > 12 * 1024) - return JZ_IDERR_BAD_HEADER; - - int rc = jz_identify_x1000_spl(data, spl_header->length); - if(rc < 0) - return rc; - - const uint8_t* dat = (const uint8_t*)data; - - /* Check the partition table is present */ - if(memcmp(&dat[0x3c00], "nand", 4)) - return JZ_IDERR_OTHER; - - /* Check first bytes of PDMA firmware. It doesn't change - * between OF versions, and Rockbox doesn't modify it. */ - static const uint8_t pdma_fw[] = {0x54, 0x25, 0x42, 0xb3, 0x70, 0x25, 0x42, 0xb3}; - if(memcmp(&dat[0x4000], pdma_fw, sizeof(pdma_fw))) - return JZ_IDERR_OTHER; - - return JZ_SUCCESS; -} 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 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2021 Aidan MacDonald - * - * 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 "jztool.h" -#include -#include -#include - -struct jz_paramlist { - int size; - char** keys; - char** values; -}; - -static int jz_paramlist_extend(jz_paramlist* pl, int count) -{ - int nsize = pl->size + count; - - /* Reallocate key list */ - char** nkeys = realloc(pl->keys, nsize * sizeof(char*)); - if(!nkeys) - return JZ_ERR_OUT_OF_MEMORY; - - for(int i = pl->size; i < nsize; ++i) - nkeys[i] = NULL; - - pl->keys = nkeys; - - /* Reallocate value list */ - char** nvalues = realloc(pl->values, nsize * sizeof(char*)); - if(!nvalues) - return JZ_ERR_OUT_OF_MEMORY; - - for(int i = pl->size; i < nsize; ++i) - nvalues[i] = NULL; - - pl->values = nvalues; - - pl->size = nsize; - return JZ_SUCCESS; -} - -jz_paramlist* jz_paramlist_new(void) -{ - jz_paramlist* pl = malloc(sizeof(struct jz_paramlist)); - if(!pl) - return NULL; - - pl->size = 0; - pl->keys = NULL; - pl->values = NULL; - return pl; -} - -void jz_paramlist_free(jz_paramlist* pl) -{ - for(int i = 0; i < pl->size; ++i) { - free(pl->keys[i]); - free(pl->values[i]); - } - - if(pl->size > 0) { - free(pl->keys); - free(pl->values); - } - - free(pl); -} - -int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value) -{ - int pos = -1; - for(int i = 0; i < pl->size; ++i) { - if(!pl->keys[i] || !strcmp(pl->keys[i], param)) { - pos = i; - break; - } - } - - if(pos == -1) { - pos = pl->size; - int rc = jz_paramlist_extend(pl, 1); - if(rc < 0) - return rc; - } - - bool need_key = (pl->keys[pos] == NULL); - if(need_key) { - char* newparam = strdup(param); - if(!newparam) - return JZ_ERR_OUT_OF_MEMORY; - - pl->keys[pos] = newparam; - } - - char* newvalue = strdup(value); - if(!newvalue) { - if(need_key) { - free(pl->keys[pos]); - pl->keys[pos] = NULL; - } - - return JZ_ERR_OUT_OF_MEMORY; - } - - pl->values[pos] = newvalue; - return JZ_SUCCESS; -} - -const char* jz_paramlist_get(jz_paramlist* pl, const char* param) -{ - for(int i = 0; i < pl->size; ++i) - if(pl->keys[i] && !strcmp(pl->keys[i], param)) - return pl->values[i]; - - return NULL; -} 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 @@ #define VR_PROGRAM_START1 4 #define VR_PROGRAM_START2 5 +/** \brief Open a USB device + * \param jz Context + * \param devptr Returns pointer to the USB device upon success + * \param vend_id USB vendor ID + * \param prod_id USB product ID + * \return either JZ_SUCCESS if device was opened, or an error below + * \retval JZ_ERR_OUT_OF_MEMORY malloc failed + * \retval JZ_ERR_USB libusb error (details are logged) + * \retval JZ_ERR_NO_DEVICE can't unambiguously find the device + */ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id) { int rc; @@ -80,7 +90,7 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p } if(dev_index < 0) { - jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%05x found", + jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%04x found", (unsigned int)vend_id, (unsigned int)prod_id); rc = JZ_ERR_NO_DEVICE; goto error; @@ -100,6 +110,8 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p goto error; } + jz_log(jz, JZ_LOG_DEBUG, "Opened device (%p, ID %04x:%04x)", + dev, (unsigned int)vend_id, (unsigned int)prod_id); dev->jz = jz; dev->handle = usb_handle; *devptr = dev; @@ -119,14 +131,20 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p goto exit; } +/** \brief Close a USB device + * \param dev Device to close; memory will be freed automatically + */ void jz_usb_close(jz_usbdev* dev) { + jz_log(dev->jz, JZ_LOG_DEBUG, "Closing device (%p)", dev); libusb_release_interface(dev->handle, 0); libusb_close(dev->handle); jz_context_unref_libusb(dev->jz); free(dev); } +// Does an Ingenic-specific vendor request +// Written with X1000 in mind but other Ingenic CPUs have the same commands static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg) { 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) jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc)); rc = JZ_ERR_USB; } else { + static const char* req_names[] = { + "GET_CPU_INFO", + "SET_DATA_ADDRESS", + "SET_DATA_LENGTH", + "FLUSH_CACHES", + "PROGRAM_START1", + "PROGRAM_START2", + }; + + jz_log(dev->jz, JZ_LOG_DEBUG, "Issued %s %08lu", + req_names[req], (unsigned long)arg); rc = JZ_SUCCESS; } return rc; } +// Bulk transfer wrapper static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf) { int xfered = 0; @@ -156,12 +186,16 @@ static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf) jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered"); rc = JZ_ERR_USB; } else { + jz_log(dev->jz, JZ_LOG_DEBUG, "Transferred %zu bytes %s", + len, write ? "to device" : "from device"); rc = JZ_SUCCESS; } return rc; } +// Memory send/receive primitive, performs the necessary vendor requests +// and then tranfers data using the bulk endpoint static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr, size_t len, void* data) { @@ -177,26 +211,54 @@ static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr, return jz_usb_transfer(dev, write, len, data); } +/** \brief Write data to device memory + * \param dev USB device + * \param addr Address where data should be written + * \param len Length of the data, in bytes, should be positive + * \param data Data buffer + * \return either JZ_SUCCESS on success or a failure code + */ int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data) { return jz_usb_sendrecv(dev, true, addr, len, (void*)data); } +/** \brief Read data to device memory + * \param dev USB device + * \param addr Address to read from + * \param len Length of the data, in bytes, should be positive + * \param data Data buffer + * \return either JZ_SUCCESS on success or a failure code + */ int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data) { return jz_usb_sendrecv(dev, false, addr, len, data); } +/** \brief Execute stage1 program jumping to the specified address + * \param dev USB device + * \param addr Address to begin execution at + * \return either JZ_SUCCESS on success or a failure code + */ int jz_usb_start1(jz_usbdev* dev, uint32_t addr) { return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr); } +/** \brief Execute stage2 program jumping to the specified address + * \param dev USB device + * \param addr Address to begin execution at + * \return either JZ_SUCCESS on success or a failure code + */ int jz_usb_start2(jz_usbdev* dev, uint32_t addr) { return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr); } +/** \brief Ask device to flush CPU caches + * \param dev USB device + * \return either JZ_SUCCESS on success or a failure code + */ int jz_usb_flush_caches(jz_usbdev* dev) { 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 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2021 Aidan MacDonald - * - * 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 "jztool_private.h" -#include "../../../firmware/target/mips/ingenic_x1000/spl-x1000-defs.h" -#include "../../../firmware/target/mips/ingenic_x1000/nand-x1000-err.h" -#include - -static uint32_t to_le32(uint32_t x) -{ - union { uint32_t u; uint8_t p[4]; } f; - f.p[0] = x & 0xff; - f.p[1] = (x >> 8) & 0xff; - f.p[2] = (x >> 16) & 0xff; - f.p[3] = (x >> 24) & 0xff; - return f.u; -} - -static uint32_t from_le32(uint32_t x) -{ - union { uint32_t u; uint8_t p[4]; } f; - f.u = x; - return f.p[0] | (f.p[1] << 8) | (f.p[2] << 16) | (f.p[3] << 24); -} - -static const char* jz_x1000_nand_strerror(int rc) -{ - switch(rc) { - case NANDERR_CHIP_UNSUPPORTED: - return "Chip unsupported"; - case NANDERR_WRITE_PROTECTED: - return "Operation forbidden by write-protect"; - case NANDERR_UNALIGNED_ADDRESS: - return "Improperly aligned address"; - case NANDERR_UNALIGNED_LENGTH: - return "Improperly aligned length"; - case NANDERR_READ_FAILED: - return "Read operation failed"; - case NANDERR_ECC_FAILED: - return "Uncorrectable ECC error on read"; - case NANDERR_ERASE_FAILED: - return "Erase operation failed"; - case NANDERR_PROGRAM_FAILED: - return "Program operation failed"; - case NANDERR_COMMAND_FAILED: - return "NAND command failed"; - default: - return "Unknown NAND error"; - } -} - - -static int jz_x1000_send_args(jz_usbdev* dev, struct x1000_spl_arguments* args) -{ - args->command = to_le32(args->command); - args->param1 = to_le32(args->param1); - args->param2 = to_le32(args->param2); - args->flags = to_le32(args->flags); - return jz_usb_send(dev, SPL_ARGUMENTS_ADDRESS, sizeof(*args), args); -} - -static int jz_x1000_recv_status(jz_usbdev* dev, struct x1000_spl_status* status) -{ - int rc = jz_usb_recv(dev, SPL_STATUS_ADDRESS, sizeof(*status), status); - if(rc < 0) - return rc; - - status->err_code = from_le32(status->err_code); - status->reserved = from_le32(status->reserved); - return JZ_SUCCESS; -} - -int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data) -{ - int rc = jz_identify_x1000_spl(spl_data, spl_len); - if(rc < 0) - return JZ_ERR_BAD_FILE_FORMAT; - if(spl_len > SPL_MAX_SIZE) - return JZ_ERR_BAD_FILE_FORMAT; - - rc = jz_usb_send(dev, SPL_LOAD_ADDRESS, spl_len, spl_data); - if(rc < 0) - return rc; - - struct x1000_spl_arguments args; - args.command = SPL_CMD_BOOT; - args.param1 = SPL_BOOTOPT_NONE; - args.param2 = 0; - args.flags = 0; - rc = jz_x1000_send_args(dev, &args); - if(rc < 0) - return rc; - - rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS); - if(rc < 0) - return rc; - - jz_sleepms(100); - - struct x1000_spl_status status; - rc = jz_x1000_recv_status(dev, &status); - if(rc < 0) - return rc; - - if(status.err_code != 0) { - jz_log(dev->jz, JZ_LOG_ERROR, "X1000 device init error: %d", status.err_code); - return JZ_ERR_OTHER; - } - - return JZ_SUCCESS; -} - -int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data) -{ - struct x1000_spl_arguments args; - args.command = SPL_CMD_FLASH_READ; - args.param1 = addr; - args.param2 = len; - args.flags = SPL_FLAG_SKIP_INIT; - int rc = jz_x1000_send_args(dev, &args); - if(rc < 0) - return rc; - - rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS); - if(rc < 0) - return rc; - - jz_sleepms(500); - - struct x1000_spl_status status; - rc = jz_x1000_recv_status(dev, &status); - if(rc < 0) - return rc; - - if(status.err_code != 0) { - jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash read error: %s", - jz_x1000_nand_strerror(status.err_code)); - return JZ_ERR_FLASH_ERROR; - } - - return jz_usb_recv(dev, SPL_BUFFER_ADDRESS, len, data); -} - -int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data) -{ - int rc = jz_usb_send(dev, SPL_BUFFER_ADDRESS, len, data); - if(rc < 0) - return rc; - - struct x1000_spl_arguments args; - args.command = SPL_CMD_FLASH_WRITE; - args.param1 = addr; - args.param2 = len; - args.flags = SPL_FLAG_SKIP_INIT; - rc = jz_x1000_send_args(dev, &args); - if(rc < 0) - return rc; - - rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS); - if(rc < 0) - return rc; - - jz_sleepms(500); - - struct x1000_spl_status status; - rc = jz_x1000_recv_status(dev, &status); - if(rc < 0) - return rc; - - if(status.err_code != 0) { - jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash write error: %s", - jz_x1000_nand_strerror(status.err_code)); - return JZ_ERR_FLASH_ERROR; - } - - return JZ_SUCCESS; -} - -int jz_x1000_boot_rockbox(jz_usbdev* dev) -{ - struct x1000_spl_arguments args; - args.command = SPL_CMD_BOOT; - args.param1 = SPL_BOOTOPT_ROCKBOX; - args.param2 = 0; - args.flags = 0; - int rc = jz_x1000_send_args(dev, &args); - if(rc < 0) - return rc; - - return jz_usb_start1(dev, SPL_EXEC_ADDRESS); -} 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) $(OBJDIR)$(LIBBZIP2): $(SILENT)$(MAKE) -C $(TOP)/bzip2 TARGET_DIR=$(OBJDIR) CC=$(CC) $@ +LIBMICROTAR = libmicrotar.a +$(LIBMICROTAR): $(OBJDIR)$(LIBMICROTAR) + +$(OBJDIR)$(LIBMICROTAR): + $(SILENT)$(MAKE) -C $(TOP)/../lib/microtar/src TARGET_DIR=$(OBJDIR) CC=$(CC) $@ + # building the standalone executable $(BINARY): $(OBJS) $(EXTRADEPS) $(addprefix $(OBJDIR),$(EXTRALIBOBJS)) $(TARGET_DIR)lib$(OUTPUT).a $(info LD $@)