summaryrefslogtreecommitdiff
path: root/utils/jztool/src/x1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/jztool/src/x1000.c')
-rw-r--r--utils/jztool/src/x1000.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/utils/jztool/src/x1000.c b/utils/jztool/src/x1000.c
new file mode 100644
index 0000000000..f59727a2ca
--- /dev/null
+++ b/utils/jztool/src/x1000.c
@@ -0,0 +1,180 @@
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 "jztool_private.h"
24#include "microtar-stdio.h"
25#include <stdbool.h>
26#include <string.h>
27
28/* TODO: these functions could be refactored to be CPU-agnostic */
29static int run_stage1(jz_usbdev* dev, jz_buffer* buf)
30{
31 int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data);
32 if(rc < 0)
33 return rc;
34
35 return jz_usb_start1(dev, 0xf4001800);
36}
37
38static int run_stage2(jz_usbdev* dev, jz_buffer* buf)
39{
40 int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data);
41 if(rc < 0)
42 return rc;
43
44 rc = jz_usb_flush_caches(dev);
45 if(rc < 0)
46 return rc;
47
48 return jz_usb_start2(dev, 0x80004000);
49}
50
51static int get_file(jz_context* jz, mtar_t* tar, const char* file,
52 bool decompress, jz_buffer** buf)
53{
54 jz_buffer* buffer = NULL;
55 const mtar_header_t* h;
56 int rc;
57
58 rc = mtar_find(tar, file);
59 if(rc != MTAR_ESUCCESS) {
60 jz_log(jz, JZ_LOG_ERROR, "can't find %s in boot file, tar error %d", file, rc);
61 return JZ_ERR_BAD_FILE_FORMAT;
62 }
63
64 h = mtar_get_header(tar);
65 buffer = jz_buffer_alloc(h->size, NULL);
66 if(!buffer)
67 return JZ_ERR_OUT_OF_MEMORY;
68
69 rc = mtar_read_data(tar, buffer->data, buffer->size);
70 if(rc < 0 || (unsigned)rc != buffer->size) {
71 jz_buffer_free(buffer);
72 jz_log(jz, JZ_LOG_ERROR, "can't read %s in boot file, tar error %d", file, rc);
73 return JZ_ERR_BAD_FILE_FORMAT;
74 }
75
76 if(decompress) {
77 uint32_t dst_len;
78 jz_buffer* nbuf = jz_ucl_unpack(buffer->data, buffer->size, &dst_len);
79 jz_buffer_free(buffer);
80 if(!nbuf) {
81 jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file);
82 return JZ_ERR_BAD_FILE_FORMAT;
83 }
84
85 /* for simplicity just forget original size of buffer */
86 nbuf->size = dst_len;
87 buffer = nbuf;
88 }
89
90 *buf = buffer;
91 return JZ_SUCCESS;
92}
93
94static int show_version(jz_context* jz, jz_buffer* info_file)
95{
96 /* Extract the version string and log it for informational purposes */
97 char* boot_version = (char*)info_file->data;
98 char* endpos = memchr(boot_version, '\n', info_file->size);
99 if(!endpos) {
100 jz_log(jz, JZ_LOG_ERROR, "invalid metadata in boot file");
101 return JZ_ERR_BAD_FILE_FORMAT;
102 }
103
104 *endpos = 0;
105 jz_log(jz, JZ_LOG_NOTICE, "Rockbox bootloader version: %s", boot_version);
106 return JZ_SUCCESS;
107}
108
109/** \brief Load the Rockbox bootloader on an X1000 device
110 * \param dev USB device freshly returned by jz_usb_open()
111 * \param filename Path to the "bootloader.target" file
112 * \return either JZ_SUCCESS or an error code
113 */
114int jz_x1000_boot(jz_usbdev* dev, jz_device_type type, const char* filename)
115{
116 const jz_device_info* dev_info;
117 char spl_filename[32];
118 jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL;
119 mtar_t tar;
120 int rc;
121
122 /* In retrospect using a model-dependent archive format was not a good
123 * idea, but it's not worth fixing just yet... */
124 dev_info = jz_get_device_info(type);
125 if(!dev_info)
126 return JZ_ERR_OTHER;
127 /* use of sprintf is safe since file_ext is short */
128 sprintf(spl_filename, "spl.%s", dev_info->file_ext);
129
130 /* Now open the archive */
131 rc = mtar_open(&tar, filename, "rb");
132 if(rc != MTAR_ESUCCESS) {
133 jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc);
134 return JZ_ERR_OPEN_FILE;
135 }
136
137 /* Extract all necessary files */
138 rc = get_file(dev->jz, &tar, spl_filename, false, &spl);
139 if(rc != JZ_SUCCESS)
140 goto error;
141
142 rc = get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader);
143 if(rc != JZ_SUCCESS)
144 goto error;
145
146 rc = get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file);
147 if(rc != JZ_SUCCESS)
148 goto error;
149
150 /* Display the version string */
151 rc = show_version(dev->jz, info_file);
152 if(rc != JZ_SUCCESS)
153 goto error;
154
155 /* Stage1 boot of SPL to set up hardware */
156 rc = run_stage1(dev, spl);
157 if(rc != JZ_SUCCESS)
158 goto error;
159
160 /* Need a bit of time for SPL to handle init */
161 jz_sleepms(500);
162
163 /* Stage2 boot into the bootloader's recovery menu
164 * User has to take manual action from there */
165 rc = run_stage2(dev, bootloader);
166 if(rc != JZ_SUCCESS)
167 goto error;
168
169 rc = JZ_SUCCESS;
170
171 error:
172 if(spl)
173 jz_buffer_free(spl);
174 if(bootloader)
175 jz_buffer_free(bootloader);
176 if(info_file)
177 jz_buffer_free(info_file);
178 mtar_close(&tar);
179 return rc;
180}