xref: /aosp_15_r20/external/coreboot/util/nvramtool/cmos_ops.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include "common.h"
4 #include "cmos_ops.h"
5 #include "cmos_lowlevel.h"
6 
7 static int prepare_cmos_op_common(const cmos_entry_t * e);
8 
9 /****************************************************************************
10  * prepare_cmos_op_common
11  *
12  * Perform a few checks common to both reads and writes.
13  ****************************************************************************/
prepare_cmos_op_common(const cmos_entry_t * e)14 static int prepare_cmos_op_common(const cmos_entry_t * e)
15 {
16 	int result;
17 
18 	if (e->config == CMOS_ENTRY_RESERVED)
19 		/* Access to reserved parameters is not permitted. */
20 		return CMOS_OP_RESERVED;
21 
22 	if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
23 		return result;
24 
25 	assert(e->length > 0);
26 	return OK;
27 }
28 
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  ****************************************************************************/
prepare_cmos_read(const cmos_entry_t * e)36 int prepare_cmos_read(const cmos_entry_t * e)
37 {
38 	int result;
39 
40 	if ((result = prepare_cmos_op_common(e)) != OK)
41 		return result;
42 
43 	switch (e->config) {
44 	case CMOS_ENTRY_ENUM:
45 	case CMOS_ENTRY_HEX:
46 	case CMOS_ENTRY_STRING:
47 		break;
48 
49 	default:
50 		BUG();
51 	}
52 
53 	return OK;
54 }
55 
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  ****************************************************************************/
prepare_cmos_write(const cmos_entry_t * e,const char value_str[],unsigned long long * value)64 int 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;
70 	char *memory = NULL;
71 	int negative, result, found_one;
72 
73 	if ((result = prepare_cmos_op_common(e)) != OK)
74 		return result;
75 
76 	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;
82 
83 			if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
84 				break;
85 		}
86 
87 		if (!found_one)
88 			return CMOS_OP_NO_MATCHING_ENUM;
89 
90 		if (q == NULL)
91 			return CMOS_OP_BAD_ENUM_VALUE;
92 
93 		out = q->value;
94 		break;
95 
96 	case CMOS_ENTRY_HEX:
97 		/* See if the first character of 'value_str' (excluding
98 		 * any initial whitespace) is a minus sign.
99 		 */
100 		for (p = value_str; isspace((int)(unsigned char)*p); p++) ;
101 		negative = (*p == '-');
102 
103 		out = strtoull(value_str, (char **)&p, 0);
104 
105 		if (*p)
106 			return CMOS_OP_INVALID_INT;
107 
108 		/* 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;
114 
115 		break;
116 
117 	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;
125 
126 	default:
127 		BUG();
128 	}
129 
130 	if ((e->length < (8 * sizeof(*value))) && (out >= (1ull << e->length))) {
131 		if (memory) free(memory);
132 		return CMOS_OP_VALUE_TOO_WIDE;
133 	}
134 
135 	*value = out;
136 	return OK;
137 }
138 
139 /****************************************************************************
140  * cmos_checksum_read
141  *
142  * Read the checksum for the coreboot parameters stored in CMOS and return
143  * this value.
144  ****************************************************************************/
cmos_checksum_read(void)145 uint16_t cmos_checksum_read(void)
146 {
147 	uint16_t lo, hi;
148 
149 	/* 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 }
154 
155 /****************************************************************************
156  * cmos_checksum_write
157  *
158  * Set the checksum for the coreboot parameters stored in CMOS to
159  * 'checksum'.
160  ****************************************************************************/
cmos_checksum_write(uint16_t checksum)161 void cmos_checksum_write(uint16_t checksum)
162 {
163 	unsigned char lo, hi;
164 
165 	/* 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 }
171 
172 /****************************************************************************
173  * cmos_checksum_compute
174  *
175  * Compute a checksum for the coreboot parameter values currently stored in
176  * CMOS and return this checksum.
177  ****************************************************************************/
cmos_checksum_compute(void)178 uint16_t cmos_checksum_compute(void)
179 {
180 	unsigned i, sum;
181 
182 	sum = 0;
183 
184 	for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
185 		sum += cmos_read_byte(i);
186 
187 	return (uint16_t)(sum & 0xffff);
188 }
189 
190 /****************************************************************************
191  * cmos_checksum_verify
192  *
193  * Verify that the coreboot CMOS checksum is valid.  If checksum is not
194  * valid then print warning message and exit.
195  ****************************************************************************/
cmos_checksum_verify(void)196 void cmos_checksum_verify(void)
197 {
198 	uint16_t computed, actual;
199 
200 	set_iopl(3);
201 	computed = cmos_checksum_compute();
202 	actual = cmos_checksum_read();
203 	set_iopl(0);
204 
205 	if (computed != actual) {
206 		fprintf(stderr, "%s: Warning: coreboot CMOS checksum is bad.\n",
207 			prog_name);
208 		fprintf(stderr, "Computed checksum: 0x%x. Stored checksum: 0x%x\n",
209 			computed, actual);
210 		exit(1);
211 	}
212 }
213