xref: /aosp_15_r20/external/coreboot/util/nvramtool/lbtable.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <stdint.h>
4 #include <inttypes.h>
5 #include <string.h>
6 #ifndef __MINGW32__
7 #include <sys/mman.h>
8 #endif
9 #include "common.h"
10 #include "coreboot_tables.h"
11 #include "ip_checksum.h"
12 #include "lbtable.h"
13 #include "layout.h"
14 #include "cmos_lowlevel.h"
15 #include "hexdump.h"
16 #include "cbfs.h"
17 
18 typedef void (*lbtable_print_fn_t) (const struct lb_record * rec);
19 
20 /* This structure represents an item in the coreboot table that may be
21  * displayed using the -l option.
22  */
23 typedef struct {
24 	uint32_t tag;
25 	const char *name;
26 	const char *description;
27 	const char *nofound_msg;
28 	lbtable_print_fn_t print_fn;
29 } lbtable_choice_t;
30 
31 typedef struct {
32 	unsigned long start;	/* address of first byte of memory range */
33 	unsigned long end;	/* address of last byte of memory range */
34 } mem_range_t;
35 
36 static const struct lb_header *lbtable_scan(unsigned long start,
37 					    unsigned long end,
38 					    int *bad_header_count,
39 					    int *bad_table_count);
40 static const char *lbrec_tag_to_str(uint32_t tag);
41 static void memory_print_fn(const struct lb_record *rec);
42 static void mainboard_print_fn(const struct lb_record *rec);
43 static void cmos_opt_table_print_fn(const struct lb_record *rec);
44 static void print_option_record(const struct cmos_entries *cmos_entry);
45 static void print_enum_record(const struct cmos_enums *cmos_enum);
46 static void print_defaults_record(const struct cmos_defaults *cmos_defaults);
47 static void print_unknown_record(const struct lb_record *cmos_item);
48 static void option_checksum_print_fn(const struct lb_record *rec);
49 static void string_print_fn(const struct lb_record *rec);
50 
51 static const char memory_desc[] =
52     "    This shows information about system memory.\n";
53 
54 static const char mainboard_desc[] =
55     "    This shows information about your mainboard.\n";
56 
57 static const char version_desc[] =
58     "    This shows coreboot version information.\n";
59 
60 static const char extra_version_desc[] =
61     "    This shows extra coreboot version information.\n";
62 
63 static const char build_desc[] = "    This shows coreboot build information.\n";
64 
65 static const char compile_time_desc[] =
66     "    This shows when coreboot was compiled.\n";
67 
68 static const char compile_by_desc[] = "    This shows who compiled coreboot.\n";
69 
70 static const char compile_host_desc[] =
71     "    This shows the name of the machine that compiled coreboot.\n";
72 
73 static const char compile_domain_desc[] =
74     "    This shows the domain name of the machine that compiled coreboot.\n";
75 
76 static const char compiler_desc[] =
77     "    This shows the name of the compiler used to build coreboot.\n";
78 
79 static const char linker_desc[] =
80     "    This shows the name of the linker used to build coreboot.\n";
81 
82 static const char assembler_desc[] =
83     "    This shows the name of the assembler used to build coreboot.\n";
84 
85 static const char cmos_opt_table_desc[] =
86     "    This does a low-level dump of the CMOS option table.  The table "
87     "contains\n"
88     "    information about the layout of the values that coreboot stores in\n"
89     "    nonvolatile RAM.\n";
90 
91 static const char option_checksum_desc[] =
92     "    This shows the location of the CMOS checksum and the area over which it "
93     "is\n" "    calculated.\n";
94 
95 static const char generic_nofound_msg[] =
96     "%s: Item %s not found in coreboot table.\n";
97 
98 static const char nofound_msg_cmos_opt_table[] =
99     "%s: Item %s not found in coreboot table.  Apparently, the "
100     "coreboot installed on this system was built without specifying "
101     "CONFIG_HAVE_OPTION_TABLE.\n";
102 
103 static const char nofound_msg_option_checksum[] =
104     "%s: Item %s not found in coreboot table. Apparently, you are "
105     "using coreboot v1.\n";
106 
107 int fd;
108 
109 /* This is the number of items from the coreboot table that may be displayed
110  * using the -l option.
111  */
112 #define NUM_LBTABLE_CHOICES 14
113 
114 /* These represent the various items from the coreboot table that may be
115  * displayed using the -l option.
116  */
117 static const lbtable_choice_t lbtable_choices[NUM_LBTABLE_CHOICES] =
118     { {LB_TAG_MEMORY, "memory",
119        memory_desc, generic_nofound_msg,
120        memory_print_fn},
121 {LB_TAG_MAINBOARD, "mainboard",
122  mainboard_desc, generic_nofound_msg,
123  mainboard_print_fn},
124 {LB_TAG_VERSION, "version",
125  version_desc, generic_nofound_msg,
126  string_print_fn},
127 {LB_TAG_EXTRA_VERSION, "extra_version",
128  extra_version_desc, generic_nofound_msg,
129  string_print_fn},
130 {LB_TAG_BUILD, "build",
131  build_desc, generic_nofound_msg,
132  string_print_fn},
133 {LB_TAG_COMPILE_TIME, "compile_time",
134  compile_time_desc, generic_nofound_msg,
135  string_print_fn},
136 {LB_TAG_COMPILE_BY, "compile_by",
137  compile_by_desc, generic_nofound_msg,
138  string_print_fn},
139 {LB_TAG_COMPILE_HOST, "compile_host",
140  compile_host_desc, generic_nofound_msg,
141  string_print_fn},
142 {LB_TAG_COMPILE_DOMAIN, "compile_domain",
143  compile_domain_desc, generic_nofound_msg,
144  string_print_fn},
145 {LB_TAG_COMPILER, "compiler",
146  compiler_desc, generic_nofound_msg,
147  string_print_fn},
148 {LB_TAG_LINKER, "linker",
149  linker_desc, generic_nofound_msg,
150  string_print_fn},
151 {LB_TAG_ASSEMBLER, "assembler",
152  assembler_desc, generic_nofound_msg,
153  string_print_fn},
154 {LB_TAG_CMOS_OPTION_TABLE, "cmos_opt_table",
155  cmos_opt_table_desc, nofound_msg_cmos_opt_table,
156  cmos_opt_table_print_fn},
157 {LB_TAG_OPTION_CHECKSUM, "option_checksum",
158  option_checksum_desc, nofound_msg_option_checksum,
159  option_checksum_print_fn}
160 };
161 
162 /* The coreboot table resides in low physical memory, which we access using
163  * /dev/mem.  These are ranges of physical memory that should be scanned for a
164  * coreboot table.
165  */
166 
167 #define NUM_MEM_RANGES 2
168 
169 static const mem_range_t mem_ranges[NUM_MEM_RANGES] =
170     { {0x00000000, 0x00000fff},
171 {0x000f0000, 0x000fffff}
172 };
173 
174 /* Pointer to low physical memory that we access by calling mmap() on
175  * /dev/mem.
176  */
177 static const void *low_phys_mem;
178 /* impossible value since not page aligned: first map request will happen */
179 static unsigned long low_phys_base = 0x1;
180 
181 /* count of mapped pages */
182 static unsigned long mapped_pages = 0;
183 
184 /* Pointer to coreboot table. */
185 static const struct lb_header *lbtable = NULL;
186 
187 static const hexdump_format_t format =
188     { 12, 4, "            ", " | ", " ", " | ", '.' };
189 
190 /****************************************************************************
191  * vtophys
192  *
193  * Convert a virtual address to a physical address.  'vaddr' is a virtual
194  * address in the address space of the current process.  It points to
195  * somewhere in the chunk of memory that we mapped by calling mmap() on
196  * /dev/mem.  This macro converts 'vaddr' to a physical address.
197  ****************************************************************************/
198 #define vtophys(vaddr) (((unsigned long) vaddr) -       \
199                         ((unsigned long) low_phys_mem) + low_phys_base)
200 
201 /****************************************************************************
202  * phystov
203  *
204  * Convert a physical address to a virtual address.  'paddr' is a physical
205  * address.  This macro converts 'paddr' to a virtual address in the address
206  * space of the current process.  The virtual to physical mapping was set up
207  * by calling mmap() on /dev/mem.
208  ****************************************************************************/
209 #define phystov(paddr) (((unsigned long) low_phys_mem) + \
210                         ((unsigned long) paddr) - low_phys_base)
211 
212 /****************************************************************************
213  * map_pages
214  *
215  * Maps just enough pages to cover base_address + length
216  * and updates affected variables
217  ****************************************************************************/
map_pages(unsigned long base_address,unsigned long length)218 static void map_pages(unsigned long base_address, unsigned long length)
219 {
220 	unsigned long num_pages = (length +
221 			(base_address & (getpagesize() - 1)) +
222 			getpagesize() - 1) >> 12;
223 	base_address &= ~(getpagesize() - 1);
224 
225 	/* no need to do anything */
226 	if ((low_phys_base == base_address) && (mapped_pages == num_pages)) {
227 		return;
228 	}
229 
230 	if (low_phys_mem) {
231 		munmap((void *)low_phys_mem, mapped_pages << 12);
232 	}
233 	if ((low_phys_mem = mmap(NULL, num_pages << 12, PROT_READ, MAP_SHARED, fd,
234 		  (off_t) base_address)) == MAP_FAILED) {
235 		fprintf(stderr,
236 			"%s: Failed to mmap /dev/mem at %lx: %s\n",
237 			prog_name, base_address, strerror(errno));
238 		exit(1);
239 	}
240 	low_phys_base = base_address;
241 }
242 
243 /****************************************************************************
244  * get_lbtable
245  *
246  * Find the coreboot table and set global variable lbtable to point to it.
247  ****************************************************************************/
get_lbtable(void)248 void get_lbtable(void)
249 {
250 	int i, bad_header_count, bad_table_count, bad_headers, bad_tables;
251 
252 	if (lbtable != NULL)
253 		return;
254 
255 	/* The coreboot table is located in low physical memory, which may be
256 	 * conveniently accessed by calling mmap() on /dev/mem.
257 	 */
258 
259 	if ((fd = open("/dev/mem", O_RDONLY, 0)) < 0) {
260 		fprintf(stderr, "%s: Can not open /dev/mem for reading: %s\n",
261 			prog_name, strerror(errno));
262 		exit(1);
263 	}
264 
265 	bad_header_count = 0;
266 	bad_table_count = 0;
267 
268 	for (i = 0; i < NUM_MEM_RANGES; i++) {
269 		lbtable = lbtable_scan(mem_ranges[i].start, mem_ranges[i].end,
270 				       &bad_headers, &bad_tables);
271 
272 		if (lbtable != NULL)
273 			return;	/* success: we found it! */
274 
275 		bad_header_count += bad_headers;
276 		bad_table_count += bad_tables;
277 	}
278 
279 	fprintf(stderr,
280 		"%s: coreboot table not found.  coreboot does not appear to\n"
281 		"        be installed on this system.  Scanning for the table "
282 		"produced the\n"
283 		"        following results:\n\n"
284 		"            %d valid signatures were found with bad header "
285 		"checksums.\n"
286 		"            %d valid headers were found with bad table "
287 		"checksums.\n", prog_name, bad_header_count, bad_table_count);
288 	exit(1);
289 }
290 
291 /****************************************************************************
292  * dump_lbtable
293  *
294  * Do a low-level dump of the coreboot table.
295  ****************************************************************************/
dump_lbtable(void)296 void dump_lbtable(void)
297 {
298 	const char *p, *data;
299 	uint32_t bytes_processed;
300 	const struct lb_record *lbrec;
301 
302 	p = ((const char *)lbtable) + lbtable->header_bytes;
303 	printf("coreboot table at physical address 0x%lx:\n"
304 	       "    signature:       0x%x (ASCII: %c%c%c%c)\n"
305 	       "    header_bytes:    0x%x (decimal: %d)\n"
306 	       "    header_checksum: 0x%x (decimal: %d)\n"
307 	       "    table_bytes:     0x%x (decimal: %d)\n"
308 	       "    table_checksum:  0x%x (decimal: %d)\n"
309 	       "    table_entries:   0x%x (decimal: %d)\n\n",
310 	       vtophys(lbtable), lbtable->signature32,
311 	       lbtable->signature[0], lbtable->signature[1],
312 	       lbtable->signature[2], lbtable->signature[3],
313 	       lbtable->header_bytes, lbtable->header_bytes,
314 	       lbtable->header_checksum, lbtable->header_checksum,
315 	       lbtable->table_bytes, lbtable->table_bytes,
316 	       lbtable->table_checksum, lbtable->table_checksum,
317 	       lbtable->table_entries, lbtable->table_entries);
318 
319 	if ((lbtable->table_bytes == 0) != (lbtable->table_entries == 0)) {
320 		printf
321 		    ("Inconsistent values for table_bytes and table_entries!!!\n"
322 		     "They should be either both 0 or both nonzero.\n");
323 		return;
324 	}
325 
326 	if (lbtable->table_bytes == 0) {
327 		printf("The coreboot table is empty!!!\n");
328 		return;
329 	}
330 
331 	for (bytes_processed = 0;;) {
332 		lbrec = (const struct lb_record *)&p[bytes_processed];
333 		printf("    %s record at physical address 0x%lx:\n"
334 		       "        tag:  0x%x (decimal: %d)\n"
335 		       "        size: 0x%x (decimal: %d)\n"
336 		       "        data:\n",
337 		       lbrec_tag_to_str(lbrec->tag), vtophys(lbrec), lbrec->tag,
338 		       lbrec->tag, lbrec->size, lbrec->size);
339 
340 		data = ((const char *)lbrec) + sizeof(*lbrec);
341 		hexdump(data, lbrec->size - sizeof(*lbrec), vtophys(data),
342 			stdout, &format);
343 
344 		bytes_processed += lbrec->size;
345 
346 		if (bytes_processed >= lbtable->table_bytes)
347 			break;
348 
349 		printf("\n");
350 	}
351 }
352 
353 /****************************************************************************
354  * list_lbtable_choices
355  *
356  * List names and informational blurbs for items from the coreboot table
357  * that may be displayed using the -l option.
358  ****************************************************************************/
list_lbtable_choices(void)359 void list_lbtable_choices(void)
360 {
361 	int i;
362 
363 	for (i = 0;;) {
364 		printf("%s:\n%s",
365 		       lbtable_choices[i].name, lbtable_choices[i].description);
366 
367 		if (++i >= NUM_LBTABLE_CHOICES)
368 			break;
369 
370 		printf("\n");
371 	}
372 }
373 
374 /****************************************************************************
375  * list_lbtable_item
376  *
377  * Show the coreboot table item specified by 'item'.
378  ****************************************************************************/
list_lbtable_item(const char item[])379 void list_lbtable_item(const char item[])
380 {
381 	int i;
382 	const struct lb_record *rec;
383 
384 	for (i = 0; i < NUM_LBTABLE_CHOICES; i++) {
385 		if (strcmp(item, lbtable_choices[i].name) == 0)
386 			break;
387 	}
388 
389 	if (i == NUM_LBTABLE_CHOICES) {
390 		fprintf(stderr, "%s: Invalid coreboot table item %s.\n",
391 			prog_name, item);
392 		exit(1);
393 	}
394 
395 	if ((rec = find_lbrec(lbtable_choices[i].tag)) == NULL) {
396 		fprintf(stderr, lbtable_choices[i].nofound_msg, prog_name,
397 			lbtable_choices[i].name);
398 		exit(1);
399 	}
400 
401 	lbtable_choices[i].print_fn(rec);
402 }
403 
404 /****************************************************************************
405  * lbtable_scan
406  *
407  * Scan the chunk of memory specified by 'start' and 'end' for a coreboot
408  * table.  The first 4 bytes of the table are marked by the signature
409  * { 'L', 'B', 'I', 'O' }.  'start' and 'end' indicate the addresses of the
410  * first and last bytes of the chunk of memory to be scanned.  For instance,
411  * values of 0x10000000 and 0x1000ffff for 'start' and 'end' specify a 64k
412  * chunk of memory starting at address 0x10000000.  'start' and 'end' are
413  * physical addresses.
414  *
415  * If a coreboot table is found, return a pointer to it.  Otherwise return
416  * NULL.  On return, *bad_header_count and *bad_table_count are set as
417  * follows:
418  *
419  *     *bad_header_count:
420  *         Indicates the number of times in which a valid signature was found
421  *         but the header checksum was invalid.
422  *
423  *     *bad_table_count:
424  *         Indicates the number of times in which a header with a valid
425  *         checksum was found but the table checksum was invalid.
426  ****************************************************************************/
lbtable_scan(unsigned long start,unsigned long end,int * bad_header_count,int * bad_table_count)427 static const struct lb_header *lbtable_scan(unsigned long start,
428 					    unsigned long end,
429 					    int *bad_header_count,
430 					    int *bad_table_count)
431 {
432 	static const char signature[4] = { 'L', 'B', 'I', 'O' };
433 	const struct lb_header *table;
434 	const struct lb_forward *forward;
435 	unsigned long p;
436 	uint32_t sig;
437 
438 	assert(end >= start);
439 	memcpy(&sig, signature, sizeof(sig));
440 	table = NULL;
441 	*bad_header_count = 0;
442 	*bad_table_count = 0;
443 
444 	/* Look for signature.  Table is aligned on 16-byte boundary.  Therefore
445 	 * only check every fourth 32-bit memory word.  As the loop is coded below,
446 	 * this function will behave in a reasonable manner for ALL possible values
447 	 * for 'start' and 'end': even weird boundary cases like 0x00000000 and
448 	 * 0xffffffff on a 32-bit architecture.
449 	 */
450 	map_pages(start, end - start);
451 	for (p = start;
452 	     (p <= end) &&
453 	     (end - p >= (sizeof(uint32_t) - 1)); p += 4) {
454 		if (*(uint32_t*)phystov(p) != sig)
455 			continue;
456 
457 		/* We found a valid signature. */
458 		table = (const struct lb_header *)phystov(p);
459 
460 		/* validate header checksum */
461 		if (compute_ip_checksum((void *)table, sizeof(*table))) {
462 			(*bad_header_count)++;
463 			continue;
464 		}
465 
466 		map_pages(p, table->table_bytes + sizeof(*table));
467 
468 		table = (const struct lb_header *)phystov(p);
469 
470 		/* validate table checksum */
471 		if (table->table_checksum !=
472 		    compute_ip_checksum(((char *)table) + sizeof(*table),
473 					table->table_bytes)) {
474 			(*bad_table_count)++;
475 			continue;
476 		}
477 
478 		/* checksums are ok: we found it! */
479 		/* But it may just be a forwarding table, so look if there's a forwarder */
480 		lbtable = table;
481 		forward = (struct lb_forward *)find_lbrec(LB_TAG_FORWARD);
482 		lbtable = NULL;
483 
484 		if (forward) {
485 			uint64_t new_phys = forward->forward;
486 			table = lbtable_scan(new_phys, new_phys + getpagesize(),
487 					 bad_header_count, bad_table_count);
488 		}
489 		return table;
490 	}
491 
492 	return NULL;
493 }
494 
495 /****************************************************************************
496  * find_lbrec
497  *
498  * Find the record in the coreboot table that matches 'tag'.  Return pointer
499  * to record on success or NULL if record not found.
500  ****************************************************************************/
find_lbrec(uint32_t tag)501 const struct lb_record *find_lbrec(uint32_t tag)
502 {
503 	const char *p;
504 	uint32_t bytes_processed;
505 	const struct lb_record *lbrec;
506 
507 	p = ((const char *)lbtable) + lbtable->header_bytes;
508 
509 	for (bytes_processed = 0;
510 	     bytes_processed < lbtable->table_bytes;
511 	     bytes_processed += lbrec->size) {
512 		lbrec = (const struct lb_record *)&p[bytes_processed];
513 
514 		if (lbrec->tag == tag)
515 			return lbrec;
516 	}
517 
518 	return NULL;
519 }
520 
521 /****************************************************************************
522  * lbrec_tag_to_str
523  *
524  * Return a pointer to the string representation of the given coreboot table
525  * tag.
526  ****************************************************************************/
lbrec_tag_to_str(uint32_t tag)527 static const char *lbrec_tag_to_str(uint32_t tag)
528 {
529 	switch (tag) {
530 	case LB_TAG_UNUSED:
531 		return "UNUSED";
532 
533 	case LB_TAG_MEMORY:
534 		return "MEMORY";
535 
536 	case LB_TAG_HWRPB:
537 		return "HWRPB";
538 
539 	case LB_TAG_MAINBOARD:
540 		return "MAINBOARD";
541 
542 	case LB_TAG_VERSION:
543 		return "VERSION";
544 
545 	case LB_TAG_EXTRA_VERSION:
546 		return "EXTRA_VERSION";
547 
548 	case LB_TAG_BUILD:
549 		return "BUILD";
550 
551 	case LB_TAG_COMPILE_TIME:
552 		return "COMPILE_TIME";
553 
554 	case LB_TAG_COMPILE_BY:
555 		return "COMPILE_BY";
556 
557 	case LB_TAG_COMPILE_HOST:
558 		return "COMPILE_HOST";
559 
560 	case LB_TAG_COMPILE_DOMAIN:
561 		return "COMPILE_DOMAIN";
562 
563 	case LB_TAG_COMPILER:
564 		return "COMPILER";
565 
566 	case LB_TAG_LINKER:
567 		return "LINKER";
568 
569 	case LB_TAG_ASSEMBLER:
570 		return "ASSEMBLER";
571 
572 	case LB_TAG_SERIAL:
573 		return "SERIAL";
574 
575 	case LB_TAG_CONSOLE:
576 		return "CONSOLE";
577 
578 	case LB_TAG_FORWARD:
579 		return "FORWARD";
580 
581 	case LB_TAG_CMOS_OPTION_TABLE:
582 		return "CMOS_OPTION_TABLE";
583 
584 	case LB_TAG_OPTION_CHECKSUM:
585 		return "OPTION_CHECKSUM";
586 
587 	default:
588 		break;
589 	}
590 
591 	return "UNKNOWN";
592 }
593 
594 /****************************************************************************
595  * memory_print_fn
596  *
597  * Display function for 'memory' item of coreboot table.
598  ****************************************************************************/
memory_print_fn(const struct lb_record * rec)599 static void memory_print_fn(const struct lb_record *rec)
600 {
601 	const struct lb_memory *p;
602 	const char *mem_type;
603 	const struct lb_memory_range *ranges;
604 	uint64_t size, start, end;
605 	int i, entries;
606 
607 	p = (const struct lb_memory *)rec;
608 	entries = (p->size - sizeof(*p)) / sizeof(p->map[0]);
609 	ranges = p->map;
610 
611 	if (entries == 0) {
612 		printf("No memory ranges were found.\n");
613 		return;
614 	}
615 
616 	for (i = 0;;) {
617 		switch (ranges[i].type) {
618 		case LB_MEM_RAM:
619 			mem_type = "AVAILABLE";
620 			break;
621 
622 		case LB_MEM_RESERVED:
623 			mem_type = "RESERVED";
624 			break;
625 
626 		case LB_MEM_TABLE:
627 			mem_type = "CONFIG_TABLE";
628 			break;
629 
630 		default:
631 			mem_type = "UNKNOWN";
632 			break;
633 		}
634 
635 		size = ranges[i].size;
636 		start = ranges[i].start;
637 		end = start + size - 1;
638 		printf("%s memory:\n"
639 		       "    from physical addresses 0x%016" PRIx64
640 		       " to 0x%016" PRIx64 "\n    size is 0x%016" PRIx64
641 		       " bytes (%" PRId64 " in decimal)\n",
642 		       mem_type, start, end, size, size);
643 
644 		if (++i >= entries)
645 			break;
646 
647 		printf("\n");
648 	}
649 }
650 
651 /****************************************************************************
652  * mainboard_print_fn
653  *
654  * Display function for 'mainboard' item of coreboot table.
655  ****************************************************************************/
mainboard_print_fn(const struct lb_record * rec)656 static void mainboard_print_fn(const struct lb_record *rec)
657 {
658 	const struct lb_mainboard *p;
659 
660 	p = (const struct lb_mainboard *)rec;
661 	printf("Vendor:      %s\n"
662 	       "Part number: %s\n",
663 	       &p->strings[p->vendor_idx], &p->strings[p->part_number_idx]);
664 }
665 
666 /****************************************************************************
667  * cmos_opt_table_print_fn
668  *
669  * Display function for 'cmos_opt_table' item of coreboot table.
670  ****************************************************************************/
cmos_opt_table_print_fn(const struct lb_record * rec)671 static void cmos_opt_table_print_fn(const struct lb_record *rec)
672 {
673 	const struct cmos_option_table *p;
674 	const struct lb_record *cmos_item;
675 	uint32_t bytes_processed, bytes_for_entries;
676 	const char *q;
677 
678 	p = (const struct cmos_option_table *)rec;
679 	q = ((const char *)p) + p->header_length;
680 	bytes_for_entries = p->size - p->header_length;
681 
682 	printf("CMOS option table at physical address 0x%lx:\n"
683 	       "    tag:           0x%x (decimal: %d)\n"
684 	       "    size:          0x%x (decimal: %d)\n"
685 	       "    header_length: 0x%x (decimal: %d)\n\n",
686 	       vtophys(p), p->tag, p->tag, p->size, p->size, p->header_length,
687 	       p->header_length);
688 
689 	if (p->header_length > p->size) {
690 		printf
691 		    ("Header length for CMOS option table is greater than the size "
692 		     "of the entire table including header!!!\n");
693 		return;
694 	}
695 
696 	if (bytes_for_entries == 0) {
697 		printf("The CMOS option table is empty!!!\n");
698 		return;
699 	}
700 
701 	for (bytes_processed = 0;;) {
702 		cmos_item = (const struct lb_record *)&q[bytes_processed];
703 
704 		switch (cmos_item->tag) {
705 		case LB_TAG_OPTION:
706 			print_option_record((const struct cmos_entries *)
707 					    cmos_item);
708 			break;
709 
710 		case LB_TAG_OPTION_ENUM:
711 			print_enum_record((const struct cmos_enums *)cmos_item);
712 			break;
713 
714 		case LB_TAG_OPTION_DEFAULTS:
715 			print_defaults_record((const struct cmos_defaults *)
716 					      cmos_item);
717 			break;
718 
719 		default:
720 			print_unknown_record(cmos_item);
721 			break;
722 		}
723 
724 		bytes_processed += cmos_item->size;
725 
726 		if (bytes_processed >= bytes_for_entries)
727 			break;
728 
729 		printf("\n");
730 	}
731 }
732 
733 /****************************************************************************
734  * print_option_record
735  *
736  * Display "option" record from CMOS option table.
737  ****************************************************************************/
print_option_record(const struct cmos_entries * cmos_entry)738 static void print_option_record(const struct cmos_entries *cmos_entry)
739 {
740 	static const size_t S_BUFSIZE = 80;
741 	char s[S_BUFSIZE];
742 
743 	switch (cmos_entry->config) {
744 	case 'e':
745 		strcpy(s, "ENUM");
746 		break;
747 
748 	case 'h':
749 		strcpy(s, "HEX");
750 		break;
751 
752 	case 'r':
753 		strcpy(s, "RESERVED");
754 		break;
755 
756 	default:
757 		snprintf(s, S_BUFSIZE, "UNKNOWN: value is 0x%x (decimal: %d)",
758 			 cmos_entry->config, cmos_entry->config);
759 		break;
760 	}
761 
762 	printf("    OPTION record at physical address 0x%lx:\n"
763 	       "        tag:       0x%x (decimal: %d)\n"
764 	       "        size:      0x%x (decimal: %d)\n"
765 	       "        bit:       0x%x (decimal: %d)\n"
766 	       "        length:    0x%x (decimal: %d)\n"
767 	       "        config:    %s\n"
768 	       "        config_id: 0x%x (decimal: %d)\n"
769 	       "        name:      %s\n",
770 	       vtophys(cmos_entry), cmos_entry->tag, cmos_entry->tag,
771 	       cmos_entry->size, cmos_entry->size, cmos_entry->bit,
772 	       cmos_entry->bit, cmos_entry->length, cmos_entry->length, s,
773 	       cmos_entry->config_id, cmos_entry->config_id, cmos_entry->name);
774 }
775 
776 /****************************************************************************
777  * print_enum_record
778  *
779  * Display "enum" record from CMOS option table.
780  ****************************************************************************/
print_enum_record(const struct cmos_enums * cmos_enum)781 static void print_enum_record(const struct cmos_enums *cmos_enum)
782 {
783 	printf("    ENUM record at physical address 0x%lx:\n"
784 	       "        tag:       0x%x (decimal: %d)\n"
785 	       "        size:      0x%x (decimal: %d)\n"
786 	       "        config_id: 0x%x (decimal: %d)\n"
787 	       "        value:     0x%x (decimal: %d)\n"
788 	       "        text:      %s\n",
789 	       vtophys(cmos_enum), cmos_enum->tag, cmos_enum->tag,
790 	       cmos_enum->size, cmos_enum->size, cmos_enum->config_id,
791 	       cmos_enum->config_id, cmos_enum->value, cmos_enum->value,
792 	       cmos_enum->text);
793 }
794 
795 /****************************************************************************
796  * print_defaults_record
797  *
798  * Display "defaults" record from CMOS option table.
799  ****************************************************************************/
print_defaults_record(const struct cmos_defaults * cmos_defaults)800 static void print_defaults_record(const struct cmos_defaults *cmos_defaults)
801 {
802 	printf("    DEFAULTS record at physical address 0x%lx:\n"
803 	       "        tag:         0x%x (decimal: %d)\n"
804 	       "        size:        0x%x (decimal: %d)\n"
805 	       "        name_length: 0x%x (decimal: %d)\n"
806 	       "        name:        %s\n"
807 	       "        default_set:\n",
808 	       vtophys(cmos_defaults), cmos_defaults->tag, cmos_defaults->tag,
809 	       cmos_defaults->size, cmos_defaults->size,
810 	       cmos_defaults->name_length, cmos_defaults->name_length,
811 	       cmos_defaults->name);
812 	hexdump(cmos_defaults->default_set, CMOS_IMAGE_BUFFER_SIZE,
813 		vtophys(cmos_defaults->default_set), stdout, &format);
814 }
815 
816 /****************************************************************************
817  * print_unknown_record
818  *
819  * Display record of unknown type from CMOS option table.
820  ****************************************************************************/
print_unknown_record(const struct lb_record * cmos_item)821 static void print_unknown_record(const struct lb_record *cmos_item)
822 {
823 	const char *data;
824 
825 	printf("    UNKNOWN record at physical address 0x%lx:\n"
826 	       "        tag:  0x%x (decimal: %d)\n"
827 	       "        size: 0x%x (decimal: %d)\n"
828 	       "        data:\n",
829 	       vtophys(cmos_item), cmos_item->tag, cmos_item->tag,
830 	       cmos_item->size, cmos_item->size);
831 	data = ((const char *)cmos_item) + sizeof(*cmos_item);
832 	hexdump(data, cmos_item->size - sizeof(*cmos_item), vtophys(data),
833 		stdout, &format);
834 }
835 
836 /****************************************************************************
837  * option_checksum_print_fn
838  *
839  * Display function for 'option_checksum' item of coreboot table.
840  ****************************************************************************/
option_checksum_print_fn(const struct lb_record * rec)841 static void option_checksum_print_fn(const struct lb_record *rec)
842 {
843 	struct cmos_checksum *p;
844 
845 	p = (struct cmos_checksum *)rec;
846 	printf("CMOS checksum from bit %d to bit %d\n"
847 	       "at position %d is type %s.\n",
848 	       p->range_start, p->range_end, p->location,
849 	       (p->type == CHECKSUM_PCBIOS) ? "PC BIOS" : "NONE");
850 }
851 
852 /****************************************************************************
853  * string_print_fn
854  *
855  * Display function for a generic item of coreboot table that simply
856  * consists of a string.
857  ****************************************************************************/
string_print_fn(const struct lb_record * rec)858 static void string_print_fn(const struct lb_record *rec)
859 {
860 	const struct lb_string *p;
861 
862 	p = (const struct lb_string *)rec;
863 	printf("%s\n", p->string);
864 }
865