diff options
Diffstat (limited to 'util/nvramtool/accessors')
-rw-r--r-- | util/nvramtool/accessors/layout-bin.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/util/nvramtool/accessors/layout-bin.c b/util/nvramtool/accessors/layout-bin.c new file mode 100644 index 0000000000..53f0530c40 --- /dev/null +++ b/util/nvramtool/accessors/layout-bin.c @@ -0,0 +1,460 @@ +/*****************************************************************************\ + * lbtable.c + ***************************************************************************** + * 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> + * and Stefan Reinauer <stepan@openbios.org>. + * 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 <arpa/inet.h> +#include <string.h> +#include <sys/mman.h> +#include "common.h" +#include "coreboot_tables.h" +#include "ip_checksum.h" +#include "lbtable.h" +#include "layout.h" +#include "cmos_lowlevel.h" +#include "hexdump.h" +#include "cbfs.h" + +static void process_cmos_table(void); +static void get_cmos_checksum_info(void); +static void try_convert_checksum_layout(cmos_checksum_layout_t * layout); +static void try_add_cmos_table_enum(cmos_enum_t * cmos_enum); +static void try_add_cmos_table_entry(cmos_entry_t * cmos_entry); +static const struct cmos_entries *first_cmos_table_entry(void); +static const struct cmos_entries *next_cmos_table_entry(const struct + cmos_entries *last); +static const struct cmos_enums *first_cmos_table_enum(void); +static const struct cmos_enums *next_cmos_table_enum + (const struct cmos_enums *last); +static const struct lb_record *first_cmos_rec(uint32_t tag); +static const struct lb_record *next_cmos_rec(const struct lb_record *last, + uint32_t tag); + +/* The CMOS option table is located within the coreboot table. It tells us + * where the CMOS parameters are located in the nonvolatile RAM. + */ +static const struct cmos_option_table *cmos_table = NULL; + +void process_layout(void) +{ + if ((cmos_table) == NULL) { + fprintf(stderr, + "%s: CMOS option table not found in coreboot table. " + "Apparently, the coreboot installed on this system was " + "built without specifying CONFIG_HAVE_OPTION_TABLE.\n", + prog_name); + exit(1); + } + + process_cmos_table(); + get_cmos_checksum_info(); +} + +void get_layout_from_cbfs_file(void) +{ + uint32_t len; + cmos_table = cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT, &len); + process_layout(); +} + +/**************************************************************************** + * process_cmos_table + * + * Extract layout information from the CMOS option table and store it in our + * internal repository. + ****************************************************************************/ +static void process_cmos_table(void) +{ + const struct cmos_enums *p; + const struct cmos_entries *q; + cmos_enum_t cmos_enum; + cmos_entry_t cmos_entry; + + /* First add the enums. */ + for (p = first_cmos_table_enum(); p != NULL; + p = next_cmos_table_enum(p)) { + cmos_enum.config_id = p->config_id; + cmos_enum.value = p->value; + strncpy(cmos_enum.text, (char *)p->text, CMOS_MAX_TEXT_LENGTH); + cmos_enum.text[CMOS_MAX_TEXT_LENGTH] = '\0'; + try_add_cmos_table_enum(&cmos_enum); + } + + /* Now add the entries. We must add the entries after the enums because + * the entries are sanity checked against the enums as they are added. + */ + for (q = first_cmos_table_entry(); q != NULL; + q = next_cmos_table_entry(q)) { + cmos_entry.bit = q->bit; + cmos_entry.length = q->length; + + switch (q->config) { + case 'e': + cmos_entry.config = CMOS_ENTRY_ENUM; + break; + + case 'h': + cmos_entry.config = CMOS_ENTRY_HEX; + break; + + case 'r': + cmos_entry.config = CMOS_ENTRY_RESERVED; + break; + + case 's': + cmos_entry.config = CMOS_ENTRY_STRING; + break; + + default: + fprintf(stderr, + "%s: Entry in CMOS option table has unknown config " + "value.\n", prog_name); + exit(1); + } + + cmos_entry.config_id = q->config_id; + strncpy(cmos_entry.name, (char *)q->name, CMOS_MAX_NAME_LENGTH); + cmos_entry.name[CMOS_MAX_NAME_LENGTH] = '\0'; + try_add_cmos_table_entry(&cmos_entry); + } +} + +/**************************************************************************** + * get_cmos_checksum_info + * + * Get layout information for CMOS checksum. + ****************************************************************************/ +static void get_cmos_checksum_info(void) +{ + const cmos_entry_t *e; + struct cmos_checksum *checksum; + cmos_checksum_layout_t layout; + unsigned index, index2; + + checksum = (struct cmos_checksum *)next_cmos_rec((const struct lb_record *)first_cmos_table_enum(), LB_TAG_OPTION_CHECKSUM); + + if (checksum != NULL) { /* We are lucky. The coreboot table hints us to the checksum. + * We might have to check the type field here though. + */ + layout.summed_area_start = checksum->range_start; + layout.summed_area_end = checksum->range_end; + layout.checksum_at = checksum->location; + try_convert_checksum_layout(&layout); + cmos_checksum_start = layout.summed_area_start; + cmos_checksum_end = layout.summed_area_end; + cmos_checksum_index = layout.checksum_at; + return; + } + + if ((e = find_cmos_entry(checksum_param_name)) == NULL) + return; + + /* If we get here, we are unlucky. The CMOS option table contains the + * location of the CMOS checksum. However, there is no information + * regarding which bytes of the CMOS area the checksum is computed over. + * Thus we have to hope our presets will be fine. + */ + + if (e->bit % 8) { + fprintf(stderr, + "%s: Error: CMOS checksum is not byte-aligned.\n", + prog_name); + exit(1); + } + + index = e->bit / 8; + index2 = index + 1; /* The CMOS checksum occupies 16 bits. */ + + if (verify_cmos_byte_index(index) || verify_cmos_byte_index(index2)) { + fprintf(stderr, + "%s: Error: CMOS checksum location out of range.\n", + prog_name); + exit(1); + } + + if (((index >= cmos_checksum_start) && (index <= cmos_checksum_end)) || + (((index2) >= cmos_checksum_start) + && ((index2) <= cmos_checksum_end))) { + fprintf(stderr, + "%s: Error: CMOS checksum overlaps checksummed area.\n", + prog_name); + exit(1); + } + + cmos_checksum_index = index; +} + +/**************************************************************************** + * try_convert_checksum_layout + * + * Perform sanity checking on CMOS checksum layout information and attempt to + * convert information from bit positions to byte positions. Return OK on + * success or an error code on failure. + ****************************************************************************/ +static void try_convert_checksum_layout(cmos_checksum_layout_t * layout) +{ + switch (checksum_layout_to_bytes(layout)) { + case OK: + return; + + case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED: + fprintf(stderr, + "%s: CMOS checksummed area start is not byte-aligned.\n", + prog_name); + break; + + case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED: + fprintf(stderr, + "%s: CMOS checksummed area end is not byte-aligned.\n", + prog_name); + break; + + case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED: + fprintf(stderr, + "%s: CMOS checksum location is not byte-aligned.\n", + prog_name); + break; + + case LAYOUT_INVALID_SUMMED_AREA: + fprintf(stderr, + "%s: CMOS checksummed area end must be greater than " + "CMOS checksummed area start.\n", prog_name); + break; + + case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA: + fprintf(stderr, + "%s: CMOS checksum overlaps checksummed area.\n", + prog_name); + break; + + case LAYOUT_SUMMED_AREA_OUT_OF_RANGE: + fprintf(stderr, + "%s: CMOS checksummed area out of range.\n", prog_name); + break; + + case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE: + fprintf(stderr, + "%s: CMOS checksum location out of range.\n", + prog_name); + break; + + default: + BUG(); + } + + exit(1); +} + +/**************************************************************************** + * try_add_cmos_table_enum + * + * Attempt to add a CMOS enum to our internal repository. Exit with an error + * message on failure. + ****************************************************************************/ +static void try_add_cmos_table_enum(cmos_enum_t * cmos_enum) +{ + switch (add_cmos_enum(cmos_enum)) { + case OK: + return; + + case LAYOUT_DUPLICATE_ENUM: + fprintf(stderr, "%s: Duplicate enum %s found in CMOS option " + "table.\n", prog_name, cmos_enum->text); + break; + + default: + BUG(); + } + + exit(1); +} + +/**************************************************************************** + * try_add_cmos_table_entry + * + * Attempt to add a CMOS entry to our internal repository. Exit with an + * error message on failure. + ****************************************************************************/ +static void try_add_cmos_table_entry(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: Bad CMOS option layout in CMOS option table entry " + "%s.\n", prog_name, cmos_entry->name); + break; + + case CMOS_AREA_TOO_WIDE: + fprintf(stderr, + "%s: Area too wide for CMOS option table entry %s.\n", + prog_name, cmos_entry->name); + break; + + case LAYOUT_ENTRY_OVERLAP: + fprintf(stderr, + "%s: CMOS option table entries %s and %s have overlapping " + "layouts.\n", prog_name, 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); +} + +/**************************************************************************** + * first_cmos_table_entry + * + * Return a pointer to the first entry in the CMOS table that represents a + * CMOS parameter. Return NULL if CMOS table is empty. + ****************************************************************************/ +static const struct cmos_entries *first_cmos_table_entry(void) +{ + return (const struct cmos_entries *)first_cmos_rec(LB_TAG_OPTION); +} + +/**************************************************************************** + * next_cmos_table_entry + * + * Return a pointer to the next entry after 'last' in the CMOS table that + * represents a CMOS parameter. Return NULL if there are no more parameters. + ****************************************************************************/ +static const struct cmos_entries *next_cmos_table_entry(const struct + cmos_entries *last) +{ + return (const struct cmos_entries *) + next_cmos_rec((const struct lb_record *)last, LB_TAG_OPTION); +} + +/**************************************************************************** + * first_cmos_table_enum + * + * Return a pointer to the first entry in the CMOS table that represents a + * possible CMOS parameter value. Return NULL if the table does not contain + * any such entries. + ****************************************************************************/ +static const struct cmos_enums *first_cmos_table_enum(void) +{ + return (const struct cmos_enums *)first_cmos_rec(LB_TAG_OPTION_ENUM); +} + +/**************************************************************************** + * next_cmos_table_enum + * + * Return a pointer to the next entry after 'last' in the CMOS table that + * represents a possible CMOS parameter value. Return NULL if there are no + * more parameter values. + ****************************************************************************/ +static const struct cmos_enums *next_cmos_table_enum + (const struct cmos_enums *last) { + return (const struct cmos_enums *) + next_cmos_rec((const struct lb_record *)last, LB_TAG_OPTION_ENUM); +} + +/**************************************************************************** + * first_cmos_rec + * + * Return a pointer to the first entry in the CMOS table whose type matches + * 'tag'. Return NULL if CMOS table contains no such entry. + * + * Possible values for 'tag' are as follows: + * + * LB_TAG_OPTION: The entry represents a CMOS parameter. + * LB_TAG_OPTION_ENUM: The entry represents a possible value for a CMOS + * parameter of type 'enum'. + * + * The CMOS table tells us where in the nonvolatile RAM to look for CMOS + * parameter values and specifies their types as 'enum', 'hex', or + * 'reserved'. + ****************************************************************************/ +static const struct lb_record *first_cmos_rec(uint32_t tag) +{ + const char *p; + uint32_t bytes_processed, bytes_for_entries; + const struct lb_record *lbrec; + + p = ((const char *)cmos_table) + cmos_table->header_length; + bytes_for_entries = cmos_table->size - cmos_table->header_length; + + for (bytes_processed = 0; + bytes_processed < bytes_for_entries; + bytes_processed += lbrec->size) { + lbrec = (const struct lb_record *)&p[bytes_processed]; + + if (lbrec->tag == tag) + return lbrec; + } + + return NULL; +} + +/**************************************************************************** + * next_cmos_rec + * + * Return a pointer to the next entry after 'last' in the CMOS table whose + * type matches 'tag'. Return NULL if the table contains no more entries of + * this type. + ****************************************************************************/ +static const struct lb_record *next_cmos_rec(const struct lb_record *last, + uint32_t tag) +{ + const char *p; + uint32_t bytes_processed, bytes_for_entries, last_offset; + const struct lb_record *lbrec; + + p = ((const char *)cmos_table) + cmos_table->header_length; + bytes_for_entries = cmos_table->size - cmos_table->header_length; + last_offset = ((const char *)last) - p; + + for (bytes_processed = last_offset + last->size; + bytes_processed < bytes_for_entries; + bytes_processed += lbrec->size) { + lbrec = (const struct lb_record *)&p[bytes_processed]; + + if (lbrec->tag == tag) + return lbrec; + } + + return NULL; +} + |