xref: /aosp_15_r20/external/coreboot/payloads/libpayload/drivers/options.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /*
2  *
3  * Copyright (C) 2008 coresystems GmbH
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #define __STDC_FORMAT_MACROS
30 
31 #include <libpayload.h>
32 #include <coreboot_tables.h>
33 #include <inttypes.h>
34 
35 u8 *mem_accessor_base;
36 
mem_read(u8 reg)37 static u8 mem_read(u8 reg)
38 {
39 	return mem_accessor_base[reg];
40 }
41 
mem_write(u8 val,u8 reg)42 static void mem_write(u8 val, u8 reg)
43 {
44 	mem_accessor_base[reg] = val;
45 }
46 
47 struct nvram_accessor *use_nvram = &(struct nvram_accessor) {
48 	nvram_read,
49 	nvram_write
50 };
51 
52 struct nvram_accessor *use_mem = &(struct nvram_accessor) {
53 	mem_read,
54 	mem_write
55 };
56 
get_system_option_table(void)57 struct cb_cmos_option_table *get_system_option_table(void)
58 {
59 	return phys_to_virt(lib_sysinfo.cmos_option_table);
60 }
61 
options_checksum_valid(const struct nvram_accessor * nvram)62 int options_checksum_valid(const struct nvram_accessor *nvram)
63 {
64 	int i;
65 	int range_start = lib_sysinfo.cmos_range_start / 8;
66 	int range_end = lib_sysinfo.cmos_range_end / 8;
67 	int checksum_location = lib_sysinfo.cmos_checksum_location / 8;
68 	u16 checksum = 0, checksum_old;
69 
70 	for(i = range_start; i <= range_end; i++) {
71 		checksum += nvram->read(i);
72 	}
73 
74 	checksum_old = ((nvram->read(checksum_location)<<8) | nvram->read(checksum_location+1));
75 
76 	return (checksum_old == checksum);
77 }
78 
fix_options_checksum_with(const struct nvram_accessor * nvram)79 void fix_options_checksum_with(const struct nvram_accessor *nvram)
80 {
81 	int i;
82 	int range_start = lib_sysinfo.cmos_range_start / 8;
83 	int range_end = lib_sysinfo.cmos_range_end / 8;
84 	int checksum_location = lib_sysinfo.cmos_checksum_location / 8;
85 	u16 checksum = 0;
86 
87 	for(i = range_start; i <= range_end; i++) {
88 		checksum += nvram->read(i);
89 	}
90 
91 	nvram->write((checksum >> 8), checksum_location);
92 	nvram->write((checksum & 0xff), checksum_location + 1);
93 }
94 
fix_options_checksum(void)95 void fix_options_checksum(void)
96 {
97 	fix_options_checksum_with(use_nvram);
98 }
99 
get_cmos_value(const struct nvram_accessor * nvram,u32 bitnum,u32 len,void * valptr)100 static int get_cmos_value(const struct nvram_accessor *nvram, u32 bitnum, u32 len, void *valptr)
101 {
102 	u8 *value = valptr;
103 	int offs = 0;
104 	u32 addr, bit;
105 	u8 reg8;
106 
107 	/* Convert to byte borders */
108 	addr=(bitnum / 8);
109 	bit=(bitnum % 8);
110 
111 	/* Handle single byte or less */
112 	if(len <= 8) {
113 		reg8 = nvram->read(addr);
114 		reg8 >>= bit;
115 		value[0] = reg8 & ((1 << len) -1);
116 		return 0;
117 	}
118 
119 	/* When handling more than a byte, copy whole bytes */
120 	while (len > 0) {
121 		len -= 8;
122 		value[offs++]=nvram->read(addr++);
123 	}
124 
125 	return 0;
126 }
127 
set_cmos_value(const struct nvram_accessor * nvram,u32 bitnum,u32 len,const void * valptr)128 static int set_cmos_value(const struct nvram_accessor *nvram, u32 bitnum, u32 len, const void *valptr)
129 {
130 	const u8 *value = valptr;
131 	int offs = 0;
132 	u32 addr, bit;
133 	u8 reg8;
134 
135 	/* Convert to byte borders */
136 	addr=(bitnum / 8);
137 	bit=(bitnum % 8);
138 
139 	/* Handle single byte or less */
140 	if (len <= 8) {
141 		reg8 = nvram->read(addr);
142 		reg8 &= ~(((1 << len) - 1) << bit);
143 		reg8 |= (value[0] & ((1 << len) - 1)) << bit;
144 		nvram->write(reg8, addr);
145 		return 0;
146 	}
147 
148 	/* When handling more than a byte, copy whole bytes */
149 	while (len > 0) {
150 		len -= 8;
151 		nvram->write(value[offs++], addr++);
152 	}
153 
154 	return 0;
155 }
156 
lookup_cmos_entry(struct cb_cmos_option_table * option_table,const char * name)157 static struct cb_cmos_entries *lookup_cmos_entry(struct cb_cmos_option_table *option_table, const char *name)
158 {
159 	struct cb_cmos_entries *cmos_entry;
160 	int len = name ? strnlen(name, CB_CMOS_MAX_NAME_LENGTH) : 0;
161 
162 	/* CMOS entries are located right after the option table */
163 	cmos_entry = first_cmos_entry(option_table);
164 	while (cmos_entry) {
165 		if (memcmp((const char*)cmos_entry->name, name, len) == 0)
166 			return cmos_entry;
167 		cmos_entry = next_cmos_entry(cmos_entry);
168 	}
169 
170 	printf("ERROR: No such CMOS option (%s)\n", name);
171 	return NULL;
172 }
173 
first_cmos_entry(struct cb_cmos_option_table * option_table)174 struct cb_cmos_entries *first_cmos_entry(struct cb_cmos_option_table *option_table)
175 {
176 	return (struct cb_cmos_entries*)((unsigned char *)option_table + option_table->header_length);
177 }
178 
next_cmos_entry(struct cb_cmos_entries * cmos_entry)179 struct cb_cmos_entries *next_cmos_entry(struct cb_cmos_entries *cmos_entry)
180 {
181 	struct cb_cmos_entries *next = (struct cb_cmos_entries*)((unsigned char *)cmos_entry + cmos_entry->size);
182 	if (next->tag == CB_TAG_OPTION)
183 		return next;
184 	else
185 		return NULL;
186 }
187 
first_cmos_enum(struct cb_cmos_option_table * option_table)188 struct cb_cmos_enums *first_cmos_enum(struct cb_cmos_option_table *option_table)
189 {
190 	struct cb_cmos_entries *cmos_entry;
191 	/* CMOS entries are located right after the option table. Skip them */
192 	cmos_entry = (struct cb_cmos_entries *)((unsigned char *)option_table + option_table->header_length);
193 	while (cmos_entry->tag == CB_TAG_OPTION)
194 		cmos_entry = (struct cb_cmos_entries*)((unsigned char *)cmos_entry + cmos_entry->size);
195 
196 	/* CMOS enums are located after CMOS entries. */
197 	return (struct cb_cmos_enums *)cmos_entry;
198 }
199 
next_cmos_enum(struct cb_cmos_enums * cmos_enum)200 struct cb_cmos_enums *next_cmos_enum(struct cb_cmos_enums *cmos_enum)
201 {
202 	if (!cmos_enum) {
203 		return NULL;
204 	}
205 
206 	cmos_enum = (struct cb_cmos_enums*)((unsigned char *)cmos_enum + cmos_enum->size);
207 	if (cmos_enum->tag == CB_TAG_OPTION_ENUM) {
208 		return cmos_enum;
209 	} else {
210 		return NULL;
211 	}
212 }
213 
next_cmos_enum_of_id(struct cb_cmos_enums * cmos_enum,int id)214 struct cb_cmos_enums *next_cmos_enum_of_id(struct cb_cmos_enums *cmos_enum, int id)
215 {
216 	while ((cmos_enum = next_cmos_enum(cmos_enum))) {
217 		if (cmos_enum->config_id == id) {
218 			return cmos_enum;
219 		}
220 	}
221 	return NULL;
222 }
223 
first_cmos_enum_of_id(struct cb_cmos_option_table * option_table,int id)224 struct cb_cmos_enums *first_cmos_enum_of_id(struct cb_cmos_option_table *option_table, int id)
225 {
226 	struct cb_cmos_enums *cmos_enum = first_cmos_enum(option_table);
227 	if (!cmos_enum) {
228 		return NULL;
229 	}
230 	if (cmos_enum->config_id == id) {
231 		return cmos_enum;
232 	}
233 
234 	return next_cmos_enum_of_id(cmos_enum, id);
235 }
236 
237 /* Either value or text must be NULL. Returns the field that matches "the other" for a given config_id */
lookup_cmos_enum_core(struct cb_cmos_option_table * option_table,int config_id,const u8 * value,const char * text)238 static struct cb_cmos_enums *lookup_cmos_enum_core(struct cb_cmos_option_table *option_table, int config_id, const u8 *value, const char *text)
239 {
240 	int len = strnlen(text, CB_CMOS_MAX_TEXT_LENGTH);
241 
242 	/* CMOS enums are located after CMOS entries. */
243 	struct cb_cmos_enums *cmos_enum;
244 	for (   cmos_enum = first_cmos_enum_of_id(option_table, config_id);
245 		cmos_enum;
246 		cmos_enum = next_cmos_enum_of_id(cmos_enum, config_id)) {
247 		if (((value == NULL) || (cmos_enum->value == *value)) &&
248 		    ((text == NULL) || (memcmp((const char*)cmos_enum->text, text, len) == 0))) {
249 			return cmos_enum;
250 		}
251 	}
252 
253 	return NULL;
254 }
255 
lookup_cmos_enum_by_value(struct cb_cmos_option_table * option_table,int config_id,const u8 * value)256 static struct cb_cmos_enums *lookup_cmos_enum_by_value(struct cb_cmos_option_table *option_table, int config_id, const u8 *value)
257 {
258 	return lookup_cmos_enum_core(option_table, config_id, value, NULL);
259 }
260 
lookup_cmos_enum_by_label(struct cb_cmos_option_table * option_table,int config_id,const char * label)261 static struct cb_cmos_enums *lookup_cmos_enum_by_label(struct cb_cmos_option_table *option_table, int config_id, const char *label)
262 {
263 	return lookup_cmos_enum_core(option_table, config_id, NULL, label);
264 }
265 
get_option_with(const struct nvram_accessor * nvram,struct cb_cmos_option_table * option_table,void * dest,const char * name)266 int get_option_with(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, void *dest, const char *name)
267 {
268 	struct cb_cmos_entries *cmos_entry = lookup_cmos_entry(option_table, name);
269 	if (cmos_entry) {
270 		if(get_cmos_value(nvram, cmos_entry->bit, cmos_entry->length, dest))
271 			return 1;
272 
273 		if(!options_checksum_valid(nvram))
274 			return 1;
275 
276 		return 0;
277 	}
278 	return 1;
279 }
280 
get_option_from(struct cb_cmos_option_table * option_table,void * dest,const char * name)281 int get_option_from(struct cb_cmos_option_table *option_table, void *dest, const char *name)
282 {
283 	return get_option_with(use_nvram, option_table, dest, name);
284 }
285 
get_option(void * dest,const char * name)286 int get_option(void *dest, const char *name)
287 {
288 	return get_option_from(get_system_option_table(), dest, name);
289 }
290 
set_option_with(const struct nvram_accessor * nvram,struct cb_cmos_option_table * option_table,const void * value,const char * name)291 int set_option_with(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, const void *value, const char *name)
292 {
293 	struct cb_cmos_entries *cmos_entry = lookup_cmos_entry(option_table, name);
294 	if (cmos_entry) {
295 		set_cmos_value(nvram, cmos_entry->bit, cmos_entry->length, value);
296 		fix_options_checksum_with(nvram);
297 		return 0;
298 	}
299 	return 1;
300 }
301 
set_option(const void * value,const char * name)302 int set_option(const void *value, const char *name)
303 {
304 	return set_option_with(use_nvram, get_system_option_table(), value, name);
305 }
306 
get_option_as_string(const struct nvram_accessor * nvram,struct cb_cmos_option_table * option_table,char ** dest,const char * name)307 int get_option_as_string(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, char **dest, const char *name)
308 {
309 	void *raw;
310 	struct cb_cmos_entries *cmos_entry = lookup_cmos_entry(option_table, name);
311 	if (!cmos_entry)
312 		return 1;
313 	int cmos_length = (cmos_entry->length+7)/8;
314 
315 	/* ensure we have enough space for u64 */
316 	if (cmos_length < 8)
317 		cmos_length = 8;
318 
319 	/* extra byte to ensure 0-terminated strings */
320 	raw = malloc(cmos_length+1);
321 	memset(raw, 0, cmos_length+1);
322 
323 	int ret = get_option_with(nvram, option_table, raw, name);
324 
325 	struct cb_cmos_enums *cmos_enum;
326 	switch (cmos_entry->config) {
327 		case 'h':
328 			/* only works on little endian.
329 			   26 bytes is enough for a 64bit value in decimal */
330 			*dest = malloc(26);
331 			sprintf(*dest, "%" PRIu64, *(u64 *)raw);
332 			break;
333 		case 's':
334 			*dest = strdup(raw);
335 			break;
336 		case 'e':
337 			cmos_enum = lookup_cmos_enum_by_value(option_table, cmos_entry->config_id, (u8*)raw);
338 			*dest = strdup((const char*)cmos_enum->text);
339 			break;
340 		default: /* fail */
341 			ret = 1;
342 	}
343 	free(raw);
344 	return ret;
345 }
346 
set_option_from_string(const struct nvram_accessor * nvram,struct cb_cmos_option_table * option_table,const char * value,const char * name)347 int set_option_from_string(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, const char *value, const char *name)
348 {
349 	void *raw;
350 	struct cb_cmos_entries *cmos_entry = lookup_cmos_entry(option_table, name);
351 	if (!cmos_entry)
352 		return 1;
353 
354 	struct cb_cmos_enums *cmos_enum;
355 	switch (cmos_entry->config) {
356 		case 'h':
357 			/* only works on little endian */
358 			raw = malloc(sizeof(u64));
359 			*(u64*)raw = strtoull(value, NULL, 0);
360 			break;
361 		case 's':
362 			raw = malloc(cmos_entry->length);
363 			if (!raw)
364 				return 1;
365 			memset(raw, 0x00, cmos_entry->length);
366 			strncpy(raw, value, cmos_entry->length);
367 			break;
368 		case 'e':
369 			cmos_enum = lookup_cmos_enum_by_label(option_table, cmos_entry->config_id, value);
370 			raw = malloc(sizeof(u32));
371 			*(u32*)raw = cmos_enum->value;
372 			break;
373 		default: /* fail */
374 			return 1;
375 	}
376 
377 	int ret = set_option_with(nvram, option_table, raw, name);
378 	free(raw);
379 	return ret;
380 }
381