2bbc8a1df6fe19014168a74a84ebf027f2e2a9a1
[coreboot.git] / util / nvramtool / cmos_ops.c
1 /*****************************************************************************\
2  * cmos_ops.c
3  *****************************************************************************
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  *
10  *  This file is part of nvramtool, a utility for reading/writing coreboot
11  *  parameters and displaying information from the coreboot table.
12  *  For details, see http://coreboot.org/nvramtool.
13  *
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.,
28  *  51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 \*****************************************************************************/
30
31 #include "common.h"
32 #include "cmos_ops.h"
33 #include "cmos_lowlevel.h"
34
35 static int prepare_cmos_op_common(const cmos_entry_t * e);
36
37 /****************************************************************************
38  * prepare_cmos_op_common
39  *
40  * Perform a few checks common to both reads and writes.
41  ****************************************************************************/
42 static int prepare_cmos_op_common(const cmos_entry_t * e)
43 {
44         int result;
45
46         if (e->config == CMOS_ENTRY_RESERVED)
47                 /* Access to reserved parameters is not permitted. */
48                 return CMOS_OP_RESERVED;
49
50         if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
51                 return result;
52
53         assert(e->length > 0);
54         return OK;
55 }
56
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  ****************************************************************************/
64 int prepare_cmos_read(const cmos_entry_t * e)
65 {
66         int result;
67
68         if ((result = prepare_cmos_op_common(e)) != OK)
69                 return result;
70
71         switch (e->config) {
72         case CMOS_ENTRY_ENUM:
73         case CMOS_ENTRY_HEX:
74         case CMOS_ENTRY_STRING:
75                 break;
76
77         default:
78                 BUG();
79         }
80
81         return OK;
82 }
83
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  ****************************************************************************/
92 int 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;
100
101         if ((result = prepare_cmos_op_common(e)) != OK)
102                 return result;
103
104         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;
110
111                         if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
112                                 break;
113                 }
114
115                 if (!found_one)
116                         return CMOS_OP_NO_MATCHING_ENUM;
117
118                 if (q == NULL)
119                         return CMOS_OP_BAD_ENUM_VALUE;
120
121                 out = q->value;
122                 break;
123
124         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((int)(unsigned char)*p); p++) ;
129                 negative = (*p == '-');
130
131                 out = strtoull(value_str, (char **)&p, 0);
132
133                 if (*p)
134                         return CMOS_OP_INVALID_INT;
135
136                 /* 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;
142
143                 break;
144
145         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;
153
154         default:
155                 BUG();
156         }
157
158         if ((e->length < (8 * sizeof(*value))) && (out >= (1ull << e->length)))
159                 return CMOS_OP_VALUE_TOO_WIDE;
160
161         *value = out;
162         return OK;
163 }
164
165 /****************************************************************************
166  * cmos_checksum_read
167  *
168  * Read the checksum for the coreboot parameters stored in CMOS and return
169  * this value.
170  ****************************************************************************/
171 uint16_t cmos_checksum_read(void)
172 {
173         uint16_t lo, hi;
174
175         /* 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 }
180
181 /****************************************************************************
182  * cmos_checksum_write
183  *
184  * Set the checksum for the coreboot parameters stored in CMOS to
185  * 'checksum'.
186  ****************************************************************************/
187 void cmos_checksum_write(uint16_t checksum)
188 {
189         unsigned char lo, hi;
190
191         /* 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 }
197
198 /****************************************************************************
199  * cmos_checksum_compute
200  *
201  * Compute a checksum for the coreboot parameter values currently stored in
202  * CMOS and return this checksum.
203  ****************************************************************************/
204 uint16_t cmos_checksum_compute(void)
205 {
206         unsigned i, sum;
207
208         sum = 0;
209
210         for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
211                 sum += cmos_read_byte(i);
212
213         return ~((uint16_t) (sum & 0xffff));
214 }
215
216 /****************************************************************************
217  * cmos_checksum_verify
218  *
219  * Verify that the coreboot CMOS checksum is valid.  If checksum is not
220  * valid then print warning message and exit.
221  ****************************************************************************/
222 void cmos_checksum_verify(void)
223 {
224         uint16_t computed, actual;
225
226         set_iopl(3);
227         computed = cmos_checksum_compute();
228         actual = cmos_checksum_read();
229         set_iopl(0);
230
231         if (computed != actual) {
232                 fprintf(stderr, "%s: Warning: Coreboot CMOS checksum is bad.\n",
233                         prog_name);
234                 exit(1);
235         }
236 }