soc/amd/common/psp/psp_def.h: increase P2C_BUFFER_MAXSIZE
[coreboot2.git] / payloads / libpayload / drivers / options.c
blobb6d234222dbe4df1f48de3f22ebb7d9a36da7b23
1 /*
3 * Copyright (C) 2008 coresystems GmbH
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.
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.
29 #define __STDC_FORMAT_MACROS
31 #include <libpayload.h>
32 #include <coreboot_tables.h>
33 #include <inttypes.h>
35 u8 *mem_accessor_base;
37 static u8 mem_read(u8 reg)
39 return mem_accessor_base[reg];
42 static void mem_write(u8 val, u8 reg)
44 mem_accessor_base[reg] = val;
47 struct nvram_accessor *use_nvram = &(struct nvram_accessor) {
48 nvram_read,
49 nvram_write
52 struct nvram_accessor *use_mem = &(struct nvram_accessor) {
53 mem_read,
54 mem_write
57 struct cb_cmos_option_table *get_system_option_table(void)
59 return phys_to_virt(lib_sysinfo.cmos_option_table);
62 int options_checksum_valid(const struct nvram_accessor *nvram)
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;
70 for(i = range_start; i <= range_end; i++) {
71 checksum += nvram->read(i);
74 checksum_old = ((nvram->read(checksum_location)<<8) | nvram->read(checksum_location+1));
76 return (checksum_old == checksum);
79 void fix_options_checksum_with(const struct nvram_accessor *nvram)
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;
87 for(i = range_start; i <= range_end; i++) {
88 checksum += nvram->read(i);
91 nvram->write((checksum >> 8), checksum_location);
92 nvram->write((checksum & 0xff), checksum_location + 1);
95 void fix_options_checksum(void)
97 fix_options_checksum_with(use_nvram);
100 static int get_cmos_value(const struct nvram_accessor *nvram, u32 bitnum, u32 len, void *valptr)
102 u8 *value = valptr;
103 int offs = 0;
104 u32 addr, bit;
105 u8 reg8;
107 /* Convert to byte borders */
108 addr=(bitnum / 8);
109 bit=(bitnum % 8);
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;
119 /* When handling more than a byte, copy whole bytes */
120 while (len > 0) {
121 len -= 8;
122 value[offs++]=nvram->read(addr++);
125 return 0;
128 static int set_cmos_value(const struct nvram_accessor *nvram, u32 bitnum, u32 len, const void *valptr)
130 const u8 *value = valptr;
131 int offs = 0;
132 u32 addr, bit;
133 u8 reg8;
135 /* Convert to byte borders */
136 addr=(bitnum / 8);
137 bit=(bitnum % 8);
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;
148 /* When handling more than a byte, copy whole bytes */
149 while (len > 0) {
150 len -= 8;
151 nvram->write(value[offs++], addr++);
154 return 0;
157 static struct cb_cmos_entries *lookup_cmos_entry(struct cb_cmos_option_table *option_table, const char *name)
159 struct cb_cmos_entries *cmos_entry;
160 int len = name ? strnlen(name, CB_CMOS_MAX_NAME_LENGTH) : 0;
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);
170 printf("ERROR: No such CMOS option (%s)\n", name);
171 return NULL;
174 struct cb_cmos_entries *first_cmos_entry(struct cb_cmos_option_table *option_table)
176 return (struct cb_cmos_entries*)((unsigned char *)option_table + option_table->header_length);
179 struct cb_cmos_entries *next_cmos_entry(struct cb_cmos_entries *cmos_entry)
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;
188 struct cb_cmos_enums *first_cmos_enum(struct cb_cmos_option_table *option_table)
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);
196 /* CMOS enums are located after CMOS entries. */
197 return (struct cb_cmos_enums *)cmos_entry;
200 struct cb_cmos_enums *next_cmos_enum(struct cb_cmos_enums *cmos_enum)
202 if (!cmos_enum) {
203 return NULL;
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;
214 struct cb_cmos_enums *next_cmos_enum_of_id(struct cb_cmos_enums *cmos_enum, int id)
216 while ((cmos_enum = next_cmos_enum(cmos_enum))) {
217 if (cmos_enum->config_id == id) {
218 return cmos_enum;
221 return NULL;
224 struct cb_cmos_enums *first_cmos_enum_of_id(struct cb_cmos_option_table *option_table, int id)
226 struct cb_cmos_enums *cmos_enum = first_cmos_enum(option_table);
227 if (!cmos_enum) {
228 return NULL;
230 if (cmos_enum->config_id == id) {
231 return cmos_enum;
234 return next_cmos_enum_of_id(cmos_enum, id);
237 /* Either value or text must be NULL. Returns the field that matches "the other" for a given config_id */
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)
240 int len = strnlen(text, CB_CMOS_MAX_TEXT_LENGTH);
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;
253 return NULL;
256 static struct cb_cmos_enums *lookup_cmos_enum_by_value(struct cb_cmos_option_table *option_table, int config_id, const u8 *value)
258 return lookup_cmos_enum_core(option_table, config_id, value, NULL);
261 static struct cb_cmos_enums *lookup_cmos_enum_by_label(struct cb_cmos_option_table *option_table, int config_id, const char *label)
263 return lookup_cmos_enum_core(option_table, config_id, NULL, label);
266 int get_option_with(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, void *dest, const char *name)
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;
273 if(!options_checksum_valid(nvram))
274 return 1;
276 return 0;
278 return 1;
281 int get_option_from(struct cb_cmos_option_table *option_table, void *dest, const char *name)
283 return get_option_with(use_nvram, option_table, dest, name);
286 int get_option(void *dest, const char *name)
288 return get_option_from(get_system_option_table(), dest, name);
291 int set_option_with(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, const void *value, const char *name)
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;
299 return 1;
302 int set_option(const void *value, const char *name)
304 return set_option_with(use_nvram, get_system_option_table(), value, name);
307 int get_option_as_string(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, char **dest, const char *name)
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;
315 /* ensure we have enough space for u64 */
316 if (cmos_length < 8)
317 cmos_length = 8;
319 /* extra byte to ensure 0-terminated strings */
320 raw = malloc(cmos_length+1);
321 memset(raw, 0, cmos_length+1);
323 int ret = get_option_with(nvram, option_table, raw, name);
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;
343 free(raw);
344 return ret;
347 int set_option_from_string(const struct nvram_accessor *nvram, struct cb_cmos_option_table *option_table, const char *value, const char *name)
349 void *raw;
350 struct cb_cmos_entries *cmos_entry = lookup_cmos_entry(option_table, name);
351 if (!cmos_entry)
352 return 1;
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;
377 int ret = set_option_with(nvram, option_table, raw, name);
378 free(raw);
379 return ret;