958 lines
28 KiB
C
958 lines
28 KiB
C
/*****************************************************************************\
|
|
* layout-text.c
|
|
*****************************************************************************
|
|
* Copyright (C) 2012, Vikram Narayanan
|
|
* Unified build_opt_tbl and nvramtool
|
|
* build_opt_tbl.c
|
|
* Copyright (C) 2003 Eric Biederman (ebiederm@xmission.com)
|
|
* Copyright (C) 2007-2010 coresystems GmbH
|
|
*
|
|
* Copyright (C) 2002-2005 The Regents of the University of California.
|
|
* Produced at the Lawrence Livermore National Laboratory.
|
|
* Written by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>.
|
|
* UCRL-CODE-2003-012
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of nvramtool, a utility for reading/writing coreboot
|
|
* parameters and displaying information from the coreboot table.
|
|
* For details, see http://coreboot.org/nvramtool.
|
|
*
|
|
* Please also read the file DISCLAIMER which is included in this software
|
|
* distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License (as published by the
|
|
* Free Software Foundation) version 2, dated June 1991.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
|
* conditions of the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
\*****************************************************************************/
|
|
|
|
#include "common.h"
|
|
#include "layout-text.h"
|
|
#include "layout.h"
|
|
#include "cmos_lowlevel.h"
|
|
#include "reg_expr.h"
|
|
|
|
static void process_layout_file(FILE * f);
|
|
static void skip_past_start(FILE * f);
|
|
static int process_entry(FILE * f, int skip_add);
|
|
static int process_enum(FILE * f, int skip_add);
|
|
static void process_checksum_info(FILE * f);
|
|
static void skip_remaining_lines(FILE * f);
|
|
static void 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[]);
|
|
static void try_add_layout_file_entry(const cmos_entry_t * cmos_entry);
|
|
static void create_enum(cmos_enum_t * cmos_enum, const char id_str[],
|
|
const char value_str[], const char text_str[]);
|
|
static void try_add_cmos_enum(const cmos_enum_t * cmos_enum);
|
|
static void set_checksum_info(const char start_str[], const char end_str[],
|
|
const char index_str[]);
|
|
static char cmos_entry_char_value(cmos_entry_config_t config);
|
|
static int get_layout_file_line(FILE * f, char line[], int line_buf_size);
|
|
static unsigned string_to_unsigned(const char str[], const char str_name[]);
|
|
static unsigned long string_to_unsigned_long(const char str[],
|
|
const char str_name[]);
|
|
static unsigned long do_string_to_unsigned_long(const char str[],
|
|
const char str_name[],
|
|
const char blurb[]);
|
|
|
|
/* matches either a blank line or a comment line */
|
|
static const char blank_or_comment_regex[] =
|
|
/* a blank line */
|
|
"(^[[:space:]]+$)" "|" /* or ... */
|
|
/* a line consisting of: optional whitespace followed by */
|
|
"(^[[:space:]]*"
|
|
/* a '#' character and optionally, additional characters */
|
|
"#.*$)";
|
|
|
|
static regex_t blank_or_comment_expr;
|
|
|
|
/* matches the line in a CMOS layout file indicating the start of the
|
|
* "entries" section.
|
|
*/
|
|
static const char start_entries_regex[] =
|
|
/* optional whitespace */
|
|
"^[[:space:]]*"
|
|
/* followed by "entries" */
|
|
"entries"
|
|
/* followed by optional whitespace */
|
|
"[[:space:]]*$";
|
|
|
|
static regex_t start_entries_expr;
|
|
|
|
/* matches the line in a CMOS layout file indicating the start of the
|
|
* "enumerations" section
|
|
*/
|
|
static const char start_enums_regex[] =
|
|
/* optional whitespace */
|
|
"^[[:space:]]*"
|
|
/* followed by "enumerations" */
|
|
"enumerations"
|
|
/* followed by optional whitespace */
|
|
"[[:space:]]*$";
|
|
|
|
static regex_t start_enums_expr;
|
|
|
|
/* matches the line in a CMOS layout file indicating the start of the
|
|
* "checksums" section
|
|
*/
|
|
static const char start_checksums_regex[] =
|
|
/* optional whitespace */
|
|
"^[[:space:]]*"
|
|
/* followed by "checksums" */
|
|
"checksums"
|
|
/* followed by optional whitespace */
|
|
"[[:space:]]*$";
|
|
|
|
static regex_t start_checksums_expr;
|
|
|
|
/* matches a line in a CMOS layout file specifying a CMOS entry */
|
|
static const char entries_line_regex[] =
|
|
/* optional whitespace */
|
|
"^[[:space:]]*"
|
|
/* followed by a chunk of nonwhitespace for start-bit field */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for length field */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for config field */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for config-ID field */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for name field */
|
|
"([^[:space:]]+)"
|
|
/* followed by optional whitespace */
|
|
"[[:space:]]*$";
|
|
|
|
static regex_t entries_line_expr;
|
|
|
|
/* matches a line in a CMOS layout file specifying a CMOS enumeration */
|
|
static const char enums_line_regex[] =
|
|
/* optional whitespace */
|
|
"^[[:space:]]*"
|
|
/* followed by a chunk of nonwhitespace for ID field */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for value field */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for text field */
|
|
"([[:print:]]*[^[:space:]])"
|
|
/* followed by optional whitespace */
|
|
"[[:space:]]*$";
|
|
|
|
static regex_t enums_line_expr;
|
|
|
|
/* matches the line in a CMOS layout file specifying CMOS checksum
|
|
* information
|
|
*/
|
|
static const char checksum_line_regex[] =
|
|
/* optional whitespace */
|
|
"^[[:space:]]*"
|
|
/* followed by "checksum" */
|
|
"checksum"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for first bit of summed area */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for last bit of summed area */
|
|
"([^[:space:]]+)"
|
|
/* followed by one or more whitespace characters */
|
|
"[[:space:]]+"
|
|
/* followed by a chunk of nonwhitespace for checksum location bit */
|
|
"([^[:space:]]+)"
|
|
/* followed by optional whitespace */
|
|
"[[:space:]]*$";
|
|
|
|
static regex_t checksum_line_expr;
|
|
|
|
static const int LINE_BUF_SIZE = 256;
|
|
|
|
static int line_num;
|
|
|
|
static const char *layout_filename = NULL;
|
|
|
|
/****************************************************************************
|
|
* set_layout_filename
|
|
*
|
|
* Set the name of the file we will obtain CMOS layout information from.
|
|
****************************************************************************/
|
|
void set_layout_filename(const char filename[])
|
|
{
|
|
layout_filename = filename;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* get_layout_from_file
|
|
*
|
|
* Read CMOS layout information from the user-specified CMOS layout file.
|
|
****************************************************************************/
|
|
void get_layout_from_file(void)
|
|
{
|
|
FILE *f;
|
|
|
|
assert(layout_filename != NULL);
|
|
|
|
if ((f = fopen(layout_filename, "r")) == NULL) {
|
|
fprintf(stderr,
|
|
"%s: Can not open CMOS layout file %s for reading: "
|
|
"%s\n", prog_name, layout_filename, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
process_layout_file(f);
|
|
fclose(f);
|
|
}
|
|
|
|
void write_cmos_layout_header(const char *header_filename)
|
|
{
|
|
FILE *fp;
|
|
const cmos_entry_t *cmos_entry;
|
|
cmos_checksum_layout_t layout;
|
|
|
|
if ((fp = fopen(header_filename, "w+")) == NULL) {
|
|
fprintf(stderr,
|
|
"%s: Can't open file %s for writing: %s\n",
|
|
prog_name, header_filename, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
fprintf(fp, "/**\n * This is an autogenerated file. Do not EDIT.\n"
|
|
" * All changes made to this file will be lost.\n"
|
|
" * See mainboard's cmos.layout file.\n */\n"
|
|
"\n#ifndef __OPTION_TABLE_H\n"
|
|
"#define __OPTION_TABLE_H\n\n");
|
|
|
|
for (cmos_entry = first_cmos_entry(); cmos_entry != NULL;
|
|
cmos_entry = next_cmos_entry(cmos_entry)) {
|
|
|
|
if (!is_ident((char *)cmos_entry->name)) {
|
|
fprintf(stderr,
|
|
"Error - Name %s is an invalid identifier\n",
|
|
cmos_entry->name);
|
|
fclose(fp);
|
|
exit(1);
|
|
}
|
|
|
|
fprintf(fp, "#define CMOS_VSTART_%s\t%d\n",
|
|
cmos_entry->name, cmos_entry->bit);
|
|
fprintf(fp, "#define CMOS_VLEN_%s\t%d\n",
|
|
cmos_entry->name, cmos_entry->length);
|
|
}
|
|
|
|
layout.summed_area_start = cmos_checksum_start;
|
|
layout.summed_area_end = cmos_checksum_end;
|
|
layout.checksum_at = cmos_checksum_index;
|
|
checksum_layout_to_bits(&layout);
|
|
|
|
fprintf(fp, "\n#define LB_CKS_RANGE_START %d\n",
|
|
layout.summed_area_start / 8);
|
|
fprintf(fp, "#define LB_CKS_RANGE_END %d\n",
|
|
layout.summed_area_end / 8);
|
|
fprintf(fp, "#define LB_CKS_LOC %d\n",
|
|
layout.checksum_at / 8);
|
|
fprintf(fp, "\n#endif /* __OPTION_TABLE_H */\n");
|
|
|
|
fclose(fp);
|
|
}
|
|
/****************************************************************************
|
|
* write_cmos_layout
|
|
*
|
|
* Write CMOS layout information to file 'f'. The output is written in the
|
|
* format that CMOS layout files adhere to.
|
|
****************************************************************************/
|
|
void write_cmos_layout(FILE * f)
|
|
{
|
|
const cmos_entry_t *cmos_entry;
|
|
const cmos_enum_t *cmos_enum;
|
|
cmos_checksum_layout_t layout;
|
|
|
|
fprintf(f, "entries\n");
|
|
|
|
for (cmos_entry = first_cmos_entry();
|
|
cmos_entry != NULL; cmos_entry = next_cmos_entry(cmos_entry))
|
|
fprintf(f, "%u %u %c %u %s\n", cmos_entry->bit,
|
|
cmos_entry->length,
|
|
cmos_entry_char_value(cmos_entry->config),
|
|
cmos_entry->config_id, cmos_entry->name);
|
|
|
|
fprintf(f, "\nenumerations\n");
|
|
|
|
for (cmos_enum = first_cmos_enum();
|
|
cmos_enum != NULL; cmos_enum = next_cmos_enum(cmos_enum))
|
|
fprintf(f, "%u %llu %s\n", cmos_enum->config_id,
|
|
cmos_enum->value, cmos_enum->text);
|
|
|
|
layout.summed_area_start = cmos_checksum_start;
|
|
layout.summed_area_end = cmos_checksum_end;
|
|
layout.checksum_at = cmos_checksum_index;
|
|
checksum_layout_to_bits(&layout);
|
|
fprintf(f, "\nchecksums\nchecksum %u %u %u\n", layout.summed_area_start,
|
|
layout.summed_area_end, layout.checksum_at);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* process_layout_file
|
|
*
|
|
* Read CMOS layout information from file 'f' and add it to our internal
|
|
* repository.
|
|
****************************************************************************/
|
|
static void process_layout_file(FILE * f)
|
|
{
|
|
compile_reg_expr(REG_EXTENDED | REG_NEWLINE, blank_or_comment_regex, &blank_or_comment_expr);
|
|
compile_reg_expr(REG_EXTENDED | REG_NEWLINE, start_entries_regex, &start_entries_expr);
|
|
compile_reg_expr(REG_EXTENDED | REG_NEWLINE, entries_line_regex, &entries_line_expr);
|
|
compile_reg_expr(REG_EXTENDED | REG_NEWLINE, start_enums_regex, &start_enums_expr);
|
|
compile_reg_expr(REG_EXTENDED | REG_NEWLINE, enums_line_regex, &enums_line_expr);
|
|
compile_reg_expr(REG_EXTENDED | REG_NEWLINE, start_checksums_regex, &start_checksums_expr);
|
|
compile_reg_expr(REG_EXTENDED | REG_NEWLINE, checksum_line_regex, &checksum_line_expr);
|
|
line_num = 1;
|
|
skip_past_start(f);
|
|
|
|
/* Skip past all entries. We will process these later when we
|
|
* make a second pass through the file.
|
|
*/
|
|
while (!process_entry(f, 1)) ;
|
|
|
|
/* Process all enums, adding them to our internal repository as
|
|
* we go. */
|
|
|
|
if (process_enum(f, 0)) {
|
|
fprintf(stderr, "%s: Error: CMOS layout file contains no "
|
|
"enumerations.\n", prog_name);
|
|
exit(1);
|
|
}
|
|
|
|
while (!process_enum(f, 0)) ;
|
|
|
|
/* Go back to start of file. */
|
|
line_num = 1;
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
skip_past_start(f);
|
|
|
|
/* Process all entries, adding them to the repository as we go.
|
|
* We must add the entries after the enums, even though they
|
|
* appear in the layout file before the enums. This is because
|
|
* the entries are sanity checked against the enums as they are
|
|
* added.
|
|
*/
|
|
|
|
if (process_entry(f, 0)) {
|
|
fprintf(stderr,
|
|
"%s: Error: CMOS layout file contains no entries.\n",
|
|
prog_name);
|
|
exit(1);
|
|
}
|
|
|
|
while (!process_entry(f, 0)) ;
|
|
|
|
/* Skip past all enums. They have already been processed. */
|
|
while (!process_enum(f, 1)) ;
|
|
|
|
/* Process CMOS checksum info. */
|
|
process_checksum_info(f);
|
|
|
|
/* See if there are any lines left to process. If so, verify
|
|
* that they are all either blank lines or comments.
|
|
*/
|
|
skip_remaining_lines(f);
|
|
|
|
regfree(&blank_or_comment_expr);
|
|
regfree(&start_entries_expr);
|
|
regfree(&entries_line_expr);
|
|
regfree(&start_enums_expr);
|
|
regfree(&enums_line_expr);
|
|
regfree(&start_checksums_expr);
|
|
regfree(&checksum_line_expr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* skip_past_start
|
|
*
|
|
* Skip past the line that marks the start of the "entries" section.
|
|
****************************************************************************/
|
|
static void skip_past_start(FILE * f)
|
|
{
|
|
char line[LINE_BUF_SIZE];
|
|
|
|
for (;; line_num++) {
|
|
if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
|
|
fprintf(stderr,
|
|
"%s: \"entries\" line not found in CMOS layout file.\n",
|
|
prog_name);
|
|
exit(1);
|
|
}
|
|
|
|
if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
|
|
continue;
|
|
|
|
if (!regexec(&start_entries_expr, line, 0, NULL, 0))
|
|
break;
|
|
|
|
fprintf(stderr,
|
|
"%s: Syntax error on line %d of CMOS layout file. "
|
|
"\"entries\" line expected.\n", prog_name, line_num);
|
|
exit(1);
|
|
}
|
|
|
|
line_num++;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* process_entry
|
|
*
|
|
* Get an entry from "entries" section of file and add it to our repository
|
|
* of layout information. Return 0 if an entry was found and processed.
|
|
* Return 1 if there are no more entries.
|
|
****************************************************************************/
|
|
static int process_entry(FILE * f, int skip_add)
|
|
{
|
|
static const size_t N_MATCHES = 6;
|
|
char line[LINE_BUF_SIZE];
|
|
regmatch_t match[N_MATCHES];
|
|
cmos_entry_t cmos_entry;
|
|
int result;
|
|
|
|
result = 1;
|
|
|
|
for (;; line_num++) {
|
|
if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
|
|
fprintf(stderr,
|
|
"%s: Unexpected end of CMOS layout file reached while "
|
|
"reading \"entries\" section.\n", prog_name);
|
|
exit(1);
|
|
}
|
|
|
|
if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
|
|
continue;
|
|
|
|
if (regexec(&entries_line_expr, line, N_MATCHES, match, 0)) {
|
|
if (regexec(&start_enums_expr, line, 0, NULL, 0)) {
|
|
fprintf(stderr,
|
|
"%s: Syntax error on line %d of CMOS layout "
|
|
"file.\n", prog_name, line_num);
|
|
exit(1);
|
|
}
|
|
|
|
break; /* start of enumerations reached: no more entries */
|
|
}
|
|
|
|
result = 0; /* next layout entry found */
|
|
|
|
if (skip_add)
|
|
break;
|
|
|
|
line[match[1].rm_eo] = '\0';
|
|
line[match[2].rm_eo] = '\0';
|
|
line[match[3].rm_eo] = '\0';
|
|
line[match[4].rm_eo] = '\0';
|
|
line[match[5].rm_eo] = '\0';
|
|
create_entry(&cmos_entry, &line[match[1].rm_so],
|
|
&line[match[2].rm_so], &line[match[3].rm_so],
|
|
&line[match[4].rm_so], &line[match[5].rm_so]);
|
|
try_add_layout_file_entry(&cmos_entry);
|
|
break;
|
|
}
|
|
|
|
line_num++;
|
|
return result;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* process_enum
|
|
*
|
|
* Get an enuneration from "enumerations" section of file and add it to our
|
|
* repository of layout information. Return 0 if an enumeration was found
|
|
* and processed. Return 1 if there are no more enumerations.
|
|
****************************************************************************/
|
|
static int process_enum(FILE * f, int skip_add)
|
|
{
|
|
static const size_t N_MATCHES = 4;
|
|
char line[LINE_BUF_SIZE];
|
|
regmatch_t match[N_MATCHES];
|
|
cmos_enum_t cmos_enum;
|
|
int result;
|
|
|
|
result = 1;
|
|
|
|
for (;; line_num++) {
|
|
if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
|
|
fprintf(stderr,
|
|
"%s: Unexpected end of CMOS layout file reached while "
|
|
"reading \"enumerations\" section.\n",
|
|
prog_name);
|
|
exit(1);
|
|
}
|
|
|
|
if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
|
|
continue;
|
|
|
|
if (regexec(&enums_line_expr, line, N_MATCHES, match, 0)) {
|
|
if (regexec(&start_checksums_expr, line, 0, NULL, 0)) {
|
|
fprintf(stderr,
|
|
"%s: Syntax error on line %d of CMOS layout "
|
|
"file.\n", prog_name, line_num);
|
|
exit(1);
|
|
}
|
|
|
|
break; /* start of checksums reached: no more enumerations */
|
|
}
|
|
|
|
result = 0; /* next layout enumeration found */
|
|
|
|
if (skip_add)
|
|
break;
|
|
|
|
line[match[1].rm_eo] = '\0';
|
|
line[match[2].rm_eo] = '\0';
|
|
line[match[3].rm_eo] = '\0';
|
|
create_enum(&cmos_enum, &line[match[1].rm_so],
|
|
&line[match[2].rm_so], &line[match[3].rm_so]);
|
|
try_add_cmos_enum(&cmos_enum);
|
|
break;
|
|
}
|
|
|
|
line_num++;
|
|
return result;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* process_checksum_info
|
|
*
|
|
* Get line conatining CMOS checksum information.
|
|
****************************************************************************/
|
|
static void process_checksum_info(FILE * f)
|
|
{
|
|
static const size_t N_MATCHES = 4;
|
|
char line[LINE_BUF_SIZE];
|
|
regmatch_t match[N_MATCHES];
|
|
|
|
for (;; line_num++) {
|
|
if (get_layout_file_line(f, line, LINE_BUF_SIZE)) {
|
|
fprintf(stderr,
|
|
"%s: Unexpected end of CMOS layout file reached while "
|
|
"reading \"checksums\" section.\n", prog_name);
|
|
exit(1);
|
|
}
|
|
|
|
if (!regexec(&blank_or_comment_expr, line, 0, NULL, 0))
|
|
continue;
|
|
|
|
if (regexec(&checksum_line_expr, line, N_MATCHES, match, 0)) {
|
|
fprintf(stderr,
|
|
"%s: Syntax error on line %d of CMOS layout "
|
|
"file. \"checksum\" line expected.\n",
|
|
prog_name, line_num);
|
|
exit(1);
|
|
}
|
|
|
|
/* checksum line found */
|
|
line[match[1].rm_eo] = '\0';
|
|
line[match[2].rm_eo] = '\0';
|
|
line[match[3].rm_eo] = '\0';
|
|
set_checksum_info(&line[match[1].rm_so], &line[match[2].rm_so],
|
|
&line[match[3].rm_so]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* skip_remaining_lines
|
|
*
|
|
* Get any remaining lines of unprocessed input. Complain if we find a line
|
|
* that contains anything other than comments and whitespace.
|
|
****************************************************************************/
|
|
static void skip_remaining_lines(FILE * f)
|
|
{
|
|
char line[LINE_BUF_SIZE];
|
|
|
|
for (line_num++;
|
|
get_layout_file_line(f, line, LINE_BUF_SIZE) == OK; line_num++) {
|
|
if (regexec(&blank_or_comment_expr, line, 0, NULL, 0)) {
|
|
fprintf(stderr,
|
|
"%s: Syntax error on line %d of CMOS layout file: "
|
|
"Only comments and/or whitespace allowed after "
|
|
"\"checksum\" line.\n", prog_name, line_num);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* create_entry
|
|
*
|
|
* Create a CMOS entry structure representing the given information. Perform
|
|
* sanity checking on input parameters.
|
|
****************************************************************************/
|
|
static void 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[])
|
|
{
|
|
cmos_entry->bit = string_to_unsigned(start_bit_str, "start-bit");
|
|
cmos_entry->length = string_to_unsigned(length_str, "length");
|
|
|
|
if (config_str[1] != '\0')
|
|
goto bad_config_str;
|
|
|
|
switch (config_str[0]) {
|
|
case 'e':
|
|
cmos_entry->config = CMOS_ENTRY_ENUM;
|
|
break;
|
|
|
|
case 'h':
|
|
cmos_entry->config = CMOS_ENTRY_HEX;
|
|
break;
|
|
|
|
case 's':
|
|
cmos_entry->config = CMOS_ENTRY_STRING;
|
|
break;
|
|
|
|
case 'r':
|
|
cmos_entry->config = CMOS_ENTRY_RESERVED;
|
|
break;
|
|
|
|
default:
|
|
goto bad_config_str;
|
|
}
|
|
|
|
cmos_entry->config_id = string_to_unsigned(config_id_str, "config-ID");
|
|
|
|
if (strlen(name_str) >= CMOS_MAX_NAME_LENGTH) {
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file: name too "
|
|
"long (max length is %d).\n", prog_name, line_num,
|
|
CMOS_MAX_NAME_LENGTH - 1);
|
|
exit(1);
|
|
}
|
|
|
|
strcpy(cmos_entry->name, name_str);
|
|
return;
|
|
|
|
bad_config_str:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file: 'e', 'h', or "
|
|
"'r' expected for config value.\n", prog_name, line_num);
|
|
exit(1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* try_add_layout_file_entry
|
|
*
|
|
* Attempt to add the given CMOS entry to our internal repository. Exit with
|
|
* an error message on failure.
|
|
****************************************************************************/
|
|
static void try_add_layout_file_entry(const cmos_entry_t * cmos_entry)
|
|
{
|
|
const cmos_entry_t *conflict;
|
|
|
|
switch (add_cmos_entry(cmos_entry, &conflict)) {
|
|
case OK:
|
|
return;
|
|
|
|
case CMOS_AREA_OUT_OF_RANGE:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. Area "
|
|
"covered by entry %s is out of range.\n", prog_name,
|
|
line_num, cmos_entry->name);
|
|
break;
|
|
|
|
case CMOS_AREA_TOO_WIDE:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. Area "
|
|
"covered by entry %s is too wide.\n", prog_name,
|
|
line_num, cmos_entry->name);
|
|
break;
|
|
|
|
case LAYOUT_ENTRY_OVERLAP:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. Layouts "
|
|
"overlap for entries %s and %s.\n", prog_name, line_num,
|
|
cmos_entry->name, conflict->name);
|
|
break;
|
|
|
|
case LAYOUT_ENTRY_BAD_LENGTH:
|
|
/* Silently ignore entries with zero length. Although this should
|
|
* never happen in practice, we should handle the case in a
|
|
* reasonable manner just to be safe.
|
|
*/
|
|
return;
|
|
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
exit(1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* create_enum
|
|
*
|
|
* Create a CMOS enumeration structure representing the given information.
|
|
* Perform sanity checking on input parameters.
|
|
****************************************************************************/
|
|
static void create_enum(cmos_enum_t * cmos_enum, const char id_str[],
|
|
const char value_str[], const char text_str[])
|
|
{
|
|
cmos_enum->config_id = string_to_unsigned(id_str, "ID");
|
|
cmos_enum->value = string_to_unsigned_long(value_str, "value");
|
|
|
|
if (strlen(text_str) >= CMOS_MAX_TEXT_LENGTH) {
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file: text too "
|
|
"long (max length is %d).\n", prog_name, line_num,
|
|
CMOS_MAX_TEXT_LENGTH - 1);
|
|
exit(1);
|
|
}
|
|
|
|
strcpy(cmos_enum->text, text_str);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* try_add_cmos_enum
|
|
*
|
|
* Attempt to add the given CMOS enum to our internal repository. Exit with
|
|
* an error message on failure.
|
|
****************************************************************************/
|
|
static void try_add_cmos_enum(const cmos_enum_t * cmos_enum)
|
|
{
|
|
switch (add_cmos_enum(cmos_enum)) {
|
|
case OK:
|
|
return;
|
|
|
|
case LAYOUT_DUPLICATE_ENUM:
|
|
fprintf(stderr, "%s: Error on line %d of CMOS layout file: "
|
|
"Enumeration found with duplicate ID/value combination.\n",
|
|
prog_name, line_num);
|
|
break;
|
|
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
exit(1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* set_checksum_info
|
|
*
|
|
* Set CMOS checksum information according to input parameters and perform
|
|
* sanity checking on input parameters.
|
|
****************************************************************************/
|
|
static void set_checksum_info(const char start_str[], const char end_str[],
|
|
const char index_str[])
|
|
{
|
|
cmos_checksum_layout_t layout;
|
|
|
|
/* These are bit positions that we want to convert to byte positions. */
|
|
layout.summed_area_start =
|
|
string_to_unsigned(start_str, "CMOS checksummed area start");
|
|
layout.summed_area_end =
|
|
string_to_unsigned(end_str, "CMOS checksummed area end");
|
|
layout.checksum_at =
|
|
string_to_unsigned(index_str, "CMOS checksum location");
|
|
|
|
switch (checksum_layout_to_bytes(&layout)) {
|
|
case OK:
|
|
break;
|
|
|
|
case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. CMOS "
|
|
"checksummed area start is not byte-aligned.\n",
|
|
prog_name, line_num);
|
|
goto fail;
|
|
|
|
case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. CMOS "
|
|
"checksummed area end is not byte-aligned.\n",
|
|
prog_name, line_num);
|
|
goto fail;
|
|
|
|
case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. CMOS "
|
|
"checksum location is not byte-aligned.\n", prog_name,
|
|
line_num);
|
|
goto fail;
|
|
|
|
case LAYOUT_INVALID_SUMMED_AREA:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. CMOS "
|
|
"checksummed area end must be greater than CMOS checksummed "
|
|
"area start.\n", prog_name, line_num);
|
|
goto fail;
|
|
|
|
case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. CMOS "
|
|
"checksum overlaps checksummed area.\n", prog_name,
|
|
line_num);
|
|
goto fail;
|
|
|
|
case LAYOUT_SUMMED_AREA_OUT_OF_RANGE:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. CMOS "
|
|
"checksummed area out of range.\n", prog_name,
|
|
line_num);
|
|
goto fail;
|
|
|
|
case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file. CMOS "
|
|
"checksum location out of range.\n", prog_name,
|
|
line_num);
|
|
goto fail;
|
|
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
cmos_checksum_start = layout.summed_area_start;
|
|
cmos_checksum_end = layout.summed_area_end;
|
|
cmos_checksum_index = layout.checksum_at;
|
|
return;
|
|
|
|
fail:
|
|
exit(1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* cmos_entry_char_value
|
|
*
|
|
* Return the character representation of 'config'.
|
|
****************************************************************************/
|
|
static char cmos_entry_char_value(cmos_entry_config_t config)
|
|
{
|
|
switch (config) {
|
|
case CMOS_ENTRY_ENUM:
|
|
return 'e';
|
|
|
|
case CMOS_ENTRY_HEX:
|
|
return 'h';
|
|
|
|
case CMOS_ENTRY_RESERVED:
|
|
return 'r';
|
|
|
|
case CMOS_ENTRY_STRING:
|
|
return 's';
|
|
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
return 0; /* not reached */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* get_layout_file_line
|
|
*
|
|
* Get a line of input from file 'f'. Store result in 'line' which is an
|
|
* array of 'line_buf_size' bytes. Return OK on success or an error code on
|
|
* failure.
|
|
****************************************************************************/
|
|
static int get_layout_file_line(FILE * f, char line[], int line_buf_size)
|
|
{
|
|
switch (get_line_from_file(f, line, line_buf_size)) {
|
|
case OK:
|
|
return OK;
|
|
|
|
case LINE_EOF:
|
|
return LINE_EOF;
|
|
|
|
case LINE_TOO_LONG:
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file: Maximum "
|
|
"line length exceeded. Max is %d characters.\n",
|
|
prog_name, line_num, line_buf_size - 2);
|
|
break;
|
|
}
|
|
|
|
exit(1);
|
|
return 1; /* keep compiler happy */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* string_to_unsigned
|
|
*
|
|
* Convert the string 'str' to an unsigned and return the result.
|
|
****************************************************************************/
|
|
static unsigned string_to_unsigned(const char str[], const char str_name[])
|
|
{
|
|
unsigned long n;
|
|
unsigned z;
|
|
|
|
n = do_string_to_unsigned_long(str, str_name, "");
|
|
|
|
if ((z = (unsigned)n) != n) {
|
|
/* This could happen on an architecture in which
|
|
* sizeof(unsigned) < sizeof(unsigned long).
|
|
*/
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file: %s value is "
|
|
"out of range.\n", prog_name, line_num, str_name);
|
|
exit(1);
|
|
}
|
|
|
|
return z;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* string_to_unsigned_long
|
|
*
|
|
* Convert the string 'str' to an unsigned long and return the result.
|
|
****************************************************************************/
|
|
static unsigned long string_to_unsigned_long(const char str[],
|
|
const char str_name[])
|
|
{
|
|
return do_string_to_unsigned_long(str, str_name, " long");
|
|
}
|
|
|
|
/****************************************************************************
|
|
* do_string_to_unsigned_long
|
|
*
|
|
* Convert the string 'str' to an unsigned long and return the result. Exit
|
|
* with an appropriate error message on failure.
|
|
****************************************************************************/
|
|
static unsigned long do_string_to_unsigned_long(const char str[],
|
|
const char str_name[],
|
|
const char blurb[])
|
|
{
|
|
unsigned long n;
|
|
char *p;
|
|
|
|
n = strtoul(str, &p, 0);
|
|
|
|
if (*p != '\0') {
|
|
fprintf(stderr,
|
|
"%s: Error on line %d of CMOS layout file: %s is not a "
|
|
"valid unsigned%s integer.\n", prog_name, line_num,
|
|
str_name, blurb);
|
|
exit(1);
|
|
}
|
|
|
|
return n;
|
|
}
|