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