1 /*****************************************************************************\
3 *****************************************************************************
4 * Copyright (C) 2002-2005 The Regents of the University of California.
5 * Produced at the Lawrence Livermore National Laboratory.
6 * Written by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>.
10 * This file is part of nvramtool, a utility for reading/writing coreboot
11 * parameters and displaying information from the coreboot table.
12 * For details, see http://coreboot.org/nvramtool.
14 * Please also read the file DISCLAIMER which is included in this software
17 * This program is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License (as published by the
19 * Free Software Foundation) version 2, dated June 1991.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
24 * conditions of the GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 \*****************************************************************************/
32 #include "layout_file.h"
34 #include "cmos_lowlevel.h"
37 static void process_layout_file(FILE * f);
38 static void skip_past_start(FILE * f);
39 static int process_entry(FILE * f, int skip_add);
40 static int process_enum(FILE * f, int skip_add);
41 static void process_checksum_info(FILE * f);
42 static void skip_remaining_lines(FILE * f);
43 static void create_entry(cmos_entry_t * cmos_entry,
44 const char start_bit_str[], const char length_str[],
45 const char config_str[], const char config_id_str[],
46 const char name_str[]);
47 static void try_add_layout_file_entry(const cmos_entry_t * cmos_entry);
48 static void create_enum(cmos_enum_t * cmos_enum, const char id_str[],
49 const char value_str[], const char text_str[]);
50 static void try_add_cmos_enum(const cmos_enum_t * cmos_enum);
51 static void set_checksum_info(const char start_str[], const char end_str[],
52 const char index_str[]);
53 static char cmos_entry_char_value(cmos_entry_config_t config);
54 static int get_layout_file_line(FILE * f, char line[], int line_buf_size);
55 static unsigned string_to_unsigned(const char str[], const char str_name[]);
56 static unsigned long string_to_unsigned_long(const char str[],
57 const char str_name[]);
58 static unsigned long do_string_to_unsigned_long(const char str[],
59 const char str_name[],
62 /* matches either a blank line or a comment line */
63 static const char blank_or_comment_regex[] =
65 "(^[[:space:]]+$)" "|" /* or ... */
66 /* a line consisting of: optional whitespace followed by */
68 /* a '#' character and optionally, additional characters */
71 static regex_t blank_or_comment_expr;
73 /* matches the line in a CMOS layout file indicating the start of the
76 static const char start_entries_regex[] =
77 /* optional whitespace */
79 /* followed by "entries" */
81 /* followed by optional whitespace */
84 static regex_t start_entries_expr;
86 /* matches the line in a CMOS layout file indicating the start of the
87 * "enumerations" section
89 static const char start_enums_regex[] =
90 /* optional whitespace */
92 /* followed by "enumerations" */
94 /* followed by optional whitespace */
97 static regex_t start_enums_expr;
99 /* matches the line in a CMOS layout file indicating the start of the
100 * "checksums" section
102 static const char start_checksums_regex[] =
103 /* optional whitespace */
105 /* followed by "checksums" */
107 /* followed by optional whitespace */
110 static regex_t start_checksums_expr;
112 /* matches a line in a CMOS layout file specifying a CMOS entry */
113 static const char entries_line_regex[] =
114 /* optional whitespace */
116 /* followed by a chunk of nonwhitespace for start-bit field */
118 /* followed by one or more whitespace characters */
120 /* followed by a chunk of nonwhitespace for length field */
122 /* followed by one or more whitespace characters */
124 /* followed by a chunk of nonwhitespace for config field */
126 /* followed by one or more whitespace characters */
128 /* followed by a chunk of nonwhitespace for config-ID field */
130 /* followed by one or more whitespace characters */
132 /* followed by a chunk of nonwhitespace for name field */
134 /* followed by optional whitespace */
137 static regex_t entries_line_expr;
139 /* matches a line in a CMOS layout file specifying a CMOS enumeration */
140 static const char enums_line_regex[] =
141 /* optional whitespace */
143 /* followed by a chunk of nonwhitespace for ID field */
145 /* followed by one or more whitespace characters */
147 /* followed by a chunk of nonwhitespace for value field */
149 /* followed by one or more whitespace characters */
151 /* followed by a chunk of nonwhitespace for text field */
153 /* followed by optional whitespace */
156 static regex_t enums_line_expr;
158 /* matches the line in a CMOS layout file specifying CMOS checksum
161 static const char checksum_line_regex[] =
162 /* optional whitespace */
164 /* followed by "checksum" */
166 /* followed by one or more whitespace characters */
168 /* followed by a chunk of nonwhitespace for first bit of summed area */
170 /* followed by one or more whitespace characters */
172 /* followed by a chunk of nonwhitespace for last bit of summed area */
174 /* followed by one or more whitespace characters */
176 /* followed by a chunk of nonwhitespace for checksum location bit */
178 /* followed by optional whitespace */
181 static regex_t checksum_line_expr;
183 static const int LINE_BUF_SIZE = 256;
187 static const char *layout_filename = NULL;
189 /****************************************************************************
190 * set_layout_filename
192 * Set the name of the file we will obtain CMOS layout information from.
193 ****************************************************************************/
194 void set_layout_filename(const char filename[])
196 layout_filename = filename;
199 /****************************************************************************
200 * get_layout_from_file
202 * Read CMOS layout information from the user-specified CMOS layout file.
203 ****************************************************************************/
204 void get_layout_from_file(void)
208 assert(layout_filename != NULL);
210 if ((f = fopen(layout_filename, "r")) == NULL) {
212 "%s: Can not open CMOS layout file %s for reading: "
213 "%s\n", prog_name, layout_filename, strerror(errno));
217 process_layout_file(f);
221 /****************************************************************************
224 * Write CMOS layout information to file 'f'. The output is written in the
225 * format that CMOS layout files adhere to.
226 ****************************************************************************/
227 void write_cmos_layout(FILE * f)
229 const cmos_entry_t *cmos_entry;
230 const cmos_enum_t *cmos_enum;
231 cmos_checksum_layout_t layout;
233 fprintf(f, "entries\n");
235 for (cmos_entry = first_cmos_entry();
236 cmos_entry != NULL; cmos_entry = next_cmos_entry(cmos_entry))
237 fprintf(f, "%u %u %c %u %s\n", cmos_entry->bit,
239 cmos_entry_char_value(cmos_entry->config),
240 cmos_entry->config_id, cmos_entry->name);
242 fprintf(f, "\nenumerations\n");
244 for (cmos_enum = first_cmos_enum();
245 cmos_enum != NULL; cmos_enum = next_cmos_enum(cmos_enum))
246 fprintf(f, "%u %llu %s\n", cmos_enum->config_id,
247 cmos_enum->value, cmos_enum->text);
249 layout.summed_area_start = cmos_checksum_start;
250 layout.summed_area_end = cmos_checksum_end;
251 layout.checksum_at = cmos_checksum_index;
252 checksum_layout_to_bits(&layout);
253 fprintf(f, "\nchecksums\nchecksum %u %u %u\n", layout.summed_area_start,
254 layout.summed_area_end, layout.checksum_at);
257 /****************************************************************************
258 * process_layout_file
260 * Read CMOS layout information from file 'f' and add it to our internal
262 ****************************************************************************/
263 static void process_layout_file(FILE * f)
265 compile_reg_exprs(REG_EXTENDED | REG_NEWLINE, 7,
266 blank_or_comment_regex, &blank_or_comment_expr,
267 start_entries_regex, &start_entries_expr,
268 entries_line_regex, &entries_line_expr,
269 start_enums_regex, &start_enums_expr,
270 enums_line_regex, &enums_line_expr,
271 start_checksums_regex, &start_checksums_expr,
272 checksum_line_regex, &checksum_line_expr);
276 /* Skip past all entries. We will process these later when we
277 * make a second pass through the file.
279 while (!process_entry(f, 1)) ;
281 /* Process all enums, adding them to our internal repository as
284 if (process_enum(f, 0)) {
285 fprintf(stderr, "%s: Error: CMOS layout file contains no "
286 "enumerations.\n", prog_name);
290 while (!process_enum(f, 0)) ;
292 /* Go back to start of file. */
294 fseek(f, 0, SEEK_SET);
298 /* Process all entries, adding them to the repository as we go.
299 * We must add the entries after the enums, even though they
300 * appear in the layout file before the enums. This is because
301 * the entries are sanity checked against the enums as they are
305 if (process_entry(f, 0)) {
307 "%s: Error: CMOS layout file contains no entries.\n",
312 while (!process_entry(f, 0)) ;
314 /* Skip past all enums. They have already been processed. */
315 while (!process_enum(f, 1)) ;
317 /* Process CMOS checksum info. */
318 process_checksum_info(f);
320 /* See if there are any lines left to process. If so, verify
321 * that they are all either blank lines or comments.
323 skip_remaining_lines(f);
325 free_reg_exprs(7, &blank_or_comment_expr, &start_entries_expr,
326 &entries_line_expr, &start_enums_expr,
327 &enums_line_expr, &start_checksums_expr,
328 &checksum_line_expr);
331 /****************************************************************************
334 * Skip past the line that marks the start of the "entries" section.
335 ****************************************************************************/
336 static void skip_past_start(FILE * f)
338 char line[LINE_BUF_SIZE];
340 for (;; line_num++) {
341 if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
343 "%s: \"entries\" line not found in CMOS layout file.\n",
348 if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
351 if (!regexec(&start_entries_expr, line, 0, NULL, 0))
355 "%s: Syntax error on line %d of CMOS layout file. "
356 "\"entries\" line expected.\n", prog_name, line_num);
363 /****************************************************************************
366 * Get an entry from "entries" section of file and add it to our repository
367 * of layout information. Return 0 if an entry was found and processed.
368 * Return 1 if there are no more entries.
369 ****************************************************************************/
370 static int process_entry(FILE * f, int skip_add)
372 static const size_t N_MATCHES = 6;
373 char line[LINE_BUF_SIZE];
374 regmatch_t match[N_MATCHES];
375 cmos_entry_t cmos_entry;
380 for (;; line_num++) {
381 if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
383 "%s: Unexpected end of CMOS layout file reached while "
384 "reading \"entries\" section.\n", prog_name);
388 if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
391 if (regexec(&entries_line_expr, line, N_MATCHES, match, 0)) {
392 if (regexec(&start_enums_expr, line, 0, NULL, 0)) {
394 "%s: Syntax error on line %d of CMOS layout "
395 "file.\n", prog_name, line_num);
399 break; /* start of enumerations reached: no more entries */
402 result = 0; /* next layout entry found */
407 line[match[1].rm_eo] = '\0';
408 line[match[2].rm_eo] = '\0';
409 line[match[3].rm_eo] = '\0';
410 line[match[4].rm_eo] = '\0';
411 line[match[5].rm_eo] = '\0';
412 create_entry(&cmos_entry, &line[match[1].rm_so],
413 &line[match[2].rm_so], &line[match[3].rm_so],
414 &line[match[4].rm_so], &line[match[5].rm_so]);
415 try_add_layout_file_entry(&cmos_entry);
423 /****************************************************************************
426 * Get an enuneration from "enumerations" section of file and add it to our
427 * repository of layout information. Return 0 if an enumeration was found
428 * and processed. Return 1 if there are no more enumerations.
429 ****************************************************************************/
430 static int process_enum(FILE * f, int skip_add)
432 static const size_t N_MATCHES = 4;
433 char line[LINE_BUF_SIZE];
434 regmatch_t match[N_MATCHES];
435 cmos_enum_t cmos_enum;
440 for (;; line_num++) {
441 if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
443 "%s: Unexpected end of CMOS layout file reached while "
444 "reading \"enumerations\" section.\n",
449 if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
452 if (regexec(&enums_line_expr, line, N_MATCHES, match, 0)) {
453 if (regexec(&start_checksums_expr, line, 0, NULL, 0)) {
455 "%s: Syntax error on line %d of CMOS layout "
456 "file.\n", prog_name, line_num);
460 break; /* start of checksums reached: no more enumerations */
463 result = 0; /* next layout enumeration found */
468 line[match[1].rm_eo] = '\0';
469 line[match[2].rm_eo] = '\0';
470 line[match[3].rm_eo] = '\0';
471 create_enum(&cmos_enum, &line[match[1].rm_so],
472 &line[match[2].rm_so], &line[match[3].rm_so]);
473 try_add_cmos_enum(&cmos_enum);
481 /****************************************************************************
482 * process_checksum_info
484 * Get line conatining CMOS checksum information.
485 ****************************************************************************/
486 static void process_checksum_info(FILE * f)
488 static const size_t N_MATCHES = 4;
489 char line[LINE_BUF_SIZE];
490 regmatch_t match[N_MATCHES];
492 for (;; line_num++) {
493 if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
495 "%s: Unexpected end of CMOS layout file reached while "
496 "reading \"checksums\" section.\n", prog_name);
500 if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
503 if (regexec(&checksum_line_expr, line, N_MATCHES, match, 0)) {
505 "%s: Syntax error on line %d of CMOS layout "
506 "file. \"checksum\" line expected.\n",
507 prog_name, line_num);
511 /* checksum line found */
512 line[match[1].rm_eo] = '\0';
513 line[match[2].rm_eo] = '\0';
514 line[match[3].rm_eo] = '\0';
515 set_checksum_info(&line[match[1].rm_so], &line[match[2].rm_so],
516 &line[match[3].rm_so]);
521 /****************************************************************************
522 * skip_remaining_lines
524 * Get any remaining lines of unprocessed input. Complain if we find a line
525 * that contains anything other than comments and whitespace.
526 ****************************************************************************/
527 static void skip_remaining_lines(FILE * f)
529 char line[LINE_BUF_SIZE];
532 get_layout_file_line(f, line, LINE_BUF_SIZE) == OK; line_num++) {
533 if (regexec(&blank_or_comment_expr, line, 0, NULL, 0)) {
535 "%s: Syntax error on line %d of CMOS layout file: "
536 "Only comments and/or whitespace allowed after "
537 "\"checksum\" line.\n", prog_name, line_num);
543 /****************************************************************************
546 * Create a CMOS entry structure representing the given information. Perform
547 * sanity checking on input parameters.
548 ****************************************************************************/
549 static void create_entry(cmos_entry_t * cmos_entry,
550 const char start_bit_str[], const char length_str[],
551 const char config_str[], const char config_id_str[],
552 const char name_str[])
554 cmos_entry->bit = string_to_unsigned(start_bit_str, "start-bit");
555 cmos_entry->length = string_to_unsigned(length_str, "length");
557 if (config_str[1] != '\0')
560 switch (config_str[0]) {
562 cmos_entry->config = CMOS_ENTRY_ENUM;
566 cmos_entry->config = CMOS_ENTRY_HEX;
570 cmos_entry->config = CMOS_ENTRY_STRING;
574 cmos_entry->config = CMOS_ENTRY_RESERVED;
581 cmos_entry->config_id = string_to_unsigned(config_id_str, "config-ID");
583 if (strlen(name_str) >= CMOS_MAX_NAME_LENGTH) {
585 "%s: Error on line %d of CMOS layout file: name too "
586 "long (max length is %d).\n", prog_name, line_num,
587 CMOS_MAX_NAME_LENGTH - 1);
591 strcpy(cmos_entry->name, name_str);
596 "%s: Error on line %d of CMOS layout file: 'e', 'h', or "
597 "'r' expected for config value.\n", prog_name, line_num);
601 /****************************************************************************
602 * try_add_layout_file_entry
604 * Attempt to add the given CMOS entry to our internal repository. Exit with
605 * an error message on failure.
606 ****************************************************************************/
607 static void try_add_layout_file_entry(const cmos_entry_t * cmos_entry)
609 const cmos_entry_t *conflict;
611 switch (add_cmos_entry(cmos_entry, &conflict)) {
615 case CMOS_AREA_OUT_OF_RANGE:
617 "%s: Error on line %d of CMOS layout file. Area "
618 "covered by entry %s is out of range.\n", prog_name,
619 line_num, cmos_entry->name);
622 case CMOS_AREA_TOO_WIDE:
624 "%s: Error on line %d of CMOS layout file. Area "
625 "covered by entry %s is too wide.\n", prog_name,
626 line_num, cmos_entry->name);
629 case LAYOUT_ENTRY_OVERLAP:
631 "%s: Error on line %d of CMOS layout file. Layouts "
632 "overlap for entries %s and %s.\n", prog_name, line_num,
633 cmos_entry->name, conflict->name);
636 case LAYOUT_ENTRY_BAD_LENGTH:
637 /* Silently ignore entries with zero length. Although this should
638 * never happen in practice, we should handle the case in a
639 * reasonable manner just to be safe.
650 /****************************************************************************
653 * Create a CMOS enumeration structure representing the given information.
654 * Perform sanity checking on input parameters.
655 ****************************************************************************/
656 static void create_enum(cmos_enum_t * cmos_enum, const char id_str[],
657 const char value_str[], const char text_str[])
659 cmos_enum->config_id = string_to_unsigned(id_str, "ID");
660 cmos_enum->value = string_to_unsigned_long(value_str, "value");
662 if (strlen(text_str) >= CMOS_MAX_TEXT_LENGTH) {
664 "%s: Error on line %d of CMOS layout file: text too "
665 "long (max length is %d).\n", prog_name, line_num,
666 CMOS_MAX_TEXT_LENGTH - 1);
670 strcpy(cmos_enum->text, text_str);
673 /****************************************************************************
676 * Attempt to add the given CMOS enum to our internal repository. Exit with
677 * an error message on failure.
678 ****************************************************************************/
679 static void try_add_cmos_enum(const cmos_enum_t * cmos_enum)
681 switch (add_cmos_enum(cmos_enum)) {
685 case LAYOUT_DUPLICATE_ENUM:
686 fprintf(stderr, "%s: Error on line %d of CMOS layout file: "
687 "Enumeration found with duplicate ID/value combination.\n",
688 prog_name, line_num);
698 /****************************************************************************
701 * Set CMOS checksum information according to input parameters and perform
702 * sanity checking on input parameters.
703 ****************************************************************************/
704 static void set_checksum_info(const char start_str[], const char end_str[],
705 const char index_str[])
707 cmos_checksum_layout_t layout;
709 /* These are bit positions that we want to convert to byte positions. */
710 layout.summed_area_start =
711 string_to_unsigned(start_str, "CMOS checksummed area start");
712 layout.summed_area_end =
713 string_to_unsigned(end_str, "CMOS checksummed area end");
715 string_to_unsigned(index_str, "CMOS checksum location");
717 switch (checksum_layout_to_bytes(&layout)) {
721 case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED:
723 "%s: Error on line %d of CMOS layout file. CMOS "
724 "checksummed area start is not byte-aligned.\n",
725 prog_name, line_num);
728 case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED:
730 "%s: Error on line %d of CMOS layout file. CMOS "
731 "checksummed area end is not byte-aligned.\n",
732 prog_name, line_num);
735 case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED:
737 "%s: Error on line %d of CMOS layout file. CMOS "
738 "checksum location is not byte-aligned.\n", prog_name,
742 case LAYOUT_INVALID_SUMMED_AREA:
744 "%s: Error on line %d of CMOS layout file. CMOS "
745 "checksummed area end must be greater than CMOS checksummed "
746 "area start.\n", prog_name, line_num);
749 case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA:
751 "%s: Error on line %d of CMOS layout file. CMOS "
752 "checksum overlaps checksummed area.\n", prog_name,
756 case LAYOUT_SUMMED_AREA_OUT_OF_RANGE:
758 "%s: Error on line %d of CMOS layout file. CMOS "
759 "checksummed area out of range.\n", prog_name,
763 case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE:
765 "%s: Error on line %d of CMOS layout file. CMOS "
766 "checksum location out of range.\n", prog_name,
774 cmos_checksum_start = layout.summed_area_start;
775 cmos_checksum_end = layout.summed_area_end;
776 cmos_checksum_index = layout.checksum_at;
783 /****************************************************************************
784 * cmos_entry_char_value
786 * Return the character representation of 'config'.
787 ****************************************************************************/
788 static char cmos_entry_char_value(cmos_entry_config_t config)
791 case CMOS_ENTRY_ENUM:
797 case CMOS_ENTRY_RESERVED:
800 case CMOS_ENTRY_STRING:
807 return 0; /* not reached */
810 /****************************************************************************
811 * get_layout_file_line
813 * Get a line of input from file 'f'. Store result in 'line' which is an
814 * array of 'line_buf_size' bytes. Return OK on success or an error code on
816 ****************************************************************************/
817 static int get_layout_file_line(FILE * f, char line[], int line_buf_size)
819 switch (get_line_from_file(f, line, line_buf_size)) {
828 "%s: Error on line %d of CMOS layout file: Maximum "
829 "line length exceeded. Max is %d characters.\n",
830 prog_name, line_num, line_buf_size - 2);
835 return 1; /* keep compiler happy */
838 /****************************************************************************
841 * Convert the string 'str' to an unsigned and return the result.
842 ****************************************************************************/
843 static unsigned string_to_unsigned(const char str[], const char str_name[])
848 n = do_string_to_unsigned_long(str, str_name, "");
850 if ((z = (unsigned)n) != n) {
851 /* This could happen on an architecture in which
852 * sizeof(unsigned) < sizeof(unsigned long).
855 "%s: Error on line %d of CMOS layout file: %s value is "
856 "out of range.\n", prog_name, line_num, str_name);
863 /****************************************************************************
864 * string_to_unsigned_long
866 * Convert the string 'str' to an unsigned long and return the result.
867 ****************************************************************************/
868 static unsigned long string_to_unsigned_long(const char str[],
869 const char str_name[])
871 return do_string_to_unsigned_long(str, str_name, " long");
874 /****************************************************************************
875 * do_string_to_unsigned_long
877 * Convert the string 'str' to an unsigned long and return the result. Exit
878 * with an appropriate error message on failure.
879 ****************************************************************************/
880 static unsigned long do_string_to_unsigned_long(const char str[],
881 const char str_name[],
887 n = strtoul(str, &p, 0);
891 "%s: Error on line %d of CMOS layout file: %s is not a "
892 "valid unsigned%s integer.\n", prog_name, line_num,