xref: /aosp_15_r20/external/gsc-utils/extra/usb_updater/desc_parser.c (revision 4f2df630800bdcf1d4f0decf95d8a1cb87344f5f)
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