1 /*
2 * Copyright 2018 The ChromiumOS Authors
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <ctype.h>
8 #include <errno.h>
9 #include <malloc.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14
15 #include "desc_parser.h"
16
17 static FILE *hash_file_;
18 static int line_count_;
19 static int section_count_;
20
21 /*
22 * This is used to verify consistency of the description database, namely that
23 * all hash sections include the same number of hash variants.
24 */
25 static size_t variant_count;
26
27 /* Size of the retrieved string or negative OS error value. */
get_next_line(char * next_line,size_t line_size)28 static ssize_t get_next_line(char *next_line, size_t line_size)
29 {
30 size_t index = 0;
31
32 while (fgets(next_line + index, line_size - index, hash_file_)) {
33 line_count_++;
34
35 if (next_line[index] == '#')
36 continue; /* Skip the comment */
37
38 if (next_line[index] == '\n') {
39 /*
40 * This is an empty line, return all collected data,
41 * pontintially an array of size zero if this is a
42 * repeated empty line.
43 */
44 next_line[index] = '\0';
45 return index;
46 }
47
48 /* Make sure next string overwrites this string's newline. */
49 index += strlen(next_line + index) - 1;
50
51 if (index >= (line_size - 1)) {
52 fprintf(stderr, "%s: Input overflow in line %d\n",
53 __func__, line_count_);
54 return -EOVERFLOW;
55 }
56 }
57
58 if (index) {
59 /*
60 * This must be the last line in the file with no empty line
61 * after it. Drop the closing newline, if it is there.
62 */
63 if (next_line[index] == '\n')
64 next_line[index--] = '\0';
65
66 return index;
67 }
68 return errno ? -errno : -ENODATA;
69 }
70
get_next_token(char * input,size_t expected_size,char ** output)71 static int get_next_token(char *input, size_t expected_size, char **output)
72 {
73 char *next_colon;
74
75 next_colon = strchr(input, ':');
76 if (next_colon)
77 *next_colon = '\0';
78 if (!next_colon || (expected_size && strlen(input) != expected_size)) {
79 fprintf(stderr, "Invalid entry in section %d\n",
80 section_count_);
81 return -EINVAL;
82 }
83
84 *output = next_colon + 1;
85 return 0;
86 }
87
get_hex_value(char * input,char ** output)88 static int get_hex_value(char *input, char **output)
89 {
90 char *e;
91 long int value;
92
93 if (strchr(input, ':'))
94 get_next_token(input, 0, output);
95 else
96 *output = NULL;
97
98 value = strtol(input, &e, 16);
99 if ((e && *e) || (strlen(input) > 8)) {
100 fprintf(stderr, "Invalid hex value %s in section %d\n", input,
101 section_count_);
102 return -EINVAL;
103 }
104
105 return value;
106 }
107
parse_range(char * next_line,size_t line_len,struct addr_range * parsed_range)108 static int parse_range(char *next_line, size_t line_len,
109 struct addr_range *parsed_range)
110 {
111 char *line_cursor;
112 char *next_token;
113 int is_a_hash_range;
114 struct result_node *node;
115 int value;
116
117 section_count_++;
118 line_cursor = next_line;
119
120 /* Range type. */
121 if (get_next_token(line_cursor, 1, &next_token))
122 return -EINVAL;
123
124 switch (*line_cursor) {
125 case 'a':
126 parsed_range->range_type = AP_RANGE;
127 break;
128 case 'e':
129 parsed_range->range_type = EC_RANGE;
130 break;
131 case 'g':
132 parsed_range->range_type = EC_GANG_RANGE;
133 break;
134 default:
135 fprintf(stderr, "Invalid range type %c in section %d\n",
136 *line_cursor, section_count_);
137 return -EINVAL;
138 }
139 line_cursor = next_token;
140
141 /* Hash or dump? */
142 if (get_next_token(line_cursor, 1, &next_token))
143 return -EINVAL;
144
145 switch (*line_cursor) {
146 case 'd':
147 is_a_hash_range = 0;
148 break;
149 case 'h':
150 is_a_hash_range = 1;
151 break;
152 default:
153 fprintf(stderr, "Invalid entry kind %c in section %d\n",
154 *line_cursor, section_count_);
155 return -EINVAL;
156 }
157 line_cursor = next_token;
158
159 /* Range base address. */
160 value = get_hex_value(line_cursor, &next_token);
161 if (value < 0)
162 return -EINVAL;
163 parsed_range->base_addr = value;
164
165 /* Range size. */
166 line_cursor = next_token;
167 value = get_hex_value(line_cursor, &next_token);
168 if (value < 0)
169 return -EINVAL;
170 parsed_range->range_size = value;
171
172 if (!next_token && is_a_hash_range) {
173 fprintf(stderr, "Missing hash in section %d\n", section_count_);
174 return -EINVAL;
175 }
176
177 if (next_token && !is_a_hash_range) {
178 fprintf(stderr, "Unexpected data in section %d\n",
179 section_count_);
180 return -EINVAL;
181 }
182
183 parsed_range->variant_count = 0;
184 if (!is_a_hash_range)
185 return 0; /* No more input for dump ranges. */
186
187 node = parsed_range->variants;
188 do { /* While line is not over. */
189 char c;
190 int i = 0;
191
192 line_cursor = next_token;
193 next_token = strchr(line_cursor, ':');
194 if (next_token)
195 *next_token++ = '\0';
196 if (strlen(line_cursor) != (2 * sizeof(*node))) {
197 fprintf(stderr,
198 "Invalid hash %zd size %zd in section %d\n",
199 parsed_range->variant_count + 1,
200 strlen(line_cursor), section_count_);
201 return -EINVAL;
202 }
203
204 while ((c = *line_cursor++) != 0) {
205 uint8_t nibble;
206
207 if (!isxdigit(c)) {
208 fprintf(stderr,
209 "Invalid hash %zd value in section %d\n",
210 parsed_range->variant_count + 1,
211 section_count_);
212 return -EINVAL;
213 }
214
215 if (c <= '9')
216 nibble = c - '0';
217 else if (c >= 'a')
218 nibble = c - 'a' + 10;
219 else
220 nibble = c - 'A' + 10;
221
222 if (i & 1)
223 node->expected_result[i / 2] |= nibble;
224 else
225 node->expected_result[i / 2] = nibble << 4;
226
227 i++;
228 }
229
230 node++;
231 parsed_range->variant_count++;
232
233 } while (next_token);
234
235 return 0;
236 }
237
parser_get_next_range(struct addr_range ** range)238 int parser_get_next_range(struct addr_range **range)
239 {
240 char next_line[1000]; /* Should be enough for the largest descriptor. */
241 ssize_t entry_size;
242 struct addr_range *new_range;
243 int rv;
244
245 /*
246 * We come here after hash descriptor database file was opened and the
247 * current board's section has been found. Just in case check if the
248 * file has been opened.
249 */
250 if (!hash_file_ || !range)
251 return -EIO;
252
253 *range = NULL;
254 do {
255 entry_size = get_next_line(next_line, sizeof(next_line));
256 if (entry_size < 0)
257 return entry_size;
258 } while (!entry_size); /* Skip empty lines. */
259
260 if (entry_size == 4) /* Next board's entry must have been reached. */
261 return -ENODATA;
262
263 /* This sure will be enough to fit parsed structure contents. */
264 new_range = malloc(sizeof(*new_range) + entry_size);
265 if (!new_range) {
266 fprintf(stderr, "Failed to allocate %zd bytes\n",
267 sizeof(*new_range) + entry_size);
268 return -ENOMEM;
269 }
270
271 /* This must be a new descriptor section, lets parse it. */
272 rv = parse_range(next_line, entry_size, new_range);
273
274 if (rv) {
275 free(new_range);
276 return rv;
277 }
278
279 if (new_range->variant_count) {
280 /*
281 * A new range was found, if this is the first hash range we
282 * encountered, save its dimensions for future reference.
283 *
284 * If this is not the first one - verify that it has the same
285 * number of hash variants as all previous hash blocks.
286 */
287 if (!variant_count) {
288 variant_count = new_range->variant_count;
289 } else if (variant_count != new_range->variant_count) {
290 fprintf(stderr,
291 "Unexpected number of variants in section %d\n",
292 section_count_);
293 free(new_range);
294 return -EINVAL;
295 }
296 }
297
298 *range = new_range;
299 return 0;
300 }
301
parser_find_board(const char * hash_file_name,const char * board_id)302 int parser_find_board(const char *hash_file_name, const char *board_id)
303 {
304 char next_line[1000]; /* Should be enough for the largest descriptor. */
305 ssize_t id_len = strlen(board_id);
306
307 if (!hash_file_) {
308 hash_file_ = fopen(hash_file_name, "r");
309 if (!hash_file_) {
310 fprintf(stderr, "Error:%s can not open file '%s'\n",
311 strerror(errno), hash_file_name);
312 return errno;
313 }
314 }
315
316 while (1) {
317 ssize_t entry_size;
318
319 entry_size = get_next_line(next_line, sizeof(next_line));
320 if (entry_size < 0) {
321 return entry_size;
322 }
323
324 if ((entry_size == id_len) &&
325 !memcmp(next_line, board_id, id_len)) {
326 variant_count = 0;
327 return 0;
328 }
329 }
330
331 return -ENODATA;
332 }
333
parser_done(void)334 void parser_done(void)
335 {
336 if (!hash_file_)
337 return;
338
339 fclose(hash_file_);
340 hash_file_ = NULL;
341 }
342
343 #ifdef TEST_PARSER
main(int argc,char ** argv)344 int main(int argc, char **argv)
345 {
346 const char *board_name = "QZUX";
347 int rv;
348 int count;
349
350 if (argc < 2) {
351 fprintf(stderr, "Name of the file to parse is required.\n");
352 return -1;
353 }
354
355 if (parser_find_board(argv[1], board_name)) {
356 fprintf(stderr, "Board %s NOT found\n", board_name);
357 return -1;
358 }
359
360 count = 0;
361 do {
362 struct addr_range *range;
363
364 rv = parser_get_next_range(&range);
365 count++;
366 printf("Section %d, rv %d\n", count, rv);
367 free(range); /* Freeing NULL is OK. */
368
369 } while (rv != -ENODATA);
370
371 return 0;
372 }
373 #endif
374