Add string support to nvramtool.
[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  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 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  { int result;
44
45    if (e->config == CMOS_ENTRY_RESERVED)
46       /* Access to reserved parameters is not permitted. */
47       return CMOS_OP_RESERVED;
48
49    if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
50       return result;
51
52    assert(e->length > 0);
53    return OK;
54  }
55
56 /****************************************************************************
57  * prepare_cmos_read
58  *
59  * The caller wishes to read a CMOS parameter represented by 'e'.  Perform
60  * sanity checking on 'e'.  If a problem was found with e, return an error
61  * code.  Else return OK.
62  ****************************************************************************/
63 int prepare_cmos_read (const cmos_entry_t *e)
64  { int result;
65
66    if ((result = prepare_cmos_op_common(e)) != OK)
67       return result;
68
69    switch (e->config)
70     { case CMOS_ENTRY_ENUM:
71       case CMOS_ENTRY_HEX:
72       case CMOS_ENTRY_STRING:
73          break;
74
75       default:
76          BUG();
77     }
78
79    return OK;
80  }
81
82 /****************************************************************************
83  * prepare_cmos_write
84  *
85  * The caller wishes to set a CMOS parameter represented by 'e' to a value
86  * whose string representation is stored in 'value_str'.  Perform sanity
87  * checking on 'value_str'.  On error, return an error code.  Else store the
88  * numeric equivalent of 'value_str' in '*value' and return OK.
89  ****************************************************************************/
90 int prepare_cmos_write (const cmos_entry_t *e, const char value_str[],
91                         unsigned long long *value)
92  { const cmos_enum_t *q;
93    unsigned long long out;
94    const char *p;
95    char *memory;
96    int negative, result, found_one;
97
98    if ((result = prepare_cmos_op_common(e)) != OK)
99       return result;
100
101    switch (e->config)
102     { case CMOS_ENTRY_ENUM:
103          /* Make sure the user's input corresponds to a valid option. */
104          for (q = first_cmos_enum_id(e->config_id), found_one = 0;
105               q != NULL;
106               q = next_cmos_enum_id(q))
107           { found_one = 1;
108
109             if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
110                break;
111           }
112
113          if (!found_one)
114             return CMOS_OP_NO_MATCHING_ENUM;
115
116          if (q == NULL)
117             return CMOS_OP_BAD_ENUM_VALUE;
118
119          out = q->value;
120          break;
121
122       case CMOS_ENTRY_HEX:
123          /* See if the first character of 'value_str' (excluding any initial
124           * whitespace) is a minus sign.
125           */
126          for (p = value_str; isspace(*p); p++);
127          negative = (*p == '-');
128
129          out = strtoull(value_str, (char **) &p, 0);
130
131          if (*p)
132             return CMOS_OP_INVALID_INT;
133
134          /* If we get this far, the user specified a valid integer.  However
135           * we do not currently support the use of negative numbers as CMOS
136           * parameter values.
137           */
138          if (negative)
139             return CMOS_OP_NEGATIVE_INT;
140
141          break;
142
143       case CMOS_ENTRY_STRING:
144          if (e->length < (8 * strlen(value_str)))
145             return CMOS_OP_VALUE_TOO_WIDE;
146          memory = malloc(e->length / 8);
147          memset(memory, 0, e->length / 8);
148          strcpy(memory, value_str);
149          out = (unsigned long)memory;
150          break;
151
152       default:
153          BUG();
154     }
155
156    if ((e->length < (8 * sizeof(*value))) &&
157        (out >= (1ull << e->length)))
158       return CMOS_OP_VALUE_TOO_WIDE;
159
160    *value = out;
161    return OK;
162  }
163
164 /****************************************************************************
165  * cmos_checksum_read
166  *
167  * Read the checksum for the coreboot parameters stored in CMOS and return
168  * this value.
169  ****************************************************************************/
170 uint16_t cmos_checksum_read (void)
171  { uint16_t lo, hi;
172
173    /* The checksum is stored in a big-endian format. */
174    hi = cmos_read_byte(cmos_checksum_index);
175    lo = cmos_read_byte(cmos_checksum_index + 1);
176    return (hi << 8) + lo;
177  }
178
179 /****************************************************************************
180  * cmos_checksum_write
181  *
182  * Set the checksum for the coreboot parameters stored in CMOS to
183  * 'checksum'.
184  ****************************************************************************/
185 void cmos_checksum_write (uint16_t checksum)
186  { unsigned char lo, hi;
187
188    /* The checksum is stored in a big-endian format. */
189    hi = (unsigned char) (checksum >> 8);
190    lo = (unsigned char) (checksum & 0x00ff);
191    cmos_write_byte(cmos_checksum_index, hi);
192    cmos_write_byte(cmos_checksum_index + 1, lo);
193  }
194
195 /****************************************************************************
196  * cmos_checksum_compute
197  *
198  * Compute a checksum for the coreboot parameter values currently stored in
199  * CMOS and return this checksum.
200  ****************************************************************************/
201 uint16_t cmos_checksum_compute (void)
202  { unsigned i, sum;
203
204    sum = 0;
205
206    for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
207       sum += cmos_read_byte(i);
208
209    return ~((uint16_t) (sum & 0xffff));
210  }
211
212 /****************************************************************************
213  * cmos_checksum_verify
214  *
215  * Verify that the coreboot CMOS checksum is valid.  If checksum is not
216  * valid then print warning message and exit.
217  ****************************************************************************/
218 void cmos_checksum_verify (void)
219  { uint16_t computed, actual;
220
221    set_iopl(3);
222    computed = cmos_checksum_compute();
223    actual = cmos_checksum_read();
224    set_iopl(0);
225
226    if (computed != actual)
227     { fprintf(stderr, "%s: Warning: Coreboot CMOS checksum is bad.\n",
228               prog_name);
229       exit(1);
230     }
231  }