blob: 34507c9af289b74e157ec228c6d7d643f0b40e0c [file] [log] [blame]
Patrick Georgi36ade672011-01-28 07:56:39 +00001/*****************************************************************************\
2 * lbtable.c
3 *****************************************************************************
Vikram Narayanana8111cf2012-04-14 15:25:13 +05304 * Copyright (C) 2012, Vikram Narayanan
5 * Unified build_opt_tbl and nvramtool
6 * build_opt_tbl.c
7 * Copyright (C) 2003 Eric Biederman (ebiederm@xmission.com)
8 * Copyright (C) 2007-2010 coresystems GmbH
9 *
Patrick Georgi36ade672011-01-28 07:56:39 +000010 * Copyright (C) 2002-2005 The Regents of the University of California.
11 * Produced at the Lawrence Livermore National Laboratory.
12 * Written by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>
13 * and Stefan Reinauer <stepan@openbios.org>.
14 * UCRL-CODE-2003-012
15 * All rights reserved.
16 *
17 * This file is part of nvramtool, a utility for reading/writing coreboot
18 * parameters and displaying information from the coreboot table.
19 * For details, see http://coreboot.org/nvramtool.
20 *
21 * Please also read the file DISCLAIMER which is included in this software
22 * distribution.
23 *
24 * This program is free software; you can redistribute it and/or modify it
25 * under the terms of the GNU General Public License (as published by the
26 * Free Software Foundation) version 2, dated June 1991.
27 *
28 * This program is distributed in the hope that it will be useful, but
29 * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
31 * conditions of the GNU General Public License for more details.
Patrick Georgi36ade672011-01-28 07:56:39 +000032\*****************************************************************************/
33
Patrick Georgi36ade672011-01-28 07:56:39 +000034#include <string.h>
Zheng Bao54516722012-10-22 16:41:42 +080035#ifndef __MINGW32__
Patrick Georgi36ade672011-01-28 07:56:39 +000036#include <sys/mman.h>
Zheng Bao54516722012-10-22 16:41:42 +080037#endif
Patrick Georgi36ade672011-01-28 07:56:39 +000038#include "common.h"
39#include "coreboot_tables.h"
40#include "ip_checksum.h"
41#include "lbtable.h"
42#include "layout.h"
43#include "cmos_lowlevel.h"
44#include "hexdump.h"
45#include "cbfs.h"
Vikram Narayanana8111cf2012-04-14 15:25:13 +053046#include "layout-text.h"
Patrick Georgi36ade672011-01-28 07:56:39 +000047
48static void process_cmos_table(void);
49static void get_cmos_checksum_info(void);
50static void try_convert_checksum_layout(cmos_checksum_layout_t * layout);
51static void try_add_cmos_table_enum(cmos_enum_t * cmos_enum);
52static void try_add_cmos_table_entry(cmos_entry_t * cmos_entry);
53static const struct cmos_entries *first_cmos_table_entry(void);
54static const struct cmos_entries *next_cmos_table_entry(const struct
55 cmos_entries *last);
56static const struct cmos_enums *first_cmos_table_enum(void);
57static const struct cmos_enums *next_cmos_table_enum
58 (const struct cmos_enums *last);
59static const struct lb_record *first_cmos_rec(uint32_t tag);
60static const struct lb_record *next_cmos_rec(const struct lb_record *last,
61 uint32_t tag);
62
63/* The CMOS option table is located within the coreboot table. It tells us
64 * where the CMOS parameters are located in the nonvolatile RAM.
65 */
66static const struct cmos_option_table *cmos_table = NULL;
67
Vikram Narayanana8111cf2012-04-14 15:25:13 +053068#define ROUNDUP4(x) (x += (4 - (x % 4)))
69
Patrick Georgi36ade672011-01-28 07:56:39 +000070void process_layout(void)
71{
72 if ((cmos_table) == NULL) {
73 fprintf(stderr,
74 "%s: CMOS option table not found in coreboot table. "
75 "Apparently, the coreboot installed on this system was "
76 "built without specifying CONFIG_HAVE_OPTION_TABLE.\n",
77 prog_name);
78 exit(1);
79 }
80
81 process_cmos_table();
82 get_cmos_checksum_info();
83}
84
85void get_layout_from_cbfs_file(void)
86{
87 uint32_t len;
88 cmos_table = cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT, &len);
89 process_layout();
90}
91
Vikram Narayanana8111cf2012-04-14 15:25:13 +053092int write_cmos_layout_bin(FILE *f)
93{
94 const cmos_entry_t *cmos_entry;
95 const cmos_enum_t *cmos_enum;
96 cmos_checksum_layout_t layout;
97 struct cmos_option_table table;
98 struct cmos_entries entry;
99 struct cmos_enums cenum;
100 struct cmos_checksum csum;
101 size_t sum = 0;
102 int len;
103
104 for (cmos_entry = first_cmos_entry(); cmos_entry != NULL;
105 cmos_entry = next_cmos_entry(cmos_entry)) {
106
107 if (cmos_entry == first_cmos_entry()) {
108 sum += sizeof(table);
109 table.header_length = sizeof(table);
110 table.tag = LB_TAG_CMOS_OPTION_TABLE;
Patrick Georgic37b05c2013-11-11 15:16:53 +0100111 table.size = 0;
Vikram Narayanana8111cf2012-04-14 15:25:13 +0530112
113 if (fwrite((char *)&table, sizeof(table), 1, f) != 1) {
114 perror("Error writing image file");
115 goto err;
116 }
117 }
118
119 memset(&entry, 0, sizeof(entry));
120 entry.tag = LB_TAG_OPTION;
121 entry.config = cmos_entry->config;
122 entry.config_id = (uint32_t)cmos_entry->config_id;
123 entry.bit = cmos_entry->bit;
124 entry.length = cmos_entry->length;
125
126 if (!is_ident((char *)cmos_entry->name)) {
127 fprintf(stderr,
128 "Error - Name %s is an invalid identifier\n",
129 cmos_entry->name);
130 goto err;
131 }
132
133 memcpy(entry.name, cmos_entry->name, strlen(cmos_entry->name));
134 entry.name[strlen(cmos_entry->name)] = '\0';
135 len = strlen(cmos_entry->name) + 1;
136
137 if (len % 4)
138 ROUNDUP4(len);
139
140 entry.size = sizeof(entry) - CMOS_MAX_NAME_LENGTH + len;
141 sum += entry.size;
142 if (fwrite((char *)&entry, entry.size, 1, f) != 1) {
143 perror("Error writing image file");
144 goto err;
145 }
146 }
147
148 for (cmos_enum = first_cmos_enum();
149 cmos_enum != NULL; cmos_enum = next_cmos_enum(cmos_enum)) {
150 memset(&cenum, 0, sizeof(cenum));
151 cenum.tag = LB_TAG_OPTION_ENUM;
152 memcpy(cenum.text, cmos_enum->text, strlen(cmos_enum->text));
153 cenum.text[strlen(cmos_enum->text)] = '\0';
154 len = strlen((char *)cenum.text) + 1;
155
156 if (len % 4)
157 ROUNDUP4(len);
158
159 cenum.config_id = cmos_enum->config_id;
160 cenum.value = cmos_enum->value;
161 cenum.size = sizeof(cenum) - CMOS_MAX_TEXT_LENGTH + len;
162 sum += cenum.size;
163 if (fwrite((char *)&cenum, cenum.size, 1, f) != 1) {
164 perror("Error writing image file");
165 goto err;
166 }
167 }
168
169 layout.summed_area_start = cmos_checksum_start;
170 layout.summed_area_end = cmos_checksum_end;
171 layout.checksum_at = cmos_checksum_index;
172 checksum_layout_to_bits(&layout);
173
174 csum.tag = LB_TAG_OPTION_CHECKSUM;
175 csum.size = sizeof(csum);
176 csum.range_start = layout.summed_area_start;
177 csum.range_end = layout.summed_area_end;
178 csum.location = layout.checksum_at;
179 csum.type = CHECKSUM_PCBIOS;
180 sum += csum.size;
181
182 if (fwrite((char *)&csum, csum.size, 1, f) != 1) {
183 perror("Error writing image file");
184 goto err;
185 }
186
Patrick Georgic37b05c2013-11-11 15:16:53 +0100187 if (fseek(f, 0, SEEK_SET) != 0) {
Vikram Narayanana8111cf2012-04-14 15:25:13 +0530188 perror("Error while seeking");
189 goto err;
190 }
191
Patrick Georgic37b05c2013-11-11 15:16:53 +0100192 table.size = sum;
193 if (fwrite((char *)&table, sizeof(table), 1, f) != 1) {
Vikram Narayanana8111cf2012-04-14 15:25:13 +0530194 perror("Error writing image file");
195 goto err;
196 }
197 return sum;
198
199err:
200 fclose(f);
201 exit(1);
202}
203
204void write_cmos_output_bin(const char *binary_filename)
205{
206 FILE *fp;
207
208 if ((fp = fopen(binary_filename, "wb")) == NULL) {
209 fprintf(stderr,
210 "%s: Can not open file %s for writing: "
211 "%s\n", prog_name, binary_filename, strerror(errno));
212 exit(1);
213 }
214 write_cmos_layout_bin(fp);
215 fclose(fp);
216}
217
Patrick Georgi36ade672011-01-28 07:56:39 +0000218/****************************************************************************
Mathias Krause155c3792011-03-10 07:52:02 +0000219 * get_layout_from_cmos_table
220 *
221 * Find the CMOS table which is stored within the coreboot table and set the
222 * global variable cmos_table to point to it.
223 ****************************************************************************/
224void get_layout_from_cmos_table(void)
225{
226 get_lbtable();
227 cmos_table = (const struct cmos_option_table *)
228 find_lbrec(LB_TAG_CMOS_OPTION_TABLE);
229 process_layout();
230}
231
232/****************************************************************************
Patrick Georgi36ade672011-01-28 07:56:39 +0000233 * process_cmos_table
234 *
235 * Extract layout information from the CMOS option table and store it in our
236 * internal repository.
237 ****************************************************************************/
238static void process_cmos_table(void)
239{
240 const struct cmos_enums *p;
241 const struct cmos_entries *q;
242 cmos_enum_t cmos_enum;
243 cmos_entry_t cmos_entry;
244
245 /* First add the enums. */
246 for (p = first_cmos_table_enum(); p != NULL;
247 p = next_cmos_table_enum(p)) {
248 cmos_enum.config_id = p->config_id;
249 cmos_enum.value = p->value;
250 strncpy(cmos_enum.text, (char *)p->text, CMOS_MAX_TEXT_LENGTH);
251 cmos_enum.text[CMOS_MAX_TEXT_LENGTH] = '\0';
252 try_add_cmos_table_enum(&cmos_enum);
253 }
254
255 /* Now add the entries. We must add the entries after the enums because
256 * the entries are sanity checked against the enums as they are added.
257 */
258 for (q = first_cmos_table_entry(); q != NULL;
259 q = next_cmos_table_entry(q)) {
260 cmos_entry.bit = q->bit;
261 cmos_entry.length = q->length;
262
263 switch (q->config) {
264 case 'e':
265 cmos_entry.config = CMOS_ENTRY_ENUM;
266 break;
267
268 case 'h':
269 cmos_entry.config = CMOS_ENTRY_HEX;
270 break;
271
272 case 'r':
273 cmos_entry.config = CMOS_ENTRY_RESERVED;
274 break;
275
276 case 's':
277 cmos_entry.config = CMOS_ENTRY_STRING;
278 break;
279
280 default:
281 fprintf(stderr,
282 "%s: Entry in CMOS option table has unknown config "
283 "value.\n", prog_name);
284 exit(1);
285 }
286
287 cmos_entry.config_id = q->config_id;
288 strncpy(cmos_entry.name, (char *)q->name, CMOS_MAX_NAME_LENGTH);
289 cmos_entry.name[CMOS_MAX_NAME_LENGTH] = '\0';
290 try_add_cmos_table_entry(&cmos_entry);
291 }
292}
293
294/****************************************************************************
295 * get_cmos_checksum_info
296 *
297 * Get layout information for CMOS checksum.
298 ****************************************************************************/
299static void get_cmos_checksum_info(void)
300{
301 const cmos_entry_t *e;
302 struct cmos_checksum *checksum;
303 cmos_checksum_layout_t layout;
304 unsigned index, index2;
305
306 checksum = (struct cmos_checksum *)next_cmos_rec((const struct lb_record *)first_cmos_table_enum(), LB_TAG_OPTION_CHECKSUM);
307
308 if (checksum != NULL) { /* We are lucky. The coreboot table hints us to the checksum.
309 * We might have to check the type field here though.
310 */
311 layout.summed_area_start = checksum->range_start;
312 layout.summed_area_end = checksum->range_end;
313 layout.checksum_at = checksum->location;
314 try_convert_checksum_layout(&layout);
315 cmos_checksum_start = layout.summed_area_start;
316 cmos_checksum_end = layout.summed_area_end;
317 cmos_checksum_index = layout.checksum_at;
318 return;
319 }
320
321 if ((e = find_cmos_entry(checksum_param_name)) == NULL)
322 return;
323
324 /* If we get here, we are unlucky. The CMOS option table contains the
325 * location of the CMOS checksum. However, there is no information
326 * regarding which bytes of the CMOS area the checksum is computed over.
327 * Thus we have to hope our presets will be fine.
328 */
329
330 if (e->bit % 8) {
331 fprintf(stderr,
332 "%s: Error: CMOS checksum is not byte-aligned.\n",
333 prog_name);
334 exit(1);
335 }
336
337 index = e->bit / 8;
338 index2 = index + 1; /* The CMOS checksum occupies 16 bits. */
339
340 if (verify_cmos_byte_index(index) || verify_cmos_byte_index(index2)) {
341 fprintf(stderr,
342 "%s: Error: CMOS checksum location out of range.\n",
343 prog_name);
344 exit(1);
345 }
346
347 if (((index >= cmos_checksum_start) && (index <= cmos_checksum_end)) ||
348 (((index2) >= cmos_checksum_start)
349 && ((index2) <= cmos_checksum_end))) {
350 fprintf(stderr,
351 "%s: Error: CMOS checksum overlaps checksummed area.\n",
352 prog_name);
353 exit(1);
354 }
355
356 cmos_checksum_index = index;
357}
358
359/****************************************************************************
360 * try_convert_checksum_layout
361 *
362 * Perform sanity checking on CMOS checksum layout information and attempt to
363 * convert information from bit positions to byte positions. Return OK on
364 * success or an error code on failure.
365 ****************************************************************************/
366static void try_convert_checksum_layout(cmos_checksum_layout_t * layout)
367{
368 switch (checksum_layout_to_bytes(layout)) {
369 case OK:
370 return;
371
372 case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED:
373 fprintf(stderr,
374 "%s: CMOS checksummed area start is not byte-aligned.\n",
375 prog_name);
376 break;
377
378 case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED:
379 fprintf(stderr,
380 "%s: CMOS checksummed area end is not byte-aligned.\n",
381 prog_name);
382 break;
383
384 case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED:
385 fprintf(stderr,
386 "%s: CMOS checksum location is not byte-aligned.\n",
387 prog_name);
388 break;
389
390 case LAYOUT_INVALID_SUMMED_AREA:
391 fprintf(stderr,
392 "%s: CMOS checksummed area end must be greater than "
393 "CMOS checksummed area start.\n", prog_name);
394 break;
395
396 case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA:
397 fprintf(stderr,
398 "%s: CMOS checksum overlaps checksummed area.\n",
399 prog_name);
400 break;
401
402 case LAYOUT_SUMMED_AREA_OUT_OF_RANGE:
403 fprintf(stderr,
404 "%s: CMOS checksummed area out of range.\n", prog_name);
405 break;
406
407 case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE:
408 fprintf(stderr,
409 "%s: CMOS checksum location out of range.\n",
410 prog_name);
411 break;
412
413 default:
414 BUG();
415 }
416
417 exit(1);
418}
419
420/****************************************************************************
421 * try_add_cmos_table_enum
422 *
423 * Attempt to add a CMOS enum to our internal repository. Exit with an error
424 * message on failure.
425 ****************************************************************************/
426static void try_add_cmos_table_enum(cmos_enum_t * cmos_enum)
427{
428 switch (add_cmos_enum(cmos_enum)) {
429 case OK:
430 return;
431
432 case LAYOUT_DUPLICATE_ENUM:
433 fprintf(stderr, "%s: Duplicate enum %s found in CMOS option "
434 "table.\n", prog_name, cmos_enum->text);
435 break;
436
437 default:
438 BUG();
439 }
440
441 exit(1);
442}
443
444/****************************************************************************
445 * try_add_cmos_table_entry
446 *
447 * Attempt to add a CMOS entry to our internal repository. Exit with an
448 * error message on failure.
449 ****************************************************************************/
450static void try_add_cmos_table_entry(cmos_entry_t * cmos_entry)
451{
452 const cmos_entry_t *conflict;
453
454 switch (add_cmos_entry(cmos_entry, &conflict)) {
455 case OK:
456 return;
457
458 case CMOS_AREA_OUT_OF_RANGE:
459 fprintf(stderr,
460 "%s: Bad CMOS option layout in CMOS option table entry "
461 "%s.\n", prog_name, cmos_entry->name);
462 break;
463
464 case CMOS_AREA_TOO_WIDE:
465 fprintf(stderr,
466 "%s: Area too wide for CMOS option table entry %s.\n",
467 prog_name, cmos_entry->name);
468 break;
469
470 case LAYOUT_ENTRY_OVERLAP:
471 fprintf(stderr,
472 "%s: CMOS option table entries %s and %s have overlapping "
473 "layouts.\n", prog_name, cmos_entry->name,
474 conflict->name);
475 break;
476
477 case LAYOUT_ENTRY_BAD_LENGTH:
478 /* Silently ignore entries with zero length. Although this should
479 * never happen in practice, we should handle the case in a
480 * reasonable manner just to be safe.
481 */
482 return;
483
484 default:
485 BUG();
486 }
487
488 exit(1);
489}
490
491/****************************************************************************
492 * first_cmos_table_entry
493 *
494 * Return a pointer to the first entry in the CMOS table that represents a
495 * CMOS parameter. Return NULL if CMOS table is empty.
496 ****************************************************************************/
497static const struct cmos_entries *first_cmos_table_entry(void)
498{
499 return (const struct cmos_entries *)first_cmos_rec(LB_TAG_OPTION);
500}
501
502/****************************************************************************
503 * next_cmos_table_entry
504 *
505 * Return a pointer to the next entry after 'last' in the CMOS table that
506 * represents a CMOS parameter. Return NULL if there are no more parameters.
507 ****************************************************************************/
508static const struct cmos_entries *next_cmos_table_entry(const struct
509 cmos_entries *last)
510{
511 return (const struct cmos_entries *)
512 next_cmos_rec((const struct lb_record *)last, LB_TAG_OPTION);
513}
514
515/****************************************************************************
516 * first_cmos_table_enum
517 *
518 * Return a pointer to the first entry in the CMOS table that represents a
519 * possible CMOS parameter value. Return NULL if the table does not contain
520 * any such entries.
521 ****************************************************************************/
522static const struct cmos_enums *first_cmos_table_enum(void)
523{
524 return (const struct cmos_enums *)first_cmos_rec(LB_TAG_OPTION_ENUM);
525}
526
527/****************************************************************************
528 * next_cmos_table_enum
529 *
530 * Return a pointer to the next entry after 'last' in the CMOS table that
531 * represents a possible CMOS parameter value. Return NULL if there are no
532 * more parameter values.
533 ****************************************************************************/
534static const struct cmos_enums *next_cmos_table_enum
535 (const struct cmos_enums *last) {
536 return (const struct cmos_enums *)
537 next_cmos_rec((const struct lb_record *)last, LB_TAG_OPTION_ENUM);
538}
539
540/****************************************************************************
541 * first_cmos_rec
542 *
543 * Return a pointer to the first entry in the CMOS table whose type matches
544 * 'tag'. Return NULL if CMOS table contains no such entry.
545 *
546 * Possible values for 'tag' are as follows:
547 *
548 * LB_TAG_OPTION: The entry represents a CMOS parameter.
549 * LB_TAG_OPTION_ENUM: The entry represents a possible value for a CMOS
550 * parameter of type 'enum'.
551 *
552 * The CMOS table tells us where in the nonvolatile RAM to look for CMOS
553 * parameter values and specifies their types as 'enum', 'hex', or
554 * 'reserved'.
555 ****************************************************************************/
556static const struct lb_record *first_cmos_rec(uint32_t tag)
557{
558 const char *p;
559 uint32_t bytes_processed, bytes_for_entries;
560 const struct lb_record *lbrec;
561
562 p = ((const char *)cmos_table) + cmos_table->header_length;
563 bytes_for_entries = cmos_table->size - cmos_table->header_length;
564
565 for (bytes_processed = 0;
566 bytes_processed < bytes_for_entries;
567 bytes_processed += lbrec->size) {
568 lbrec = (const struct lb_record *)&p[bytes_processed];
569
570 if (lbrec->tag == tag)
571 return lbrec;
572 }
573
574 return NULL;
575}
576
577/****************************************************************************
578 * next_cmos_rec
579 *
580 * Return a pointer to the next entry after 'last' in the CMOS table whose
581 * type matches 'tag'. Return NULL if the table contains no more entries of
582 * this type.
583 ****************************************************************************/
584static const struct lb_record *next_cmos_rec(const struct lb_record *last,
585 uint32_t tag)
586{
587 const char *p;
588 uint32_t bytes_processed, bytes_for_entries, last_offset;
589 const struct lb_record *lbrec;
590
591 p = ((const char *)cmos_table) + cmos_table->header_length;
592 bytes_for_entries = cmos_table->size - cmos_table->header_length;
593 last_offset = ((const char *)last) - p;
594
595 for (bytes_processed = last_offset + last->size;
596 bytes_processed < bytes_for_entries;
597 bytes_processed += lbrec->size) {
598 lbrec = (const struct lb_record *)&p[bytes_processed];
599
600 if (lbrec->tag == tag)
601 return lbrec;
602 }
603
604 return NULL;
605}