blob: 73d68e0a23bc7a05ec274eaa40ea457821d1095d [file] [log] [blame]
Stefan Reinauer6540ae52007-07-12 16:35:42 +00001/*****************************************************************************\
2 * cmos_ops.c
Stefan Reinauer6540ae52007-07-12 16:35:42 +00003 *****************************************************************************
4 * Copyright (C) 2002-2005 The Regents of the University of California.
5 * Produced at the Lawrence Livermore National Laboratory.
6 * Written by David S. Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>.
7 * UCRL-CODE-2003-012
8 * All rights reserved.
9 *
Uwe Hermann6e565942008-03-01 19:06:32 +000010 * This file is part of nvramtool, a utility for reading/writing coreboot
Stefan Reinauerf527e702008-01-18 15:33:49 +000011 * parameters and displaying information from the coreboot table.
Uwe Hermann6e565942008-03-01 19:06:32 +000012 * For details, see http://coreboot.org/nvramtool.
Stefan Reinauer6540ae52007-07-12 16:35:42 +000013 *
14 * Please also read the file DISCLAIMER which is included in this software
15 * distribution.
16 *
17 * This program is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License (as published by the
19 * Free Software Foundation) version 2, dated June 1991.
20 *
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
24 * conditions of the GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
Stefan Reinauerac7a2d22009-09-23 21:53:25 +000028 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Stefan Reinauer6540ae52007-07-12 16:35:42 +000029\*****************************************************************************/
30
31#include "common.h"
32#include "cmos_ops.h"
33#include "cmos_lowlevel.h"
34
Stefan Reinauer90b96b62010-01-13 21:00:23 +000035static int prepare_cmos_op_common(const cmos_entry_t * e);
Stefan Reinauer6540ae52007-07-12 16:35:42 +000036
37/****************************************************************************
38 * prepare_cmos_op_common
39 *
40 * Perform a few checks common to both reads and writes.
41 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +000042static int prepare_cmos_op_common(const cmos_entry_t * e)
43{
44 int result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000045
Stefan Reinauer90b96b62010-01-13 21:00:23 +000046 if (e->config == CMOS_ENTRY_RESERVED)
47 /* Access to reserved parameters is not permitted. */
48 return CMOS_OP_RESERVED;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000049
Stefan Reinauer90b96b62010-01-13 21:00:23 +000050 if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
51 return result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000052
Stefan Reinauer90b96b62010-01-13 21:00:23 +000053 assert(e->length > 0);
54 return OK;
55}
Stefan Reinauer6540ae52007-07-12 16:35:42 +000056
57/****************************************************************************
58 * prepare_cmos_read
59 *
60 * The caller wishes to read a CMOS parameter represented by 'e'. Perform
61 * sanity checking on 'e'. If a problem was found with e, return an error
62 * code. Else return OK.
63 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +000064int prepare_cmos_read(const cmos_entry_t * e)
65{
66 int result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000067
Stefan Reinauer90b96b62010-01-13 21:00:23 +000068 if ((result = prepare_cmos_op_common(e)) != OK)
69 return result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000070
Stefan Reinauer90b96b62010-01-13 21:00:23 +000071 switch (e->config) {
72 case CMOS_ENTRY_ENUM:
73 case CMOS_ENTRY_HEX:
74 case CMOS_ENTRY_STRING:
75 break;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000076
Stefan Reinauer90b96b62010-01-13 21:00:23 +000077 default:
78 BUG();
79 }
Stefan Reinauer6540ae52007-07-12 16:35:42 +000080
Stefan Reinauer90b96b62010-01-13 21:00:23 +000081 return OK;
82}
Stefan Reinauer6540ae52007-07-12 16:35:42 +000083
84/****************************************************************************
85 * prepare_cmos_write
86 *
87 * The caller wishes to set a CMOS parameter represented by 'e' to a value
88 * whose string representation is stored in 'value_str'. Perform sanity
89 * checking on 'value_str'. On error, return an error code. Else store the
90 * numeric equivalent of 'value_str' in '*value' and return OK.
91 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +000092int prepare_cmos_write(const cmos_entry_t * e, const char value_str[],
93 unsigned long long *value)
94{
95 const cmos_enum_t *q;
96 unsigned long long out;
97 const char *p;
98 char *memory;
99 int negative, result, found_one;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000100
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000101 if ((result = prepare_cmos_op_common(e)) != OK)
102 return result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000103
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000104 switch (e->config) {
105 case CMOS_ENTRY_ENUM:
106 /* Make sure the user's input corresponds to a valid option. */
107 for (q = first_cmos_enum_id(e->config_id), found_one = 0;
108 q != NULL; q = next_cmos_enum_id(q)) {
109 found_one = 1;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000110
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000111 if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
112 break;
113 }
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000114
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000115 if (!found_one)
116 return CMOS_OP_NO_MATCHING_ENUM;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000117
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000118 if (q == NULL)
119 return CMOS_OP_BAD_ENUM_VALUE;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000120
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000121 out = q->value;
122 break;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000123
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000124 case CMOS_ENTRY_HEX:
125 /* See if the first character of 'value_str' (excluding
126 * any initial whitespace) is a minus sign.
127 */
128 for (p = value_str; isspace(*p); p++) ;
129 negative = (*p == '-');
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000130
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000131 out = strtoull(value_str, (char **)&p, 0);
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000132
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000133 if (*p)
134 return CMOS_OP_INVALID_INT;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000135
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000136 /* If we get this far, the user specified a valid integer.
137 * However we do not currently support the use of negative
138 * numbers as CMOS parameter values.
139 */
140 if (negative)
141 return CMOS_OP_NEGATIVE_INT;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000142
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000143 break;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000144
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000145 case CMOS_ENTRY_STRING:
146 if (e->length < (8 * strlen(value_str)))
147 return CMOS_OP_VALUE_TOO_WIDE;
148 memory = malloc(e->length / 8);
149 memset(memory, 0, e->length / 8);
150 strcpy(memory, value_str);
151 out = (unsigned long)memory;
152 break;
Stefan Reinauera67aab72008-09-27 10:08:28 +0000153
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000154 default:
155 BUG();
156 }
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000157
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000158 if ((e->length < (8 * sizeof(*value))) && (out >= (1ull << e->length)))
159 return CMOS_OP_VALUE_TOO_WIDE;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000160
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000161 *value = out;
162 return OK;
163}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000164
165/****************************************************************************
166 * cmos_checksum_read
167 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000168 * Read the checksum for the coreboot parameters stored in CMOS and return
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000169 * this value.
170 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000171uint16_t cmos_checksum_read(void)
172{
173 uint16_t lo, hi;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000174
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000175 /* The checksum is stored in a big-endian format. */
176 hi = cmos_read_byte(cmos_checksum_index);
177 lo = cmos_read_byte(cmos_checksum_index + 1);
178 return (hi << 8) + lo;
179}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000180
181/****************************************************************************
182 * cmos_checksum_write
183 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000184 * Set the checksum for the coreboot parameters stored in CMOS to
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000185 * 'checksum'.
186 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000187void cmos_checksum_write(uint16_t checksum)
188{
189 unsigned char lo, hi;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000190
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000191 /* The checksum is stored in a big-endian format. */
192 hi = (unsigned char)(checksum >> 8);
193 lo = (unsigned char)(checksum & 0x00ff);
194 cmos_write_byte(cmos_checksum_index, hi);
195 cmos_write_byte(cmos_checksum_index + 1, lo);
196}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000197
198/****************************************************************************
199 * cmos_checksum_compute
200 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000201 * Compute a checksum for the coreboot parameter values currently stored in
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000202 * CMOS and return this checksum.
203 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000204uint16_t cmos_checksum_compute(void)
205{
206 unsigned i, sum;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000207
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000208 sum = 0;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000209
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000210 for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
211 sum += cmos_read_byte(i);
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000212
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000213 return ~((uint16_t) (sum & 0xffff));
214}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000215
216/****************************************************************************
217 * cmos_checksum_verify
218 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000219 * Verify that the coreboot CMOS checksum is valid. If checksum is not
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000220 * valid then print warning message and exit.
221 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000222void cmos_checksum_verify(void)
223{
224 uint16_t computed, actual;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000225
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000226 set_iopl(3);
227 computed = cmos_checksum_compute();
228 actual = cmos_checksum_read();
229 set_iopl(0);
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000230
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000231 if (computed != actual) {
232 fprintf(stderr, "%s: Warning: Coreboot CMOS checksum is bad.\n",
233 prog_name);
234 exit(1);
235 }
236}