2007-07-12 18:35:42 +02:00
|
|
|
/*****************************************************************************\
|
|
|
|
* cmos_ops.c
|
|
|
|
*****************************************************************************
|
2008-03-01 20:06:32 +01:00
|
|
|
* This file is part of nvramtool, a utility for reading/writing coreboot
|
2008-01-18 16:33:49 +01:00
|
|
|
* parameters and displaying information from the coreboot table.
|
2017-06-05 12:33:23 +02:00
|
|
|
* For details, see https://coreboot.org/nvramtool.
|
2007-07-12 18:35:42 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "cmos_ops.h"
|
|
|
|
#include "cmos_lowlevel.h"
|
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
static int prepare_cmos_op_common(const cmos_entry_t * e);
|
2007-07-12 18:35:42 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* prepare_cmos_op_common
|
|
|
|
*
|
|
|
|
* Perform a few checks common to both reads and writes.
|
|
|
|
****************************************************************************/
|
2010-01-13 22:00:23 +01:00
|
|
|
static int prepare_cmos_op_common(const cmos_entry_t * e)
|
|
|
|
{
|
|
|
|
int result;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
if (e->config == CMOS_ENTRY_RESERVED)
|
|
|
|
/* Access to reserved parameters is not permitted. */
|
|
|
|
return CMOS_OP_RESERVED;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
|
|
|
|
return result;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
assert(e->length > 0);
|
|
|
|
return OK;
|
|
|
|
}
|
2007-07-12 18:35:42 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* prepare_cmos_read
|
|
|
|
*
|
|
|
|
* The caller wishes to read a CMOS parameter represented by 'e'. Perform
|
|
|
|
* sanity checking on 'e'. If a problem was found with e, return an error
|
|
|
|
* code. Else return OK.
|
|
|
|
****************************************************************************/
|
2010-01-13 22:00:23 +01:00
|
|
|
int prepare_cmos_read(const cmos_entry_t * e)
|
|
|
|
{
|
|
|
|
int result;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
if ((result = prepare_cmos_op_common(e)) != OK)
|
|
|
|
return result;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
switch (e->config) {
|
|
|
|
case CMOS_ENTRY_ENUM:
|
|
|
|
case CMOS_ENTRY_HEX:
|
|
|
|
case CMOS_ENTRY_STRING:
|
|
|
|
break;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
return OK;
|
|
|
|
}
|
2007-07-12 18:35:42 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* prepare_cmos_write
|
|
|
|
*
|
|
|
|
* The caller wishes to set a CMOS parameter represented by 'e' to a value
|
|
|
|
* whose string representation is stored in 'value_str'. Perform sanity
|
|
|
|
* checking on 'value_str'. On error, return an error code. Else store the
|
|
|
|
* numeric equivalent of 'value_str' in '*value' and return OK.
|
|
|
|
****************************************************************************/
|
2010-01-13 22:00:23 +01:00
|
|
|
int prepare_cmos_write(const cmos_entry_t * e, const char value_str[],
|
|
|
|
unsigned long long *value)
|
|
|
|
{
|
|
|
|
const cmos_enum_t *q;
|
|
|
|
unsigned long long out;
|
|
|
|
const char *p;
|
2014-08-09 17:06:20 +02:00
|
|
|
char *memory = NULL;
|
2010-01-13 22:00:23 +01:00
|
|
|
int negative, result, found_one;
|
|
|
|
|
|
|
|
if ((result = prepare_cmos_op_common(e)) != OK)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
switch (e->config) {
|
|
|
|
case CMOS_ENTRY_ENUM:
|
|
|
|
/* Make sure the user's input corresponds to a valid option. */
|
|
|
|
for (q = first_cmos_enum_id(e->config_id), found_one = 0;
|
|
|
|
q != NULL; q = next_cmos_enum_id(q)) {
|
|
|
|
found_one = 1;
|
|
|
|
|
|
|
|
if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found_one)
|
|
|
|
return CMOS_OP_NO_MATCHING_ENUM;
|
|
|
|
|
|
|
|
if (q == NULL)
|
|
|
|
return CMOS_OP_BAD_ENUM_VALUE;
|
|
|
|
|
|
|
|
out = q->value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CMOS_ENTRY_HEX:
|
2010-04-27 08:56:47 +02:00
|
|
|
/* See if the first character of 'value_str' (excluding
|
2010-01-13 22:00:23 +01:00
|
|
|
* any initial whitespace) is a minus sign.
|
|
|
|
*/
|
2011-04-19 21:34:25 +02:00
|
|
|
for (p = value_str; isspace((int)(unsigned char)*p); p++) ;
|
2010-01-13 22:00:23 +01:00
|
|
|
negative = (*p == '-');
|
|
|
|
|
|
|
|
out = strtoull(value_str, (char **)&p, 0);
|
|
|
|
|
|
|
|
if (*p)
|
|
|
|
return CMOS_OP_INVALID_INT;
|
|
|
|
|
|
|
|
/* If we get this far, the user specified a valid integer.
|
|
|
|
* However we do not currently support the use of negative
|
|
|
|
* numbers as CMOS parameter values.
|
|
|
|
*/
|
|
|
|
if (negative)
|
|
|
|
return CMOS_OP_NEGATIVE_INT;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CMOS_ENTRY_STRING:
|
|
|
|
if (e->length < (8 * strlen(value_str)))
|
|
|
|
return CMOS_OP_VALUE_TOO_WIDE;
|
|
|
|
memory = malloc(e->length / 8);
|
|
|
|
memset(memory, 0, e->length / 8);
|
|
|
|
strcpy(memory, value_str);
|
|
|
|
out = (unsigned long)memory;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
|
2014-08-09 17:06:20 +02:00
|
|
|
if ((e->length < (8 * sizeof(*value))) && (out >= (1ull << e->length))) {
|
|
|
|
if (memory) free(memory);
|
2010-01-13 22:00:23 +01:00
|
|
|
return CMOS_OP_VALUE_TOO_WIDE;
|
2014-08-09 17:06:20 +02:00
|
|
|
}
|
2010-01-13 22:00:23 +01:00
|
|
|
|
|
|
|
*value = out;
|
|
|
|
return OK;
|
|
|
|
}
|
2007-07-12 18:35:42 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* cmos_checksum_read
|
|
|
|
*
|
2008-01-18 16:33:49 +01:00
|
|
|
* Read the checksum for the coreboot parameters stored in CMOS and return
|
2007-07-12 18:35:42 +02:00
|
|
|
* this value.
|
|
|
|
****************************************************************************/
|
2010-01-13 22:00:23 +01:00
|
|
|
uint16_t cmos_checksum_read(void)
|
|
|
|
{
|
|
|
|
uint16_t lo, hi;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
/* The checksum is stored in a big-endian format. */
|
|
|
|
hi = cmos_read_byte(cmos_checksum_index);
|
|
|
|
lo = cmos_read_byte(cmos_checksum_index + 1);
|
|
|
|
return (hi << 8) + lo;
|
|
|
|
}
|
2007-07-12 18:35:42 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* cmos_checksum_write
|
|
|
|
*
|
2008-01-18 16:33:49 +01:00
|
|
|
* Set the checksum for the coreboot parameters stored in CMOS to
|
2007-07-12 18:35:42 +02:00
|
|
|
* 'checksum'.
|
|
|
|
****************************************************************************/
|
2010-01-13 22:00:23 +01:00
|
|
|
void cmos_checksum_write(uint16_t checksum)
|
|
|
|
{
|
|
|
|
unsigned char lo, hi;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
/* The checksum is stored in a big-endian format. */
|
|
|
|
hi = (unsigned char)(checksum >> 8);
|
|
|
|
lo = (unsigned char)(checksum & 0x00ff);
|
|
|
|
cmos_write_byte(cmos_checksum_index, hi);
|
|
|
|
cmos_write_byte(cmos_checksum_index + 1, lo);
|
|
|
|
}
|
2007-07-12 18:35:42 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* cmos_checksum_compute
|
|
|
|
*
|
2008-01-18 16:33:49 +01:00
|
|
|
* Compute a checksum for the coreboot parameter values currently stored in
|
2007-07-12 18:35:42 +02:00
|
|
|
* CMOS and return this checksum.
|
|
|
|
****************************************************************************/
|
2010-01-13 22:00:23 +01:00
|
|
|
uint16_t cmos_checksum_compute(void)
|
|
|
|
{
|
|
|
|
unsigned i, sum;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
sum = 0;
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2010-01-13 22:00:23 +01:00
|
|
|
for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
|
|
|
|
sum += cmos_read_byte(i);
|
2007-07-12 18:35:42 +02:00
|
|
|
|
2011-10-17 17:58:27 +02:00
|
|
|
return (uint16_t)(sum & 0xffff);
|
2010-01-13 22:00:23 +01:00
|
|
|
}
|
2007-07-12 18:35:42 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* cmos_checksum_verify
|
|
|
|
*
|
2008-01-18 16:33:49 +01:00
|
|
|
* Verify that the coreboot CMOS checksum is valid. If checksum is not
|
2007-07-12 18:35:42 +02:00
|
|
|
* valid then print warning message and exit.
|
|
|
|
****************************************************************************/
|
2010-01-13 22:00:23 +01:00
|
|
|
void cmos_checksum_verify(void)
|
|
|
|
{
|
|
|
|
uint16_t computed, actual;
|
|
|
|
|
|
|
|
set_iopl(3);
|
|
|
|
computed = cmos_checksum_compute();
|
|
|
|
actual = cmos_checksum_read();
|
|
|
|
set_iopl(0);
|
|
|
|
|
|
|
|
if (computed != actual) {
|
2017-06-04 04:05:42 +02:00
|
|
|
fprintf(stderr, "%s: Warning: coreboot CMOS checksum is bad.\n",
|
2010-01-13 22:00:23 +01:00
|
|
|
prog_name);
|
2016-02-20 18:49:13 +01:00
|
|
|
fprintf(stderr, "Computed checksum: 0x%x. Stored checksum: 0x%x\n",
|
|
|
|
computed, actual);
|
2010-01-13 22:00:23 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|