1 /*****************************************************************************\
3 *****************************************************************************
4 * Copyright (C) 2012, Vikram Narayanan
5 * Unified build_opt_tbl and nvramtool
7 * Copyright (C) 2003 Eric Biederman (ebiederm@xmission.com)
8 * Copyright (C) 2007-2010 coresystems GmbH
10 * 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>.
15 * All rights reserved.
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 https://coreboot.org/nvramtool.
21 * Please also read the file DISCLAIMER which is included in this software
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.
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.
32 \*****************************************************************************/
39 #include "coreboot_tables.h"
40 #include "ip_checksum.h"
43 #include "cmos_lowlevel.h"
46 #include "layout-text.h"
48 static void process_cmos_table(void);
49 static void get_cmos_checksum_info(void);
50 static void try_convert_checksum_layout(cmos_checksum_layout_t
* layout
);
51 static void try_add_cmos_table_enum(cmos_enum_t
* cmos_enum
);
52 static void try_add_cmos_table_entry(cmos_entry_t
* cmos_entry
);
53 static const struct cmos_entries
*first_cmos_table_entry(void);
54 static const struct cmos_entries
*next_cmos_table_entry(const struct
56 static const struct cmos_enums
*first_cmos_table_enum(void);
57 static const struct cmos_enums
*next_cmos_table_enum
58 (const struct cmos_enums
*last
);
59 static const struct lb_record
*first_cmos_rec(uint32_t tag
);
60 static const struct lb_record
*next_cmos_rec(const struct lb_record
*last
,
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.
66 static const struct cmos_option_table
*cmos_table
= NULL
;
68 #define ROUNDUP4(x) (x += (4 - (x % 4)))
70 void process_layout(void)
72 if ((cmos_table
) == NULL
) {
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",
82 get_cmos_checksum_info();
85 void get_layout_from_cbfs_file(void)
88 cmos_table
= cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT
, &len
);
92 int write_cmos_layout_bin(FILE *f
)
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
;
104 for (cmos_entry
= first_cmos_entry(); cmos_entry
!= NULL
;
105 cmos_entry
= next_cmos_entry(cmos_entry
)) {
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
;
113 if (fwrite((char *)&table
, sizeof(table
), 1, f
) != 1) {
114 perror("Error writing image file");
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
;
126 if (!is_ident((char *)cmos_entry
->name
)) {
128 "Error - Name %s is an invalid identifier\n",
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;
140 entry
.size
= sizeof(entry
) - CMOS_MAX_NAME_LENGTH
+ len
;
142 if (fwrite((char *)&entry
, entry
.size
, 1, f
) != 1) {
143 perror("Error writing image file");
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;
159 cenum
.config_id
= cmos_enum
->config_id
;
160 cenum
.value
= cmos_enum
->value
;
161 cenum
.size
= sizeof(cenum
) - CMOS_MAX_TEXT_LENGTH
+ len
;
163 if (fwrite((char *)&cenum
, cenum
.size
, 1, f
) != 1) {
164 perror("Error writing image file");
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
);
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
;
182 if (fwrite((char *)&csum
, csum
.size
, 1, f
) != 1) {
183 perror("Error writing image file");
187 if (fseek(f
, 0, SEEK_SET
) != 0) {
188 perror("Error while seeking");
193 if (fwrite((char *)&table
, sizeof(table
), 1, f
) != 1) {
194 perror("Error writing image file");
204 void write_cmos_output_bin(const char *binary_filename
)
208 if ((fp
= fopen(binary_filename
, "wb")) == NULL
) {
210 "%s: Can not open file %s for writing: "
211 "%s\n", prog_name
, binary_filename
, strerror(errno
));
214 write_cmos_layout_bin(fp
);
218 /****************************************************************************
219 * get_layout_from_cmos_table
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 ****************************************************************************/
224 void get_layout_from_cmos_table(void)
227 cmos_table
= (const struct cmos_option_table
*)
228 find_lbrec(LB_TAG_CMOS_OPTION_TABLE
);
232 /****************************************************************************
235 * Extract layout information from the CMOS option table and store it in our
236 * internal repository.
237 ****************************************************************************/
238 static void process_cmos_table(void)
240 const struct cmos_enums
*p
;
241 const struct cmos_entries
*q
;
242 cmos_enum_t cmos_enum
;
243 cmos_entry_t cmos_entry
;
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
);
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.
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
;
265 cmos_entry
.config
= CMOS_ENTRY_ENUM
;
269 cmos_entry
.config
= CMOS_ENTRY_HEX
;
273 cmos_entry
.config
= CMOS_ENTRY_RESERVED
;
277 cmos_entry
.config
= CMOS_ENTRY_STRING
;
282 "%s: Entry in CMOS option table has unknown config "
283 "value.\n", prog_name
);
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
);
294 /****************************************************************************
295 * get_cmos_checksum_info
297 * Get layout information for CMOS checksum.
298 ****************************************************************************/
299 static void get_cmos_checksum_info(void)
301 const cmos_entry_t
*e
;
302 struct cmos_checksum
*checksum
;
303 cmos_checksum_layout_t layout
;
304 unsigned index
, index2
;
306 checksum
= (struct cmos_checksum
*)next_cmos_rec((const struct lb_record
*)first_cmos_table_enum(), LB_TAG_OPTION_CHECKSUM
);
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.
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
;
321 if ((e
= find_cmos_entry(checksum_param_name
)) == NULL
)
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.
332 "%s: Error: CMOS checksum is not byte-aligned.\n",
338 index2
= index
+ 1; /* The CMOS checksum occupies 16 bits. */
340 if (verify_cmos_byte_index(index
) || verify_cmos_byte_index(index2
)) {
342 "%s: Error: CMOS checksum location out of range.\n",
347 if (((index
>= cmos_checksum_start
) && (index
<= cmos_checksum_end
)) ||
348 (((index2
) >= cmos_checksum_start
)
349 && ((index2
) <= cmos_checksum_end
))) {
351 "%s: Error: CMOS checksum overlaps checksummed area.\n",
356 cmos_checksum_index
= index
;
359 /****************************************************************************
360 * try_convert_checksum_layout
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 ****************************************************************************/
366 static void try_convert_checksum_layout(cmos_checksum_layout_t
* layout
)
368 switch (checksum_layout_to_bytes(layout
)) {
372 case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED
:
374 "%s: CMOS checksummed area start is not byte-aligned.\n",
378 case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED
:
380 "%s: CMOS checksummed area end is not byte-aligned.\n",
384 case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED
:
386 "%s: CMOS checksum location is not byte-aligned.\n",
390 case LAYOUT_INVALID_SUMMED_AREA
:
392 "%s: CMOS checksummed area end must be greater than "
393 "CMOS checksummed area start.\n", prog_name
);
396 case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA
:
398 "%s: CMOS checksum overlaps checksummed area.\n",
402 case LAYOUT_SUMMED_AREA_OUT_OF_RANGE
:
404 "%s: CMOS checksummed area out of range.\n", prog_name
);
407 case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE
:
409 "%s: CMOS checksum location out of range.\n",
420 /****************************************************************************
421 * try_add_cmos_table_enum
423 * Attempt to add a CMOS enum to our internal repository. Exit with an error
424 * message on failure.
425 ****************************************************************************/
426 static void try_add_cmos_table_enum(cmos_enum_t
* cmos_enum
)
428 switch (add_cmos_enum(cmos_enum
)) {
432 case LAYOUT_DUPLICATE_ENUM
:
433 fprintf(stderr
, "%s: Duplicate enum %s found in CMOS option "
434 "table.\n", prog_name
, cmos_enum
->text
);
444 /****************************************************************************
445 * try_add_cmos_table_entry
447 * Attempt to add a CMOS entry to our internal repository. Exit with an
448 * error message on failure.
449 ****************************************************************************/
450 static void try_add_cmos_table_entry(cmos_entry_t
* cmos_entry
)
452 const cmos_entry_t
*conflict
;
454 switch (add_cmos_entry(cmos_entry
, &conflict
)) {
458 case CMOS_AREA_OUT_OF_RANGE
:
460 "%s: Bad CMOS option layout in CMOS option table entry "
461 "%s.\n", prog_name
, cmos_entry
->name
);
464 case CMOS_AREA_TOO_WIDE
:
466 "%s: Area too wide for CMOS option table entry %s.\n",
467 prog_name
, cmos_entry
->name
);
470 case LAYOUT_ENTRY_OVERLAP
:
472 "%s: CMOS option table entries %s and %s have overlapping "
473 "layouts.\n", prog_name
, cmos_entry
->name
,
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.
484 case LAYOUT_MULTIBYTE_ENTRY_NOT_ALIGNED
:
486 "%s: Unaligned CMOS option table entry %s "
487 "spans multiple bytes.\n", prog_name
, cmos_entry
->name
);
497 /****************************************************************************
498 * first_cmos_table_entry
500 * Return a pointer to the first entry in the CMOS table that represents a
501 * CMOS parameter. Return NULL if CMOS table is empty.
502 ****************************************************************************/
503 static const struct cmos_entries
*first_cmos_table_entry(void)
505 return (const struct cmos_entries
*)first_cmos_rec(LB_TAG_OPTION
);
508 /****************************************************************************
509 * next_cmos_table_entry
511 * Return a pointer to the next entry after 'last' in the CMOS table that
512 * represents a CMOS parameter. Return NULL if there are no more parameters.
513 ****************************************************************************/
514 static const struct cmos_entries
*next_cmos_table_entry(const struct
517 return (const struct cmos_entries
*)
518 next_cmos_rec((const struct lb_record
*)last
, LB_TAG_OPTION
);
521 /****************************************************************************
522 * first_cmos_table_enum
524 * Return a pointer to the first entry in the CMOS table that represents a
525 * possible CMOS parameter value. Return NULL if the table does not contain
527 ****************************************************************************/
528 static const struct cmos_enums
*first_cmos_table_enum(void)
530 return (const struct cmos_enums
*)first_cmos_rec(LB_TAG_OPTION_ENUM
);
533 /****************************************************************************
534 * next_cmos_table_enum
536 * Return a pointer to the next entry after 'last' in the CMOS table that
537 * represents a possible CMOS parameter value. Return NULL if there are no
538 * more parameter values.
539 ****************************************************************************/
540 static const struct cmos_enums
*next_cmos_table_enum
541 (const struct cmos_enums
*last
) {
542 return (const struct cmos_enums
*)
543 next_cmos_rec((const struct lb_record
*)last
, LB_TAG_OPTION_ENUM
);
546 /****************************************************************************
549 * Return a pointer to the first entry in the CMOS table whose type matches
550 * 'tag'. Return NULL if CMOS table contains no such entry.
552 * Possible values for 'tag' are as follows:
554 * LB_TAG_OPTION: The entry represents a CMOS parameter.
555 * LB_TAG_OPTION_ENUM: The entry represents a possible value for a CMOS
556 * parameter of type 'enum'.
558 * The CMOS table tells us where in the nonvolatile RAM to look for CMOS
559 * parameter values and specifies their types as 'enum', 'hex', or
561 ****************************************************************************/
562 static const struct lb_record
*first_cmos_rec(uint32_t tag
)
565 uint32_t bytes_processed
, bytes_for_entries
;
566 const struct lb_record
*lbrec
;
568 p
= ((const char *)cmos_table
) + cmos_table
->header_length
;
569 bytes_for_entries
= cmos_table
->size
- cmos_table
->header_length
;
571 for (bytes_processed
= 0;
572 bytes_processed
< bytes_for_entries
;
573 bytes_processed
+= lbrec
->size
) {
574 lbrec
= (const struct lb_record
*)&p
[bytes_processed
];
576 if (lbrec
->tag
== tag
)
583 /****************************************************************************
586 * Return a pointer to the next entry after 'last' in the CMOS table whose
587 * type matches 'tag'. Return NULL if the table contains no more entries of
589 ****************************************************************************/
590 static const struct lb_record
*next_cmos_rec(const struct lb_record
*last
,
594 uint32_t bytes_processed
, bytes_for_entries
, last_offset
;
595 const struct lb_record
*lbrec
;
597 p
= ((const char *)cmos_table
) + cmos_table
->header_length
;
598 bytes_for_entries
= cmos_table
->size
- cmos_table
->header_length
;
599 last_offset
= ((const char *)last
) - p
;
601 for (bytes_processed
= last_offset
+ last
->size
;
602 bytes_processed
< bytes_for_entries
;
603 bytes_processed
+= lbrec
->size
) {
604 lbrec
= (const struct lb_record
*)&p
[bytes_processed
];
606 if (lbrec
->tag
== tag
)