/ payloads / libpayload / drivers / options.c
options.c
  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  
 37  static u8 mem_read(u8 reg)
 38  {
 39  	return mem_accessor_base[reg];
 40  }
 41  
 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  
 57  struct cb_cmos_option_table *get_system_option_table(void)
 58  {
 59  	return phys_to_virt(lib_sysinfo.cmos_option_table);
 60  }
 61  
 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  
 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  
 95  void fix_options_checksum(void)
 96  {
 97  	fix_options_checksum_with(use_nvram);
 98  }
 99  
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  
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  
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  
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  
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  
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  
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  
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  
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 */
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  
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  
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  
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  
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  
286  int get_option(void *dest, const char *name)
287  {
288  	return get_option_from(get_system_option_table(), dest, name);
289  }
290  
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  
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  
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  
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  }