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