diff options
author | Stefan Reinauer <stepan@coresystems.de> | 2007-07-12 16:35:42 +0000 |
---|---|---|
committer | Stefan Reinauer <stepan@openbios.org> | 2007-07-12 16:35:42 +0000 |
commit | 6540ae5ea0a52e05ceb31675a2419d07197db738 (patch) | |
tree | 0dd2a6401ef2cfa5b06de374a6ba6db11e0b61c6 /util/lxbios/lbtable.c | |
parent | 6cf687783bc9c54146cf85ce5e526e63ab7047fb (diff) |
Signed-off-by: Stefan Reinauer <stepan@coresystems.de>
Acked-by: Jordan Crouse <jordan.crouse@amd.com>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@2736 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'util/lxbios/lbtable.c')
-rw-r--r-- | util/lxbios/lbtable.c | 1270 |
1 files changed, 1270 insertions, 0 deletions
diff --git a/util/lxbios/lbtable.c b/util/lxbios/lbtable.c new file mode 100644 index 0000000000..0fd5e91363 --- /dev/null +++ b/util/lxbios/lbtable.c @@ -0,0 +1,1270 @@ +/*****************************************************************************\ + * lbtable.c + * $Id: lbtable.c,v 1.3 2006/01/24 00:25:40 dsp_llnl Exp $ + ***************************************************************************** + * 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 lxbios, a utility for reading/writing LinuxBIOS + * parameters and displaying information from the LinuxBIOS table. + * For details, see <http://www.llnl.gov/linux/lxbios/>. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +\*****************************************************************************/ + +#include <sys/mman.h> +#include "common.h" +#include "linuxbios_tables.h" +#include "ip_checksum.h" +#include "lbtable.h" +#include "layout.h" +#include "cmos_lowlevel.h" +#include "hexdump.h" + +typedef void (*lbtable_print_fn_t) (const struct lb_record *rec); + +/* This structure represents an item in the LinuxBIOS table that may be + * displayed using the -l option. + */ +typedef struct + { uint32_t tag; + const char *name; + const char *description; + const char *nofound_msg; + lbtable_print_fn_t print_fn; + } +lbtable_choice_t; + +typedef struct + { unsigned long start; /* address of first byte of memory range */ + unsigned long end; /* address of last byte of memory range */ + } +mem_range_t; + +static const struct lb_header * lbtable_scan (unsigned long start, + unsigned long end, + int *bad_header_count, + int *bad_table_count); +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 lb_record * find_lbrec (uint32_t tag); +static const char * lbrec_tag_to_str (uint32_t tag); +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); +static void memory_print_fn (const struct lb_record *rec); +static void mainboard_print_fn (const struct lb_record *rec); +static void cmos_opt_table_print_fn (const struct lb_record *rec); +static void print_option_record (const struct cmos_entries *cmos_entry); +static void print_enum_record (const struct cmos_enums *cmos_enum); +static void print_defaults_record (const struct cmos_defaults *cmos_defaults); +static void print_unknown_record (const struct lb_record *cmos_item); +static void option_checksum_print_fn (const struct lb_record *rec); +static void string_print_fn (const struct lb_record *rec); +static void uint64_to_hex_string (char str[], uint64_t n); + +static const char memory_desc[] = +" This shows information about system memory.\n"; + +static const char mainboard_desc[] = +" This shows information about your mainboard.\n"; + +static const char version_desc[] = +" This shows LinuxBIOS version information.\n"; + +static const char extra_version_desc[] = +" This shows extra LinuxBIOS version information.\n"; + +static const char build_desc[] = +" This shows LinuxBIOS build information.\n"; + +static const char compile_time_desc[] = +" This shows when LinuxBIOS was compiled.\n"; + +static const char compile_by_desc[] = +" This shows who compiled LinuxBIOS.\n"; + +static const char compile_host_desc[] = +" This shows the name of the machine that compiled LinuxBIOS.\n"; + +static const char compile_domain_desc[] = +" This shows the domain name of the machine that compiled LinuxBIOS.\n"; + +static const char compiler_desc[] = +" This shows the name of the compiler used to build LinuxBIOS.\n"; + +static const char linker_desc[] = +" This shows the name of the linker used to build LinuxBIOS.\n"; + +static const char assembler_desc[] = +" This shows the name of the assembler used to build LinuxBIOS.\n"; + +static const char cmos_opt_table_desc[] = +" This does a low-level dump of the CMOS option table. The table " +"contains\n" +" information about the layout of the values that LinuxBIOS stores in\n" +" nonvolatile RAM.\n"; + +static const char option_checksum_desc[] = +" This shows the location of the CMOS checksum and the area over which it " +"is\n" +" calculated.\n"; + +static const char generic_nofound_msg[] = +"%s: Item %s not found in LinuxBIOS table.\n"; + +static const char nofound_msg_cmos_opt_table[] = +"%s: Item %s not found in LinuxBIOS table. Apparently, the " +"LinuxBIOS installed on this system was built without specifying " +"HAVE_OPTION_TABLE.\n"; + +static const char nofound_msg_option_checksum[] = +"%s: Item %s not found in LinuxBIOS table. Apparently, you are " +"using LinuxBIOS v1.\n"; + +/* This is the number of items from the LinuxBIOS table that may be displayed + * using the -l option. + */ +#define NUM_LBTABLE_CHOICES 14 + +/* These represent the various items from the LinuxBIOS table that may be + * displayed using the -l option. + */ +static const lbtable_choice_t lbtable_choices[NUM_LBTABLE_CHOICES] = + { { LB_TAG_MEMORY, "memory", + memory_desc, generic_nofound_msg, + memory_print_fn + }, + { LB_TAG_MAINBOARD, "mainboard", + mainboard_desc, generic_nofound_msg, + mainboard_print_fn + }, + { LB_TAG_VERSION, "version", + version_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_EXTRA_VERSION, "extra_version", + extra_version_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_BUILD, "build", + build_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_COMPILE_TIME, "compile_time", + compile_time_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_COMPILE_BY, "compile_by", + compile_by_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_COMPILE_HOST, "compile_host", + compile_host_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_COMPILE_DOMAIN, "compile_domain", + compile_domain_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_COMPILER, "compiler", + compiler_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_LINKER, "linker", + linker_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_ASSEMBLER, "assembler", + assembler_desc, generic_nofound_msg, + string_print_fn + }, + { LB_TAG_CMOS_OPTION_TABLE, "cmos_opt_table", + cmos_opt_table_desc, nofound_msg_cmos_opt_table, + cmos_opt_table_print_fn + }, + { LB_TAG_OPTION_CHECKSUM, "option_checksum", + option_checksum_desc, nofound_msg_option_checksum, + option_checksum_print_fn + } + }; + +/* The LinuxBIOS table resides in low physical memory, which we access using + * /dev/mem. These are ranges of physical memory that should be scanned for a + * LinuxBIOS table. + */ + +#define NUM_MEM_RANGES 2 + +static const mem_range_t mem_ranges[NUM_MEM_RANGES] = + { { 0x00000000, 0x00000fff }, + { 0x000f0000, 0x000fffff } + }; + +/* This is the number of bytes of physical memory to map, starting at physical + * address 0. This value must be large enough to contain all memory ranges + * specified in mem_ranges above plus the maximum possible size of the + * LinuxBIOS table (since the start of the table could potentially occur at + * the end of the last memory range). + */ +static const size_t BYTES_TO_MAP = (1024 * 1024); + +/* Pointer to low physical memory that we access by calling mmap() on + * /dev/mem. + */ +static const void *low_phys_mem; + +/* Pointer to LinuxBIOS table. */ +static const struct lb_header *lbtable = NULL; + +/* The CMOS option table is located within the LinuxBIOS table. It tells us + * where the CMOS parameters are located in the nonvolatile RAM. + */ +static const struct cmos_option_table *cmos_table = NULL; + +static const hexdump_format_t format = + { 12, 4, " ", " | ", " ", " | ", '.', NULL }; + +/**************************************************************************** + * vtophys + * + * Convert a virtual address to a physical address. 'vaddr' is a virtual + * address in the address space of the current process. It points to + * somewhere in the chunk of memory that we mapped by calling mmap() on + * /dev/mem. This macro converts 'vaddr' to a physical address. + ****************************************************************************/ +#define vtophys(vaddr) (((unsigned long) vaddr) - \ + ((unsigned long) low_phys_mem)) + +/**************************************************************************** + * phystov + * + * Convert a physical address to a virtual address. 'paddr' is a physical + * address. This macro converts 'paddr' to a virtual address in the address + * space of the current process. The virtual to physical mapping was set up + * by calling mmap() on /dev/mem. + ****************************************************************************/ +#define phystov(paddr) (((unsigned long) low_phys_mem) + \ + ((unsigned long) paddr)) + +/**************************************************************************** + * get_lbtable + * + * Find the LinuxBIOS table and set global variable lbtable to point to it. + ****************************************************************************/ +void get_lbtable (void) + { int fd, i, bad_header_count, bad_table_count, bad_headers, bad_tables; + + if (lbtable != NULL) + return; + + /* The LinuxBIOS table is located in low physical memory, which may be + * conveniently accessed by calling mmap() on /dev/mem. + */ + + if ((fd = open("/dev/mem", O_RDONLY, 0)) < 0) + { fprintf(stderr, "%s: Can not open /dev/mem for reading: %s\n", + prog_name, strerror(errno)); + exit(1); + } + + if ((low_phys_mem = mmap(NULL, BYTES_TO_MAP, PROT_READ, MAP_SHARED, fd, 0)) + == MAP_FAILED) + { fprintf(stderr, "%s: Failed to mmap /dev/mem: %s\n", prog_name, + strerror(errno)); + exit(1); + } + + bad_header_count = 0; + bad_table_count = 0; + + for (i = 0; i < NUM_MEM_RANGES; i++) + { lbtable = lbtable_scan(phystov(mem_ranges[i].start), + phystov(mem_ranges[i].end), + &bad_headers, &bad_tables); + + if (lbtable != NULL) + return; /* success: we found it! */ + + bad_header_count += bad_headers; + bad_table_count += bad_tables; + } + + fprintf(stderr, + "%s: LinuxBIOS table not found. LinuxBIOS does not appear to\n" + " be installed on this system. Scanning for the table " + "produced the\n" + " following results:\n\n" + " %d valid signatures were found with bad header " + "checksums.\n" + " %d valid headers were found with bad table " + "checksums.\n", + prog_name, bad_header_count, bad_table_count); + exit(1); + } + +/**************************************************************************** + * get_layout_from_cmos_table + * + * Find the CMOS table which is stored within the LinuxBIOS table and set the + * global variable cmos_table to point to it. + ****************************************************************************/ +void get_layout_from_cmos_table (void) + { + + get_lbtable(); + cmos_table = (const struct cmos_option_table *) + find_lbrec(LB_TAG_CMOS_OPTION_TABLE); + + if ((cmos_table) == NULL) + { fprintf(stderr, + "%s: CMOS option table not found in LinuxBIOS table. " + "Apparently, the LinuxBIOS installed on this system was " + "built without specifying HAVE_OPTION_TABLE.\n", + prog_name); + exit(1); + } + + process_cmos_table(); + get_cmos_checksum_info(); + } + +/**************************************************************************** + * dump_lbtable + * + * Do a low-level dump of the LinuxBIOS table. + ****************************************************************************/ +void dump_lbtable (void) + { const char *p, *data; + uint32_t bytes_processed; + const struct lb_record *lbrec; + + p = ((const char *) lbtable) + lbtable->header_bytes; + printf("LinuxBIOS table at physical address 0x%lx:\n" + " signature: 0x%x (ASCII: %c%c%c%c)\n" + " header_bytes: 0x%x (decimal: %d)\n" + " header_checksum: 0x%x (decimal: %d)\n" + " table_bytes: 0x%x (decimal: %d)\n" + " table_checksum: 0x%x (decimal: %d)\n" + " table_entries: 0x%x (decimal: %d)\n\n", + vtophys(lbtable), *((uint32_t *) lbtable->signature), + lbtable->signature[0], lbtable->signature[1],lbtable->signature[2], + lbtable->signature[3], lbtable->header_bytes, lbtable->header_bytes, + lbtable->header_checksum, lbtable->header_checksum, + lbtable->table_bytes, lbtable->table_bytes, lbtable->table_checksum, + lbtable->table_checksum, lbtable->table_entries, + lbtable->table_entries); + + if ((lbtable->table_bytes == 0) != (lbtable->table_entries == 0)) + { printf("Inconsistent values for table_bytes and table_entries!!!\n" + "They should be either both 0 or both nonzero.\n"); + return; + } + + if (lbtable->table_bytes == 0) + { printf("The LinuxBIOS table is empty!!!\n"); + return; + } + + for (bytes_processed = 0; ; ) + { lbrec = (const struct lb_record *) &p[bytes_processed]; + printf(" %s record at physical address 0x%lx:\n" + " tag: 0x%x (decimal: %d)\n" + " size: 0x%x (decimal: %d)\n" + " data:\n", + lbrec_tag_to_str(lbrec->tag), vtophys(lbrec), lbrec->tag, + lbrec->tag, lbrec->size, lbrec->size); + + data = ((const char *) lbrec) + sizeof(*lbrec); + hexdump(data, lbrec->size - sizeof(*lbrec), vtophys(data), stdout, + &format); + + bytes_processed += lbrec->size; + + if (bytes_processed >= lbtable->table_bytes) + break; + + printf("\n"); + } + } + +/**************************************************************************** + * list_lbtable_choices + * + * List names and informational blurbs for items from the LinuxBIOS table + * that may be displayed using the -l option. + ****************************************************************************/ +void list_lbtable_choices (void) + { int i; + + for (i = 0; ; ) + { printf("%s:\n%s", + lbtable_choices[i].name, lbtable_choices[i].description); + + if (++i >= NUM_LBTABLE_CHOICES) + break; + + printf("\n"); + } + } + +/**************************************************************************** + * list_lbtable_item + * + * Show the LinuxBIOS table item specified by 'item'. + ****************************************************************************/ +void list_lbtable_item (const char item[]) + { int i; + const struct lb_record *rec; + + for (i = 0; i < NUM_LBTABLE_CHOICES; i++) + { if (strcmp(item, lbtable_choices[i].name) == 0) + break; + } + + if (i == NUM_LBTABLE_CHOICES) + { fprintf(stderr, "%s: Invalid LinuxBIOS table item %s.\n", prog_name, + item); + exit(1); + } + + if ((rec = find_lbrec(lbtable_choices[i].tag)) == NULL) + { fprintf(stderr, lbtable_choices[i].nofound_msg, prog_name, + lbtable_choices[i].name); + exit(1); + } + + lbtable_choices[i].print_fn(rec); + } + +/**************************************************************************** + * lbtable_scan + * + * Scan the chunk of memory specified by 'start' and 'end' for a LinuxBIOS + * table. The first 4 bytes of the table are marked by the signature + * { 'L', 'B', 'I', 'O' }. 'start' and 'end' indicate the addresses of the + * first and last bytes of the chunk of memory to be scanned. For instance, + * values of 0x10000000 and 0x1000ffff for 'start' and 'end' specify a 64k + * chunk of memory starting at address 0x10000000. 'start' and 'end' are + * virtual addresses in the address space of the current process. They + * represent a chunk of memory obtained by calling mmap() on /dev/mem. + * + * If a LinuxBIOS table is found, return a pointer to it. Otherwise return + * NULL. On return, *bad_header_count and *bad_table_count are set as + * follows: + * + * *bad_header_count: + * Indicates the number of times in which a valid signature was found + * but the header checksum was invalid. + * + * *bad_table_count: + * Indicates the number of times in which a header with a valid + * checksum was found but the table checksum was invalid. + ****************************************************************************/ +static const struct lb_header * lbtable_scan (unsigned long start, + unsigned long end, + int *bad_header_count, + int *bad_table_count) + { static const char signature[] = { 'L', 'B', 'I', 'O' }; + const struct lb_header *table; + const uint32_t *p; + uint32_t sig; + + assert(end >= start); + sig = (*((const uint32_t *) signature)); + table = NULL; + *bad_header_count = 0; + *bad_table_count = 0; + + /* Look for signature. Table is aligned on 16-byte boundary. Therefore + * only check every fourth 32-bit memory word. As the loop is coded below, + * this function will behave in a reasonable manner for ALL possible values + * for 'start' and 'end': even weird boundary cases like 0x00000000 and + * 0xffffffff on a 32-bit architecture. + */ + for (p = (const uint32_t *) start; + (((unsigned long) p) <= end) && + ((end - (unsigned long) p) >= (sizeof(uint32_t) - 1)); + p += 4) + { if (*p != sig) + continue; + + /* We found a valid signature. */ + table = (const struct lb_header *) p; + + /* validate header checksum */ + if (compute_ip_checksum((void *) table, sizeof(*table))) + { (*bad_header_count)++; + continue; + } + + /* validate table checksum */ + if (table->table_checksum != + compute_ip_checksum(((char *) table) + sizeof(*table), + table->table_bytes)) + { (*bad_table_count)++; + continue; + } + + /* checksums are ok: we found it! */ + return table; + } + + return NULL; + } + +/**************************************************************************** + * 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, 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; + + 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, 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 *) find_lbrec(LB_TAG_OPTION_CHECKSUM); + + if (checksum != NULL) + { /* We are lucky. The LinuxBIOS 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); + } + +/**************************************************************************** + * find_lbrec + * + * Find the record in the LinuxBIOS table that matches 'tag'. Return pointer + * to record on success or NULL if record not found. + ****************************************************************************/ +static const struct lb_record * find_lbrec (uint32_t tag) + { const char *p; + uint32_t bytes_processed; + const struct lb_record *lbrec; + + p = ((const char *) lbtable) + lbtable->header_bytes; + + for (bytes_processed = 0; + bytes_processed < lbtable->table_bytes; + bytes_processed += lbrec->size) + { lbrec = (const struct lb_record *) &p[bytes_processed]; + + if (lbrec->tag == tag) + return lbrec; + } + + return NULL; + } + +/**************************************************************************** + * lbrec_tag_to_str + * + * Return a pointer to the string representation of the given LinuxBIOS table + * tag. + ****************************************************************************/ +static const char * lbrec_tag_to_str (uint32_t tag) + { switch (tag) + { case LB_TAG_UNUSED: + return "UNUSED"; + + case LB_TAG_MEMORY: + return "MEMORY"; + + case LB_TAG_HWRPB: + return "HWRPB"; + + case LB_TAG_MAINBOARD: + return "MAINBOARD"; + + case LB_TAG_VERSION: + return "VERSION"; + + case LB_TAG_EXTRA_VERSION: + return "EXTRA_VERSION"; + + case LB_TAG_BUILD: + return "BUILD"; + + case LB_TAG_COMPILE_TIME: + return "COMPILE_TIME"; + + case LB_TAG_COMPILE_BY: + return "COMPILE_BY"; + + case LB_TAG_COMPILE_HOST: + return "COMPILE_HOST"; + + case LB_TAG_COMPILE_DOMAIN: + return "COMPILE_DOMAIN"; + + case LB_TAG_COMPILER: + return "COMPILER"; + + case LB_TAG_LINKER: + return "LINKER"; + + case LB_TAG_ASSEMBLER: + return "ASSEMBLER"; + + case LB_TAG_CMOS_OPTION_TABLE: + return "CMOS_OPTION_TABLE"; + + case LB_TAG_OPTION_CHECKSUM: + return "OPTION_CHECKSUM"; + + default: + break; + } + + return "UNKNOWN"; + } + +/**************************************************************************** + * 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; + } + +/**************************************************************************** + * memory_print_fn + * + * Display function for 'memory' item of LinuxBIOS table. + ****************************************************************************/ +static void memory_print_fn (const struct lb_record *rec) + { char start_str[19], end_str[19], size_str[19]; + const struct lb_memory *p; + const char *mem_type; + const struct lb_memory_range *ranges; + uint64_t size, start, end; + int i, entries; + + p = (const struct lb_memory *) rec; + entries = (p->size - sizeof(*p)) / sizeof(p->map[0]); + ranges = p->map; + + if (entries == 0) + { printf("No memory ranges were found.\n"); + return; + } + + for (i = 0; ; ) + { switch (ranges[i].type) + { case LB_MEM_RAM: + mem_type = "AVAILABLE"; + break; + + case LB_MEM_RESERVED: + mem_type = "RESERVED"; + break; + + case LB_MEM_TABLE: + mem_type = "CONFIG_TABLE"; + break; + + default: + mem_type = "UNKNOWN"; + break; + } + + size = unpack_lb64(ranges[i].size); + start = unpack_lb64(ranges[i].start); + end = start + size - 1; + uint64_to_hex_string(start_str, start); + uint64_to_hex_string(end_str, end); + uint64_to_hex_string(size_str, size); + printf("%s memory:\n" + " from physical addresses %s to %s\n" + " size is %s bytes (%lld in decimal)\n", + mem_type, start_str, end_str, size_str, + (unsigned long long) size); + + if (++i >= entries) + break; + + printf("\n"); + } + } + +/**************************************************************************** + * mainboard_print_fn + * + * Display function for 'mainboard' item of LinuxBIOS table. + ****************************************************************************/ +static void mainboard_print_fn (const struct lb_record *rec) + { const struct lb_mainboard *p; + + p = (const struct lb_mainboard *) rec; + printf("Vendor: %s\n" + "Part number: %s\n", + &p->strings[p->vendor_idx], + &p->strings[p->part_number_idx]); + } + +/**************************************************************************** + * cmos_opt_table_print_fn + * + * Display function for 'cmos_opt_table' item of LinuxBIOS table. + ****************************************************************************/ +static void cmos_opt_table_print_fn (const struct lb_record *rec) + { + const struct cmos_option_table *p; + const struct lb_record *cmos_item; + uint32_t bytes_processed, bytes_for_entries; + const char *q; + + p = (const struct cmos_option_table *) rec; + q = ((const char *) p) + p->header_length; + bytes_for_entries = p->size - p->header_length; + + printf("CMOS option table at physical address 0x%lx:\n" + " tag: 0x%x (decimal: %d)\n" + " size: 0x%x (decimal: %d)\n" + " header_length: 0x%x (decimal: %d)\n\n", + vtophys(p), p->tag, p->tag, p->size, p->size, p->header_length, + p->header_length); + + if (p->header_length > p->size) + { printf("Header length for CMOS option table is greater than the size " + "of the entire table including header!!!\n"); + return; + } + + if (bytes_for_entries == 0) + { printf("The CMOS option table is empty!!!\n"); + return; + } + + for (bytes_processed = 0; ; ) + { cmos_item = (const struct lb_record *) &q[bytes_processed]; + + switch (cmos_item->tag) + { case LB_TAG_OPTION: + print_option_record((const struct cmos_entries *) cmos_item); + break; + + case LB_TAG_OPTION_ENUM: + print_enum_record((const struct cmos_enums *) cmos_item); + break; + + case LB_TAG_OPTION_DEFAULTS: + print_defaults_record((const struct cmos_defaults *) cmos_item); + break; + + default: + print_unknown_record(cmos_item); + break; + } + + bytes_processed += cmos_item->size; + + if (bytes_processed >= bytes_for_entries) + break; + + printf("\n"); + } + } + +/**************************************************************************** + * print_option_record + * + * Display "option" record from CMOS option table. + ****************************************************************************/ +static void print_option_record (const struct cmos_entries *cmos_entry) + { static const size_t S_BUFSIZE = 80; + char s[S_BUFSIZE]; + + switch (cmos_entry->config) + { case 'e': + strcpy(s, "ENUM"); + break; + + case 'h': + strcpy(s, "HEX"); + break; + + case 'r': + strcpy(s, "RESERVED"); + break; + + default: + snprintf(s, S_BUFSIZE, "UNKNOWN: value is 0x%x (decimal: %d)", + cmos_entry->config, cmos_entry->config); + break; + } + + printf(" OPTION record at physical address 0x%lx:\n" + " tag: 0x%x (decimal: %d)\n" + " size: 0x%x (decimal: %d)\n" + " bit: 0x%x (decimal: %d)\n" + " length: 0x%x (decimal: %d)\n" + " config: %s\n" + " config_id: 0x%x (decimal: %d)\n" + " name: %s\n", + vtophys(cmos_entry), cmos_entry->tag, cmos_entry->tag, + cmos_entry->size, cmos_entry->size, cmos_entry->bit, + cmos_entry->bit, cmos_entry->length, cmos_entry->length, s, + cmos_entry->config_id, cmos_entry->config_id, cmos_entry->name); + } + +/**************************************************************************** + * print_enum_record + * + * Display "enum" record from CMOS option table. + ****************************************************************************/ +static void print_enum_record (const struct cmos_enums *cmos_enum) + { printf(" ENUM record at physical address 0x%lx:\n" + " tag: 0x%x (decimal: %d)\n" + " size: 0x%x (decimal: %d)\n" + " config_id: 0x%x (decimal: %d)\n" + " value: 0x%x (decimal: %d)\n" + " text: %s\n", + vtophys(cmos_enum), cmos_enum->tag, cmos_enum->tag, cmos_enum->size, + cmos_enum->size, cmos_enum->config_id, cmos_enum->config_id, + cmos_enum->value, cmos_enum->value, cmos_enum->text); + } + +/**************************************************************************** + * print_defaults_record + * + * Display "defaults" record from CMOS option table. + ****************************************************************************/ +static void print_defaults_record (const struct cmos_defaults *cmos_defaults) + { printf(" DEFAULTS record at physical address 0x%lx:\n" + " tag: 0x%x (decimal: %d)\n" + " size: 0x%x (decimal: %d)\n" + " name_length: 0x%x (decimal: %d)\n" + " name: %s\n" + " default_set:\n", + vtophys(cmos_defaults), cmos_defaults->tag, cmos_defaults->tag, + cmos_defaults->size, cmos_defaults->size, + cmos_defaults->name_length, cmos_defaults->name_length, + cmos_defaults->name); + hexdump(cmos_defaults->default_set, CMOS_IMAGE_BUFFER_SIZE, + vtophys(cmos_defaults->default_set), stdout, &format); + } + +/**************************************************************************** + * print_unknown_record + * + * Display record of unknown type from CMOS option table. + ****************************************************************************/ +static void print_unknown_record (const struct lb_record *cmos_item) + { const char *data; + + printf(" UNKNOWN record at physical address 0x%lx:\n" + " tag: 0x%x (decimal: %d)\n" + " size: 0x%x (decimal: %d)\n" + " data:\n", + vtophys(cmos_item), cmos_item->tag, cmos_item->tag, + cmos_item->size, cmos_item->size); + data = ((const char *) cmos_item) + sizeof(*cmos_item); + hexdump(data, cmos_item->size - sizeof(*cmos_item), vtophys(data), stdout, + &format); + } + +/**************************************************************************** + * option_checksum_print_fn + * + * Display function for 'option_checksum' item of LinuxBIOS table. + ****************************************************************************/ +static void option_checksum_print_fn (const struct lb_record *rec) + { struct cmos_checksum *p; + + p = (struct cmos_checksum *) rec; + printf("CMOS checksum from bit %d to bit %d\n" + "at position %d is type %s.\n", + p->range_start, p->range_end, p->location, + (p->type == CHECKSUM_PCBIOS) ? "PC BIOS" : "NONE"); + } + +/**************************************************************************** + * string_print_fn + * + * Display function for a generic item of LinuxBIOS table that simply + * consists of a string. + ****************************************************************************/ +static void string_print_fn (const struct lb_record *rec) + { const struct lb_string *p; + + p = (const struct lb_string *) rec; + printf("%s\n", p->string); + } + +/**************************************************************************** + * uint64_to_hex_string + * + * Convert the 64-bit integer 'n' to its hexadecimal string representation, + * storing the result in 's'. 's' must point to a buffer at least 19 bytes + * long. The result is displayed with as many leading zeros as needed to + * make a 16-digit hex number including a 0x prefix (example: the number 1 + * will be displayed as "0x0000000000000001"). + ****************************************************************************/ +static void uint64_to_hex_string (char str[], uint64_t n) + { int chars_printed; + + str[0] = '0'; + str[1] = 'x'; + + /* Print the result right-justified with leading spaces in a + * 16-character field. */ + chars_printed = sprintf(&str[2], "%016llx", (unsigned long long) n); + assert(chars_printed == 16); + } |