1 /* SPDX-License-Identifier: GPL-2.0-only */
8 #include "coreboot_tables.h"
9 #include "ip_checksum.h"
12 #include "cmos_lowlevel.h"
15 #include "layout-text.h"
17 static void process_cmos_table(void);
18 static void get_cmos_checksum_info(void);
19 static void try_convert_checksum_layout(cmos_checksum_layout_t
* layout
);
20 static void try_add_cmos_table_enum(cmos_enum_t
* cmos_enum
);
21 static void try_add_cmos_table_entry(cmos_entry_t
* cmos_entry
);
22 static const struct cmos_entries
*first_cmos_table_entry(void);
23 static const struct cmos_entries
*next_cmos_table_entry(const struct
25 static const struct cmos_enums
*first_cmos_table_enum(void);
26 static const struct cmos_enums
*next_cmos_table_enum
27 (const struct cmos_enums
*last
);
28 static const struct lb_record
*first_cmos_rec(uint32_t tag
);
29 static const struct lb_record
*next_cmos_rec(const struct lb_record
*last
,
32 /* The CMOS option table is located within the coreboot table. It tells us
33 * where the CMOS parameters are located in the nonvolatile RAM.
35 static const struct cmos_option_table
*cmos_table
= NULL
;
37 #define ROUNDUP4(x) (x += (4 - (x % 4)))
39 void process_layout(void)
41 if ((cmos_table
) == NULL
) {
43 "%s: CMOS option table not found in coreboot table. "
44 "Apparently, the coreboot installed on this system was "
45 "built without selecting CONFIG_USE_OPTION_TABLE.\n",
51 get_cmos_checksum_info();
54 void get_layout_from_cbfs_file(void)
57 cmos_table
= cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT
, &len
);
61 static int write_cmos_layout_bin(FILE *f
)
63 const cmos_entry_t
*cmos_entry
;
64 const cmos_enum_t
*cmos_enum
;
65 cmos_checksum_layout_t layout
;
66 struct cmos_option_table table
;
67 struct cmos_entries entry
;
68 struct cmos_enums cenum
;
69 struct cmos_checksum csum
;
73 for (cmos_entry
= first_cmos_entry(); cmos_entry
!= NULL
;
74 cmos_entry
= next_cmos_entry(cmos_entry
)) {
76 if (cmos_entry
== first_cmos_entry()) {
78 table
.header_length
= sizeof(table
);
79 table
.tag
= LB_TAG_CMOS_OPTION_TABLE
;
82 if (fwrite((char *)&table
, sizeof(table
), 1, f
) != 1) {
83 perror("Error writing image file");
88 memset(&entry
, 0, sizeof(entry
));
89 entry
.tag
= LB_TAG_OPTION
;
90 entry
.config
= cmos_entry
->config
;
91 entry
.config_id
= (uint32_t)cmos_entry
->config_id
;
92 entry
.bit
= cmos_entry
->bit
;
93 entry
.length
= cmos_entry
->length
;
95 if (!is_ident((char *)cmos_entry
->name
)) {
97 "Error - Name %s is an invalid identifier\n",
102 memcpy(entry
.name
, cmos_entry
->name
, strlen(cmos_entry
->name
));
103 entry
.name
[strlen(cmos_entry
->name
)] = '\0';
104 len
= strlen(cmos_entry
->name
) + 1;
109 entry
.size
= sizeof(entry
) - CMOS_MAX_NAME_LENGTH
+ len
;
111 if (fwrite((char *)&entry
, entry
.size
, 1, f
) != 1) {
112 perror("Error writing image file");
117 for (cmos_enum
= first_cmos_enum();
118 cmos_enum
!= NULL
; cmos_enum
= next_cmos_enum(cmos_enum
)) {
119 memset(&cenum
, 0, sizeof(cenum
));
120 cenum
.tag
= LB_TAG_OPTION_ENUM
;
121 memcpy(cenum
.text
, cmos_enum
->text
, strlen(cmos_enum
->text
));
122 cenum
.text
[strlen(cmos_enum
->text
)] = '\0';
123 len
= strlen((char *)cenum
.text
) + 1;
128 cenum
.config_id
= cmos_enum
->config_id
;
129 cenum
.value
= cmos_enum
->value
;
130 cenum
.size
= sizeof(cenum
) - CMOS_MAX_TEXT_LENGTH
+ len
;
132 if (fwrite((char *)&cenum
, cenum
.size
, 1, f
) != 1) {
133 perror("Error writing image file");
138 layout
.summed_area_start
= cmos_checksum_start
;
139 layout
.summed_area_end
= cmos_checksum_end
;
140 layout
.checksum_at
= cmos_checksum_index
;
141 checksum_layout_to_bits(&layout
);
143 csum
.tag
= LB_TAG_OPTION_CHECKSUM
;
144 csum
.size
= sizeof(csum
);
145 csum
.range_start
= layout
.summed_area_start
;
146 csum
.range_end
= layout
.summed_area_end
;
147 csum
.location
= layout
.checksum_at
;
148 csum
.type
= CHECKSUM_PCBIOS
;
151 if (fwrite((char *)&csum
, csum
.size
, 1, f
) != 1) {
152 perror("Error writing image file");
156 if (fseek(f
, 0, SEEK_SET
) != 0) {
157 perror("Error while seeking");
162 if (fwrite((char *)&table
, sizeof(table
), 1, f
) != 1) {
163 perror("Error writing image file");
173 void write_cmos_output_bin(const char *binary_filename
)
177 if ((fp
= fopen(binary_filename
, "wb")) == NULL
) {
179 "%s: Can not open file %s for writing: "
180 "%s\n", prog_name
, binary_filename
, strerror(errno
));
183 write_cmos_layout_bin(fp
);
187 /****************************************************************************
188 * get_layout_from_cmos_table
190 * Find the CMOS table which is stored within the coreboot table and set the
191 * global variable cmos_table to point to it.
192 ****************************************************************************/
193 void get_layout_from_cmos_table(void)
196 cmos_table
= (const struct cmos_option_table
*)
197 find_lbrec(LB_TAG_CMOS_OPTION_TABLE
);
201 /****************************************************************************
204 * Extract layout information from the CMOS option table and store it in our
205 * internal repository.
206 ****************************************************************************/
207 static void process_cmos_table(void)
209 const struct cmos_enums
*p
;
210 const struct cmos_entries
*q
;
211 cmos_enum_t cmos_enum
;
212 cmos_entry_t cmos_entry
;
214 /* First add the enums. */
215 for (p
= first_cmos_table_enum(); p
!= NULL
;
216 p
= next_cmos_table_enum(p
)) {
217 cmos_enum
.config_id
= p
->config_id
;
218 cmos_enum
.value
= p
->value
;
219 strncpy(cmos_enum
.text
, (char *)p
->text
, CMOS_MAX_TEXT_LENGTH
);
220 cmos_enum
.text
[CMOS_MAX_TEXT_LENGTH
] = '\0';
221 try_add_cmos_table_enum(&cmos_enum
);
224 /* Now add the entries. We must add the entries after the enums because
225 * the entries are sanity checked against the enums as they are added.
227 for (q
= first_cmos_table_entry(); q
!= NULL
;
228 q
= next_cmos_table_entry(q
)) {
229 cmos_entry
.bit
= q
->bit
;
230 cmos_entry
.length
= q
->length
;
234 cmos_entry
.config
= CMOS_ENTRY_ENUM
;
238 cmos_entry
.config
= CMOS_ENTRY_HEX
;
242 cmos_entry
.config
= CMOS_ENTRY_RESERVED
;
246 cmos_entry
.config
= CMOS_ENTRY_STRING
;
251 "%s: Entry in CMOS option table has unknown config "
252 "value.\n", prog_name
);
256 cmos_entry
.config_id
= q
->config_id
;
257 strncpy(cmos_entry
.name
, (char *)q
->name
, CMOS_MAX_NAME_LENGTH
);
258 cmos_entry
.name
[CMOS_MAX_NAME_LENGTH
] = '\0';
259 try_add_cmos_table_entry(&cmos_entry
);
263 /****************************************************************************
264 * get_cmos_checksum_info
266 * Get layout information for CMOS checksum.
267 ****************************************************************************/
268 static void get_cmos_checksum_info(void)
270 const cmos_entry_t
*e
;
271 struct cmos_checksum
*checksum
;
272 cmos_checksum_layout_t layout
;
273 unsigned index
, index2
;
275 checksum
= (struct cmos_checksum
*)next_cmos_rec((const struct lb_record
*)first_cmos_table_enum(), LB_TAG_OPTION_CHECKSUM
);
277 if (checksum
!= NULL
) { /* We are lucky. The coreboot table hints us to the checksum.
278 * We might have to check the type field here though.
280 layout
.summed_area_start
= checksum
->range_start
;
281 layout
.summed_area_end
= checksum
->range_end
;
282 layout
.checksum_at
= checksum
->location
;
283 try_convert_checksum_layout(&layout
);
284 cmos_checksum_start
= layout
.summed_area_start
;
285 cmos_checksum_end
= layout
.summed_area_end
;
286 cmos_checksum_index
= layout
.checksum_at
;
290 if ((e
= find_cmos_entry(checksum_param_name
)) == NULL
)
293 /* If we get here, we are unlucky. The CMOS option table contains the
294 * location of the CMOS checksum. However, there is no information
295 * regarding which bytes of the CMOS area the checksum is computed over.
296 * Thus we have to hope our presets will be fine.
301 "%s: Error: CMOS checksum is not byte-aligned.\n",
307 index2
= index
+ 1; /* The CMOS checksum occupies 16 bits. */
309 if (verify_cmos_byte_index(index
) || verify_cmos_byte_index(index2
)) {
311 "%s: Error: CMOS checksum location out of range.\n",
316 if (((index
>= cmos_checksum_start
) && (index
<= cmos_checksum_end
)) ||
317 (((index2
) >= cmos_checksum_start
)
318 && ((index2
) <= cmos_checksum_end
))) {
320 "%s: Error: CMOS checksum overlaps checksummed area.\n",
325 cmos_checksum_index
= index
;
328 /****************************************************************************
329 * try_convert_checksum_layout
331 * Perform sanity checking on CMOS checksum layout information and attempt to
332 * convert information from bit positions to byte positions. Return OK on
333 * success or an error code on failure.
334 ****************************************************************************/
335 static void try_convert_checksum_layout(cmos_checksum_layout_t
* layout
)
337 switch (checksum_layout_to_bytes(layout
)) {
341 case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED
:
343 "%s: CMOS checksummed area start is not byte-aligned.\n",
347 case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED
:
349 "%s: CMOS checksummed area end is not byte-aligned.\n",
353 case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED
:
355 "%s: CMOS checksum location is not byte-aligned.\n",
359 case LAYOUT_INVALID_SUMMED_AREA
:
361 "%s: CMOS checksummed area end must be greater than "
362 "CMOS checksummed area start.\n", prog_name
);
365 case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA
:
367 "%s: CMOS checksum overlaps checksummed area.\n",
371 case LAYOUT_SUMMED_AREA_OUT_OF_RANGE
:
373 "%s: CMOS checksummed area out of range.\n", prog_name
);
376 case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE
:
378 "%s: CMOS checksum location out of range.\n",
389 /****************************************************************************
390 * try_add_cmos_table_enum
392 * Attempt to add a CMOS enum to our internal repository. Exit with an error
393 * message on failure.
394 ****************************************************************************/
395 static void try_add_cmos_table_enum(cmos_enum_t
* cmos_enum
)
397 switch (add_cmos_enum(cmos_enum
)) {
401 case LAYOUT_DUPLICATE_ENUM
:
402 fprintf(stderr
, "%s: Duplicate enum %s found in CMOS option "
403 "table.\n", prog_name
, cmos_enum
->text
);
413 /****************************************************************************
414 * try_add_cmos_table_entry
416 * Attempt to add a CMOS entry to our internal repository. Exit with an
417 * error message on failure.
418 ****************************************************************************/
419 static void try_add_cmos_table_entry(cmos_entry_t
* cmos_entry
)
421 const cmos_entry_t
*conflict
;
423 switch (add_cmos_entry(cmos_entry
, &conflict
)) {
427 case CMOS_AREA_OUT_OF_RANGE
:
429 "%s: Bad CMOS option layout in CMOS option table entry "
430 "%s.\n", prog_name
, cmos_entry
->name
);
433 case CMOS_AREA_TOO_WIDE
:
435 "%s: Area too wide for CMOS option table entry %s.\n",
436 prog_name
, cmos_entry
->name
);
439 case LAYOUT_ENTRY_OVERLAP
:
441 "%s: CMOS option table entries %s and %s have overlapping "
442 "layouts.\n", prog_name
, cmos_entry
->name
,
446 case LAYOUT_ENTRY_BAD_LENGTH
:
447 /* Silently ignore entries with zero length. Although this should
448 * never happen in practice, we should handle the case in a
449 * reasonable manner just to be safe.
453 case LAYOUT_MULTIBYTE_ENTRY_NOT_ALIGNED
:
455 "%s: Unaligned CMOS option table entry %s "
456 "spans multiple bytes.\n", prog_name
, cmos_entry
->name
);
466 /****************************************************************************
467 * first_cmos_table_entry
469 * Return a pointer to the first entry in the CMOS table that represents a
470 * CMOS parameter. Return NULL if CMOS table is empty.
471 ****************************************************************************/
472 static const struct cmos_entries
*first_cmos_table_entry(void)
474 return (const struct cmos_entries
*)first_cmos_rec(LB_TAG_OPTION
);
477 /****************************************************************************
478 * next_cmos_table_entry
480 * Return a pointer to the next entry after 'last' in the CMOS table that
481 * represents a CMOS parameter. Return NULL if there are no more parameters.
482 ****************************************************************************/
483 static const struct cmos_entries
*next_cmos_table_entry(const struct
486 return (const struct cmos_entries
*)
487 next_cmos_rec((const struct lb_record
*)last
, LB_TAG_OPTION
);
490 /****************************************************************************
491 * first_cmos_table_enum
493 * Return a pointer to the first entry in the CMOS table that represents a
494 * possible CMOS parameter value. Return NULL if the table does not contain
496 ****************************************************************************/
497 static const struct cmos_enums
*first_cmos_table_enum(void)
499 return (const struct cmos_enums
*)first_cmos_rec(LB_TAG_OPTION_ENUM
);
502 /****************************************************************************
503 * next_cmos_table_enum
505 * Return a pointer to the next entry after 'last' in the CMOS table that
506 * represents a possible CMOS parameter value. Return NULL if there are no
507 * more parameter values.
508 ****************************************************************************/
509 static const struct cmos_enums
*next_cmos_table_enum
510 (const struct cmos_enums
*last
) {
511 return (const struct cmos_enums
*)
512 next_cmos_rec((const struct lb_record
*)last
, LB_TAG_OPTION_ENUM
);
515 /****************************************************************************
518 * Return a pointer to the first entry in the CMOS table whose type matches
519 * 'tag'. Return NULL if CMOS table contains no such entry.
521 * Possible values for 'tag' are as follows:
523 * LB_TAG_OPTION: The entry represents a CMOS parameter.
524 * LB_TAG_OPTION_ENUM: The entry represents a possible value for a CMOS
525 * parameter of type 'enum'.
527 * The CMOS table tells us where in the nonvolatile RAM to look for CMOS
528 * parameter values and specifies their types as 'enum', 'hex', or
530 ****************************************************************************/
531 static const struct lb_record
*first_cmos_rec(uint32_t tag
)
534 uint32_t bytes_processed
, bytes_for_entries
;
535 const struct lb_record
*lbrec
;
537 p
= ((const char *)cmos_table
) + cmos_table
->header_length
;
538 bytes_for_entries
= cmos_table
->size
- cmos_table
->header_length
;
540 for (bytes_processed
= 0;
541 bytes_processed
< bytes_for_entries
;
542 bytes_processed
+= lbrec
->size
) {
543 lbrec
= (const struct lb_record
*)&p
[bytes_processed
];
545 if (lbrec
->tag
== tag
)
552 /****************************************************************************
555 * Return a pointer to the next entry after 'last' in the CMOS table whose
556 * type matches 'tag'. Return NULL if the table contains no more entries of
558 ****************************************************************************/
559 static const struct lb_record
*next_cmos_rec(const struct lb_record
*last
,
563 uint32_t bytes_processed
, bytes_for_entries
, last_offset
;
564 const struct lb_record
*lbrec
;
566 p
= ((const char *)cmos_table
) + cmos_table
->header_length
;
567 bytes_for_entries
= cmos_table
->size
- cmos_table
->header_length
;
568 last_offset
= ((const char *)last
) - p
;
570 for (bytes_processed
= last_offset
+ last
->size
;
571 bytes_processed
< bytes_for_entries
;
572 bytes_processed
+= lbrec
->size
) {
573 lbrec
= (const struct lb_record
*)&p
[bytes_processed
];
575 if (lbrec
->tag
== tag
)