blob: 02863090d9e7f55917a98d497e956c1c7fac7b6f [file] [log] [blame]
Patrick Georgi55189c92020-05-10 20:09:31 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Stefan Reinauer6540ae52007-07-12 16:35:42 +00002
3#include "common.h"
4#include "cmos_ops.h"
5#include "cmos_lowlevel.h"
6
Stefan Reinauer90b96b62010-01-13 21:00:23 +00007static int prepare_cmos_op_common(const cmos_entry_t * e);
Stefan Reinauer6540ae52007-07-12 16:35:42 +00008
9/****************************************************************************
10 * prepare_cmos_op_common
11 *
12 * Perform a few checks common to both reads and writes.
13 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +000014static int prepare_cmos_op_common(const cmos_entry_t * e)
15{
16 int result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000017
Stefan Reinauer90b96b62010-01-13 21:00:23 +000018 if (e->config == CMOS_ENTRY_RESERVED)
19 /* Access to reserved parameters is not permitted. */
20 return CMOS_OP_RESERVED;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000021
Stefan Reinauer90b96b62010-01-13 21:00:23 +000022 if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
23 return result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000024
Stefan Reinauer90b96b62010-01-13 21:00:23 +000025 assert(e->length > 0);
26 return OK;
27}
Stefan Reinauer6540ae52007-07-12 16:35:42 +000028
29/****************************************************************************
30 * prepare_cmos_read
31 *
32 * The caller wishes to read a CMOS parameter represented by 'e'. Perform
33 * sanity checking on 'e'. If a problem was found with e, return an error
34 * code. Else return OK.
35 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +000036int prepare_cmos_read(const cmos_entry_t * e)
37{
38 int result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000039
Stefan Reinauer90b96b62010-01-13 21:00:23 +000040 if ((result = prepare_cmos_op_common(e)) != OK)
41 return result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000042
Stefan Reinauer90b96b62010-01-13 21:00:23 +000043 switch (e->config) {
44 case CMOS_ENTRY_ENUM:
45 case CMOS_ENTRY_HEX:
46 case CMOS_ENTRY_STRING:
47 break;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000048
Stefan Reinauer90b96b62010-01-13 21:00:23 +000049 default:
50 BUG();
51 }
Stefan Reinauer6540ae52007-07-12 16:35:42 +000052
Stefan Reinauer90b96b62010-01-13 21:00:23 +000053 return OK;
54}
Stefan Reinauer6540ae52007-07-12 16:35:42 +000055
56/****************************************************************************
57 * prepare_cmos_write
58 *
59 * The caller wishes to set a CMOS parameter represented by 'e' to a value
60 * whose string representation is stored in 'value_str'. Perform sanity
61 * checking on 'value_str'. On error, return an error code. Else store the
62 * numeric equivalent of 'value_str' in '*value' and return OK.
63 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +000064int prepare_cmos_write(const cmos_entry_t * e, const char value_str[],
65 unsigned long long *value)
66{
67 const cmos_enum_t *q;
68 unsigned long long out;
69 const char *p;
Patrick Georgidd1aab92014-08-09 17:06:20 +020070 char *memory = NULL;
Stefan Reinauer90b96b62010-01-13 21:00:23 +000071 int negative, result, found_one;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000072
Stefan Reinauer90b96b62010-01-13 21:00:23 +000073 if ((result = prepare_cmos_op_common(e)) != OK)
74 return result;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000075
Stefan Reinauer90b96b62010-01-13 21:00:23 +000076 switch (e->config) {
77 case CMOS_ENTRY_ENUM:
78 /* Make sure the user's input corresponds to a valid option. */
79 for (q = first_cmos_enum_id(e->config_id), found_one = 0;
80 q != NULL; q = next_cmos_enum_id(q)) {
81 found_one = 1;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000082
Stefan Reinauer90b96b62010-01-13 21:00:23 +000083 if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
84 break;
85 }
Stefan Reinauer6540ae52007-07-12 16:35:42 +000086
Stefan Reinauer90b96b62010-01-13 21:00:23 +000087 if (!found_one)
88 return CMOS_OP_NO_MATCHING_ENUM;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000089
Stefan Reinauer90b96b62010-01-13 21:00:23 +000090 if (q == NULL)
91 return CMOS_OP_BAD_ENUM_VALUE;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000092
Stefan Reinauer90b96b62010-01-13 21:00:23 +000093 out = q->value;
94 break;
Stefan Reinauer6540ae52007-07-12 16:35:42 +000095
Stefan Reinauer90b96b62010-01-13 21:00:23 +000096 case CMOS_ENTRY_HEX:
Stefan Reinauer14e22772010-04-27 06:56:47 +000097 /* See if the first character of 'value_str' (excluding
Stefan Reinauer90b96b62010-01-13 21:00:23 +000098 * any initial whitespace) is a minus sign.
99 */
Jonathan Kollasch1571dc92011-04-19 19:34:25 +0000100 for (p = value_str; isspace((int)(unsigned char)*p); p++) ;
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000101 negative = (*p == '-');
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000102
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000103 out = strtoull(value_str, (char **)&p, 0);
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000104
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000105 if (*p)
106 return CMOS_OP_INVALID_INT;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000107
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000108 /* If we get this far, the user specified a valid integer.
109 * However we do not currently support the use of negative
110 * numbers as CMOS parameter values.
111 */
112 if (negative)
113 return CMOS_OP_NEGATIVE_INT;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000114
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000115 break;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000116
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000117 case CMOS_ENTRY_STRING:
118 if (e->length < (8 * strlen(value_str)))
119 return CMOS_OP_VALUE_TOO_WIDE;
120 memory = malloc(e->length / 8);
121 memset(memory, 0, e->length / 8);
122 strcpy(memory, value_str);
123 out = (unsigned long)memory;
124 break;
Stefan Reinauera67aab72008-09-27 10:08:28 +0000125
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000126 default:
127 BUG();
128 }
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000129
Patrick Georgidd1aab92014-08-09 17:06:20 +0200130 if ((e->length < (8 * sizeof(*value))) && (out >= (1ull << e->length))) {
131 if (memory) free(memory);
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000132 return CMOS_OP_VALUE_TOO_WIDE;
Patrick Georgidd1aab92014-08-09 17:06:20 +0200133 }
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000134
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000135 *value = out;
136 return OK;
137}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000138
139/****************************************************************************
140 * cmos_checksum_read
141 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000142 * Read the checksum for the coreboot parameters stored in CMOS and return
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000143 * this value.
144 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000145uint16_t cmos_checksum_read(void)
146{
147 uint16_t lo, hi;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000148
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000149 /* The checksum is stored in a big-endian format. */
150 hi = cmos_read_byte(cmos_checksum_index);
151 lo = cmos_read_byte(cmos_checksum_index + 1);
152 return (hi << 8) + lo;
153}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000154
155/****************************************************************************
156 * cmos_checksum_write
157 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000158 * Set the checksum for the coreboot parameters stored in CMOS to
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000159 * 'checksum'.
160 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000161void cmos_checksum_write(uint16_t checksum)
162{
163 unsigned char lo, hi;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000164
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000165 /* The checksum is stored in a big-endian format. */
166 hi = (unsigned char)(checksum >> 8);
167 lo = (unsigned char)(checksum & 0x00ff);
168 cmos_write_byte(cmos_checksum_index, hi);
169 cmos_write_byte(cmos_checksum_index + 1, lo);
170}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000171
172/****************************************************************************
173 * cmos_checksum_compute
174 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000175 * Compute a checksum for the coreboot parameter values currently stored in
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000176 * CMOS and return this checksum.
177 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000178uint16_t cmos_checksum_compute(void)
179{
180 unsigned i, sum;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000181
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000182 sum = 0;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000183
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000184 for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
185 sum += cmos_read_byte(i);
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000186
Stefan Reinauerc31c4de2011-10-17 08:58:27 -0700187 return (uint16_t)(sum & 0xffff);
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000188}
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000189
190/****************************************************************************
191 * cmos_checksum_verify
192 *
Stefan Reinauerf527e702008-01-18 15:33:49 +0000193 * Verify that the coreboot CMOS checksum is valid. If checksum is not
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000194 * valid then print warning message and exit.
195 ****************************************************************************/
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000196void cmos_checksum_verify(void)
197{
198 uint16_t computed, actual;
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000199
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000200 set_iopl(3);
201 computed = cmos_checksum_compute();
202 actual = cmos_checksum_read();
203 set_iopl(0);
Stefan Reinauer6540ae52007-07-12 16:35:42 +0000204
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000205 if (computed != actual) {
Martin Rotha5648112017-06-03 20:05:42 -0600206 fprintf(stderr, "%s: Warning: coreboot CMOS checksum is bad.\n",
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000207 prog_name);
Denis 'GNUtoo' Carikli9c44b252016-02-20 18:49:13 +0100208 fprintf(stderr, "Computed checksum: 0x%x. Stored checksum: 0x%x\n",
209 computed, actual);
Stefan Reinauer90b96b62010-01-13 21:00:23 +0000210 exit(1);
211 }
212}