xref: /aosp_15_r20/external/coreboot/util/nvramtool/accessors/layout-text.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include "common.h"
4 #include "layout-text.h"
5 #include "layout.h"
6 #include "cmos_lowlevel.h"
7 #include "reg_expr.h"
8 
9 static void process_layout_file(FILE * f);
10 static void skip_past_start(FILE * f);
11 static int process_entry(FILE * f, int skip_add);
12 static int process_enum(FILE * f, int skip_add);
13 static void process_checksum_info(FILE * f);
14 static void skip_remaining_lines(FILE * f);
15 static void create_entry(cmos_entry_t * cmos_entry,
16 			 const char start_bit_str[], const char length_str[],
17 			 const char config_str[], const char config_id_str[],
18 			 const char name_str[]);
19 static void try_add_layout_file_entry(const cmos_entry_t * cmos_entry);
20 static void create_enum(cmos_enum_t * cmos_enum, const char id_str[],
21 			const char value_str[], const char text_str[]);
22 static void try_add_cmos_enum(const cmos_enum_t * cmos_enum);
23 static void set_checksum_info(const char start_str[], const char end_str[],
24 			      const char index_str[]);
25 static char cmos_entry_char_value(cmos_entry_config_t config);
26 static int get_layout_file_line(FILE * f, char line[], int line_buf_size);
27 static unsigned string_to_unsigned(const char str[], const char str_name[]);
28 static unsigned long string_to_unsigned_long(const char str[],
29 					     const char str_name[]);
30 static unsigned long do_string_to_unsigned_long(const char str[],
31 						const char str_name[],
32 						const char blurb[]);
33 
34 /* matches either a blank line or a comment line */
35 static const char blank_or_comment_regex[] =
36     /* a blank line */
37     "(^[[:space:]]+$)" "|"	/* or ... */
38     /* a line consisting of: optional whitespace followed by */
39     "(^[[:space:]]*"
40     /* a '#' character and optionally, additional characters */
41     "#.*$)";
42 
43 static regex_t blank_or_comment_expr;
44 
45 /* matches the line in a CMOS layout file indicating the start of the
46  * "entries" section.
47  */
48 static const char start_entries_regex[] =
49     /* optional whitespace */
50     "^[[:space:]]*"
51     /* followed by "entries" */
52     "entries"
53     /* followed by optional whitespace */
54     "[[:space:]]*$";
55 
56 static regex_t start_entries_expr;
57 
58 /* matches the line in a CMOS layout file indicating the start of the
59  * "enumerations" section
60  */
61 static const char start_enums_regex[] =
62     /* optional whitespace */
63     "^[[:space:]]*"
64     /* followed by "enumerations" */
65     "enumerations"
66     /* followed by optional whitespace */
67     "[[:space:]]*$";
68 
69 static regex_t start_enums_expr;
70 
71 /* matches the line in a CMOS layout file indicating the start of the
72  * "checksums" section
73  */
74 static const char start_checksums_regex[] =
75     /* optional whitespace */
76     "^[[:space:]]*"
77     /* followed by "checksums" */
78     "checksums"
79     /* followed by optional whitespace */
80     "[[:space:]]*$";
81 
82 static regex_t start_checksums_expr;
83 
84 /* matches a line in a CMOS layout file specifying a CMOS entry */
85 static const char entries_line_regex[] =
86     /* optional whitespace */
87     "^[[:space:]]*"
88     /* followed by a chunk of nonwhitespace for start-bit field */
89     "([^[:space:]]+)"
90     /* followed by one or more whitespace characters */
91     "[[:space:]]+"
92     /* followed by a chunk of nonwhitespace for length field */
93     "([^[:space:]]+)"
94     /* followed by one or more whitespace characters */
95     "[[:space:]]+"
96     /* followed by a chunk of nonwhitespace for config field */
97     "([^[:space:]]+)"
98     /* followed by one or more whitespace characters */
99     "[[:space:]]+"
100     /* followed by a chunk of nonwhitespace for config-ID field */
101     "([^[:space:]]+)"
102     /* followed by one or more whitespace characters */
103     "[[:space:]]+"
104     /* followed by a chunk of nonwhitespace for name field */
105     "([^[:space:]]+)"
106     /* followed by optional whitespace */
107     "[[:space:]]*$";
108 
109 static regex_t entries_line_expr;
110 
111 /* matches a line in a CMOS layout file specifying a CMOS enumeration */
112 static const char enums_line_regex[] =
113     /* optional whitespace */
114     "^[[:space:]]*"
115     /* followed by a chunk of nonwhitespace for ID field */
116     "([^[:space:]]+)"
117     /* followed by one or more whitespace characters */
118     "[[:space:]]+"
119     /* followed by a chunk of nonwhitespace for value field */
120     "([^[:space:]]+)"
121     /* followed by one or more whitespace characters */
122     "[[:space:]]+"
123     /* followed by a chunk of nonwhitespace for text field */
124     "([[:print:]]*[^[:space:]])"
125     /* followed by optional whitespace */
126     "[[:space:]]*$";
127 
128 static regex_t enums_line_expr;
129 
130 /* matches the line in a CMOS layout file specifying CMOS checksum
131  * information
132  */
133 static const char checksum_line_regex[] =
134     /* optional whitespace */
135     "^[[:space:]]*"
136     /* followed by "checksum" */
137     "checksum"
138     /* followed by one or more whitespace characters */
139     "[[:space:]]+"
140     /* followed by a chunk of nonwhitespace for first bit of summed area */
141     "([^[:space:]]+)"
142     /* followed by one or more whitespace characters */
143     "[[:space:]]+"
144     /* followed by a chunk of nonwhitespace for last bit of summed area */
145     "([^[:space:]]+)"
146     /* followed by one or more whitespace characters */
147     "[[:space:]]+"
148     /* followed by a chunk of nonwhitespace for checksum location bit */
149     "([^[:space:]]+)"
150     /* followed by optional whitespace */
151     "[[:space:]]*$";
152 
153 static regex_t checksum_line_expr;
154 
155 static const int LINE_BUF_SIZE = 256;
156 
157 static int line_num;
158 
159 static const char *layout_filename = NULL;
160 
161 /****************************************************************************
162  * set_layout_filename
163  *
164  * Set the name of the file we will obtain CMOS layout information from.
165  ****************************************************************************/
set_layout_filename(const char filename[])166 void set_layout_filename(const char filename[])
167 {
168 	layout_filename = filename;
169 }
170 
171 /****************************************************************************
172  * get_layout_from_file
173  *
174  * Read CMOS layout information from the user-specified CMOS layout file.
175  ****************************************************************************/
get_layout_from_file(void)176 void get_layout_from_file(void)
177 {
178 	FILE *f;
179 
180 	assert(layout_filename != NULL);
181 
182 	if ((f = fopen(layout_filename, "r")) == NULL) {
183 		fprintf(stderr,
184 			"%s: Can not open CMOS layout file %s for reading: "
185 			"%s\n", prog_name, layout_filename, strerror(errno));
186 		exit(1);
187 	}
188 
189 	process_layout_file(f);
190 	fclose(f);
191 }
192 
write_cmos_layout_header(const char * header_filename)193 void write_cmos_layout_header(const char *header_filename)
194 {
195 	FILE *fp;
196 	const cmos_entry_t *cmos_entry;
197 	cmos_checksum_layout_t layout;
198 
199 	if ((fp = fopen(header_filename, "w+")) == NULL) {
200 		fprintf(stderr,
201 				"%s: Can't open file %s for writing: %s\n",
202 				prog_name, header_filename, strerror(errno));
203 			exit(1);
204 	}
205 
206 	fprintf(fp, "/**\n * This is an autogenerated file. Do not EDIT.\n"
207 			" * All changes made to this file will be lost.\n"
208 			" * See mainboard's cmos.layout file.\n */\n"
209 			"\n#ifndef __OPTION_TABLE_H\n"
210 			"#define __OPTION_TABLE_H\n\n");
211 
212 	for (cmos_entry = first_cmos_entry(); cmos_entry != NULL;
213 			cmos_entry = next_cmos_entry(cmos_entry)) {
214 
215 		if (!is_ident((char *)cmos_entry->name)) {
216 			fprintf(stderr,
217 				"Error - Name %s is an invalid identifier\n",
218 				cmos_entry->name);
219 			fclose(fp);
220 			exit(1);
221 		}
222 
223 		fprintf(fp, "#define CMOS_VSTART_%s\t%d\n",
224 				cmos_entry->name, cmos_entry->bit);
225 		fprintf(fp, "#define CMOS_VLEN_%s\t%d\n",
226 				cmos_entry->name, cmos_entry->length);
227 	}
228 
229 	layout.summed_area_start = cmos_checksum_start;
230 	layout.summed_area_end = cmos_checksum_end;
231 	layout.checksum_at = cmos_checksum_index;
232 	checksum_layout_to_bits(&layout);
233 
234 	fprintf(fp, "\n#define LB_CKS_RANGE_START %d\n",
235 			layout.summed_area_start / 8);
236 	fprintf(fp, "#define LB_CKS_RANGE_END %d\n",
237 			layout.summed_area_end / 8);
238 	fprintf(fp, "#define LB_CKS_LOC %d\n",
239 			layout.checksum_at / 8);
240 	fprintf(fp, "\n#endif /* __OPTION_TABLE_H */\n");
241 
242 	fclose(fp);
243 }
244 /****************************************************************************
245  * write_cmos_layout
246  *
247  * Write CMOS layout information to file 'f'.  The output is written in the
248  * format that CMOS layout files adhere to.
249  ****************************************************************************/
write_cmos_layout(FILE * f)250 void write_cmos_layout(FILE * f)
251 {
252 	const cmos_entry_t *cmos_entry;
253 	const cmos_enum_t *cmos_enum;
254 	cmos_checksum_layout_t layout;
255 
256 	fprintf(f, "entries\n");
257 
258 	for (cmos_entry = first_cmos_entry();
259 	     cmos_entry != NULL; cmos_entry = next_cmos_entry(cmos_entry))
260 		fprintf(f, "%u %u %c %u %s\n", cmos_entry->bit,
261 			cmos_entry->length,
262 			cmos_entry_char_value(cmos_entry->config),
263 			cmos_entry->config_id, cmos_entry->name);
264 
265 	fprintf(f, "\nenumerations\n");
266 
267 	for (cmos_enum = first_cmos_enum();
268 	     cmos_enum != NULL; cmos_enum = next_cmos_enum(cmos_enum))
269 		fprintf(f, "%u %llu %s\n", cmos_enum->config_id,
270 			cmos_enum->value, cmos_enum->text);
271 
272 	layout.summed_area_start = cmos_checksum_start;
273 	layout.summed_area_end = cmos_checksum_end;
274 	layout.checksum_at = cmos_checksum_index;
275 	checksum_layout_to_bits(&layout);
276 	fprintf(f, "\nchecksums\nchecksum %u %u %u\n", layout.summed_area_start,
277 		layout.summed_area_end, layout.checksum_at);
278 }
279 
280 /****************************************************************************
281  * process_layout_file
282  *
283  * Read CMOS layout information from file 'f' and add it to our internal
284  * repository.
285  ****************************************************************************/
process_layout_file(FILE * f)286 static void process_layout_file(FILE * f)
287 {
288 	compile_reg_expr(REG_EXTENDED | REG_NEWLINE, blank_or_comment_regex, &blank_or_comment_expr);
289 	compile_reg_expr(REG_EXTENDED | REG_NEWLINE, start_entries_regex, &start_entries_expr);
290 	compile_reg_expr(REG_EXTENDED | REG_NEWLINE, entries_line_regex, &entries_line_expr);
291 	compile_reg_expr(REG_EXTENDED | REG_NEWLINE, start_enums_regex, &start_enums_expr);
292 	compile_reg_expr(REG_EXTENDED | REG_NEWLINE, enums_line_regex, &enums_line_expr);
293 	compile_reg_expr(REG_EXTENDED | REG_NEWLINE, start_checksums_regex, &start_checksums_expr);
294 	compile_reg_expr(REG_EXTENDED | REG_NEWLINE, checksum_line_regex, &checksum_line_expr);
295 	line_num = 1;
296 	skip_past_start(f);
297 
298 	/* Skip past all entries.  We will process these later when we
299 	 * make a second pass through the file.
300 	 */
301 	while (!process_entry(f, 1)) ;
302 
303 	/* Process all enums, adding them to our internal repository as
304 	 * we go. */
305 
306 	if (process_enum(f, 0)) {
307 		fprintf(stderr, "%s: Error: CMOS layout file contains no "
308 			"enumerations.\n", prog_name);
309 		exit(1);
310 	}
311 
312 	while (!process_enum(f, 0)) ;
313 
314 	/* Go back to start of file. */
315 	line_num = 1;
316 	fseek(f, 0, SEEK_SET);
317 
318 	skip_past_start(f);
319 
320 	/* Process all entries, adding them to the repository as we go.
321 	 * We must add the entries after the enums, even though they
322 	 * appear in the layout file before the enums.  This is because
323 	 * the entries are sanity checked against the enums as they are
324 	 * added.
325 	 */
326 
327 	if (process_entry(f, 0)) {
328 		fprintf(stderr,
329 			"%s: Error: CMOS layout file contains no entries.\n",
330 			prog_name);
331 		exit(1);
332 	}
333 
334 	while (!process_entry(f, 0)) ;
335 
336 	/* Skip past all enums.  They have already been processed. */
337 	while (!process_enum(f, 1)) ;
338 
339 	/* Process CMOS checksum info. */
340 	process_checksum_info(f);
341 
342 	/* See if there are any lines left to process.  If so, verify
343 	 * that they are all either blank lines or comments.
344 	 */
345 	skip_remaining_lines(f);
346 
347 	regfree(&blank_or_comment_expr);
348 	regfree(&start_entries_expr);
349 	regfree(&entries_line_expr);
350 	regfree(&start_enums_expr);
351 	regfree(&enums_line_expr);
352 	regfree(&start_checksums_expr);
353 	regfree(&checksum_line_expr);
354 }
355 
356 /****************************************************************************
357  * skip_past_start
358  *
359  * Skip past the line that marks the start of the "entries" section.
360  ****************************************************************************/
skip_past_start(FILE * f)361 static void skip_past_start(FILE * f)
362 {
363 	char line[LINE_BUF_SIZE];
364 
365 	for (;; line_num++) {
366 		if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
367 			fprintf(stderr,
368 				"%s: \"entries\" line not found in CMOS layout file.\n",
369 				prog_name);
370 			exit(1);
371 		}
372 
373 		if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
374 			continue;
375 
376 		if (!regexec(&start_entries_expr, line, 0, NULL, 0))
377 			break;
378 
379 		fprintf(stderr,
380 			"%s: Syntax error on line %d of CMOS layout file.  "
381 			"\"entries\" line expected.\n", prog_name, line_num);
382 		exit(1);
383 	}
384 
385 	line_num++;
386 }
387 
388 /****************************************************************************
389  * process_entry
390  *
391  * Get an entry from "entries" section of file and add it to our repository
392  * of layout information.  Return 0 if an entry was found and processed.
393  * Return 1 if there are no more entries.
394  ****************************************************************************/
process_entry(FILE * f,int skip_add)395 static int process_entry(FILE * f, int skip_add)
396 {
397 	static const size_t N_MATCHES = 6;
398 	char line[LINE_BUF_SIZE];
399 	regmatch_t match[N_MATCHES];
400 	cmos_entry_t cmos_entry;
401 	int result;
402 
403 	result = 1;
404 
405 	for (;; line_num++) {
406 		if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
407 			fprintf(stderr,
408 				"%s: Unexpected end of CMOS layout file reached while "
409 				"reading \"entries\" section.\n", prog_name);
410 			exit(1);
411 		}
412 
413 		if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
414 			continue;
415 
416 		if (regexec(&entries_line_expr, line, N_MATCHES, match, 0)) {
417 			if (regexec(&start_enums_expr, line, 0, NULL, 0)) {
418 				fprintf(stderr,
419 					"%s: Syntax error on line %d of CMOS layout "
420 					"file.\n", prog_name, line_num);
421 				exit(1);
422 			}
423 
424 			break;	/* start of enumerations reached: no more entries */
425 		}
426 
427 		result = 0;	/* next layout entry found */
428 
429 		if (skip_add)
430 			break;
431 
432 		line[match[1].rm_eo] = '\0';
433 		line[match[2].rm_eo] = '\0';
434 		line[match[3].rm_eo] = '\0';
435 		line[match[4].rm_eo] = '\0';
436 		line[match[5].rm_eo] = '\0';
437 		create_entry(&cmos_entry, &line[match[1].rm_so],
438 			     &line[match[2].rm_so], &line[match[3].rm_so],
439 			     &line[match[4].rm_so], &line[match[5].rm_so]);
440 		try_add_layout_file_entry(&cmos_entry);
441 		break;
442 	}
443 
444 	line_num++;
445 	return result;
446 }
447 
448 /****************************************************************************
449  * process_enum
450  *
451  * Get an enuneration from "enumerations" section of file and add it to our
452  * repository of layout information.  Return 0 if an enumeration was found
453  * and processed.  Return 1 if there are no more enumerations.
454  ****************************************************************************/
process_enum(FILE * f,int skip_add)455 static int process_enum(FILE * f, int skip_add)
456 {
457 	static const size_t N_MATCHES = 4;
458 	char line[LINE_BUF_SIZE];
459 	regmatch_t match[N_MATCHES];
460 	cmos_enum_t cmos_enum;
461 	int result;
462 
463 	result = 1;
464 
465 	for (;; line_num++) {
466 		if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
467 			fprintf(stderr,
468 				"%s: Unexpected end of CMOS layout file reached while "
469 				"reading \"enumerations\" section.\n",
470 				prog_name);
471 			exit(1);
472 		}
473 
474 		if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
475 			continue;
476 
477 		if (regexec(&enums_line_expr, line, N_MATCHES, match, 0)) {
478 			if (regexec(&start_checksums_expr, line, 0, NULL, 0)) {
479 				fprintf(stderr,
480 					"%s: Syntax error on line %d of CMOS layout "
481 					"file.\n", prog_name, line_num);
482 				exit(1);
483 			}
484 
485 			break;	/* start of checksums reached: no more enumerations */
486 		}
487 
488 		result = 0;	/* next layout enumeration found */
489 
490 		if (skip_add)
491 			break;
492 
493 		line[match[1].rm_eo] = '\0';
494 		line[match[2].rm_eo] = '\0';
495 		line[match[3].rm_eo] = '\0';
496 		create_enum(&cmos_enum, &line[match[1].rm_so],
497 			    &line[match[2].rm_so], &line[match[3].rm_so]);
498 		try_add_cmos_enum(&cmos_enum);
499 		break;
500 	}
501 
502 	line_num++;
503 	return result;
504 }
505 
506 /****************************************************************************
507  * process_checksum_info
508  *
509  * Get line containing CMOS checksum information.
510  ****************************************************************************/
process_checksum_info(FILE * f)511 static void process_checksum_info(FILE * f)
512 {
513 	static const size_t N_MATCHES = 4;
514 	char line[LINE_BUF_SIZE];
515 	regmatch_t match[N_MATCHES];
516 
517 	for (;; line_num++) {
518 		if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
519 			fprintf(stderr,
520 				"%s: Unexpected end of CMOS layout file reached while "
521 				"reading \"checksums\" section.\n", prog_name);
522 			exit(1);
523 		}
524 
525 		if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
526 			continue;
527 
528 		if (regexec(&checksum_line_expr, line, N_MATCHES, match, 0)) {
529 			fprintf(stderr,
530 				"%s: Syntax error on line %d of CMOS layout "
531 				"file.  \"checksum\" line expected.\n",
532 				prog_name, line_num);
533 			exit(1);
534 		}
535 
536 		/* checksum line found */
537 		line[match[1].rm_eo] = '\0';
538 		line[match[2].rm_eo] = '\0';
539 		line[match[3].rm_eo] = '\0';
540 		set_checksum_info(&line[match[1].rm_so], &line[match[2].rm_so],
541 				  &line[match[3].rm_so]);
542 		break;
543 	}
544 }
545 
546 /****************************************************************************
547  * skip_remaining_lines
548  *
549  * Get any remaining lines of unprocessed input.  Complain if we find a line
550  * that contains anything other than comments and whitespace.
551  ****************************************************************************/
skip_remaining_lines(FILE * f)552 static void skip_remaining_lines(FILE * f)
553 {
554 	char line[LINE_BUF_SIZE];
555 
556 	for (line_num++;
557 	     get_layout_file_line(f, line, LINE_BUF_SIZE) == OK; line_num++) {
558 		if (regexec(&blank_or_comment_expr, line, 0, NULL, 0)) {
559 			fprintf(stderr,
560 				"%s: Syntax error on line %d of CMOS layout file: "
561 				"Only comments and/or whitespace allowed after "
562 				"\"checksum\" line.\n", prog_name, line_num);
563 			exit(1);
564 		}
565 	}
566 }
567 
568 /****************************************************************************
569  * create_entry
570  *
571  * Create a CMOS entry structure representing the given information.  Perform
572  * sanity checking on input parameters.
573  ****************************************************************************/
create_entry(cmos_entry_t * cmos_entry,const char start_bit_str[],const char length_str[],const char config_str[],const char config_id_str[],const char name_str[])574 static void create_entry(cmos_entry_t * cmos_entry,
575 			 const char start_bit_str[], const char length_str[],
576 			 const char config_str[], const char config_id_str[],
577 			 const char name_str[])
578 {
579 	cmos_entry->bit = string_to_unsigned(start_bit_str, "start-bit");
580 	cmos_entry->length = string_to_unsigned(length_str, "length");
581 
582 	if (config_str[1] != '\0')
583 		goto bad_config_str;
584 
585 	switch (config_str[0]) {
586 	case 'e':
587 		cmos_entry->config = CMOS_ENTRY_ENUM;
588 		break;
589 
590 	case 'h':
591 		cmos_entry->config = CMOS_ENTRY_HEX;
592 		break;
593 
594 	case 's':
595 		cmos_entry->config = CMOS_ENTRY_STRING;
596 		break;
597 
598 	case 'r':
599 		cmos_entry->config = CMOS_ENTRY_RESERVED;
600 		break;
601 
602 	default:
603 		goto bad_config_str;
604 	}
605 
606 	cmos_entry->config_id = string_to_unsigned(config_id_str, "config-ID");
607 
608 	if (strlen(name_str) >= CMOS_MAX_NAME_LENGTH) {
609 		fprintf(stderr,
610 			"%s: Error on line %d of CMOS layout file: name too "
611 			"long (max length is %d).\n", prog_name, line_num,
612 			CMOS_MAX_NAME_LENGTH - 1);
613 		exit(1);
614 	}
615 
616 	strcpy(cmos_entry->name, name_str);
617 	return;
618 
619       bad_config_str:
620 	fprintf(stderr,
621 		"%s: Error on line %d of CMOS layout file: 'e', 'h', or "
622 		"'r' expected for config value.\n", prog_name, line_num);
623 	exit(1);
624 }
625 
626 /****************************************************************************
627  * try_add_layout_file_entry
628  *
629  * Attempt to add the given CMOS entry to our internal repository.  Exit with
630  * an error message on failure.
631  ****************************************************************************/
try_add_layout_file_entry(const cmos_entry_t * cmos_entry)632 static void try_add_layout_file_entry(const cmos_entry_t * cmos_entry)
633 {
634 	const cmos_entry_t *conflict;
635 
636 	switch (add_cmos_entry(cmos_entry, &conflict)) {
637 	case OK:
638 		return;
639 
640 	case CMOS_AREA_OUT_OF_RANGE:
641 		fprintf(stderr,
642 			"%s: Error on line %d of CMOS layout file.  Area "
643 			"covered by entry %s is out of range.\n", prog_name,
644 			line_num, cmos_entry->name);
645 		break;
646 
647 	case CMOS_AREA_TOO_WIDE:
648 		fprintf(stderr,
649 			"%s: Error on line %d of CMOS layout file.  Area "
650 			"covered by entry %s is too wide.\n", prog_name,
651 			line_num, cmos_entry->name);
652 		break;
653 
654 	case LAYOUT_ENTRY_OVERLAP:
655 		fprintf(stderr,
656 			"%s: Error on line %d of CMOS layout file.  Layouts "
657 			"overlap for entries %s and %s.\n", prog_name, line_num,
658 			cmos_entry->name, conflict->name);
659 		break;
660 
661 	case LAYOUT_ENTRY_BAD_LENGTH:
662 		/* Silently ignore entries with zero length.  Although this should
663 		 * never happen in practice, we should handle the case in a
664 		 * reasonable manner just to be safe.
665 		 */
666 		return;
667 
668 	case LAYOUT_MULTIBYTE_ENTRY_NOT_ALIGNED:
669 		fprintf(stderr,
670 			"%s: Unaligned CMOS option table entry %s "
671 			"spans multiple bytes.\n", prog_name, cmos_entry->name);
672 		break;
673 
674 	default:
675 		BUG();
676 	}
677 
678 	exit(1);
679 }
680 
681 /****************************************************************************
682  * create_enum
683  *
684  * Create a CMOS enumeration structure representing the given information.
685  * Perform sanity checking on input parameters.
686  ****************************************************************************/
create_enum(cmos_enum_t * cmos_enum,const char id_str[],const char value_str[],const char text_str[])687 static void create_enum(cmos_enum_t * cmos_enum, const char id_str[],
688 			const char value_str[], const char text_str[])
689 {
690 	cmos_enum->config_id = string_to_unsigned(id_str, "ID");
691 	cmos_enum->value = string_to_unsigned_long(value_str, "value");
692 
693 	if (strlen(text_str) >= CMOS_MAX_TEXT_LENGTH) {
694 		fprintf(stderr,
695 			"%s: Error on line %d of CMOS layout file: text too "
696 			"long (max length is %d).\n", prog_name, line_num,
697 			CMOS_MAX_TEXT_LENGTH - 1);
698 		exit(1);
699 	}
700 
701 	strcpy(cmos_enum->text, text_str);
702 }
703 
704 /****************************************************************************
705  * try_add_cmos_enum
706  *
707  * Attempt to add the given CMOS enum to our internal repository.  Exit with
708  * an error message on failure.
709  ****************************************************************************/
try_add_cmos_enum(const cmos_enum_t * cmos_enum)710 static void try_add_cmos_enum(const cmos_enum_t * cmos_enum)
711 {
712 	switch (add_cmos_enum(cmos_enum)) {
713 	case OK:
714 		return;
715 
716 	case LAYOUT_DUPLICATE_ENUM:
717 		fprintf(stderr, "%s: Error on line %d of CMOS layout file: "
718 			"Enumeration found with duplicate ID/value combination.\n",
719 			prog_name, line_num);
720 		break;
721 
722 	default:
723 		BUG();
724 	}
725 
726 	exit(1);
727 }
728 
729 /****************************************************************************
730  * set_checksum_info
731  *
732  * Set CMOS checksum information according to input parameters and perform
733  * sanity checking on input parameters.
734  ****************************************************************************/
set_checksum_info(const char start_str[],const char end_str[],const char index_str[])735 static void set_checksum_info(const char start_str[], const char end_str[],
736 			      const char index_str[])
737 {
738 	cmos_checksum_layout_t layout;
739 
740 	/* These are bit positions that we want to convert to byte positions. */
741 	layout.summed_area_start =
742 	    string_to_unsigned(start_str, "CMOS checksummed area start");
743 	layout.summed_area_end =
744 	    string_to_unsigned(end_str, "CMOS checksummed area end");
745 	layout.checksum_at =
746 	    string_to_unsigned(index_str, "CMOS checksum location");
747 
748 	switch (checksum_layout_to_bytes(&layout)) {
749 	case OK:
750 		break;
751 
752 	case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED:
753 		fprintf(stderr,
754 			"%s: Error on line %d of CMOS layout file.  CMOS "
755 			"checksummed area start is not byte-aligned.\n",
756 			prog_name, line_num);
757 		goto fail;
758 
759 	case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED:
760 		fprintf(stderr,
761 			"%s: Error on line %d of CMOS layout file.  CMOS "
762 			"checksummed area end is not byte-aligned.\n",
763 			prog_name, line_num);
764 		goto fail;
765 
766 	case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED:
767 		fprintf(stderr,
768 			"%s: Error on line %d of CMOS layout file.  CMOS "
769 			"checksum location is not byte-aligned.\n", prog_name,
770 			line_num);
771 		goto fail;
772 
773 	case LAYOUT_INVALID_SUMMED_AREA:
774 		fprintf(stderr,
775 			"%s: Error on line %d of CMOS layout file.  CMOS "
776 			"checksummed area end must be greater than CMOS checksummed "
777 			"area start.\n", prog_name, line_num);
778 		goto fail;
779 
780 	case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA:
781 		fprintf(stderr,
782 			"%s: Error on line %d of CMOS layout file.  CMOS "
783 			"checksum overlaps checksummed area.\n", prog_name,
784 			line_num);
785 		goto fail;
786 
787 	case LAYOUT_SUMMED_AREA_OUT_OF_RANGE:
788 		fprintf(stderr,
789 			"%s: Error on line %d of CMOS layout file.  CMOS "
790 			"checksummed area out of range.\n", prog_name,
791 			line_num);
792 		goto fail;
793 
794 	case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE:
795 		fprintf(stderr,
796 			"%s: Error on line %d of CMOS layout file.  CMOS "
797 			"checksum location out of range.\n", prog_name,
798 			line_num);
799 		goto fail;
800 
801 	default:
802 		BUG();
803 	}
804 
805 	cmos_checksum_start = layout.summed_area_start;
806 	cmos_checksum_end = layout.summed_area_end;
807 	cmos_checksum_index = layout.checksum_at;
808 	return;
809 
810       fail:
811 	exit(1);
812 }
813 
814 /****************************************************************************
815  * cmos_entry_char_value
816  *
817  * Return the character representation of 'config'.
818  ****************************************************************************/
cmos_entry_char_value(cmos_entry_config_t config)819 static char cmos_entry_char_value(cmos_entry_config_t config)
820 {
821 	switch (config) {
822 	case CMOS_ENTRY_ENUM:
823 		return 'e';
824 
825 	case CMOS_ENTRY_HEX:
826 		return 'h';
827 
828 	case CMOS_ENTRY_RESERVED:
829 		return 'r';
830 
831 	case CMOS_ENTRY_STRING:
832 		return 's';
833 
834 	default:
835 		BUG();
836 	}
837 
838 	return 0;		/* not reached */
839 }
840 
841 /****************************************************************************
842  * get_layout_file_line
843  *
844  * Get a line of input from file 'f'.  Store result in 'line' which is an
845  * array of 'line_buf_size' bytes.  Return OK on success or an error code on
846  * failure.
847  ****************************************************************************/
get_layout_file_line(FILE * f,char line[],int line_buf_size)848 static int get_layout_file_line(FILE * f, char line[], int line_buf_size)
849 {
850 	switch (get_line_from_file(f, line, line_buf_size)) {
851 	case OK:
852 		return OK;
853 
854 	case LINE_EOF:
855 		return LINE_EOF;
856 
857 	case LINE_TOO_LONG:
858 		fprintf(stderr,
859 			"%s: Error on line %d of CMOS layout file: Maximum "
860 			"line length exceeded.  Max is %d characters.\n",
861 			prog_name, line_num, line_buf_size - 2);
862 		break;
863 	}
864 
865 	exit(1);
866 	return 1;		/* keep compiler happy */
867 }
868 
869 /****************************************************************************
870  * string_to_unsigned
871  *
872  * Convert the string 'str' to an unsigned and return the result.
873  ****************************************************************************/
string_to_unsigned(const char str[],const char str_name[])874 static unsigned string_to_unsigned(const char str[], const char str_name[])
875 {
876 	unsigned long n;
877 	unsigned z;
878 
879 	n = do_string_to_unsigned_long(str, str_name, "");
880 
881 	if ((z = (unsigned)n) != n) {
882 		/* This could happen on an architecture in which
883 		 * sizeof(unsigned) < sizeof(unsigned long).
884 		 */
885 		fprintf(stderr,
886 			"%s: Error on line %d of CMOS layout file: %s value is "
887 			"out of range.\n", prog_name, line_num, str_name);
888 		exit(1);
889 	}
890 
891 	return z;
892 }
893 
894 /****************************************************************************
895  * string_to_unsigned_long
896  *
897  * Convert the string 'str' to an unsigned long and return the result.
898  ****************************************************************************/
string_to_unsigned_long(const char str[],const char str_name[])899 static unsigned long string_to_unsigned_long(const char str[],
900 					     const char str_name[])
901 {
902 	return do_string_to_unsigned_long(str, str_name, " long");
903 }
904 
905 /****************************************************************************
906  * do_string_to_unsigned_long
907  *
908  * Convert the string 'str' to an unsigned long and return the result.  Exit
909  * with an appropriate error message on failure.
910  ****************************************************************************/
do_string_to_unsigned_long(const char str[],const char str_name[],const char blurb[])911 static unsigned long do_string_to_unsigned_long(const char str[],
912 						const char str_name[],
913 						const char blurb[])
914 {
915 	unsigned long n;
916 	char *p;
917 
918 	n = strtoul(str, &p, 0);
919 
920 	if (*p != '\0') {
921 		fprintf(stderr,
922 			"%s: Error on line %d of CMOS layout file: %s is not a "
923 			"valid unsigned%s integer.\n", prog_name, line_num,
924 			str_name, blurb);
925 		exit(1);
926 	}
927 
928 	return n;
929 }
930