summaryrefslogtreecommitdiff
path: root/lib/x1000-installer/src
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-12-19 21:46:52 +0000
committerAidan MacDonald <amachronic@protonmail.com>2022-12-23 19:54:18 +0000
commitba010851fafea3da0d0655b103158f0a9efe6406 (patch)
treebb26879ee1ac5e2d578948701b20c1bb33db14a9 /lib/x1000-installer/src
parent6e794c9a2d9e91a926f70d0fcc66e255b0bdc221 (diff)
downloadrockbox-ba010851fafea3da0d0655b103158f0a9efe6406.tar.gz
rockbox-ba010851fafea3da0d0655b103158f0a9efe6406.zip
Remove lib/x1000-installer
Carrying this library is somewhat of a maintenance burden because some Rockbox APIs are mocked or duplicated in the test suite and thus need to be fixed up when refactoring. It's also unused & incomplete, so there's no good reason to keep it in tree any more. Change-Id: If39c62744b4edc0d81b1b6608ee5df69430e6581
Diffstat (limited to 'lib/x1000-installer/src')
-rw-r--r--lib/x1000-installer/src/xf_error.c42
-rw-r--r--lib/x1000-installer/src/xf_flashmap.c327
-rw-r--r--lib/x1000-installer/src/xf_nandio.c292
-rw-r--r--lib/x1000-installer/src/xf_package.c261
-rw-r--r--lib/x1000-installer/src/xf_stream.c211
-rw-r--r--lib/x1000-installer/src/xf_update.c149
6 files changed, 0 insertions, 1282 deletions
diff --git a/lib/x1000-installer/src/xf_error.c b/lib/x1000-installer/src/xf_error.c
deleted file mode 100644
index 3d4b342a92..0000000000
--- a/lib/x1000-installer/src/xf_error.c
+++ /dev/null
@@ -1,42 +0,0 @@
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 "xf_error.h"
23
24const char* xf_strerror(int err)
25{
26 switch(err) {
27 case XF_E_SUCCESS: return "Success";
28 case XF_E_IO: return "I/O error";
29 case XF_E_LINE_TOO_LONG: return "Line too long";
30 case XF_E_FILENAME_TOO_LONG: return "Filename too long";
31 case XF_E_INT_OVERFLOW: return "Numeric overflow";
32 case XF_E_BUF_OVERFLOW: return "Buffer overflowed";
33 case XF_E_SYNTAX_ERROR: return "Syntax error";
34 case XF_E_INVALID_PARAMETER: return "Invalid parameter";
35 case XF_E_NAND: return "NAND flash error";
36 case XF_E_OUT_OF_MEMORY: return "Out of memory";
37 case XF_E_OUT_OF_RANGE: return "Out of range";
38 case XF_E_VERIFY_FAILED: return "Verification failed";
39 case XF_E_CANNOT_OPEN_FILE: return "Cannot open file";
40 default: return "Unknown error";
41 }
42}
diff --git a/lib/x1000-installer/src/xf_flashmap.c b/lib/x1000-installer/src/xf_flashmap.c
deleted file mode 100644
index 972bf320ad..0000000000
--- a/lib/x1000-installer/src/xf_flashmap.c
+++ /dev/null
@@ -1,327 +0,0 @@
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 "xf_flashmap.h"
23#include "xf_error.h"
24#include <stdbool.h>
25#include <stdlib.h>
26#include <ctype.h>
27#include <string.h>
28
29static int xdigit_to_int(char c)
30{
31 if(c >= 'a' && c <= 'f')
32 return 10 + (c - 'a');
33 if(c >= 'A' && c <= 'F')
34 return 10 + (c - 'A');
35 if(c >= '0' && c <= '9')
36 return c - '0';
37 return -1;
38}
39
40static int isfilenamechar(char c)
41{
42 return (c >= 'a' && c <= 'z') ||
43 (c >= 'A' && c <= 'Z') ||
44 (c >= '0' && c <= '9') ||
45 c == '$' || c == '%' || c == '\'' || c == '-' || c == '_' ||
46 c == '@' || c == '~' || c == '`' || c == '!' || c == '(' ||
47 c == ')' || c == '{' || c == '}' || c == '^' || c == '#' ||
48 c == '&' || c == '+' || c == ',' || c == ';' || c == '=' ||
49 c == '[' || c == ']' || c == '.';
50}
51
52int xf_map_parseline(const char* line, struct xf_map* map)
53{
54 enum {
55 s_name,
56 s_md5,
57 s_first_num,
58 s_file_len = s_first_num,
59 s_offset,
60 s_length,
61 s_done,
62 };
63
64#define skipws() do { while(*line == ' ' || *line == '\t') ++line; } while(0)
65#define nextstate() do { ++state; length = 0; ++line; skipws(); } while(0)
66
67 int state = s_name;
68 int length = 0;
69 int digit_val;
70 uint32_t int_val;
71 uint32_t* num_ptr[3] = {&map->file_length, &map->offset, &map->length};
72 bool has_md5 = true;
73
74 skipws();
75 while(*line && *line != '\n') {
76 switch(state) {
77 case s_name:
78 if(*line == ' ' || *line == '\t') {
79 nextstate();
80 continue;
81 } else if(isfilenamechar(*line)) {
82 if(length == XF_MAP_NAMELEN)
83 return XF_E_FILENAME_TOO_LONG;
84
85 map->name[length++] = *line++;
86 map->name[length] = '\0';
87 continue;
88 } else {
89 return XF_E_SYNTAX_ERROR;
90 }
91
92 case s_md5:
93 if(*line == '-') {
94 memset(map->md5, 0, 16);
95 map->file_length = 0;
96 has_md5 = false;
97 ++line;
98 } else {
99 for(int i = 0; i < 16; ++i) {
100 int_val = 0;
101 for(int j = 0; j < 2; ++j) {
102 digit_val = xdigit_to_int(*line++);
103 if(digit_val < 0)
104 return XF_E_SYNTAX_ERROR;
105
106 int_val <<= 4;
107 int_val |= digit_val;
108 }
109
110 map->md5[i] = int_val;
111 }
112 }
113
114 if(*line == ' ' || *line == '\t') {
115 /* skip file length if md5 is not present */
116 if(!has_md5)
117 ++state;
118
119 nextstate();
120 continue;
121 } else {
122 return XF_E_SYNTAX_ERROR;
123 }
124
125 case s_file_len:
126 case s_offset:
127 case s_length:
128 int_val = 0;
129
130 if(*line == '0') {
131 ++line;
132 if(*line == 'x' || *line == 'X') {
133 ++line;
134 while((digit_val = xdigit_to_int(*line)) >= 0) {
135 ++line;
136
137 if(int_val > UINT32_MAX/16)
138 return XF_E_INT_OVERFLOW;
139
140 int_val *= 16;
141 int_val |= digit_val;
142 }
143 }
144 } else if(*line >= '1' && *line <= '9') {
145 do {
146 if(int_val > UINT32_MAX/10)
147 return XF_E_INT_OVERFLOW;
148 int_val *= 10;
149
150 digit_val = *line++ - '0';
151 if(int_val > UINT32_MAX - digit_val)
152 return XF_E_INT_OVERFLOW;
153
154 int_val += digit_val;
155 } while(*line >= '0' && *line <= '9');
156 }
157
158 *num_ptr[state - s_first_num] = int_val;
159
160 if(*line == ' ' || *line == '\t') {
161 nextstate();
162 continue;
163 } else if(state+1 == s_done && *line == '\0') {
164 /* end of input */
165 continue;
166 } else {
167 return XF_E_SYNTAX_ERROR;
168 }
169
170 case s_done:
171 if(isspace(*line)) {
172 line++;
173 continue; /* swallow trailing spaces, carriage return, etc */
174 } else
175 return XF_E_SYNTAX_ERROR;
176 }
177 }
178
179#undef skipws
180#undef nextstate
181
182 /* one last overflow check - ensure mapped range is addressable */
183 if(map->offset > UINT32_MAX - map->length)
184 return XF_E_INT_OVERFLOW;
185
186 if(has_md5)
187 map->flags = XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH;
188 else
189 map->flags = 0;
190
191 return XF_E_SUCCESS;
192}
193
194struct map_parse_args {
195 struct xf_map* map;
196 int num;
197 int maxnum;
198};
199
200int map_parse_line_cb(int n, char* buf, void* arg)
201{
202 (void)n;
203
204 struct map_parse_args* args = arg;
205
206 /* skip whitespace */
207 while(*buf && isspace(*buf))
208 ++buf;
209
210 /* ignore comments and blank lines */
211 if(*buf == '#' || *buf == '\0')
212 return 0;
213
214 struct xf_map dummy_map;
215 struct xf_map* dst_map;
216 if(args->num < args->maxnum)
217 dst_map = &args->map[args->num];
218 else
219 dst_map = &dummy_map;
220
221 int rc = xf_map_parseline(buf, dst_map);
222 if(rc)
223 return rc;
224
225 args->num++;
226 return 0;
227}
228
229int xf_map_parse(struct xf_stream* s, struct xf_map* map, int maxnum)
230{
231 char buf[200];
232 struct map_parse_args args;
233 args.map = map;
234 args.num = 0;
235 args.maxnum = maxnum;
236
237 int rc = xf_stream_read_lines(s, buf, sizeof(buf),
238 map_parse_line_cb, &args);
239 if(rc < 0)
240 return rc;
241
242 return args.num;
243}
244
245static int xf_map_compare(const void* a, const void* b)
246{
247 const struct xf_map* mapA = a;
248 const struct xf_map* mapB = b;
249
250 if(mapA->offset < mapB->offset)
251 return -1;
252 else if(mapA->offset == mapB->offset)
253 return 0;
254 else
255 return 1;
256}
257
258int xf_map_sort(struct xf_map* map, int num)
259{
260 qsort(map, num, sizeof(struct xf_map), xf_map_compare);
261 return xf_map_validate(map, num);
262}
263
264int xf_map_validate(const struct xf_map* map, int num)
265{
266 for(int i = 1; i < num; ++i)
267 if(map[i].offset <= map[i-1].offset)
268 return -1;
269
270 for(int i = 1; i < num; ++i)
271 if(map[i-1].offset + map[i-1].length > map[i].offset)
272 return i;
273
274 return 0;
275}
276
277int xf_map_write(struct xf_map* map, int num, struct xf_stream* s)
278{
279 static const char hex[] = "0123456789abcdef";
280 char buf[200];
281 char md5str[33];
282 int total_len = 0;
283
284 md5str[32] = '\0';
285
286 for(int i = 0; i < num; ++i) {
287 bool has_md5 = false;
288 if(map->flags & XF_MAP_HAS_MD5) {
289 if(!(map->flags & XF_MAP_HAS_FILE_LENGTH))
290 return XF_E_INVALID_PARAMETER;
291
292 has_md5 = true;
293 for(int j = 0; j < 16; ++j) {
294 uint8_t byte = map[i].md5[j];
295 md5str[2*j] = hex[(byte >> 4) & 0xf];
296 md5str[2*j+1] = hex[byte & 0xf];
297 }
298 }
299
300 int len;
301 if(!has_md5) {
302 len = snprintf(buf, sizeof(buf), "%s - %lx %lu\n",
303 map[i].name,
304 (unsigned long)map[i].offset,
305 (unsigned long)map[i].length);
306 } else {
307 len = snprintf(buf, sizeof(buf), "%s %s %lu 0x%lx %lu\n",
308 map[i].name, md5str,
309 (unsigned long)map[i].file_length,
310 (unsigned long)map[i].offset,
311 (unsigned long)map[i].length);
312 }
313
314 if(len < 0 || (size_t)len >= sizeof(buf))
315 return XF_E_LINE_TOO_LONG;
316
317 if(s) {
318 int rc = xf_stream_write(s, buf, len);
319 if(rc != len)
320 return XF_E_IO;
321 }
322
323 total_len += len;
324 }
325
326 return total_len;
327}
diff --git a/lib/x1000-installer/src/xf_nandio.c b/lib/x1000-installer/src/xf_nandio.c
deleted file mode 100644
index 6dc87bc420..0000000000
--- a/lib/x1000-installer/src/xf_nandio.c
+++ /dev/null
@@ -1,292 +0,0 @@
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 "xf_nandio.h"
23#include "xf_error.h"
24#include "core_alloc.h"
25#include "system.h"
26#include <string.h>
27#include <stdbool.h>
28
29int xf_nandio_init(struct xf_nandio* nio)
30{
31 int rc;
32
33 memset(nio, 0, sizeof(*nio));
34
35 /* open NAND */
36 nio->ndrv = nand_init();
37 nand_lock(nio->ndrv);
38 rc = nand_open(nio->ndrv);
39 if(rc != NAND_SUCCESS) {
40 nio->nand_err = rc;
41 rc = XF_E_NAND;
42 goto out;
43 }
44
45 /* read chip parameters */
46 nio->page_size = nio->ndrv->chip->page_size;
47 nio->block_size = nio->page_size << nio->ndrv->chip->log2_ppb;
48
49 /* allocate memory */
50 size_t alloc_size = 0;
51 alloc_size += CACHEALIGN_SIZE - 1;
52 alloc_size += nio->block_size * 2;
53
54 nio->alloc_handle = core_alloc_ex("xf_nandio", alloc_size, &buflib_ops_locked);
55 if(nio->alloc_handle < 0) {
56 rc = XF_E_OUT_OF_MEMORY;
57 goto out_nclose;
58 }
59
60 uint8_t* buffer = core_get_data(nio->alloc_handle);
61 CACHEALIGN_BUFFER(buffer, alloc_size);
62
63 nio->old_buf = buffer;
64 nio->new_buf = &buffer[nio->block_size];
65
66 rc = XF_E_SUCCESS;
67 goto out;
68
69 out_nclose:
70 nand_close(nio->ndrv);
71 out:
72 nand_unlock(nio->ndrv);
73 return rc;
74}
75
76void xf_nandio_destroy(struct xf_nandio* nio)
77{
78 nio->alloc_handle = core_free(nio->alloc_handle);
79
80 if(nio->ndrv) {
81 nand_lock(nio->ndrv);
82 nand_close(nio->ndrv);
83 nand_unlock(nio->ndrv);
84 nio->ndrv = NULL;
85 }
86}
87
88static bool is_page_blank(const uint8_t* buf, uint32_t length)
89{
90 for(uint32_t i = 0; i < length; ++i)
91 if(buf[i] != 0xff)
92 return false;
93
94 return true;
95}
96
97static int flush_block(struct xf_nandio* nio, bool invalidate)
98{
99 /* no block, or only reading - flush is a no-op */
100 if(!nio->block_valid || nio->mode == XF_NANDIO_READ)
101 return XF_E_SUCCESS;
102
103 /* nothing to do if new data is same as old data */
104 if(!memcmp(nio->old_buf, nio->new_buf, nio->block_size))
105 return XF_E_SUCCESS;
106
107 /* data mismatch during verification - report the error */
108 if(nio->mode == XF_NANDIO_VERIFY)
109 return XF_E_VERIFY_FAILED;
110
111 /* erase the block */
112 int rc = nand_block_erase(nio->ndrv, nio->cur_block);
113 if(rc != NAND_SUCCESS) {
114 nio->block_valid = false;
115 nio->nand_err = rc;
116 return XF_E_NAND;
117 }
118
119 size_t oob_size = nio->ndrv->chip->oob_size;
120
121 unsigned page = 0;
122 nand_page_t page_addr = nio->cur_block;
123 for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
124 /* skip programming blank pages to go faster & reduce wear */
125 uint8_t* page_data = &nio->new_buf[page * nio->page_size];
126 if(is_page_blank(page_data, nio->page_size))
127 continue;
128
129 /* copy page and write blank OOB data */
130 memcpy(nio->ndrv->page_buf, page_data, nio->page_size);
131 memset(&nio->ndrv->page_buf[nio->page_size], 0xff, oob_size);
132
133 /* program the page */
134 rc = nand_page_program(nio->ndrv, page_addr, nio->ndrv->page_buf);
135 if(rc != NAND_SUCCESS) {
136 nio->block_valid = false;
137 nio->nand_err = rc;
138 return XF_E_NAND;
139 }
140 }
141
142 if(invalidate)
143 nio->block_valid = false;
144 else {
145 /* update our 'old' buffer so a subsequent flush
146 * will not reprogram the same block */
147 memcpy(nio->old_buf, nio->new_buf, nio->block_size);
148 }
149
150 return XF_E_SUCCESS;
151}
152
153static int seek_to_block(struct xf_nandio* nio, nand_block_t block_addr,
154 size_t offset_in_block)
155{
156 /* already on this block? */
157 if(nio->block_valid && block_addr == nio->cur_block) {
158 nio->offset_in_block = offset_in_block;
159 return XF_E_SUCCESS;
160 }
161
162 /* ensure new block is within range */
163 if(block_addr >= (nio->ndrv->chip->nr_blocks << nio->ndrv->chip->log2_ppb))
164 return XF_E_OUT_OF_RANGE;
165
166 /* flush old block */
167 int rc = flush_block(nio, true);
168 if(rc)
169 return rc;
170
171 nio->block_valid = false;
172
173 /* read the new block */
174 unsigned page = 0;
175 nand_page_t page_addr = block_addr;
176 for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
177 rc = nand_page_read(nio->ndrv, page_addr, nio->ndrv->page_buf);
178 if(rc != NAND_SUCCESS) {
179 nio->nand_err = rc;
180 return XF_E_NAND;
181 }
182
183 memcpy(&nio->old_buf[page * nio->page_size], nio->ndrv->page_buf, nio->page_size);
184 }
185
186 /* copy to 2nd buffer */
187 memcpy(nio->new_buf, nio->old_buf, nio->block_size);
188
189 /* update position */
190 nio->cur_block = block_addr;
191 nio->offset_in_block = offset_in_block;
192 nio->block_valid = true;
193 return XF_E_SUCCESS;
194}
195
196int xf_nandio_set_mode(struct xf_nandio* nio, enum xf_nandio_mode mode)
197{
198 nand_lock(nio->ndrv);
199
200 /* flush the current block before switching to the new mode,
201 * to ensure consistency */
202 int rc = flush_block(nio, false);
203 if(rc)
204 goto err;
205
206 nio->mode = mode;
207 rc = XF_E_SUCCESS;
208
209 err:
210 nand_unlock(nio->ndrv);
211 return rc;
212}
213
214static int nandio_rdwr(struct xf_nandio* nio, void* buf, size_t count, bool write)
215{
216 while(count > 0) {
217 void* ptr;
218 size_t amount = count;
219 int rc = xf_nandio_get_buffer(nio, &ptr, &amount);
220 if(rc)
221 return rc;
222
223 if(write)
224 memcpy(ptr, buf, amount);
225 else
226 memcpy(buf, ptr, amount);
227
228 count -= amount;
229 }
230
231 return XF_E_SUCCESS;
232}
233
234int xf_nandio_seek(struct xf_nandio* nio, size_t offset)
235{
236 uint32_t block_nr = offset / nio->block_size;
237 size_t offset_in_block = offset % nio->block_size;
238 nand_block_t block_addr = block_nr << nio->ndrv->chip->log2_ppb;
239
240 nand_lock(nio->ndrv);
241 int rc = seek_to_block(nio, block_addr, offset_in_block);
242 nand_unlock(nio->ndrv);
243
244 return rc;
245}
246
247int xf_nandio_read(struct xf_nandio* nio, void* buf, size_t count)
248{
249 return nandio_rdwr(nio, buf, count, false);
250}
251
252int xf_nandio_write(struct xf_nandio* nio, const void* buf, size_t count)
253{
254 return nandio_rdwr(nio, (void*)buf, count, true);
255}
256
257int xf_nandio_get_buffer(struct xf_nandio* nio, void** buf, size_t* count)
258{
259 nand_lock(nio->ndrv);
260
261 /* make sure the current block data is read in */
262 int rc = seek_to_block(nio, nio->cur_block, nio->offset_in_block);
263 if(rc)
264 goto err;
265
266 size_t amount_left = nio->block_size - nio->offset_in_block;
267 if(amount_left == 0) {
268 amount_left = nio->block_size;
269 rc = seek_to_block(nio, nio->cur_block + nio->ndrv->ppb, 0);
270 if(rc)
271 goto err;
272 }
273
274 *buf = &nio->new_buf[nio->offset_in_block];
275 *count = MIN(*count, amount_left);
276
277 nio->offset_in_block += *count;
278 rc = XF_E_SUCCESS;
279
280 err:
281 nand_unlock(nio->ndrv);
282 return rc;
283}
284
285int xf_nandio_flush(struct xf_nandio* nio)
286{
287 nand_lock(nio->ndrv);
288 int rc = flush_block(nio, false);
289 nand_unlock(nio->ndrv);
290
291 return rc;
292}
diff --git a/lib/x1000-installer/src/xf_package.c b/lib/x1000-installer/src/xf_package.c
deleted file mode 100644
index fb107aef72..0000000000
--- a/lib/x1000-installer/src/xf_package.c
+++ /dev/null
@@ -1,261 +0,0 @@
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 "xf_package.h"
23#include "xf_error.h"
24#include "pathfuncs.h"
25#include "file.h"
26#include "core_alloc.h"
27#include "md5.h"
28#include "system.h"
29#include <stdbool.h>
30#include <string.h>
31
32#ifdef ROCKBOX
33# include "microtar-rockbox.h"
34#else
35# include "microtar-stdio.h"
36#endif
37
38#define METADATA_SIZE 4096 /* size of the metadata buffer */
39#define MAX_MAP_SIZE 32 /* maximum number of map entries */
40
41static int pkg_alloc(struct xf_package* pkg)
42{
43 memset(pkg, 0, sizeof(*pkg));
44
45 /* calculate allocation size */
46 size_t alloc_size = 0;
47 alloc_size += ALIGN_UP_P2(sizeof(mtar_t), 3);
48 alloc_size += ALIGN_UP_P2(sizeof(struct xf_map) * MAX_MAP_SIZE, 3);
49 alloc_size += ALIGN_UP_P2(METADATA_SIZE, 3);
50 alloc_size += 7; /* for alignment */
51
52 pkg->alloc_handle = core_alloc_ex("xf_package", alloc_size, &buflib_ops_locked);
53 if(pkg->alloc_handle < 0)
54 return XF_E_OUT_OF_MEMORY;
55
56 /* distribute memory */
57 uint8_t* buf = (uint8_t*)core_get_data(pkg->alloc_handle);
58 memset(buf, 0, alloc_size);
59 ALIGN_BUFFER(buf, alloc_size, 8);
60
61 pkg->tar = (mtar_t*)buf;
62 buf += ALIGN_UP_P2(sizeof(mtar_t), 3);
63
64 pkg->map = (struct xf_map*)buf;
65 buf += ALIGN_UP_P2(sizeof(struct xf_map) * MAX_MAP_SIZE, 3);
66
67 pkg->metadata = (char*)buf;
68 buf += ALIGN_UP_P2(METADATA_SIZE, 3);
69
70 return XF_E_SUCCESS;
71}
72
73static int read_meta_line_cb(int n, char* buf, void* arg)
74{
75 struct xf_package* pkg = (struct xf_package*)arg;
76 size_t length = strlen(buf);
77
78 /* skip blank lines and the first line (it's reserved for old format) */
79 if(n == 0 || length == 0)
80 return 0;
81
82 /* metadata lines require an '=' sign to separate key and value */
83 if(!strchr(buf, '='))
84 return XF_E_SYNTAX_ERROR;
85
86 /* we need space to copy the key-value pair plus a null terminator */
87 if(length + 1 >= METADATA_SIZE - pkg->metadata_len)
88 return XF_E_BUF_OVERFLOW;
89
90 memcpy(&pkg->metadata[pkg->metadata_len], buf, length + 1);
91 pkg->metadata_len += length + 1;
92 return 0;
93}
94
95static int pkg_read_meta(struct xf_package* pkg)
96{
97 struct xf_stream stream;
98 int rc = xf_open_tar(pkg->tar, "bootloader-info.txt", &stream);
99 if(rc)
100 return XF_E_CANNOT_OPEN_FILE;
101
102 char buf[200];
103 rc = xf_stream_read_lines(&stream, buf, sizeof(buf), read_meta_line_cb, pkg);
104 xf_stream_close(&stream);
105 return rc;
106}
107
108static int pkg_read_map(struct xf_package* pkg,
109 const struct xf_map* dflt_map, int dflt_map_size)
110{
111 /* Attempt to load and parse the map file */
112 struct xf_stream stream;
113 int rc = xf_open_tar(pkg->tar, "flashmap.txt", &stream);
114
115 /* If the flash map is absent but a default map has been provided,
116 * then the update is in the old fixed format. */
117 if(rc == MTAR_ENOTFOUND && dflt_map) {
118 if(dflt_map_size > MAX_MAP_SIZE)
119 return XF_E_INVALID_PARAMETER;
120
121 for(int i = 0; i < dflt_map_size; ++i) {
122 pkg->map[i] = dflt_map[i];
123 pkg->map[i].flags &= ~(XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH);
124 }
125
126 pkg->map_size = dflt_map_size;
127 return XF_E_SUCCESS;
128 }
129
130 if(rc != MTAR_ESUCCESS)
131 return XF_E_CANNOT_OPEN_FILE;
132
133 rc = xf_map_parse(&stream, pkg->map, MAX_MAP_SIZE);
134 if(rc < 0)
135 goto err;
136
137 /* Sort the map; reject it if there is any overlap. */
138 pkg->map_size = rc;
139 if(xf_map_sort(pkg->map, pkg->map_size)) {
140 rc = XF_E_INVALID_PARAMETER;
141 goto err;
142 }
143
144 /* All packages in the 'new' format are required to have MD5 sums. */
145 for(int i = 0; i < pkg->map_size; ++i) {
146 if(!(pkg->map[i].flags & XF_MAP_HAS_MD5) ||
147 !(pkg->map[i].flags & XF_MAP_HAS_FILE_LENGTH)) {
148 rc = XF_E_VERIFY_FAILED;
149 goto err;
150 }
151 }
152
153 rc = XF_E_SUCCESS;
154
155 err:
156 xf_stream_close(&stream);
157 return rc;
158}
159
160static int pkg_verify(struct xf_package* pkg)
161{
162 struct xf_stream stream;
163 md5_context ctx;
164 uint8_t buffer[128];
165
166 for(int i = 0; i < pkg->map_size; ++i) {
167 /* At a bare minimum, check that the file exists. */
168 int rc = xf_open_tar(pkg->tar, pkg->map[i].name, &stream);
169 if(rc)
170 return XF_E_VERIFY_FAILED;
171
172 /* Also check that it isn't bigger than the update region.
173 * That would normally indicate a problem. */
174 off_t streamsize = xf_stream_get_size(&stream);
175 if(streamsize > (off_t)pkg->map[i].length) {
176 rc = XF_E_VERIFY_FAILED;
177 goto err;
178 }
179
180 /* Check against the listed file length. */
181 if(pkg->map[i].flags & XF_MAP_HAS_FILE_LENGTH) {
182 if(streamsize != (off_t)pkg->map[i].file_length) {
183 rc = XF_E_VERIFY_FAILED;
184 goto err;
185 }
186 }
187
188 /* Check the MD5 sum if we have it. */
189 if(pkg->map[i].flags & XF_MAP_HAS_MD5) {
190 md5_starts(&ctx);
191 while(1) {
192 ssize_t n = xf_stream_read(&stream, buffer, sizeof(buffer));
193 if(n < 0) {
194 rc = XF_E_IO;
195 goto err;
196 }
197
198 md5_update(&ctx, buffer, n);
199 if((size_t)n < sizeof(buffer))
200 break;
201 }
202
203 md5_finish(&ctx, buffer);
204 if(memcpy(buffer, pkg->map[i].md5, 16)) {
205 rc = XF_E_VERIFY_FAILED;
206 goto err;
207 }
208 }
209
210 err:
211 xf_stream_close(&stream);
212 if(rc)
213 return rc;
214 }
215
216 /* All files passed verification */
217 return XF_E_SUCCESS;
218}
219
220int xf_package_open_ex(struct xf_package* pkg, const char* file,
221 const struct xf_map* dflt_map, int dflt_map_size)
222{
223 int rc = pkg_alloc(pkg);
224 if(rc)
225 return rc;
226
227#ifdef ROCKBOX
228 rc = mtar_open(pkg->tar, file, O_RDONLY);
229#else
230 rc = mtar_open(pkg->tar, file, "r");
231#endif
232 if(rc != MTAR_ESUCCESS) {
233 rc = XF_E_CANNOT_OPEN_FILE;
234 goto err;
235 }
236
237 rc = pkg_read_meta(pkg);
238 if(rc)
239 goto err;
240
241 rc = pkg_read_map(pkg, dflt_map, dflt_map_size);
242 if(rc)
243 goto err;
244
245 rc = pkg_verify(pkg);
246 if(rc)
247 goto err;
248
249 err:
250 if(rc)
251 xf_package_close(pkg);
252 return rc;
253}
254
255void xf_package_close(struct xf_package* pkg)
256{
257 if(mtar_is_open(pkg->tar))
258 mtar_close(pkg->tar);
259
260 pkg->alloc_handle = core_free(pkg->alloc_handle);
261}
diff --git a/lib/x1000-installer/src/xf_stream.c b/lib/x1000-installer/src/xf_stream.c
deleted file mode 100644
index b6391b2c8d..0000000000
--- a/lib/x1000-installer/src/xf_stream.c
+++ /dev/null
@@ -1,211 +0,0 @@
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 "xf_stream.h"
23#include "xf_error.h"
24#include "file.h"
25#include <stdbool.h>
26#include <string.h>
27#include <ctype.h>
28
29/*
30 * File streams
31 */
32
33static off_t file_stream_get_size(struct xf_stream* s)
34{
35 return filesize(s->data);
36}
37
38static ssize_t file_stream_read(struct xf_stream* s, void* buf, size_t len)
39{
40 return read(s->data, buf, len);
41}
42
43static ssize_t file_stream_write(struct xf_stream* s, const void* buf, size_t len)
44{
45 return write(s->data, buf, len);
46}
47
48static int file_stream_close(struct xf_stream* s)
49{
50 return close(s->data);
51}
52
53static const struct xf_stream_ops file_stream_ops = {
54 .xget_size = file_stream_get_size,
55 .xread = file_stream_read,
56 .xwrite = file_stream_write,
57 .xclose = file_stream_close,
58};
59
60int xf_open_file(const char* file, int flags, struct xf_stream* s)
61{
62 s->data = open(file, flags, 0666);
63 s->ops = &file_stream_ops;
64 return (s->data >= 0) ? 0 : -1;
65}
66
67/*
68 * Tar streams
69 */
70
71static off_t tar_stream_get_size(struct xf_stream* s)
72{
73 mtar_t* mtar = (mtar_t*)s->data;
74 return mtar_get_header(mtar)->size;
75}
76
77static ssize_t tar_stream_read(struct xf_stream* s, void* buffer, size_t count)
78{
79 mtar_t* mtar = (mtar_t*)s->data;
80
81 int ret = mtar_read_data(mtar, buffer, count);
82 if(ret < 0)
83 return -1;
84
85 return ret;
86}
87
88static ssize_t tar_stream_write(struct xf_stream* s, const void* buffer, size_t count)
89{
90 mtar_t* mtar = (mtar_t*)s->data;
91
92 int ret = mtar_write_data(mtar, buffer, count);
93 if(ret < 0)
94 return -1;
95
96 return ret;
97}
98
99static int tar_stream_close(struct xf_stream* s)
100{
101 mtar_t* mtar = (mtar_t*)s->data;
102
103 if(mtar_access_mode(mtar) == MTAR_WRITE) {
104 if(mtar_update_file_size(mtar) != MTAR_ESUCCESS)
105 return -1;
106 if(mtar_end_data(mtar) != MTAR_ESUCCESS)
107 return -1;
108 }
109
110 return 0;
111}
112
113static const struct xf_stream_ops tar_stream_ops = {
114 .xget_size = tar_stream_get_size,
115 .xread = tar_stream_read,
116 .xwrite = tar_stream_write,
117 .xclose = tar_stream_close,
118};
119
120int xf_open_tar(mtar_t* mtar, const char* file, struct xf_stream* s)
121{
122 int err = mtar_find(mtar, file);
123 if(err != MTAR_ESUCCESS)
124 return err;
125
126 /* must only read normal files */
127 const mtar_header_t* h = mtar_get_header(mtar);
128 if(h->type != 0 && h->type != MTAR_TREG)
129 return MTAR_EFAILURE;
130
131 s->data = (intptr_t)mtar;
132 s->ops = &tar_stream_ops;
133 return MTAR_ESUCCESS;
134}
135
136int xf_create_tar(mtar_t* mtar, const char* file, size_t size, struct xf_stream* s)
137{
138 int err = mtar_write_file_header(mtar, file, size);
139 if(err)
140 return err;
141
142 s->data = (intptr_t)mtar;
143 s->ops = &tar_stream_ops;
144 return MTAR_ESUCCESS;
145}
146
147/*
148 * Utility functions
149 */
150
151int xf_stream_read_lines(struct xf_stream* s, char* buf, size_t bufsz,
152 int(*callback)(int n, char* buf, void* arg), void* arg)
153{
154 char* startp, *endp;
155 ssize_t bytes_read;
156 int rc;
157
158 int n = 0;
159 size_t pos = 0;
160 bool at_eof = false;
161
162 if(bufsz <= 1)
163 return XF_E_LINE_TOO_LONG;
164
165 while(!at_eof) {
166 bytes_read = xf_stream_read(s, &buf[pos], bufsz - pos - 1);
167 if(bytes_read < 0)
168 return XF_E_IO;
169
170 /* short read is end of file */
171 if((size_t)bytes_read < bufsz - pos - 1)
172 at_eof = true;
173
174 pos += bytes_read;
175 buf[pos] = '\0';
176
177 startp = endp = buf;
178 while(endp != &buf[pos]) {
179 endp = strchr(startp, '\n');
180 if(endp) {
181 *endp = '\0';
182 } else {
183 if(!at_eof) {
184 if(startp == buf)
185 return XF_E_LINE_TOO_LONG;
186 else
187 break; /* read ahead to look for newline */
188 } else {
189 if(startp == &buf[pos])
190 break; /* nothing left to do */
191 else
192 endp = &buf[pos]; /* last line missing a newline -
193 * treat EOF as newline */
194 }
195 }
196
197 rc = callback(n++, startp, arg);
198 if(rc != 0)
199 return rc;
200
201 startp = endp + 1;
202 }
203
204 if(startp <= &buf[pos]) {
205 memmove(buf, startp, &buf[pos] - startp);
206 pos = &buf[pos] - startp;
207 }
208 }
209
210 return 0;
211}
diff --git a/lib/x1000-installer/src/xf_update.c b/lib/x1000-installer/src/xf_update.c
deleted file mode 100644
index 5a7c3b0430..0000000000
--- a/lib/x1000-installer/src/xf_update.c
+++ /dev/null
@@ -1,149 +0,0 @@
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 "xf_update.h"
23#include "xf_error.h"
24#include "file.h"
25#include "md5.h"
26#include <string.h>
27
28static int process_stream(struct xf_nandio* nio,
29 struct xf_map* map,
30 struct xf_stream* stream)
31{
32 void* buffer;
33 size_t count;
34 int rc;
35
36 /* calculate MD5 on the fly if taking a backup */
37 md5_context md5_ctx;
38 if(nio->mode == XF_NANDIO_READ)
39 md5_starts(&md5_ctx);
40
41 /* first deal with the file data */
42 size_t bytes_left = map->length;
43 while(bytes_left > 0) {
44 count = bytes_left;
45 rc = xf_nandio_get_buffer(nio, &buffer, &count);
46 if(rc)
47 return rc;
48
49 if(nio->mode == XF_NANDIO_READ) {
50 md5_update(&md5_ctx, buffer, count);
51 rc = xf_stream_write(stream, buffer, count);
52 } else {
53 rc = xf_stream_read(stream, buffer, count);
54 }
55
56 bytes_left -= count;
57
58 if(rc < 0 || (size_t)rc > count)
59 return XF_E_IO;
60
61 if((size_t)rc < count) {
62 /* backup - we could not write all the data */
63 if(nio->mode == XF_NANDIO_READ)
64 return XF_E_IO;
65
66 /* update - clear rest of buffer to 0xff */
67 memset(buffer + rc, 0xff, count - rc);
68 break;
69 }
70 }
71
72 /* if updating - write blanks to the remainder of the region */
73 while(bytes_left > 0) {
74 count = bytes_left;
75 rc = xf_nandio_get_buffer(nio, &buffer, &count);
76 if(rc)
77 return rc;
78
79 memset(buffer, 0xff, count);
80 bytes_left -= count;
81 }
82
83 /* finalize the MD5 sum */
84 if(nio->mode == XF_NANDIO_READ) {
85 md5_finish(&md5_ctx, map->md5);
86 map->file_length = map->length;
87 map->flags |= XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH;
88 }
89
90 return XF_E_SUCCESS;
91}
92
93static int process_map(struct xf_nandio* nio, struct xf_map* map, int map_size,
94 xf_update_open_stream_cb open_stream, void* os_arg)
95{
96 int rc, rc2;
97 struct xf_stream stream;
98
99 /* ensure the map is sequential and non-overlapping before continuing */
100 if(xf_map_validate(map, map_size) != 0)
101 return XF_E_INVALID_PARAMETER;
102
103 for(int i = 0; i < map_size; ++i) {
104 /* seek to initial offset */
105 rc = xf_nandio_seek(nio, map[i].offset);
106 if(rc)
107 return rc;
108
109 rc = open_stream(os_arg, map[i].name, &stream);
110 if(rc)
111 return XF_E_CANNOT_OPEN_FILE;
112
113 /* process the stream and be sure to close it even on error */
114 rc = process_stream(nio, &map[i], &stream);
115 rc2 = xf_stream_close(&stream);
116
117 /* bail if either operation raised an error */
118 if(rc)
119 return rc;
120 if(rc2)
121 return rc2;
122 }
123
124 /* now flush to ensure all data was written */
125 rc = xf_nandio_flush(nio);
126 if(rc)
127 return rc;
128
129 return XF_E_SUCCESS;
130}
131
132static const enum xf_nandio_mode update_mode_to_nandio[] = {
133 [XF_UPDATE] = XF_NANDIO_PROGRAM,
134 [XF_BACKUP] = XF_NANDIO_READ,
135 [XF_VERIFY] = XF_NANDIO_VERIFY,
136};
137
138int xf_updater_run(enum xf_update_mode mode, struct xf_nandio* nio,
139 struct xf_map* map, int map_size,
140 xf_update_open_stream_cb open_stream, void* arg)
141{
142 /* Switch NAND I/O into the correct mode */
143 int rc = xf_nandio_set_mode(nio, update_mode_to_nandio[mode]);
144 if(rc)
145 return rc;
146
147 /* This does all the real work */
148 return process_map(nio, map, map_size, open_stream, arg);
149}