blob: c0b39ea6d0045c78de25fb8d02cc492ce15bb57e [file] [log] [blame]
Patrick Georgi7333a112020-05-08 20:48:04 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07002
Nico Huber8e4bb9282013-05-26 18:17:54 +02003#include <inttypes.h>
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01004#include <stdbool.h>
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07005#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08008#include <unistd.h>
9#include <getopt.h>
Julius Werner337de4c2014-06-16 23:02:03 -070010#include <dirent.h>
Stefan Reinauer05cbce62013-01-03 14:30:33 -080011#include <errno.h>
12#include <fcntl.h>
Stefan Reinauera9c83612013-07-16 17:47:35 -070013#include <ctype.h>
Stefan Reinauer7f681502013-06-19 15:39:09 -070014#include <arpa/inet.h>
Stefan Reinauer05cbce62013-01-03 14:30:33 -080015#include <sys/types.h>
16#include <sys/stat.h>
17#include <sys/mman.h>
Stefan Reinauerd37ab452012-12-18 16:23:28 -080018#include <libgen.h>
19#include <assert.h>
Julius Wernerb7b64a92017-04-28 16:31:46 -070020#include <regex.h>
Jakub Czapigaea619422021-11-23 08:43:25 +000021#include <commonlib/bsd/cbmem_id.h>
Julius Werner984d03c2022-01-21 15:33:47 -080022#include <commonlib/loglevel.h>
Aaron Durbindc9f5cd2015-09-08 13:34:43 -050023#include <commonlib/timestamp_serialized.h>
Sergii Dmytruk2710df72022-11-10 00:40:51 +020024#include <commonlib/tpm_log_serialized.h>
Aaron Durbindc9f5cd2015-09-08 13:34:43 -050025#include <commonlib/coreboot_tables.h>
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070026
Patrick Georgid00f1802015-07-13 16:53:50 +020027#ifdef __OpenBSD__
28#include <sys/param.h>
29#include <sys/sysctl.h>
30#endif
31
Mattias Nisslerecc165b2022-02-08 23:01:50 +000032#if defined(__i386__) || defined(__x86_64__)
33#include <x86intrin.h>
34#endif
35
Stefan Reinauer05cbce62013-01-03 14:30:33 -080036#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070037
Julius Werner337de4c2014-06-16 23:02:03 -070038typedef uint8_t u8;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070039typedef uint16_t u16;
40typedef uint32_t u32;
41typedef uint64_t u64;
42
Aaron Durbin46300aa2017-09-26 16:22:38 -060043/* Return < 0 on error, 0 on success. */
44static int parse_cbtable(u64 address, size_t table_size);
45
46struct mapping {
47 void *virt;
48 size_t offset;
49 size_t virt_size;
50 unsigned long long phys;
51 size_t size;
52};
53
Stefan Reinauera9c83612013-07-16 17:47:35 -070054#define CBMEM_VERSION "1.1"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -080055
Stefan Reinauer05cbce62013-01-03 14:30:33 -080056/* verbose output? */
57static int verbose = 0;
58#define debug(x...) if(verbose) printf(x)
59
60/* File handle used to access /dev/mem */
Julius Werner337de4c2014-06-16 23:02:03 -070061static int mem_fd;
Aaron Durbin46300aa2017-09-26 16:22:38 -060062static struct mapping lbtable_mapping;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070063
Mattias Nisslerecc165b2022-02-08 23:01:50 +000064/* TSC frequency from the LB_TAG_TSC_INFO record. 0 if not present. */
65static uint32_t tsc_freq_khz = 0;
66
Aaron Durbin46300aa2017-09-26 16:22:38 -060067static void die(const char *msg)
68{
69 if (msg)
70 fputs(msg, stderr);
71 exit(1);
72}
73
74static unsigned long long system_page_size(void)
75{
76 static unsigned long long page_size;
77
78 if (!page_size)
79 page_size = getpagesize();
80
81 return page_size;
82}
83
84static inline size_t size_to_mib(size_t sz)
85{
86 return sz >> 20;
87}
88
89/* Return mapping of physical address requested. */
Mattias Nisslerecc165b2022-02-08 23:01:50 +000090static void *mapping_virt(const struct mapping *mapping)
Aaron Durbin46300aa2017-09-26 16:22:38 -060091{
Mattias Nisslerecc165b2022-02-08 23:01:50 +000092 char *v = mapping->virt;
Aaron Durbin46300aa2017-09-26 16:22:38 -060093
94 if (v == NULL)
95 return NULL;
96
97 return v + mapping->offset;
98}
99
Aaron Durbincf20c902017-09-28 17:52:59 -0600100/* Returns virtual address on success, NULL on error. mapping is filled in. */
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000101static void *map_memory_with_prot(struct mapping *mapping,
102 unsigned long long phys, size_t sz, int prot)
Aaron Durbin46300aa2017-09-26 16:22:38 -0600103{
104 void *v;
105 unsigned long long page_size;
106
107 page_size = system_page_size();
108
109 mapping->virt = NULL;
110 mapping->offset = phys % page_size;
111 mapping->virt_size = sz + mapping->offset;
112 mapping->size = sz;
113 mapping->phys = phys;
114
115 if (size_to_mib(mapping->virt_size) == 0) {
116 debug("Mapping %zuB of physical memory at 0x%llx (requested 0x%llx).\n",
117 mapping->virt_size, phys - mapping->offset, phys);
118 } else {
119 debug("Mapping %zuMB of physical memory at 0x%llx (requested 0x%llx).\n",
120 size_to_mib(mapping->virt_size), phys - mapping->offset,
121 phys);
122 }
123
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000124 v = mmap(NULL, mapping->virt_size, prot, MAP_SHARED, mem_fd,
Aaron Durbin46300aa2017-09-26 16:22:38 -0600125 phys - mapping->offset);
126
127 if (v == MAP_FAILED) {
128 debug("Mapping failed %zuB of physical memory at 0x%llx.\n",
129 mapping->virt_size, phys - mapping->offset);
130 return NULL;
131 }
132
133 mapping->virt = v;
134
135 if (mapping->offset != 0)
136 debug(" ... padding virtual address with 0x%zx bytes.\n",
137 mapping->offset);
138
139 return mapping_virt(mapping);
140}
141
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000142/* Convenience helper for the common case of read-only mappings. */
143static const void *map_memory(struct mapping *mapping, unsigned long long phys,
144 size_t sz)
145{
146 return map_memory_with_prot(mapping, phys, sz, PROT_READ);
147}
148
149
Aaron Durbin46300aa2017-09-26 16:22:38 -0600150/* Returns 0 on success, < 0 on error. mapping is cleared if successful. */
151static int unmap_memory(struct mapping *mapping)
152{
153 if (mapping->virt == NULL)
154 return -1;
155
156 munmap(mapping->virt, mapping->virt_size);
157 mapping->virt = NULL;
158 mapping->offset = 0;
159 mapping->virt_size = 0;
160
161 return 0;
162}
163
164/* Return size of physical address mapping requested. */
165static size_t mapping_size(const struct mapping *mapping)
166{
167 if (mapping->virt == NULL)
168 return 0;
169
170 return mapping->size;
171}
Timothy Pearsondf699d52015-05-16 14:55:54 -0500172
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700173/*
Julius Werner127a79e2017-02-03 12:50:03 -0800174 * Some architectures map /dev/mem memory in a way that doesn't support
175 * unaligned accesses. Most normal libc memcpy()s aren't safe to use in this
176 * case, so build our own which makes sure to never do unaligned accesses on
177 * *src (*dest is fine since we never map /dev/mem for writing).
178 */
179static void *aligned_memcpy(void *dest, const void *src, size_t n)
180{
181 u8 *d = dest;
182 const volatile u8 *s = src; /* volatile to prevent optimization */
183
184 while ((uintptr_t)s & (sizeof(size_t) - 1)) {
185 if (n-- == 0)
186 return dest;
187 *d++ = *s++;
188 }
189
190 while (n >= sizeof(size_t)) {
191 *(size_t *)d = *(const volatile size_t *)s;
192 d += sizeof(size_t);
193 s += sizeof(size_t);
194 n -= sizeof(size_t);
195 }
196
197 while (n-- > 0)
198 *d++ = *s++;
199
200 return dest;
201}
202
203/*
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700204 * calculate ip checksum (16 bit quantities) on a passed in buffer. In case
205 * the buffer length is odd last byte is excluded from the calculation
206 */
207static u16 ipchcksum(const void *addr, unsigned size)
208{
209 const u16 *p = addr;
210 unsigned i, n = size / 2; /* don't expect odd sized blocks */
211 u32 sum = 0;
212
213 for (i = 0; i < n; i++)
214 sum += p[i];
215
216 sum = (sum >> 16) + (sum & 0xffff);
217 sum += (sum >> 16);
218 sum = ~sum & 0xffff;
219 return (u16) sum;
220}
221
Aaron Durbin09c0c112015-09-30 12:33:01 -0500222/* Find the first cbmem entry filling in the details. */
223static int find_cbmem_entry(uint32_t id, uint64_t *addr, size_t *size)
224{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600225 const uint8_t *table;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500226 size_t offset;
227 int ret = -1;
228
Aaron Durbin46300aa2017-09-26 16:22:38 -0600229 table = mapping_virt(&lbtable_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500230
231 if (table == NULL)
232 return -1;
233
234 offset = 0;
235
Aaron Durbin46300aa2017-09-26 16:22:38 -0600236 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -0600237 const struct lb_record *lbr;
Yidi Lin0811a642022-09-15 15:47:59 +0800238 struct lb_cbmem_entry lbe;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500239
Aaron Durbinf2a38222017-09-26 17:44:28 -0600240 lbr = (const void *)(table + offset);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500241 offset += lbr->size;
242
243 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
244 continue;
245
Yidi Lin0811a642022-09-15 15:47:59 +0800246 aligned_memcpy(&lbe, lbr, sizeof(lbe));
247 if (lbe.id != id)
Aaron Durbin09c0c112015-09-30 12:33:01 -0500248 continue;
249
Yidi Lin0811a642022-09-15 15:47:59 +0800250 *addr = lbe.address;
251 *size = lbe.entry_size;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500252 ret = 0;
253 break;
254 }
255
Aaron Durbin09c0c112015-09-30 12:33:01 -0500256 return ret;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700257}
258
259/*
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800260 * Try finding the timestamp table and coreboot cbmem console starting from the
261 * passed in memory offset. Could be called recursively in case a forwarding
262 * entry is found.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700263 *
Patrick Georgi220c2092020-01-30 12:58:08 +0100264 * Returns pointer to a memory buffer containing the timestamp table or zero if
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700265 * none found.
266 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800267
268static struct lb_cbmem_ref timestamps;
269static struct lb_cbmem_ref console;
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200270static struct lb_cbmem_ref tpm_cb_log;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800271static struct lb_memory_range cbmem;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800272
Stefan Reinauer8c594772013-04-19 14:22:29 -0700273/* This is a work-around for a nasty problem introduced by initially having
274 * pointer sized entries in the lb_cbmem_ref structures. This caused problems
275 * on 64bit x86 systems because coreboot is 32bit on those systems.
276 * When the problem was found, it was corrected, but there are a lot of
277 * systems out there with a firmware that does not produce the right
278 * lb_cbmem_ref structure. Hence we try to autocorrect this issue here.
279 */
Aaron Durbinf2a38222017-09-26 17:44:28 -0600280static struct lb_cbmem_ref parse_cbmem_ref(const struct lb_cbmem_ref *cbmem_ref)
Stefan Reinauer8c594772013-04-19 14:22:29 -0700281{
282 struct lb_cbmem_ref ret;
283
Julius Wernere3c23912018-11-26 15:57:59 -0800284 aligned_memcpy(&ret, cbmem_ref, sizeof(ret));
Stefan Reinauer8c594772013-04-19 14:22:29 -0700285
286 if (cbmem_ref->size < sizeof(*cbmem_ref))
287 ret.cbmem_addr = (uint32_t)ret.cbmem_addr;
288
Stefan Reinauera9c83612013-07-16 17:47:35 -0700289 debug(" cbmem_addr = %" PRIx64 "\n", ret.cbmem_addr);
290
Stefan Reinauer8c594772013-04-19 14:22:29 -0700291 return ret;
292}
293
Aaron Durbin46300aa2017-09-26 16:22:38 -0600294static void parse_memory_tags(const struct lb_memory *mem)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700295{
Aaron Durbin46300aa2017-09-26 16:22:38 -0600296 int num_entries;
297 int i;
298
299 /* Peel off the header size and calculate the number of entries. */
300 num_entries = (mem->size - sizeof(*mem)) / sizeof(mem->map[0]);
301
302 for (i = 0; i < num_entries; i++) {
303 if (mem->map[i].type != LB_MEM_TABLE)
304 continue;
305 debug(" LB_MEM_TABLE found.\n");
306 /* The last one found is CBMEM */
Aaron Durbineb722282019-01-31 09:40:26 -0700307 aligned_memcpy(&cbmem, &mem->map[i], sizeof(cbmem));
Aaron Durbin46300aa2017-09-26 16:22:38 -0600308 }
309}
310
311/* Return < 0 on error, 0 on success, 1 if forwarding table entry found. */
312static int parse_cbtable_entries(const struct mapping *table_mapping)
313{
314 size_t i;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200315 const struct lb_record *lbr_p;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600316 size_t table_size = mapping_size(table_mapping);
317 const void *lbtable = mapping_virt(table_mapping);
318 int forwarding_table_found = 0;
319
320 for (i = 0; i < table_size; i += lbr_p->size) {
321 lbr_p = lbtable + i;
322 debug(" coreboot table entry 0x%02x\n", lbr_p->tag);
323 switch (lbr_p->tag) {
324 case LB_TAG_MEMORY:
325 debug(" Found memory map.\n");
326 parse_memory_tags(lbtable + i);
327 continue;
328 case LB_TAG_TIMESTAMPS: {
329 debug(" Found timestamp table.\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200330 timestamps =
331 parse_cbmem_ref((struct lb_cbmem_ref *)lbr_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600332 continue;
333 }
334 case LB_TAG_CBMEM_CONSOLE: {
335 debug(" Found cbmem console.\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200336 console = parse_cbmem_ref((struct lb_cbmem_ref *)lbr_p);
337 continue;
338 }
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200339 case LB_TAG_TPM_CB_LOG: {
340 debug(" Found TPM CB log table.\n");
341 tpm_cb_log =
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200342 parse_cbmem_ref((struct lb_cbmem_ref *)lbr_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600343 continue;
344 }
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000345 case LB_TAG_TSC_INFO:
346 debug(" Found TSC info.\n");
347 tsc_freq_khz = ((struct lb_tsc_info *)lbr_p)->freq_khz;
348 continue;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600349 case LB_TAG_FORWARD: {
350 int ret;
351 /*
352 * This is a forwarding entry - repeat the
353 * search at the new address.
354 */
355 struct lb_forward lbf_p =
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200356 *(const struct lb_forward *)lbr_p;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600357 debug(" Found forwarding entry.\n");
358 ret = parse_cbtable(lbf_p.forward, 0);
359
360 /* Assume the forwarding entry is valid. If this fails
361 * then there's a total failure. */
362 if (ret < 0)
363 return -1;
364 forwarding_table_found = 1;
365 }
366 default:
367 break;
368 }
369 }
370
371 return forwarding_table_found;
372}
373
374/* Return < 0 on error, 0 on success. */
375static int parse_cbtable(u64 address, size_t table_size)
376{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600377 const void *buf;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600378 struct mapping header_mapping;
379 size_t req_size;
380 size_t i;
381
382 req_size = table_size;
383 /* Default to 4 KiB search space. */
384 if (req_size == 0)
385 req_size = 4 * 1024;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800386
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500387 debug("Looking for coreboot table at %" PRIx64 " %zd bytes.\n",
Aaron Durbin46300aa2017-09-26 16:22:38 -0600388 address, req_size);
389
390 buf = map_memory(&header_mapping, address, req_size);
391
Timothy Pearsonbea71402015-09-05 18:07:17 -0500392 if (!buf)
Aaron Durbin46300aa2017-09-26 16:22:38 -0600393 return -1;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700394
Aaron Durbin46300aa2017-09-26 16:22:38 -0600395 /* look at every 16 bytes */
Aaron Durbincf20c902017-09-28 17:52:59 -0600396 for (i = 0; i <= req_size - sizeof(struct lb_header); i += 16) {
Aaron Durbin46300aa2017-09-26 16:22:38 -0600397 int ret;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600398 const struct lb_header *lbh;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600399 struct mapping table_mapping;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700400
Aaron Durbin46300aa2017-09-26 16:22:38 -0600401 lbh = buf + i;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800402 if (memcmp(lbh->signature, "LBIO", sizeof(lbh->signature)) ||
403 !lbh->header_bytes ||
404 ipchcksum(lbh, sizeof(*lbh))) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700405 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700406 }
407
Aaron Durbin46300aa2017-09-26 16:22:38 -0600408 /* Map in the whole table to parse. */
409 if (!map_memory(&table_mapping, address + i + lbh->header_bytes,
410 lbh->table_bytes)) {
411 debug("Couldn't map in table\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700412 continue;
413 }
414
Aaron Durbin46300aa2017-09-26 16:22:38 -0600415 if (ipchcksum(mapping_virt(&table_mapping), lbh->table_bytes) !=
416 lbh->table_checksum) {
417 debug("Signature found, but wrong checksum.\n");
418 unmap_memory(&table_mapping);
419 continue;
420 }
421
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800422 debug("Found!\n");
423
Aaron Durbin46300aa2017-09-26 16:22:38 -0600424 ret = parse_cbtable_entries(&table_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500425
Aaron Durbin46300aa2017-09-26 16:22:38 -0600426 /* Table parsing failed. */
427 if (ret < 0) {
428 unmap_memory(&table_mapping);
429 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700430 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800431
Aaron Durbin46300aa2017-09-26 16:22:38 -0600432 /* Succeeded in parsing the table. Header not needed anymore. */
433 unmap_memory(&header_mapping);
434
435 /*
436 * Table parsing succeeded. If forwarding table not found update
437 * coreboot table mapping for future use.
438 */
439 if (ret == 0)
440 lbtable_mapping = table_mapping;
441 else
442 unmap_memory(&table_mapping);
443
444 return 0;
445 }
446
447 unmap_memory(&header_mapping);
448
449 return -1;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700450}
451
Patrick Georgid00f1802015-07-13 16:53:50 +0200452#if defined(linux) && (defined(__i386__) || defined(__x86_64__))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700453/*
Aaron Durbin08e920e2016-03-12 08:41:34 +0100454 * read CPU frequency from a sysfs file, return an frequency in Megahertz as
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700455 * an int or exit on any error.
456 */
Aaron Durbinc49014e2015-08-30 21:19:55 -0500457static unsigned long arch_tick_frequency(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700458{
459 FILE *cpuf;
460 char freqs[100];
461 int size;
462 char *endp;
463 u64 rv;
464
465 const char* freq_file =
466 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
467
468 cpuf = fopen(freq_file, "r");
469 if (!cpuf) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800470 fprintf(stderr, "Could not open %s: %s\n",
471 freq_file, strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700472 exit(1);
473 }
474
475 memset(freqs, 0, sizeof(freqs));
476 size = fread(freqs, 1, sizeof(freqs), cpuf);
477 if (!size || (size == sizeof(freqs))) {
478 fprintf(stderr, "Wrong number of bytes(%d) read from %s\n",
479 size, freq_file);
480 exit(1);
481 }
482 fclose(cpuf);
483 rv = strtoull(freqs, &endp, 10);
484
485 if (*endp == '\0' || *endp == '\n')
Aaron Durbin08e920e2016-03-12 08:41:34 +0100486 /* cpuinfo_max_freq is in kHz. Convert it to MHz. */
487 return rv / 1000;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700488 fprintf(stderr, "Wrong formatted value ^%s^ read from %s\n",
489 freqs, freq_file);
490 exit(1);
491}
Patrick Georgid00f1802015-07-13 16:53:50 +0200492#elif defined(__OpenBSD__) && (defined(__i386__) || defined(__x86_64__))
Aaron Durbinc49014e2015-08-30 21:19:55 -0500493static unsigned long arch_tick_frequency(void)
Patrick Georgid00f1802015-07-13 16:53:50 +0200494{
495 int mib[2] = { CTL_HW, HW_CPUSPEED };
496 static int value = 0;
497 size_t value_len = sizeof(value);
498
Aaron Durbinc49014e2015-08-30 21:19:55 -0500499 /* Return 1 MHz when sysctl fails. */
Patrick Georgid00f1802015-07-13 16:53:50 +0200500 if ((value == 0) && (sysctl(mib, 2, &value, &value_len, NULL, 0) == -1))
Aaron Durbinc49014e2015-08-30 21:19:55 -0500501 return 1;
Patrick Georgid00f1802015-07-13 16:53:50 +0200502
Aaron Durbinc49014e2015-08-30 21:19:55 -0500503 return value;
Patrick Georgid00f1802015-07-13 16:53:50 +0200504}
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700505#else
Aaron Durbinc49014e2015-08-30 21:19:55 -0500506static unsigned long arch_tick_frequency(void)
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700507{
Aaron Durbinc49014e2015-08-30 21:19:55 -0500508 /* 1 MHz = 1us. */
509 return 1;
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700510}
511#endif
512
Aaron Durbinc49014e2015-08-30 21:19:55 -0500513static unsigned long tick_freq_mhz;
514
515static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz)
516{
517 tick_freq_mhz = table_tick_freq_mhz;
518
Martin Rothac9d6b82017-12-11 13:27:56 -0700519 /* Honor table frequency if present. */
520 if (!tick_freq_mhz)
521 tick_freq_mhz = arch_tick_frequency();
Aaron Durbinc49014e2015-08-30 21:19:55 -0500522
523 if (!tick_freq_mhz) {
524 fprintf(stderr, "Cannot determine timestamp tick frequency.\n");
525 exit(1);
526 }
Martin Rothac9d6b82017-12-11 13:27:56 -0700527
528 debug("Timestamp tick frequency: %ld MHz\n", tick_freq_mhz);
Aaron Durbinc49014e2015-08-30 21:19:55 -0500529}
530
Jacob Garber79a2f472019-06-27 17:23:25 -0600531static u64 arch_convert_raw_ts_entry(u64 ts)
Aaron Durbinc49014e2015-08-30 21:19:55 -0500532{
533 return ts / tick_freq_mhz;
534}
535
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700536/*
537 * Print an integer in 'normalized' form - with commas separating every three
Julius Wernera7d92442014-12-02 20:51:19 -0800538 * decimal orders.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700539 */
Julius Wernera7d92442014-12-02 20:51:19 -0800540static void print_norm(u64 v)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700541{
Julius Wernera7d92442014-12-02 20:51:19 -0800542 if (v >= 1000) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700543 /* print the higher order sections first */
Julius Wernera7d92442014-12-02 20:51:19 -0800544 print_norm(v / 1000);
545 printf(",%3.3u", (u32)(v % 1000));
546 } else {
547 printf("%u", (u32)(v % 1000));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700548 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700549}
550
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000551static uint64_t timestamp_get(uint64_t table_tick_freq_mhz)
552{
553#if defined(__i386__) || defined(__x86_64__)
554 uint64_t tsc = __rdtsc();
555
556 /* No tick frequency specified means raw TSC values. */
557 if (!table_tick_freq_mhz)
558 return tsc;
559
560 if (tsc_freq_khz)
561 return tsc * table_tick_freq_mhz * 1000 / tsc_freq_khz;
Nick Vaccaro946e2922022-04-26 17:14:03 -0700562#else
563 (void)table_tick_freq_mhz;
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000564#endif
565 die("Don't know how to obtain timestamps on this platform.\n");
566 return 0;
567}
568
Aaron Durbinfbff3012015-08-30 22:00:12 -0500569static const char *timestamp_name(uint32_t id)
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700570{
Jacob Garber414d5d82019-06-27 16:02:32 -0600571 for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
Aaron Durbinfbff3012015-08-30 22:00:12 -0500572 if (timestamp_ids[i].id == id)
573 return timestamp_ids[i].name;
574 }
575 return "<unknown>";
576}
577
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000578static uint32_t timestamp_enum_name_to_id(const char *name)
579{
580 for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
581 if (!strcmp(timestamp_ids[i].enum_name, name))
582 return timestamp_ids[i].id;
583 }
584 return 0;
585}
586
Aaron Durbinfbff3012015-08-30 22:00:12 -0500587static uint64_t timestamp_print_parseable_entry(uint32_t id, uint64_t stamp,
588 uint64_t prev_stamp)
589{
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700590 const char *name;
Aaron Durbin799bf782015-08-06 13:52:08 -0500591 uint64_t step_time;
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700592
Aaron Durbinfbff3012015-08-30 22:00:12 -0500593 name = timestamp_name(id);
594
595 step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
596
597 /* ID<tab>absolute time<tab>relative time<tab>description */
598 printf("%d\t", id);
599 printf("%llu\t", (long long)arch_convert_raw_ts_entry(stamp));
600 printf("%llu\t", (long long)step_time);
601 printf("%s\n", name);
602
603 return step_time;
604}
605
Jacob Garber79a2f472019-06-27 17:23:25 -0600606static uint64_t timestamp_print_entry(uint32_t id, uint64_t stamp, uint64_t prev_stamp)
Aaron Durbinfbff3012015-08-30 22:00:12 -0500607{
608 const char *name;
609 uint64_t step_time;
610
611 name = timestamp_name(id);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700612
613 printf("%4d:", id);
Julius Wernera7d92442014-12-02 20:51:19 -0800614 printf("%-50s", name);
615 print_norm(arch_convert_raw_ts_entry(stamp));
Aaron Durbin799bf782015-08-06 13:52:08 -0500616 step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700617 if (prev_stamp) {
618 printf(" (");
Aaron Durbin799bf782015-08-06 13:52:08 -0500619 print_norm(step_time);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700620 printf(")");
621 }
622 printf("\n");
Aaron Durbin799bf782015-08-06 13:52:08 -0500623
624 return step_time;
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700625}
626
Raul E Rangeld4fec682018-05-11 11:08:07 -0600627static int compare_timestamp_entries(const void *a, const void *b)
628{
629 const struct timestamp_entry *tse_a = (struct timestamp_entry *)a;
630 const struct timestamp_entry *tse_b = (struct timestamp_entry *)b;
631
Furquan Shaikh8f567322018-05-17 15:08:03 -0700632 if (tse_a->entry_stamp > tse_b->entry_stamp)
633 return 1;
634 else if (tse_a->entry_stamp < tse_b->entry_stamp)
635 return -1;
636
637 return 0;
Raul E Rangeld4fec682018-05-11 11:08:07 -0600638}
639
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100640static int find_matching_end(struct timestamp_table *sorted_tst_p, uint32_t start, uint32_t end)
641{
642 uint32_t id = sorted_tst_p->entries[start].entry_id;
643 uint32_t possible_match = 0;
644
645 for (uint32_t i = 0; i < ARRAY_SIZE(timestamp_ids); ++i) {
646 if (timestamp_ids[i].id == id) {
647 possible_match = timestamp_ids[i].id_end;
648 break;
649 }
650 }
651
652 /* No match found or timestamp not defined in IDs table */
653 if (!possible_match)
654 return -1;
655
656 for (uint32_t i = start + 1; i < end; i++)
657 if (sorted_tst_p->entries[i].entry_id == possible_match)
658 return i;
659
660 return -1;
661}
662
663static const char *get_timestamp_name(const uint32_t id)
664{
665 for (uint32_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++)
666 if (timestamp_ids[i].id == id)
667 return timestamp_ids[i].enum_name;
668
669 return "UNKNOWN";
670}
671
672struct ts_range_stack {
673 const char *name;
674 const char *end_name;
675 uint32_t end;
676};
677
678static void print_with_path(struct ts_range_stack *range_stack, const int stacklvl,
679 const uint64_t stamp, const char *last_part)
680{
681 for (int i = 1; i <= stacklvl; ++i) {
682 printf("%s -> %s", range_stack[i].name, range_stack[i].end_name);
683 if (i < stacklvl || last_part)
684 putchar(';');
685 }
686 if (last_part)
687 printf("%s", last_part);
Paul Fagerburgac683842022-04-02 08:28:40 -0600688 printf(" %llu\n", (long long)arch_convert_raw_ts_entry(stamp));
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100689}
690
691enum timestamps_print_type {
692 TIMESTAMPS_PRINT_NONE,
693 TIMESTAMPS_PRINT_NORMAL,
694 TIMESTAMPS_PRINT_MACHINE_READABLE,
695 TIMESTAMPS_PRINT_STACKED,
696};
697
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700698/* dump the timestamp table */
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100699static void dump_timestamps(enum timestamps_print_type output_type)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700700{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600701 const struct timestamp_table *tst_p;
Raul E Rangeld4fec682018-05-11 11:08:07 -0600702 struct timestamp_table *sorted_tst_p;
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500703 size_t size;
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100704 uint64_t prev_stamp = 0;
705 uint64_t total_time = 0;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600706 struct mapping timestamp_mapping;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800707
708 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
709 fprintf(stderr, "No timestamps found in coreboot table.\n");
710 return;
711 }
712
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500713 size = sizeof(*tst_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600714 tst_p = map_memory(&timestamp_mapping, timestamps.cbmem_addr, size);
715 if (!tst_p)
716 die("Unable to map timestamp header\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700717
Aaron Durbinc49014e2015-08-30 21:19:55 -0500718 timestamp_set_tick_freq(tst_p->tick_freq_mhz);
719
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100720 if (output_type == TIMESTAMPS_PRINT_NORMAL)
Aaron Durbinfbff3012015-08-30 22:00:12 -0500721 printf("%d entries total:\n\n", tst_p->num_entries);
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500722 size += tst_p->num_entries * sizeof(tst_p->entries[0]);
723
Aaron Durbin46300aa2017-09-26 16:22:38 -0600724 unmap_memory(&timestamp_mapping);
725
726 tst_p = map_memory(&timestamp_mapping, timestamps.cbmem_addr, size);
727 if (!tst_p)
728 die("Unable to map full timestamp table\n");
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500729
Bora Guvendike383b3d2021-11-22 16:12:48 -0800730 sorted_tst_p = malloc(size + sizeof(struct timestamp_entry));
Raul E Rangeld4fec682018-05-11 11:08:07 -0600731 if (!sorted_tst_p)
732 die("Failed to allocate memory");
Julius Wernere3c23912018-11-26 15:57:59 -0800733 aligned_memcpy(sorted_tst_p, tst_p, size);
Raul E Rangeld4fec682018-05-11 11:08:07 -0600734
Bora Guvendike383b3d2021-11-22 16:12:48 -0800735 /*
736 * Insert a timestamp to represent the base time (start of coreboot),
737 * in case we have to rebase for negative timestamps below.
738 */
739 sorted_tst_p->entries[tst_p->num_entries].entry_id = 0;
740 sorted_tst_p->entries[tst_p->num_entries].entry_stamp = 0;
741 sorted_tst_p->num_entries += 1;
742
Raul E Rangeld4fec682018-05-11 11:08:07 -0600743 qsort(&sorted_tst_p->entries[0], sorted_tst_p->num_entries,
744 sizeof(struct timestamp_entry), compare_timestamp_entries);
745
Bora Guvendike383b3d2021-11-22 16:12:48 -0800746 /*
747 * If there are negative timestamp entries, rebase all of the
748 * timestamps to the lowest one in the list.
749 */
Bora Guvendikc79da5f2022-02-10 20:13:50 -0800750 if (sorted_tst_p->entries[0].entry_stamp < 0) {
Bora Guvendike383b3d2021-11-22 16:12:48 -0800751 sorted_tst_p->base_time = -sorted_tst_p->entries[0].entry_stamp;
Bora Guvendikc79da5f2022-02-10 20:13:50 -0800752 prev_stamp = 0;
753 } else {
754 prev_stamp = tst_p->base_time;
755 }
Bora Guvendike383b3d2021-11-22 16:12:48 -0800756
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100757 struct ts_range_stack range_stack[20];
758 range_stack[0].end = sorted_tst_p->num_entries;
759 int stacklvl = 0;
760
Jacob Garber414d5d82019-06-27 16:02:32 -0600761 for (uint32_t i = 0; i < sorted_tst_p->num_entries; i++) {
Aaron Durbin31540fb2015-07-11 12:44:10 -0500762 uint64_t stamp;
Raul E Rangeld4fec682018-05-11 11:08:07 -0600763 const struct timestamp_entry *tse = &sorted_tst_p->entries[i];
Aaron Durbin31540fb2015-07-11 12:44:10 -0500764
765 /* Make all timestamps absolute. */
Raul E Rangeld4fec682018-05-11 11:08:07 -0600766 stamp = tse->entry_stamp + sorted_tst_p->base_time;
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100767 if (output_type == TIMESTAMPS_PRINT_MACHINE_READABLE) {
768 timestamp_print_parseable_entry(tse->entry_id, stamp, prev_stamp);
769 } else if (output_type == TIMESTAMPS_PRINT_NORMAL) {
770 total_time += timestamp_print_entry(tse->entry_id, stamp, prev_stamp);
771 } else if (output_type == TIMESTAMPS_PRINT_STACKED) {
772 bool end_of_range = false;
773 /* Iterate over stacked entries to pop all ranges, which are closed by
774 current element. For example, assuming two ranges: (TS_A, TS_C),
775 (TS_B, TS_C) it will pop all of them instead of just last one. */
776 while (stacklvl > 0 && range_stack[stacklvl].end == i) {
777 end_of_range = true;
778 stacklvl--;
779 }
780
781 int match =
782 find_matching_end(sorted_tst_p, i, range_stack[stacklvl].end);
783 if (match != -1) {
784 const uint64_t match_stamp =
785 sorted_tst_p->entries[match].entry_stamp
786 + sorted_tst_p->base_time;
787 stacklvl++;
788 assert(stacklvl < (int)ARRAY_SIZE(range_stack));
789 range_stack[stacklvl].name = get_timestamp_name(tse->entry_id);
790 range_stack[stacklvl].end_name = get_timestamp_name(
791 sorted_tst_p->entries[match].entry_id);
792 range_stack[stacklvl].end = match;
793 print_with_path(range_stack, stacklvl, match_stamp - stamp,
794 NULL);
795 } else if (!end_of_range) {
796 print_with_path(range_stack, stacklvl, stamp - prev_stamp,
797 get_timestamp_name(tse->entry_id));
798 }
799 /* else: No match && end_of_range == true */
800 }
Aaron Durbin31540fb2015-07-11 12:44:10 -0500801 prev_stamp = stamp;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700802 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800803
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100804 if (output_type == TIMESTAMPS_PRINT_NORMAL) {
Aaron Durbinfbff3012015-08-30 22:00:12 -0500805 printf("\nTotal Time: ");
806 print_norm(total_time);
807 printf("\n");
808 }
Aaron Durbin799bf782015-08-06 13:52:08 -0500809
Aaron Durbin46300aa2017-09-26 16:22:38 -0600810 unmap_memory(&timestamp_mapping);
Raul E Rangeld4fec682018-05-11 11:08:07 -0600811 free(sorted_tst_p);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700812}
813
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000814/* add a timestamp entry */
815static void timestamp_add_now(uint32_t timestamp_id)
816{
817 struct timestamp_table *tst_p;
818 struct mapping timestamp_mapping;
819
820 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
821 die("No timestamps found in coreboot table.\n");
822 }
823
824 tst_p = map_memory_with_prot(&timestamp_mapping, timestamps.cbmem_addr,
825 timestamps.size, PROT_READ | PROT_WRITE);
826 if (!tst_p)
827 die("Unable to map timestamp table\n");
828
829 /*
830 * Note that coreboot sizes the cbmem entry in the table according to
831 * max_entries, so it's OK to just add more entries if there's room.
832 */
833 if (tst_p->num_entries >= tst_p->max_entries) {
834 die("Not enough space to add timestamp.\n");
835 } else {
836 int64_t time =
837 timestamp_get(tst_p->tick_freq_mhz) - tst_p->base_time;
838 tst_p->entries[tst_p->num_entries].entry_id = timestamp_id;
839 tst_p->entries[tst_p->num_entries].entry_stamp = time;
840 tst_p->num_entries += 1;
841 }
842
843 unmap_memory(&timestamp_mapping);
844}
845
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200846/* dump the TPM CB log table */
847static void dump_tpm_cb_log(void)
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200848{
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200849 const struct tpm_cb_log_table *tclt_p;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200850 size_t size;
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200851 struct mapping log_mapping;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200852
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200853 if (tpm_cb_log.tag != LB_TAG_TPM_CB_LOG) {
854 fprintf(stderr, "No TPM log found in coreboot table.\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200855 return;
856 }
857
858 size = sizeof(*tclt_p);
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200859 tclt_p = map_memory(&log_mapping, tpm_cb_log.cbmem_addr, size);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200860 if (!tclt_p)
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200861 die("Unable to map TPM log header\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200862
863 size += tclt_p->num_entries * sizeof(tclt_p->entries[0]);
864
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200865 unmap_memory(&log_mapping);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200866
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200867 tclt_p = map_memory(&log_mapping, tpm_cb_log.cbmem_addr, size);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200868 if (!tclt_p)
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200869 die("Unable to map full TPM log table\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200870
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200871 printf("coreboot TPM log:\n\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200872
Jacob Garber414d5d82019-06-27 16:02:32 -0600873 for (uint16_t i = 0; i < tclt_p->num_entries; i++) {
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200874 const struct tpm_cb_log_entry *tce = &tclt_p->entries[i];
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200875
Philipp Deppenwiesec9b7d1f2018-11-10 00:35:02 +0100876 printf(" PCR-%u ", tce->pcr);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200877
Jacob Garber414d5d82019-06-27 16:02:32 -0600878 for (uint32_t j = 0; j < tce->digest_length; j++)
Philipp Deppenwiesec9b7d1f2018-11-10 00:35:02 +0100879 printf("%02x", tce->digest[j]);
880
881 printf(" %s [%s]\n", tce->digest_type, tce->name);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200882 }
883
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200884 unmap_memory(&log_mapping);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200885}
886
Julius Wernerd67c6872017-02-02 17:32:00 -0800887struct cbmem_console {
888 u32 size;
889 u32 cursor;
890 u8 body[0];
891} __attribute__ ((__packed__));
892
893#define CBMC_CURSOR_MASK ((1 << 28) - 1)
894#define CBMC_OVERFLOW (1 << 31)
895
Julius Werner8202fc42021-09-08 16:10:15 -0700896enum console_print_type {
897 CONSOLE_PRINT_FULL = 0,
898 CONSOLE_PRINT_LAST,
899 CONSOLE_PRINT_PREVIOUS,
900};
901
Julius Wernerb8258bd2022-02-09 17:26:39 -0800902static int parse_loglevel(char *arg, int *print_unknown_logs)
903{
904 if (arg[0] == '+') {
905 *print_unknown_logs = 1;
906 arg++;
907 } else {
908 *print_unknown_logs = 0;
909 }
910
911 char *endptr;
912 int loglevel = strtol(arg, &endptr, 0);
913 if (*endptr == '\0' && loglevel >= BIOS_EMERG && loglevel <= BIOS_LOG_PREFIX_MAX_LEVEL)
914 return loglevel;
915
916 /* Only match first 3 characters so `NOTE` and `NOTICE` both match. */
917 for (int i = BIOS_EMERG; i <= BIOS_LOG_PREFIX_MAX_LEVEL; i++)
918 if (!strncasecmp(arg, bios_log_prefix[i], 3))
919 return i;
920
921 *print_unknown_logs = 1;
922 return BIOS_NEVER;
923}
924
Stefan Reinauer19f87562013-01-07 13:37:12 -0800925/* dump the cbmem console */
Julius Wernerb8258bd2022-02-09 17:26:39 -0800926static void dump_console(enum console_print_type type, int max_loglevel, int print_unknown_logs)
Stefan Reinauer19f87562013-01-07 13:37:12 -0800927{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600928 const struct cbmem_console *console_p;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800929 char *console_c;
Julius Werner8202fc42021-09-08 16:10:15 -0700930 size_t size, cursor, previous;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600931 struct mapping console_mapping;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800932
933 if (console.tag != LB_TAG_CBMEM_CONSOLE) {
934 fprintf(stderr, "No console found in coreboot table.\n");
935 return;
936 }
937
Julius Wernerd67c6872017-02-02 17:32:00 -0800938 size = sizeof(*console_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600939 console_p = map_memory(&console_mapping, console.cbmem_addr, size);
940 if (!console_p)
941 die("Unable to map console object.\n");
942
Julius Wernerd67c6872017-02-02 17:32:00 -0800943 cursor = console_p->cursor & CBMC_CURSOR_MASK;
944 if (!(console_p->cursor & CBMC_OVERFLOW) && cursor < console_p->size)
Vladimir Serbinenkof4a0d012013-03-30 12:15:12 +0100945 size = cursor;
Julius Wernerd67c6872017-02-02 17:32:00 -0800946 else
947 size = console_p->size;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600948 unmap_memory(&console_mapping);
Julius Wernerd67c6872017-02-02 17:32:00 -0800949
950 console_c = malloc(size + 1);
Stefan Reinauer19f87562013-01-07 13:37:12 -0800951 if (!console_c) {
952 fprintf(stderr, "Not enough memory for console.\n");
953 exit(1);
954 }
Julius Wernerd67c6872017-02-02 17:32:00 -0800955 console_c[size] = '\0';
Stefan Reinauer19f87562013-01-07 13:37:12 -0800956
Aaron Durbin46300aa2017-09-26 16:22:38 -0600957 console_p = map_memory(&console_mapping, console.cbmem_addr,
958 size + sizeof(*console_p));
959
960 if (!console_p)
961 die("Unable to map full console object.\n");
962
Julius Wernerd67c6872017-02-02 17:32:00 -0800963 if (console_p->cursor & CBMC_OVERFLOW) {
964 if (cursor >= size) {
965 printf("cbmem: ERROR: CBMEM console struct is illegal, "
966 "output may be corrupt or out of order!\n\n");
967 cursor = 0;
968 }
969 aligned_memcpy(console_c, console_p->body + cursor,
970 size - cursor);
971 aligned_memcpy(console_c + size - cursor,
972 console_p->body, cursor);
973 } else {
974 aligned_memcpy(console_c, console_p->body, size);
975 }
Stefan Reinauer19f87562013-01-07 13:37:12 -0800976
Julius Wernerd67c6872017-02-02 17:32:00 -0800977 /* Slight memory corruption may occur between reboots and give us a few
978 unprintable characters like '\0'. Replace them with '?' on output. */
979 for (cursor = 0; cursor < size; cursor++)
Julius Werner984d03c2022-01-21 15:33:47 -0800980 if (!isprint(console_c[cursor]) && !isspace(console_c[cursor])
981 && !BIOS_LOG_IS_MARKER(console_c[cursor]))
Julius Wernerd67c6872017-02-02 17:32:00 -0800982 console_c[cursor] = '?';
Stefan Reinauer19f87562013-01-07 13:37:12 -0800983
Julius Werner8202fc42021-09-08 16:10:15 -0700984 /* We detect the reboot cutoff by looking for a bootblock, romstage or
Julius Wernerb7b64a92017-04-28 16:31:46 -0700985 ramstage banner, in that order (to account for platforms without
986 CONFIG_BOOTBLOCK_CONSOLE and/or CONFIG_EARLY_CONSOLE). Once we find
Julius Werner8202fc42021-09-08 16:10:15 -0700987 a banner, store the last two matches for that stage and stop. */
988 cursor = previous = 0;
989 if (type != CONSOLE_PRINT_FULL) {
You-Cheng Syu1430b392019-07-01 16:40:25 +0800990#define BANNER_REGEX(stage) \
Julius Wernerce878322022-03-09 16:51:43 -0800991 "\n\n.?coreboot-[^\n]* " stage " starting.*\\.\\.\\.\n"
992#define OVERFLOW_REGEX(stage) "\n.?\\*\\*\\* Pre-CBMEM " stage " console overflow"
Kangheui Won4b5c8b52020-10-07 14:29:38 +1100993 const char *regex[] = { BANNER_REGEX("verstage-before-bootblock"),
994 BANNER_REGEX("bootblock"),
You-Cheng Syu1430b392019-07-01 16:40:25 +0800995 BANNER_REGEX("verstage"),
Julius Wernerd906bb62017-05-16 13:54:18 -0700996 OVERFLOW_REGEX("romstage"),
Furquan Shaikh35972de2018-02-16 16:12:02 -0800997 BANNER_REGEX("romstage"),
998 OVERFLOW_REGEX("ramstage"),
999 BANNER_REGEX("ramstage") };
Julius Wernerb7b64a92017-04-28 16:31:46 -07001000
Jacob Garber414d5d82019-06-27 16:02:32 -06001001 for (size_t i = 0; !cursor && i < ARRAY_SIZE(regex); i++) {
Julius Wernerb7b64a92017-04-28 16:31:46 -07001002 regex_t re;
1003 regmatch_t match;
Julius Wernerce878322022-03-09 16:51:43 -08001004 int res = regcomp(&re, regex[i], REG_EXTENDED);
1005 assert(res == 0);
Julius Wernerb7b64a92017-04-28 16:31:46 -07001006
1007 /* Keep looking for matches so we find the last one. */
Julius Werner8202fc42021-09-08 16:10:15 -07001008 while (!regexec(&re, console_c + cursor, 1, &match, 0)) {
1009 previous = cursor;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001010 cursor += match.rm_so + 1;
Julius Werner8202fc42021-09-08 16:10:15 -07001011 }
Julius Wernerb7b64a92017-04-28 16:31:46 -07001012 regfree(&re);
1013 }
1014 }
1015
Julius Werner8202fc42021-09-08 16:10:15 -07001016 if (type == CONSOLE_PRINT_PREVIOUS) {
1017 console_c[cursor] = '\0';
1018 cursor = previous;
1019 }
1020
Julius Werner984d03c2022-01-21 15:33:47 -08001021 char c;
Julius Wernerb8258bd2022-02-09 17:26:39 -08001022 int suppressed = 0;
Julius Werner984d03c2022-01-21 15:33:47 -08001023 int tty = isatty(fileno(stdout));
1024 while ((c = console_c[cursor++])) {
1025 if (BIOS_LOG_IS_MARKER(c)) {
1026 int lvl = BIOS_LOG_MARKER_TO_LEVEL(c);
Julius Wernerb8258bd2022-02-09 17:26:39 -08001027 if (lvl > max_loglevel) {
1028 suppressed = 1;
1029 continue;
1030 }
1031 suppressed = 0;
Julius Werner984d03c2022-01-21 15:33:47 -08001032 if (tty)
1033 printf(BIOS_LOG_ESCAPE_PATTERN, bios_log_escape[lvl]);
1034 printf(BIOS_LOG_PREFIX_PATTERN, bios_log_prefix[lvl]);
1035 } else {
Julius Wernerb8258bd2022-02-09 17:26:39 -08001036 if (!suppressed)
1037 putchar(c);
1038 if (c == '\n') {
1039 if (tty && !suppressed)
1040 printf(BIOS_LOG_ESCAPE_RESET);
1041 suppressed = !print_unknown_logs;
1042 }
Julius Werner984d03c2022-01-21 15:33:47 -08001043 }
1044 }
1045 if (tty)
1046 printf(BIOS_LOG_ESCAPE_RESET);
1047
Stefan Reinauer19f87562013-01-07 13:37:12 -08001048 free(console_c);
Aaron Durbin46300aa2017-09-26 16:22:38 -06001049 unmap_memory(&console_mapping);
Stefan Reinauer19f87562013-01-07 13:37:12 -08001050}
1051
Stefan Reinauera9c83612013-07-16 17:47:35 -07001052static void hexdump(unsigned long memory, int length)
1053{
1054 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -06001055 const uint8_t *m;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001056 int all_zero = 0;
Aaron Durbin46300aa2017-09-26 16:22:38 -06001057 struct mapping hexdump_mapping;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001058
Aaron Durbin46300aa2017-09-26 16:22:38 -06001059 m = map_memory(&hexdump_mapping, memory, length);
1060 if (!m)
1061 die("Unable to map hexdump memory.\n");
Stefan Reinauera9c83612013-07-16 17:47:35 -07001062
1063 for (i = 0; i < length; i += 16) {
1064 int j;
1065
1066 all_zero++;
1067 for (j = 0; j < 16; j++) {
1068 if(m[i+j] != 0) {
1069 all_zero = 0;
1070 break;
1071 }
1072 }
1073
1074 if (all_zero < 2) {
1075 printf("%08lx:", memory + i);
1076 for (j = 0; j < 16; j++)
1077 printf(" %02x", m[i+j]);
1078 printf(" ");
1079 for (j = 0; j < 16; j++)
1080 printf("%c", isprint(m[i+j]) ? m[i+j] : '.');
1081 printf("\n");
1082 } else if (all_zero == 2) {
1083 printf("...\n");
1084 }
1085 }
1086
Aaron Durbin46300aa2017-09-26 16:22:38 -06001087 unmap_memory(&hexdump_mapping);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001088}
1089
1090static void dump_cbmem_hex(void)
1091{
1092 if (cbmem.type != LB_MEM_TABLE) {
1093 fprintf(stderr, "No coreboot CBMEM area found!\n");
1094 return;
1095 }
1096
Jianjun Wangb2537bd2022-04-08 16:57:28 +08001097 hexdump(cbmem.start, cbmem.size);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001098}
1099
Jacob Garber79a2f472019-06-27 17:23:25 -06001100static void rawdump(uint64_t base, uint64_t size)
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001101{
Aaron Durbinf2a38222017-09-26 17:44:28 -06001102 const uint8_t *m;
Aaron Durbin46300aa2017-09-26 16:22:38 -06001103 struct mapping dump_mapping;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001104
Aaron Durbin46300aa2017-09-26 16:22:38 -06001105 m = map_memory(&dump_mapping, base, size);
1106 if (!m)
1107 die("Unable to map rawdump memory\n");
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001108
Jacob Garber414d5d82019-06-27 16:02:32 -06001109 for (uint64_t i = 0 ; i < size; i++)
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001110 printf("%c", m[i]);
Aaron Durbin46300aa2017-09-26 16:22:38 -06001111
1112 unmap_memory(&dump_mapping);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001113}
1114
1115static void dump_cbmem_raw(unsigned int id)
1116{
Aaron Durbinf2a38222017-09-26 17:44:28 -06001117 const uint8_t *table;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001118 size_t offset;
1119 uint64_t base = 0;
1120 uint64_t size = 0;
1121
Aaron Durbin46300aa2017-09-26 16:22:38 -06001122 table = mapping_virt(&lbtable_mapping);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001123
1124 if (table == NULL)
1125 return;
1126
1127 offset = 0;
1128
Aaron Durbin46300aa2017-09-26 16:22:38 -06001129 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -06001130 const struct lb_record *lbr;
Yidi Lin0811a642022-09-15 15:47:59 +08001131 struct lb_cbmem_entry lbe;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001132
Aaron Durbinf2a38222017-09-26 17:44:28 -06001133 lbr = (const void *)(table + offset);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001134 offset += lbr->size;
1135
1136 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
1137 continue;
1138
Yidi Lin0811a642022-09-15 15:47:59 +08001139 aligned_memcpy(&lbe, lbr, sizeof(lbe));
1140 if (lbe.id == id) {
1141 debug("found id for raw dump %0x", lbe.id);
1142 base = lbe.address;
1143 size = lbe.entry_size;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001144 break;
1145 }
1146 }
1147
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001148 if (!base)
1149 fprintf(stderr, "id %0x not found in cbtable\n", id);
1150 else
1151 rawdump(base, size);
1152}
1153
Aaron Durbin0dff57d2015-03-05 21:18:33 -06001154struct cbmem_id_to_name {
1155 uint32_t id;
1156 const char *name;
1157};
Vadim Bendebury8b143c52014-05-14 10:12:55 -07001158static const struct cbmem_id_to_name cbmem_ids[] = { CBMEM_ID_TO_NAME_TABLE };
Stefan Reinauerc0199072013-01-07 16:26:10 -08001159
Kyösti Mälkkieab5c122017-09-04 11:10:17 +03001160#define MAX_STAGEx 10
Jacob Garber79a2f472019-06-27 17:23:25 -06001161static void cbmem_print_entry(int n, uint32_t id, uint64_t base, uint64_t size)
Stefan Reinauera9c83612013-07-16 17:47:35 -07001162{
Stefan Reinauera9c83612013-07-16 17:47:35 -07001163 const char *name;
Kyösti Mälkkieab5c122017-09-04 11:10:17 +03001164 char stage_x[20];
Stefan Reinauera9c83612013-07-16 17:47:35 -07001165
1166 name = NULL;
Jacob Garber414d5d82019-06-27 16:02:32 -06001167 for (size_t i = 0; i < ARRAY_SIZE(cbmem_ids); i++) {
Stefan Reinauera9c83612013-07-16 17:47:35 -07001168 if (cbmem_ids[i].id == id) {
1169 name = cbmem_ids[i].name;
1170 break;
1171 }
Kyösti Mälkkieab5c122017-09-04 11:10:17 +03001172 if (id >= CBMEM_ID_STAGEx_META &&
1173 id < CBMEM_ID_STAGEx_META + MAX_STAGEx) {
1174 snprintf(stage_x, sizeof(stage_x), "STAGE%d META",
1175 (id - CBMEM_ID_STAGEx_META));
1176 name = stage_x;
1177 }
1178 if (id >= CBMEM_ID_STAGEx_CACHE &&
1179 id < CBMEM_ID_STAGEx_CACHE + MAX_STAGEx) {
1180 snprintf(stage_x, sizeof(stage_x), "STAGE%d $ ",
1181 (id - CBMEM_ID_STAGEx_CACHE));
1182 name = stage_x;
1183 }
Stefan Reinauera9c83612013-07-16 17:47:35 -07001184 }
1185
1186 printf("%2d. ", n);
1187 if (name == NULL)
Martin Roth90e4f3d2022-10-20 21:11:30 -06001188 name = "(unknown)";
1189 printf("%-20s %08x", name, id);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001190 printf(" %08" PRIx64 " ", base);
Martin Roth90e4f3d2022-10-20 21:11:30 -06001191 printf(" %08" PRIx64 "\n", size);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001192}
1193
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001194static void dump_cbmem_toc(void)
Stefan Reinauerc0199072013-01-07 16:26:10 -08001195{
Aaron Durbin09c0c112015-09-30 12:33:01 -05001196 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -06001197 const uint8_t *table;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001198 size_t offset;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001199
Aaron Durbin46300aa2017-09-26 16:22:38 -06001200 table = mapping_virt(&lbtable_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -05001201
1202 if (table == NULL)
Stefan Reinauerc0199072013-01-07 16:26:10 -08001203 return;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001204
1205 printf("CBMEM table of contents:\n");
Martin Roth90e4f3d2022-10-20 21:11:30 -06001206 printf(" %-20s %-8s %-8s %-8s\n", "NAME", "ID", "START",
1207 "LENGTH");
Aaron Durbin09c0c112015-09-30 12:33:01 -05001208
1209 i = 0;
1210 offset = 0;
1211
Aaron Durbin46300aa2017-09-26 16:22:38 -06001212 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -06001213 const struct lb_record *lbr;
Yidi Lin0811a642022-09-15 15:47:59 +08001214 struct lb_cbmem_entry lbe;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001215
Aaron Durbinf2a38222017-09-26 17:44:28 -06001216 lbr = (const void *)(table + offset);
Aaron Durbin09c0c112015-09-30 12:33:01 -05001217 offset += lbr->size;
1218
1219 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
1220 continue;
1221
Yidi Lin0811a642022-09-15 15:47:59 +08001222 aligned_memcpy(&lbe, lbr, sizeof(lbe));
1223 cbmem_print_entry(i, lbe.id, lbe.address, lbe.entry_size);
Aaron Durbin09c0c112015-09-30 12:33:01 -05001224 i++;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001225 }
Stefan Reinauerc0199072013-01-07 16:26:10 -08001226}
Stefan Reinauer19f87562013-01-07 13:37:12 -08001227
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001228#define COVERAGE_MAGIC 0x584d4153
1229struct file {
1230 uint32_t magic;
1231 uint32_t next;
1232 uint32_t filename;
1233 uint32_t data;
1234 int offset;
1235 int len;
1236};
1237
1238static int mkpath(char *path, mode_t mode)
1239{
1240 assert (path && *path);
1241 char *p;
1242 for (p = strchr(path+1, '/'); p; p = strchr(p + 1, '/')) {
1243 *p = '\0';
1244 if (mkdir(path, mode) == -1) {
1245 if (errno != EEXIST) {
1246 *p = '/';
1247 return -1;
1248 }
1249 }
1250 *p = '/';
1251 }
1252 return 0;
1253}
1254
1255static void dump_coverage(void)
1256{
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001257 uint64_t start;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001258 size_t size;
Aaron Durbinf2a38222017-09-26 17:44:28 -06001259 const void *coverage;
Aaron Durbin46300aa2017-09-26 16:22:38 -06001260 struct mapping coverage_mapping;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001261 unsigned long phys_offset;
1262#define phys_to_virt(x) ((void *)(unsigned long)(x) + phys_offset)
1263
Aaron Durbin09c0c112015-09-30 12:33:01 -05001264 if (find_cbmem_entry(CBMEM_ID_COVERAGE, &start, &size)) {
1265 fprintf(stderr, "No coverage information found\n");
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001266 return;
1267 }
1268
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001269 /* Map coverage area */
Aaron Durbin46300aa2017-09-26 16:22:38 -06001270 coverage = map_memory(&coverage_mapping, start, size);
1271 if (!coverage)
1272 die("Unable to map coverage area.\n");
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001273 phys_offset = (unsigned long)coverage - (unsigned long)start;
1274
1275 printf("Dumping coverage data...\n");
1276
1277 struct file *file = (struct file *)coverage;
1278 while (file && file->magic == COVERAGE_MAGIC) {
1279 FILE *f;
1280 char *filename;
1281
1282 debug(" -> %s\n", (char *)phys_to_virt(file->filename));
1283 filename = strdup((char *)phys_to_virt(file->filename));
1284 if (mkpath(filename, 0755) == -1) {
1285 perror("Directory for coverage data could "
1286 "not be created");
1287 exit(1);
1288 }
1289 f = fopen(filename, "wb");
1290 if (!f) {
1291 printf("Could not open %s: %s\n",
1292 filename, strerror(errno));
1293 exit(1);
1294 }
1295 if (fwrite((void *)phys_to_virt(file->data),
1296 file->len, 1, f) != 1) {
1297 printf("Could not write to %s: %s\n",
1298 filename, strerror(errno));
1299 exit(1);
1300 }
1301 fclose(f);
1302 free(filename);
1303
1304 if (file->next)
1305 file = (struct file *)phys_to_virt(file->next);
1306 else
1307 file = NULL;
1308 }
Aaron Durbin46300aa2017-09-26 16:22:38 -06001309 unmap_memory(&coverage_mapping);
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001310}
1311
1312static void print_version(void)
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001313{
1314 printf("cbmem v%s -- ", CBMEM_VERSION);
1315 printf("Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.\n\n");
1316 printf(
1317 "This program is free software: you can redistribute it and/or modify\n"
1318 "it under the terms of the GNU General Public License as published by\n"
1319 "the Free Software Foundation, version 2 of the License.\n\n"
1320 "This program is distributed in the hope that it will be useful,\n"
1321 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1322 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
Martin Rotheb20e602016-01-12 13:30:50 -07001323 "GNU General Public License for more details.\n\n");
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001324}
1325
Martin Roth8448ac42016-09-11 15:43:22 -06001326static void print_usage(const char *name, int exit_code)
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001327{
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001328 printf("usage: %s [-cCltTLxVvh?]\n", name);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001329 printf("\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001330 " -c | --console: print cbmem console\n"
Julius Wernerb7b64a92017-04-28 16:31:46 -07001331 " -1 | --oneboot: print cbmem console for last boot only\n"
Julius Werner8202fc42021-09-08 16:10:15 -07001332 " -2 | --2ndtolast: print cbmem console for the boot that came before the last one only\n"
Julius Wernerb8258bd2022-02-09 17:26:39 -08001333 " -B | --loglevel: maximum loglevel to print; prefix `+` (e.g. -B +INFO) to also print lines that have no level\n"
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001334 " -C | --coverage: dump coverage information\n"
Stefan Reinauerc0199072013-01-07 16:26:10 -08001335 " -l | --list: print cbmem table of contents\n"
Stefan Reinauera9c83612013-07-16 17:47:35 -07001336 " -x | --hexdump: print hexdump of cbmem area\n"
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001337 " -r | --rawdump ID: print rawdump of specific ID (in hex) of cbtable\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001338 " -t | --timestamps: print timestamp information\n"
Aaron Durbinfbff3012015-08-30 22:00:12 -05001339 " -T | --parseable-timestamps: print parseable timestamps\n"
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001340 " -S | --stacked-timestamps: print stacked timestamps (e.g. for flame graph tools)\n"
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001341 " -a | --add-timestamp ID: append timestamp with ID\n"
Sergii Dmytruk2710df72022-11-10 00:40:51 +02001342 " -L | --tcpa-log print TPM log\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001343 " -V | --verbose: verbose (debugging) output\n"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001344 " -v | --version: print the version\n"
1345 " -h | --help: print this help\n"
1346 "\n");
Martin Roth8448ac42016-09-11 15:43:22 -06001347 exit(exit_code);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001348}
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001349
Adam Kallai66c22502018-11-30 11:31:50 +01001350#if defined(__arm__) || defined(__aarch64__)
Julius Werner337de4c2014-06-16 23:02:03 -07001351static void dt_update_cells(const char *name, int *addr_cells_ptr,
1352 int *size_cells_ptr)
1353{
1354 if (*addr_cells_ptr >= 0 && *size_cells_ptr >= 0)
1355 return;
1356
1357 int buffer;
1358 size_t nlen = strlen(name);
1359 char *prop = alloca(nlen + sizeof("/#address-cells"));
1360 strcpy(prop, name);
1361
1362 if (*addr_cells_ptr < 0) {
1363 strcpy(prop + nlen, "/#address-cells");
1364 int fd = open(prop, O_RDONLY);
1365 if (fd < 0 && errno != ENOENT) {
1366 perror(prop);
1367 } else if (fd >= 0) {
1368 if (read(fd, &buffer, sizeof(int)) < 0)
1369 perror(prop);
1370 else
1371 *addr_cells_ptr = ntohl(buffer);
1372 close(fd);
1373 }
1374 }
1375
1376 if (*size_cells_ptr < 0) {
1377 strcpy(prop + nlen, "/#size-cells");
1378 int fd = open(prop, O_RDONLY);
1379 if (fd < 0 && errno != ENOENT) {
1380 perror(prop);
1381 } else if (fd >= 0) {
1382 if (read(fd, &buffer, sizeof(int)) < 0)
1383 perror(prop);
1384 else
1385 *size_cells_ptr = ntohl(buffer);
1386 close(fd);
1387 }
1388 }
1389}
1390
1391static char *dt_find_compat(const char *parent, const char *compat,
1392 int *addr_cells_ptr, int *size_cells_ptr)
1393{
1394 char *ret = NULL;
1395 struct dirent *entry;
1396 DIR *dir;
1397
1398 if (!(dir = opendir(parent))) {
1399 perror(parent);
1400 return NULL;
1401 }
1402
1403 /* Loop through all files in the directory (DT node). */
1404 while ((entry = readdir(dir))) {
1405 /* We only care about compatible props or subnodes. */
1406 if (entry->d_name[0] == '.' || !((entry->d_type & DT_DIR) ||
1407 !strcmp(entry->d_name, "compatible")))
1408 continue;
1409
1410 /* Assemble the file name (on the stack, for speed). */
1411 size_t plen = strlen(parent);
1412 char *name = alloca(plen + strlen(entry->d_name) + 2);
1413
1414 strcpy(name, parent);
1415 name[plen] = '/';
1416 strcpy(name + plen + 1, entry->d_name);
1417
1418 /* If it's a subnode, recurse. */
1419 if (entry->d_type & DT_DIR) {
1420 ret = dt_find_compat(name, compat, addr_cells_ptr,
1421 size_cells_ptr);
1422
1423 /* There is only one matching node to find, abort. */
1424 if (ret) {
1425 /* Gather cells values on the way up. */
1426 dt_update_cells(parent, addr_cells_ptr,
1427 size_cells_ptr);
1428 break;
1429 }
1430 continue;
1431 }
1432
1433 /* If it's a compatible string, see if it's the right one. */
1434 int fd = open(name, O_RDONLY);
1435 int clen = strlen(compat);
1436 char *buffer = alloca(clen + 1);
1437
1438 if (fd < 0) {
1439 perror(name);
1440 continue;
1441 }
1442
1443 if (read(fd, buffer, clen + 1) < 0) {
1444 perror(name);
1445 close(fd);
1446 continue;
1447 }
1448 close(fd);
1449
1450 if (!strcmp(compat, buffer)) {
1451 /* Initialize these to "unset" for the way up. */
1452 *addr_cells_ptr = *size_cells_ptr = -1;
1453
1454 /* Can't leave string on the stack or we'll lose it! */
1455 ret = strdup(parent);
1456 break;
1457 }
1458 }
1459
1460 closedir(dir);
1461 return ret;
1462}
Adam Kallai66c22502018-11-30 11:31:50 +01001463#endif /* defined(__arm__) || defined(__aarch64__) */
Julius Werner337de4c2014-06-16 23:02:03 -07001464
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001465int main(int argc, char** argv)
1466{
Stefan Reinauer19f87562013-01-07 13:37:12 -08001467 int print_defaults = 1;
1468 int print_console = 0;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001469 int print_coverage = 0;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001470 int print_list = 0;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001471 int print_hexdump = 0;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001472 int print_rawdump = 0;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001473 int print_tcpa_log = 0;
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001474 enum timestamps_print_type timestamp_type = TIMESTAMPS_PRINT_NONE;
Julius Werner8202fc42021-09-08 16:10:15 -07001475 enum console_print_type console_type = CONSOLE_PRINT_FULL;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001476 unsigned int rawdump_id = 0;
Julius Wernerb8258bd2022-02-09 17:26:39 -08001477 int max_loglevel = BIOS_NEVER;
1478 int print_unknown_logs = 1;
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001479 uint32_t timestamp_id = 0;
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001480
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001481 int opt, option_index = 0;
1482 static struct option long_options[] = {
Stefan Reinauer19f87562013-01-07 13:37:12 -08001483 {"console", 0, 0, 'c'},
Julius Wernerb7b64a92017-04-28 16:31:46 -07001484 {"oneboot", 0, 0, '1'},
Julius Werner8202fc42021-09-08 16:10:15 -07001485 {"2ndtolast", 0, 0, '2'},
Julius Wernerb8258bd2022-02-09 17:26:39 -08001486 {"loglevel", required_argument, 0, 'B'},
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001487 {"coverage", 0, 0, 'C'},
Stefan Reinauerc0199072013-01-07 16:26:10 -08001488 {"list", 0, 0, 'l'},
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001489 {"tcpa-log", 0, 0, 'L'},
Stefan Reinauer19f87562013-01-07 13:37:12 -08001490 {"timestamps", 0, 0, 't'},
Aaron Durbinfbff3012015-08-30 22:00:12 -05001491 {"parseable-timestamps", 0, 0, 'T'},
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001492 {"stacked-timestamps", 0, 0, 'S'},
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001493 {"add-timestamp", required_argument, 0, 'a'},
Stefan Reinauera9c83612013-07-16 17:47:35 -07001494 {"hexdump", 0, 0, 'x'},
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001495 {"rawdump", required_argument, 0, 'r'},
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001496 {"verbose", 0, 0, 'V'},
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001497 {"version", 0, 0, 'v'},
1498 {"help", 0, 0, 'h'},
1499 {0, 0, 0, 0}
1500 };
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001501 while ((opt = getopt_long(argc, argv, "c12B:CltTSa:LxVvh?r:",
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001502 long_options, &option_index)) != EOF) {
1503 switch (opt) {
Stefan Reinauer19f87562013-01-07 13:37:12 -08001504 case 'c':
1505 print_console = 1;
1506 print_defaults = 0;
1507 break;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001508 case '1':
1509 print_console = 1;
Julius Werner8202fc42021-09-08 16:10:15 -07001510 console_type = CONSOLE_PRINT_LAST;
1511 print_defaults = 0;
1512 break;
1513 case '2':
1514 print_console = 1;
1515 console_type = CONSOLE_PRINT_PREVIOUS;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001516 print_defaults = 0;
1517 break;
Julius Wernerb8258bd2022-02-09 17:26:39 -08001518 case 'B':
1519 max_loglevel = parse_loglevel(optarg, &print_unknown_logs);
1520 break;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001521 case 'C':
1522 print_coverage = 1;
1523 print_defaults = 0;
1524 break;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001525 case 'l':
1526 print_list = 1;
1527 print_defaults = 0;
1528 break;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001529 case 'L':
1530 print_tcpa_log = 1;
1531 print_defaults = 0;
1532 break;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001533 case 'x':
1534 print_hexdump = 1;
1535 print_defaults = 0;
1536 break;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001537 case 'r':
1538 print_rawdump = 1;
1539 print_defaults = 0;
1540 rawdump_id = strtoul(optarg, NULL, 16);
1541 break;
Stefan Reinauer19f87562013-01-07 13:37:12 -08001542 case 't':
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001543 timestamp_type = TIMESTAMPS_PRINT_NORMAL;
Stefan Reinauer19f87562013-01-07 13:37:12 -08001544 print_defaults = 0;
1545 break;
Aaron Durbinfbff3012015-08-30 22:00:12 -05001546 case 'T':
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001547 timestamp_type = TIMESTAMPS_PRINT_MACHINE_READABLE;
1548 print_defaults = 0;
1549 break;
1550 case 'S':
1551 timestamp_type = TIMESTAMPS_PRINT_STACKED;
Aaron Durbinfbff3012015-08-30 22:00:12 -05001552 print_defaults = 0;
1553 break;
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001554 case 'a':
1555 print_defaults = 0;
1556 timestamp_id = timestamp_enum_name_to_id(optarg);
1557 /* Parse numeric value if name is unknown */
1558 if (timestamp_id == 0)
1559 timestamp_id = strtoul(optarg, NULL, 0);
1560 break;
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001561 case 'V':
1562 verbose = 1;
1563 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001564 case 'v':
1565 print_version();
1566 exit(0);
1567 break;
1568 case 'h':
Martin Roth8448ac42016-09-11 15:43:22 -06001569 print_usage(argv[0], 0);
1570 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001571 case '?':
1572 default:
Martin Roth8448ac42016-09-11 15:43:22 -06001573 print_usage(argv[0], 1);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001574 break;
1575 }
1576 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001577
Arthur Heymans8cd17ea2018-08-01 17:21:32 +02001578 if (optind < argc) {
1579 fprintf(stderr, "Error: Extra parameter found.\n");
1580 print_usage(argv[0], 1);
1581 }
1582
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001583 mem_fd = open("/dev/mem", timestamp_id ? O_RDWR : O_RDONLY, 0);
Julius Werner337de4c2014-06-16 23:02:03 -07001584 if (mem_fd < 0) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001585 fprintf(stderr, "Failed to gain memory access: %s\n",
1586 strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001587 return 1;
1588 }
1589
Adam Kallai66c22502018-11-30 11:31:50 +01001590#if defined(__arm__) || defined(__aarch64__)
Julius Werner337de4c2014-06-16 23:02:03 -07001591 int addr_cells, size_cells;
1592 char *coreboot_node = dt_find_compat("/proc/device-tree", "coreboot",
1593 &addr_cells, &size_cells);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001594
Julius Werner337de4c2014-06-16 23:02:03 -07001595 if (!coreboot_node) {
1596 fprintf(stderr, "Could not find 'coreboot' compatible node!\n");
Stefan Reinauer7f681502013-06-19 15:39:09 -07001597 return 1;
1598 }
1599
Julius Werner337de4c2014-06-16 23:02:03 -07001600 if (addr_cells < 0) {
1601 fprintf(stderr, "Warning: no #address-cells node in tree!\n");
1602 addr_cells = 1;
1603 }
1604
1605 int nlen = strlen(coreboot_node);
1606 char *reg = alloca(nlen + sizeof("/reg"));
1607
1608 strcpy(reg, coreboot_node);
1609 strcpy(reg + nlen, "/reg");
1610 free(coreboot_node);
1611
1612 int fd = open(reg, O_RDONLY);
1613 if (fd < 0) {
1614 perror(reg);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001615 return 1;
1616 }
Stefan Reinauer7f681502013-06-19 15:39:09 -07001617
Julius Werner337de4c2014-06-16 23:02:03 -07001618 int i;
Aaron Durbinb58f9e32014-10-07 14:56:35 -05001619 size_t size_to_read = addr_cells * 4 + size_cells * 4;
1620 u8 *dtbuffer = alloca(size_to_read);
1621 if (read(fd, dtbuffer, size_to_read) < 0) {
Julius Werner337de4c2014-06-16 23:02:03 -07001622 perror(reg);
1623 return 1;
1624 }
1625 close(fd);
1626
1627 /* No variable-length byte swap function anywhere in C... how sad. */
1628 u64 baseaddr = 0;
1629 for (i = 0; i < addr_cells * 4; i++) {
1630 baseaddr <<= 8;
Aaron Durbinb58f9e32014-10-07 14:56:35 -05001631 baseaddr |= *dtbuffer;
1632 dtbuffer++;
1633 }
1634 u64 cb_table_size = 0;
1635 for (i = 0; i < size_cells * 4; i++) {
1636 cb_table_size <<= 8;
1637 cb_table_size |= *dtbuffer;
1638 dtbuffer++;
Julius Werner337de4c2014-06-16 23:02:03 -07001639 }
1640
Aaron Durbin46300aa2017-09-26 16:22:38 -06001641 parse_cbtable(baseaddr, cb_table_size);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001642#else
Aaron Durbin46300aa2017-09-26 16:22:38 -06001643 unsigned long long possible_base_addresses[] = { 0, 0xf0000 };
Stefan Reinauer7f681502013-06-19 15:39:09 -07001644
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001645 /* Find and parse coreboot table */
Jacob Garber414d5d82019-06-27 16:02:32 -06001646 for (size_t j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
Aaron Durbin46300aa2017-09-26 16:22:38 -06001647 if (!parse_cbtable(possible_base_addresses[j], 0))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001648 break;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001649 }
Stefan Reinauer7f681502013-06-19 15:39:09 -07001650#endif
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001651
Aaron Durbin46300aa2017-09-26 16:22:38 -06001652 if (mapping_virt(&lbtable_mapping) == NULL)
1653 die("Table not found.\n");
1654
Stefan Reinauer19f87562013-01-07 13:37:12 -08001655 if (print_console)
Julius Wernerb8258bd2022-02-09 17:26:39 -08001656 dump_console(console_type, max_loglevel, print_unknown_logs);
Stefan Reinauer19f87562013-01-07 13:37:12 -08001657
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001658 if (print_coverage)
1659 dump_coverage();
1660
Stefan Reinauerc0199072013-01-07 16:26:10 -08001661 if (print_list)
1662 dump_cbmem_toc();
1663
Stefan Reinauera9c83612013-07-16 17:47:35 -07001664 if (print_hexdump)
1665 dump_cbmem_hex();
1666
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001667 if (print_rawdump)
1668 dump_cbmem_raw(rawdump_id);
1669
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001670 if (timestamp_id)
1671 timestamp_add_now(timestamp_id);
1672
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001673 if (print_defaults)
1674 timestamp_type = TIMESTAMPS_PRINT_NORMAL;
1675
1676 if (timestamp_type != TIMESTAMPS_PRINT_NONE)
1677 dump_timestamps(timestamp_type);
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001678
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001679 if (print_tcpa_log)
Sergii Dmytruk2710df72022-11-10 00:40:51 +02001680 dump_tpm_cb_log();
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001681
Aaron Durbin46300aa2017-09-26 16:22:38 -06001682 unmap_memory(&lbtable_mapping);
1683
Julius Werner337de4c2014-06-16 23:02:03 -07001684 close(mem_fd);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001685 return 0;
1686}