summaryrefslogtreecommitdiff
path: root/lib/x1000-installer/src/xf_flashmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/x1000-installer/src/xf_flashmap.c')
-rw-r--r--lib/x1000-installer/src/xf_flashmap.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/lib/x1000-installer/src/xf_flashmap.c b/lib/x1000-installer/src/xf_flashmap.c
new file mode 100644
index 0000000000..75cd3c5905
--- /dev/null
+++ b/lib/x1000-installer/src/xf_flashmap.c
@@ -0,0 +1,313 @@
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
40int xf_map_parseline(const char* line, struct xf_map* map)
41{
42 enum {
43 s_name,
44 s_md5,
45 s_first_num,
46 s_file_len = s_first_num,
47 s_offset,
48 s_length,
49 s_done,
50 };
51
52#define skipws() do { while(*line == ' ' || *line == '\t') ++line; } while(0)
53#define nextstate() do { ++state; length = 0; ++line; skipws(); } while(0)
54
55 int state = s_name;
56 int length = 0;
57 int digit_val;
58 uint32_t int_val;
59 uint32_t* num_ptr[3] = {&map->file_length, &map->offset, &map->length};
60 bool has_md5 = true;
61
62 skipws();
63 while(*line && *line != '\n') {
64 switch(state) {
65 case s_name:
66 if(*line == ' ' || *line == '\t') {
67 nextstate();
68 continue;
69 } else if(isgraph((unsigned char)*line)) {
70 if(length == XF_MAP_NAMELEN)
71 return XF_E_FILENAME_TOO_LONG;
72
73 map->name[length++] = *line++;
74 map->name[length] = '\0';
75 continue;
76 } else {
77 return XF_E_SYNTAX_ERROR;
78 }
79
80 case s_md5:
81 if(*line == '-') {
82 memset(map->md5, 0, 16);
83 map->file_length = 0;
84 has_md5 = false;
85 ++line;
86 } else {
87 for(int i = 0; i < 16; ++i) {
88 int_val = 0;
89 for(int j = 0; j < 2; ++j) {
90 digit_val = xdigit_to_int(*line++);
91 if(digit_val < 0)
92 return XF_E_SYNTAX_ERROR;
93
94 int_val <<= 4;
95 int_val |= digit_val;
96 }
97
98 map->md5[i] = int_val;
99 }
100 }
101
102 if(*line == ' ' || *line == '\t') {
103 /* skip file length if md5 is not present */
104 if(!has_md5)
105 ++state;
106
107 nextstate();
108 continue;
109 } else {
110 return XF_E_SYNTAX_ERROR;
111 }
112
113 case s_file_len:
114 case s_offset:
115 case s_length:
116 int_val = 0;
117
118 if(*line == '0') {
119 ++line;
120 if(*line == 'x' || *line == 'X') {
121 ++line;
122 while((digit_val = xdigit_to_int(*line)) >= 0) {
123 ++line;
124
125 if(int_val > UINT32_MAX/16)
126 return XF_E_INT_OVERFLOW;
127 int_val *= 16;
128
129 if(int_val > UINT32_MAX - digit_val)
130 return XF_E_INT_OVERFLOW;
131 int_val |= digit_val;
132 }
133 }
134 } else if(*line >= '1' && *line <= '9') {
135 do {
136 if(int_val > UINT32_MAX/10)
137 return XF_E_INT_OVERFLOW;
138 int_val *= 10;
139
140 digit_val = *line++ - '0';
141 if(int_val > UINT32_MAX - digit_val)
142 return XF_E_INT_OVERFLOW;
143
144 int_val += digit_val;
145 } while(*line >= '0' && *line <= '9');
146 }
147
148 *num_ptr[state - s_first_num] = int_val;
149
150 if(*line == ' ' || *line == '\t') {
151 nextstate();
152 continue;
153 } else if(state+1 == s_done && *line == '\0') {
154 /* end of input */
155 continue;
156 } else {
157 return XF_E_SYNTAX_ERROR;
158 }
159
160 case s_done:
161 if(isspace(*line)) {
162 line++;
163 continue; /* swallow trailing spaces, carriage return, etc */
164 } else
165 return XF_E_SYNTAX_ERROR;
166 }
167 }
168
169#undef skipws
170#undef nextstate
171
172 /* one last overflow check - ensure mapped range is addressable */
173 if(map->offset > UINT32_MAX - map->length)
174 return XF_E_INT_OVERFLOW;
175
176 if(has_md5)
177 map->flags = XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH;
178 else
179 map->flags = 0;
180
181 return XF_E_SUCCESS;
182}
183
184struct map_parse_args {
185 struct xf_map* map;
186 int num;
187 int maxnum;
188};
189
190int map_parse_line_cb(int n, char* buf, void* arg)
191{
192 (void)n;
193
194 struct map_parse_args* args = arg;
195
196 /* ignore comments and blank lines */
197 if(*buf == '#' || *buf == '\0')
198 return 0;
199
200 struct xf_map dummy_map;
201 struct xf_map* dst_map;
202 if(args->num < args->maxnum)
203 dst_map = &args->map[args->num];
204 else
205 dst_map = &dummy_map;
206
207 int rc = xf_map_parseline(buf, dst_map);
208 if(rc)
209 return rc;
210
211 args->num++;
212 return 0;
213}
214
215int xf_map_parse(struct xf_stream* s, struct xf_map* map, int maxnum)
216{
217 char buf[200];
218 struct map_parse_args args;
219 args.map = map;
220 args.num = 0;
221 args.maxnum = maxnum;
222
223 int rc = xf_stream_read_lines(s, buf, sizeof(buf),
224 map_parse_line_cb, &args);
225 if(rc < 0)
226 return rc;
227
228 return args.num;
229}
230
231static int xf_map_compare(const void* a, const void* b)
232{
233 const struct xf_map* mapA = a;
234 const struct xf_map* mapB = b;
235
236 if(mapA->offset < mapB->offset)
237 return -1;
238 else if(mapA->offset == mapB->offset)
239 return 0;
240 else
241 return 1;
242}
243
244int xf_map_sort(struct xf_map* map, int num)
245{
246 qsort(map, num, sizeof(struct xf_map), xf_map_compare);
247 return xf_map_validate(map, num);
248}
249
250int xf_map_validate(const struct xf_map* map, int num)
251{
252 for(int i = 1; i < num; ++i)
253 if(map[i].offset <= map[i-1].offset)
254 return -1;
255
256 for(int i = 1; i < num; ++i)
257 if(map[i-1].offset + map[i-1].length > map[i].offset)
258 return i;
259
260 return 0;
261}
262
263int xf_map_write(struct xf_map* map, int num, struct xf_stream* s)
264{
265 static const char hex[] = "0123456789abcdef";
266 char buf[200];
267 char md5str[33];
268 int total_len = 0;
269
270 md5str[32] = '\0';
271
272 for(int i = 0; i < num; ++i) {
273 bool has_md5 = false;
274 if(map->flags & XF_MAP_HAS_MD5) {
275 if(!(map->flags & XF_MAP_HAS_FILE_LENGTH))
276 return XF_E_INVALID_PARAMETER;
277
278 has_md5 = true;
279 for(int j = 0; j < 16; ++j) {
280 uint8_t byte = map[i].md5[j];
281 md5str[2*j] = hex[(byte >> 4) & 0xf];
282 md5str[2*j+1] = hex[byte & 0xf];
283 }
284 }
285
286 int len;
287 if(!has_md5) {
288 len = snprintf(buf, sizeof(buf), "%s - %lx %lu\n",
289 map[i].name,
290 (unsigned long)map[i].offset,
291 (unsigned long)map[i].length);
292 } else {
293 len = snprintf(buf, sizeof(buf), "%s %s %lu 0x%lx %lu\n",
294 map[i].name, md5str,
295 (unsigned long)map[i].file_length,
296 (unsigned long)map[i].offset,
297 (unsigned long)map[i].length);
298 }
299
300 if(len < 0 || (size_t)len >= sizeof(buf))
301 return XF_E_LINE_TOO_LONG;
302
303 if(s) {
304 int rc = xf_stream_write(s, buf, len);
305 if(rc != len)
306 return XF_E_IO;
307 }
308
309 total_len += len;
310 }
311
312 return total_len;
313}