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 }