blob: 65c8544656d7b55e7526fb9089a392c5ffa709c1 [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
Julius Werner337de4c2014-06-16 23:02:03 -070036typedef uint8_t u8;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070037typedef uint16_t u16;
38typedef uint32_t u32;
39typedef uint64_t u64;
40
Aaron Durbin46300aa2017-09-26 16:22:38 -060041/* Return < 0 on error, 0 on success. */
42static int parse_cbtable(u64 address, size_t table_size);
43
44struct mapping {
45 void *virt;
46 size_t offset;
47 size_t virt_size;
48 unsigned long long phys;
49 size_t size;
50};
51
Stefan Reinauera9c83612013-07-16 17:47:35 -070052#define CBMEM_VERSION "1.1"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -080053
Stefan Reinauer05cbce62013-01-03 14:30:33 -080054/* verbose output? */
55static int verbose = 0;
56#define debug(x...) if(verbose) printf(x)
57
58/* File handle used to access /dev/mem */
Julius Werner337de4c2014-06-16 23:02:03 -070059static int mem_fd;
Aaron Durbin46300aa2017-09-26 16:22:38 -060060static struct mapping lbtable_mapping;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070061
Mattias Nisslerecc165b2022-02-08 23:01:50 +000062/* TSC frequency from the LB_TAG_TSC_INFO record. 0 if not present. */
63static uint32_t tsc_freq_khz = 0;
64
Aaron Durbin46300aa2017-09-26 16:22:38 -060065static void die(const char *msg)
66{
67 if (msg)
68 fputs(msg, stderr);
69 exit(1);
70}
71
72static unsigned long long system_page_size(void)
73{
74 static unsigned long long page_size;
75
76 if (!page_size)
77 page_size = getpagesize();
78
79 return page_size;
80}
81
82static inline size_t size_to_mib(size_t sz)
83{
84 return sz >> 20;
85}
86
87/* Return mapping of physical address requested. */
Mattias Nisslerecc165b2022-02-08 23:01:50 +000088static void *mapping_virt(const struct mapping *mapping)
Aaron Durbin46300aa2017-09-26 16:22:38 -060089{
Mattias Nisslerecc165b2022-02-08 23:01:50 +000090 char *v = mapping->virt;
Aaron Durbin46300aa2017-09-26 16:22:38 -060091
92 if (v == NULL)
93 return NULL;
94
95 return v + mapping->offset;
96}
97
Aaron Durbincf20c902017-09-28 17:52:59 -060098/* Returns virtual address on success, NULL on error. mapping is filled in. */
Mattias Nisslerecc165b2022-02-08 23:01:50 +000099static void *map_memory_with_prot(struct mapping *mapping,
100 unsigned long long phys, size_t sz, int prot)
Aaron Durbin46300aa2017-09-26 16:22:38 -0600101{
102 void *v;
103 unsigned long long page_size;
104
105 page_size = system_page_size();
106
107 mapping->virt = NULL;
108 mapping->offset = phys % page_size;
109 mapping->virt_size = sz + mapping->offset;
110 mapping->size = sz;
111 mapping->phys = phys;
112
113 if (size_to_mib(mapping->virt_size) == 0) {
114 debug("Mapping %zuB of physical memory at 0x%llx (requested 0x%llx).\n",
115 mapping->virt_size, phys - mapping->offset, phys);
116 } else {
117 debug("Mapping %zuMB of physical memory at 0x%llx (requested 0x%llx).\n",
118 size_to_mib(mapping->virt_size), phys - mapping->offset,
119 phys);
120 }
121
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000122 v = mmap(NULL, mapping->virt_size, prot, MAP_SHARED, mem_fd,
Aaron Durbin46300aa2017-09-26 16:22:38 -0600123 phys - mapping->offset);
124
125 if (v == MAP_FAILED) {
126 debug("Mapping failed %zuB of physical memory at 0x%llx.\n",
127 mapping->virt_size, phys - mapping->offset);
128 return NULL;
129 }
130
131 mapping->virt = v;
132
133 if (mapping->offset != 0)
134 debug(" ... padding virtual address with 0x%zx bytes.\n",
135 mapping->offset);
136
137 return mapping_virt(mapping);
138}
139
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000140/* Convenience helper for the common case of read-only mappings. */
141static const void *map_memory(struct mapping *mapping, unsigned long long phys,
142 size_t sz)
143{
144 return map_memory_with_prot(mapping, phys, sz, PROT_READ);
145}
146
147
Aaron Durbin46300aa2017-09-26 16:22:38 -0600148/* Returns 0 on success, < 0 on error. mapping is cleared if successful. */
149static int unmap_memory(struct mapping *mapping)
150{
151 if (mapping->virt == NULL)
152 return -1;
153
154 munmap(mapping->virt, mapping->virt_size);
155 mapping->virt = NULL;
156 mapping->offset = 0;
157 mapping->virt_size = 0;
158
159 return 0;
160}
161
162/* Return size of physical address mapping requested. */
163static size_t mapping_size(const struct mapping *mapping)
164{
165 if (mapping->virt == NULL)
166 return 0;
167
168 return mapping->size;
169}
Timothy Pearsondf699d52015-05-16 14:55:54 -0500170
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700171/*
Julius Werner127a79e2017-02-03 12:50:03 -0800172 * Some architectures map /dev/mem memory in a way that doesn't support
173 * unaligned accesses. Most normal libc memcpy()s aren't safe to use in this
174 * case, so build our own which makes sure to never do unaligned accesses on
175 * *src (*dest is fine since we never map /dev/mem for writing).
176 */
177static void *aligned_memcpy(void *dest, const void *src, size_t n)
178{
179 u8 *d = dest;
180 const volatile u8 *s = src; /* volatile to prevent optimization */
181
182 while ((uintptr_t)s & (sizeof(size_t) - 1)) {
183 if (n-- == 0)
184 return dest;
185 *d++ = *s++;
186 }
187
188 while (n >= sizeof(size_t)) {
189 *(size_t *)d = *(const volatile size_t *)s;
190 d += sizeof(size_t);
191 s += sizeof(size_t);
192 n -= sizeof(size_t);
193 }
194
195 while (n-- > 0)
196 *d++ = *s++;
197
198 return dest;
199}
200
201/*
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700202 * calculate ip checksum (16 bit quantities) on a passed in buffer. In case
203 * the buffer length is odd last byte is excluded from the calculation
204 */
205static u16 ipchcksum(const void *addr, unsigned size)
206{
207 const u16 *p = addr;
208 unsigned i, n = size / 2; /* don't expect odd sized blocks */
209 u32 sum = 0;
210
211 for (i = 0; i < n; i++)
212 sum += p[i];
213
214 sum = (sum >> 16) + (sum & 0xffff);
215 sum += (sum >> 16);
216 sum = ~sum & 0xffff;
217 return (u16) sum;
218}
219
Aaron Durbin09c0c112015-09-30 12:33:01 -0500220/* Find the first cbmem entry filling in the details. */
221static int find_cbmem_entry(uint32_t id, uint64_t *addr, size_t *size)
222{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600223 const uint8_t *table;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500224 size_t offset;
225 int ret = -1;
226
Aaron Durbin46300aa2017-09-26 16:22:38 -0600227 table = mapping_virt(&lbtable_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500228
229 if (table == NULL)
230 return -1;
231
232 offset = 0;
233
Aaron Durbin46300aa2017-09-26 16:22:38 -0600234 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -0600235 const struct lb_record *lbr;
Yidi Lin0811a642022-09-15 15:47:59 +0800236 struct lb_cbmem_entry lbe;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500237
Aaron Durbinf2a38222017-09-26 17:44:28 -0600238 lbr = (const void *)(table + offset);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500239 offset += lbr->size;
240
241 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
242 continue;
243
Yidi Lin0811a642022-09-15 15:47:59 +0800244 aligned_memcpy(&lbe, lbr, sizeof(lbe));
245 if (lbe.id != id)
Aaron Durbin09c0c112015-09-30 12:33:01 -0500246 continue;
247
Yidi Lin0811a642022-09-15 15:47:59 +0800248 *addr = lbe.address;
249 *size = lbe.entry_size;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500250 ret = 0;
251 break;
252 }
253
Aaron Durbin09c0c112015-09-30 12:33:01 -0500254 return ret;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700255}
256
257/*
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800258 * Try finding the timestamp table and coreboot cbmem console starting from the
259 * passed in memory offset. Could be called recursively in case a forwarding
260 * entry is found.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700261 *
Patrick Georgi220c2092020-01-30 12:58:08 +0100262 * Returns pointer to a memory buffer containing the timestamp table or zero if
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700263 * none found.
264 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800265
266static struct lb_cbmem_ref timestamps;
267static struct lb_cbmem_ref console;
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200268static struct lb_cbmem_ref tpm_cb_log;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800269static struct lb_memory_range cbmem;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800270
Stefan Reinauer8c594772013-04-19 14:22:29 -0700271/* This is a work-around for a nasty problem introduced by initially having
272 * pointer sized entries in the lb_cbmem_ref structures. This caused problems
273 * on 64bit x86 systems because coreboot is 32bit on those systems.
274 * When the problem was found, it was corrected, but there are a lot of
275 * systems out there with a firmware that does not produce the right
276 * lb_cbmem_ref structure. Hence we try to autocorrect this issue here.
277 */
Aaron Durbinf2a38222017-09-26 17:44:28 -0600278static struct lb_cbmem_ref parse_cbmem_ref(const struct lb_cbmem_ref *cbmem_ref)
Stefan Reinauer8c594772013-04-19 14:22:29 -0700279{
280 struct lb_cbmem_ref ret;
281
Julius Wernere3c23912018-11-26 15:57:59 -0800282 aligned_memcpy(&ret, cbmem_ref, sizeof(ret));
Stefan Reinauer8c594772013-04-19 14:22:29 -0700283
284 if (cbmem_ref->size < sizeof(*cbmem_ref))
285 ret.cbmem_addr = (uint32_t)ret.cbmem_addr;
286
Stefan Reinauera9c83612013-07-16 17:47:35 -0700287 debug(" cbmem_addr = %" PRIx64 "\n", ret.cbmem_addr);
288
Stefan Reinauer8c594772013-04-19 14:22:29 -0700289 return ret;
290}
291
Aaron Durbin46300aa2017-09-26 16:22:38 -0600292static void parse_memory_tags(const struct lb_memory *mem)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700293{
Aaron Durbin46300aa2017-09-26 16:22:38 -0600294 int num_entries;
295 int i;
296
297 /* Peel off the header size and calculate the number of entries. */
298 num_entries = (mem->size - sizeof(*mem)) / sizeof(mem->map[0]);
299
300 for (i = 0; i < num_entries; i++) {
301 if (mem->map[i].type != LB_MEM_TABLE)
302 continue;
303 debug(" LB_MEM_TABLE found.\n");
304 /* The last one found is CBMEM */
Aaron Durbineb722282019-01-31 09:40:26 -0700305 aligned_memcpy(&cbmem, &mem->map[i], sizeof(cbmem));
Aaron Durbin46300aa2017-09-26 16:22:38 -0600306 }
307}
308
309/* Return < 0 on error, 0 on success, 1 if forwarding table entry found. */
310static int parse_cbtable_entries(const struct mapping *table_mapping)
311{
312 size_t i;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200313 const struct lb_record *lbr_p;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600314 size_t table_size = mapping_size(table_mapping);
315 const void *lbtable = mapping_virt(table_mapping);
316 int forwarding_table_found = 0;
317
318 for (i = 0; i < table_size; i += lbr_p->size) {
319 lbr_p = lbtable + i;
320 debug(" coreboot table entry 0x%02x\n", lbr_p->tag);
321 switch (lbr_p->tag) {
322 case LB_TAG_MEMORY:
323 debug(" Found memory map.\n");
324 parse_memory_tags(lbtable + i);
325 continue;
326 case LB_TAG_TIMESTAMPS: {
327 debug(" Found timestamp table.\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200328 timestamps =
329 parse_cbmem_ref((struct lb_cbmem_ref *)lbr_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600330 continue;
331 }
332 case LB_TAG_CBMEM_CONSOLE: {
333 debug(" Found cbmem console.\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200334 console = parse_cbmem_ref((struct lb_cbmem_ref *)lbr_p);
335 continue;
336 }
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200337 case LB_TAG_TPM_CB_LOG: {
338 debug(" Found TPM CB log table.\n");
339 tpm_cb_log =
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200340 parse_cbmem_ref((struct lb_cbmem_ref *)lbr_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600341 continue;
342 }
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000343 case LB_TAG_TSC_INFO:
344 debug(" Found TSC info.\n");
345 tsc_freq_khz = ((struct lb_tsc_info *)lbr_p)->freq_khz;
346 continue;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600347 case LB_TAG_FORWARD: {
348 int ret;
349 /*
350 * This is a forwarding entry - repeat the
351 * search at the new address.
352 */
353 struct lb_forward lbf_p =
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200354 *(const struct lb_forward *)lbr_p;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600355 debug(" Found forwarding entry.\n");
356 ret = parse_cbtable(lbf_p.forward, 0);
357
358 /* Assume the forwarding entry is valid. If this fails
359 * then there's a total failure. */
360 if (ret < 0)
361 return -1;
362 forwarding_table_found = 1;
363 }
364 default:
365 break;
366 }
367 }
368
369 return forwarding_table_found;
370}
371
372/* Return < 0 on error, 0 on success. */
373static int parse_cbtable(u64 address, size_t table_size)
374{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600375 const void *buf;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600376 struct mapping header_mapping;
377 size_t req_size;
378 size_t i;
379
380 req_size = table_size;
381 /* Default to 4 KiB search space. */
382 if (req_size == 0)
383 req_size = 4 * 1024;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800384
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500385 debug("Looking for coreboot table at %" PRIx64 " %zd bytes.\n",
Aaron Durbin46300aa2017-09-26 16:22:38 -0600386 address, req_size);
387
388 buf = map_memory(&header_mapping, address, req_size);
389
Timothy Pearsonbea71402015-09-05 18:07:17 -0500390 if (!buf)
Aaron Durbin46300aa2017-09-26 16:22:38 -0600391 return -1;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700392
Aaron Durbin46300aa2017-09-26 16:22:38 -0600393 /* look at every 16 bytes */
Aaron Durbincf20c902017-09-28 17:52:59 -0600394 for (i = 0; i <= req_size - sizeof(struct lb_header); i += 16) {
Aaron Durbin46300aa2017-09-26 16:22:38 -0600395 int ret;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600396 const struct lb_header *lbh;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600397 struct mapping table_mapping;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700398
Aaron Durbin46300aa2017-09-26 16:22:38 -0600399 lbh = buf + i;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800400 if (memcmp(lbh->signature, "LBIO", sizeof(lbh->signature)) ||
401 !lbh->header_bytes ||
402 ipchcksum(lbh, sizeof(*lbh))) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700403 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700404 }
405
Aaron Durbin46300aa2017-09-26 16:22:38 -0600406 /* Map in the whole table to parse. */
407 if (!map_memory(&table_mapping, address + i + lbh->header_bytes,
408 lbh->table_bytes)) {
409 debug("Couldn't map in table\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700410 continue;
411 }
412
Aaron Durbin46300aa2017-09-26 16:22:38 -0600413 if (ipchcksum(mapping_virt(&table_mapping), lbh->table_bytes) !=
414 lbh->table_checksum) {
415 debug("Signature found, but wrong checksum.\n");
416 unmap_memory(&table_mapping);
417 continue;
418 }
419
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800420 debug("Found!\n");
421
Aaron Durbin46300aa2017-09-26 16:22:38 -0600422 ret = parse_cbtable_entries(&table_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500423
Aaron Durbin46300aa2017-09-26 16:22:38 -0600424 /* Table parsing failed. */
425 if (ret < 0) {
426 unmap_memory(&table_mapping);
427 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700428 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800429
Aaron Durbin46300aa2017-09-26 16:22:38 -0600430 /* Succeeded in parsing the table. Header not needed anymore. */
431 unmap_memory(&header_mapping);
432
433 /*
434 * Table parsing succeeded. If forwarding table not found update
435 * coreboot table mapping for future use.
436 */
437 if (ret == 0)
438 lbtable_mapping = table_mapping;
439 else
440 unmap_memory(&table_mapping);
441
442 return 0;
443 }
444
445 unmap_memory(&header_mapping);
446
447 return -1;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700448}
449
Patrick Georgid00f1802015-07-13 16:53:50 +0200450#if defined(linux) && (defined(__i386__) || defined(__x86_64__))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700451/*
Aaron Durbin08e920e2016-03-12 08:41:34 +0100452 * read CPU frequency from a sysfs file, return an frequency in Megahertz as
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700453 * an int or exit on any error.
454 */
Aaron Durbinc49014e2015-08-30 21:19:55 -0500455static unsigned long arch_tick_frequency(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700456{
457 FILE *cpuf;
458 char freqs[100];
459 int size;
460 char *endp;
461 u64 rv;
462
463 const char* freq_file =
464 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
465
466 cpuf = fopen(freq_file, "r");
467 if (!cpuf) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800468 fprintf(stderr, "Could not open %s: %s\n",
469 freq_file, strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700470 exit(1);
471 }
472
473 memset(freqs, 0, sizeof(freqs));
474 size = fread(freqs, 1, sizeof(freqs), cpuf);
475 if (!size || (size == sizeof(freqs))) {
476 fprintf(stderr, "Wrong number of bytes(%d) read from %s\n",
477 size, freq_file);
478 exit(1);
479 }
480 fclose(cpuf);
481 rv = strtoull(freqs, &endp, 10);
482
483 if (*endp == '\0' || *endp == '\n')
Aaron Durbin08e920e2016-03-12 08:41:34 +0100484 /* cpuinfo_max_freq is in kHz. Convert it to MHz. */
485 return rv / 1000;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700486 fprintf(stderr, "Wrong formatted value ^%s^ read from %s\n",
487 freqs, freq_file);
488 exit(1);
489}
Patrick Georgid00f1802015-07-13 16:53:50 +0200490#elif defined(__OpenBSD__) && (defined(__i386__) || defined(__x86_64__))
Aaron Durbinc49014e2015-08-30 21:19:55 -0500491static unsigned long arch_tick_frequency(void)
Patrick Georgid00f1802015-07-13 16:53:50 +0200492{
493 int mib[2] = { CTL_HW, HW_CPUSPEED };
494 static int value = 0;
495 size_t value_len = sizeof(value);
496
Aaron Durbinc49014e2015-08-30 21:19:55 -0500497 /* Return 1 MHz when sysctl fails. */
Patrick Georgid00f1802015-07-13 16:53:50 +0200498 if ((value == 0) && (sysctl(mib, 2, &value, &value_len, NULL, 0) == -1))
Aaron Durbinc49014e2015-08-30 21:19:55 -0500499 return 1;
Patrick Georgid00f1802015-07-13 16:53:50 +0200500
Aaron Durbinc49014e2015-08-30 21:19:55 -0500501 return value;
Patrick Georgid00f1802015-07-13 16:53:50 +0200502}
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700503#else
Aaron Durbinc49014e2015-08-30 21:19:55 -0500504static unsigned long arch_tick_frequency(void)
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700505{
Aaron Durbinc49014e2015-08-30 21:19:55 -0500506 /* 1 MHz = 1us. */
507 return 1;
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700508}
509#endif
510
Aaron Durbinc49014e2015-08-30 21:19:55 -0500511static unsigned long tick_freq_mhz;
512
513static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz)
514{
515 tick_freq_mhz = table_tick_freq_mhz;
516
Martin Rothac9d6b82017-12-11 13:27:56 -0700517 /* Honor table frequency if present. */
518 if (!tick_freq_mhz)
519 tick_freq_mhz = arch_tick_frequency();
Aaron Durbinc49014e2015-08-30 21:19:55 -0500520
521 if (!tick_freq_mhz) {
522 fprintf(stderr, "Cannot determine timestamp tick frequency.\n");
523 exit(1);
524 }
Martin Rothac9d6b82017-12-11 13:27:56 -0700525
526 debug("Timestamp tick frequency: %ld MHz\n", tick_freq_mhz);
Aaron Durbinc49014e2015-08-30 21:19:55 -0500527}
528
Jacob Garber79a2f472019-06-27 17:23:25 -0600529static u64 arch_convert_raw_ts_entry(u64 ts)
Aaron Durbinc49014e2015-08-30 21:19:55 -0500530{
531 return ts / tick_freq_mhz;
532}
533
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700534/*
535 * Print an integer in 'normalized' form - with commas separating every three
Julius Wernera7d92442014-12-02 20:51:19 -0800536 * decimal orders.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700537 */
Julius Wernera7d92442014-12-02 20:51:19 -0800538static void print_norm(u64 v)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700539{
Julius Wernera7d92442014-12-02 20:51:19 -0800540 if (v >= 1000) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700541 /* print the higher order sections first */
Julius Wernera7d92442014-12-02 20:51:19 -0800542 print_norm(v / 1000);
543 printf(",%3.3u", (u32)(v % 1000));
544 } else {
545 printf("%u", (u32)(v % 1000));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700546 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700547}
548
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000549static uint64_t timestamp_get(uint64_t table_tick_freq_mhz)
550{
551#if defined(__i386__) || defined(__x86_64__)
552 uint64_t tsc = __rdtsc();
553
554 /* No tick frequency specified means raw TSC values. */
555 if (!table_tick_freq_mhz)
556 return tsc;
557
558 if (tsc_freq_khz)
559 return tsc * table_tick_freq_mhz * 1000 / tsc_freq_khz;
Nick Vaccaro946e2922022-04-26 17:14:03 -0700560#else
561 (void)table_tick_freq_mhz;
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000562#endif
563 die("Don't know how to obtain timestamps on this platform.\n");
564 return 0;
565}
566
Aaron Durbinfbff3012015-08-30 22:00:12 -0500567static const char *timestamp_name(uint32_t id)
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700568{
Jacob Garber414d5d82019-06-27 16:02:32 -0600569 for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
Aaron Durbinfbff3012015-08-30 22:00:12 -0500570 if (timestamp_ids[i].id == id)
571 return timestamp_ids[i].name;
572 }
573 return "<unknown>";
574}
575
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000576static uint32_t timestamp_enum_name_to_id(const char *name)
577{
578 for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
579 if (!strcmp(timestamp_ids[i].enum_name, name))
580 return timestamp_ids[i].id;
581 }
582 return 0;
583}
584
Aaron Durbinfbff3012015-08-30 22:00:12 -0500585static uint64_t timestamp_print_parseable_entry(uint32_t id, uint64_t stamp,
586 uint64_t prev_stamp)
587{
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700588 const char *name;
Aaron Durbin799bf782015-08-06 13:52:08 -0500589 uint64_t step_time;
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700590
Aaron Durbinfbff3012015-08-30 22:00:12 -0500591 name = timestamp_name(id);
592
593 step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
594
595 /* ID<tab>absolute time<tab>relative time<tab>description */
596 printf("%d\t", id);
597 printf("%llu\t", (long long)arch_convert_raw_ts_entry(stamp));
598 printf("%llu\t", (long long)step_time);
599 printf("%s\n", name);
600
601 return step_time;
602}
603
Jacob Garber79a2f472019-06-27 17:23:25 -0600604static uint64_t timestamp_print_entry(uint32_t id, uint64_t stamp, uint64_t prev_stamp)
Aaron Durbinfbff3012015-08-30 22:00:12 -0500605{
606 const char *name;
607 uint64_t step_time;
608
609 name = timestamp_name(id);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700610
611 printf("%4d:", id);
Julius Wernera7d92442014-12-02 20:51:19 -0800612 printf("%-50s", name);
613 print_norm(arch_convert_raw_ts_entry(stamp));
Aaron Durbin799bf782015-08-06 13:52:08 -0500614 step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700615 if (prev_stamp) {
616 printf(" (");
Aaron Durbin799bf782015-08-06 13:52:08 -0500617 print_norm(step_time);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700618 printf(")");
619 }
620 printf("\n");
Aaron Durbin799bf782015-08-06 13:52:08 -0500621
622 return step_time;
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700623}
624
Raul E Rangeld4fec682018-05-11 11:08:07 -0600625static int compare_timestamp_entries(const void *a, const void *b)
626{
627 const struct timestamp_entry *tse_a = (struct timestamp_entry *)a;
628 const struct timestamp_entry *tse_b = (struct timestamp_entry *)b;
629
Furquan Shaikh8f567322018-05-17 15:08:03 -0700630 if (tse_a->entry_stamp > tse_b->entry_stamp)
631 return 1;
632 else if (tse_a->entry_stamp < tse_b->entry_stamp)
633 return -1;
634
635 return 0;
Raul E Rangeld4fec682018-05-11 11:08:07 -0600636}
637
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100638static int find_matching_end(struct timestamp_table *sorted_tst_p, uint32_t start, uint32_t end)
639{
640 uint32_t id = sorted_tst_p->entries[start].entry_id;
641 uint32_t possible_match = 0;
642
643 for (uint32_t i = 0; i < ARRAY_SIZE(timestamp_ids); ++i) {
644 if (timestamp_ids[i].id == id) {
645 possible_match = timestamp_ids[i].id_end;
646 break;
647 }
648 }
649
650 /* No match found or timestamp not defined in IDs table */
651 if (!possible_match)
652 return -1;
653
654 for (uint32_t i = start + 1; i < end; i++)
655 if (sorted_tst_p->entries[i].entry_id == possible_match)
656 return i;
657
658 return -1;
659}
660
661static const char *get_timestamp_name(const uint32_t id)
662{
663 for (uint32_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++)
664 if (timestamp_ids[i].id == id)
665 return timestamp_ids[i].enum_name;
666
667 return "UNKNOWN";
668}
669
670struct ts_range_stack {
671 const char *name;
672 const char *end_name;
673 uint32_t end;
674};
675
676static void print_with_path(struct ts_range_stack *range_stack, const int stacklvl,
677 const uint64_t stamp, const char *last_part)
678{
679 for (int i = 1; i <= stacklvl; ++i) {
680 printf("%s -> %s", range_stack[i].name, range_stack[i].end_name);
681 if (i < stacklvl || last_part)
682 putchar(';');
683 }
684 if (last_part)
685 printf("%s", last_part);
Paul Fagerburgac683842022-04-02 08:28:40 -0600686 printf(" %llu\n", (long long)arch_convert_raw_ts_entry(stamp));
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100687}
688
689enum timestamps_print_type {
690 TIMESTAMPS_PRINT_NONE,
691 TIMESTAMPS_PRINT_NORMAL,
692 TIMESTAMPS_PRINT_MACHINE_READABLE,
693 TIMESTAMPS_PRINT_STACKED,
694};
695
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700696/* dump the timestamp table */
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100697static void dump_timestamps(enum timestamps_print_type output_type)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700698{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600699 const struct timestamp_table *tst_p;
Raul E Rangeld4fec682018-05-11 11:08:07 -0600700 struct timestamp_table *sorted_tst_p;
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500701 size_t size;
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100702 uint64_t prev_stamp = 0;
703 uint64_t total_time = 0;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600704 struct mapping timestamp_mapping;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800705
706 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
707 fprintf(stderr, "No timestamps found in coreboot table.\n");
708 return;
709 }
710
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500711 size = sizeof(*tst_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600712 tst_p = map_memory(&timestamp_mapping, timestamps.cbmem_addr, size);
713 if (!tst_p)
714 die("Unable to map timestamp header\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700715
Aaron Durbinc49014e2015-08-30 21:19:55 -0500716 timestamp_set_tick_freq(tst_p->tick_freq_mhz);
717
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100718 if (output_type == TIMESTAMPS_PRINT_NORMAL)
Aaron Durbinfbff3012015-08-30 22:00:12 -0500719 printf("%d entries total:\n\n", tst_p->num_entries);
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500720 size += tst_p->num_entries * sizeof(tst_p->entries[0]);
721
Aaron Durbin46300aa2017-09-26 16:22:38 -0600722 unmap_memory(&timestamp_mapping);
723
724 tst_p = map_memory(&timestamp_mapping, timestamps.cbmem_addr, size);
725 if (!tst_p)
726 die("Unable to map full timestamp table\n");
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500727
Bora Guvendike383b3d2021-11-22 16:12:48 -0800728 sorted_tst_p = malloc(size + sizeof(struct timestamp_entry));
Raul E Rangeld4fec682018-05-11 11:08:07 -0600729 if (!sorted_tst_p)
730 die("Failed to allocate memory");
Julius Wernere3c23912018-11-26 15:57:59 -0800731 aligned_memcpy(sorted_tst_p, tst_p, size);
Raul E Rangeld4fec682018-05-11 11:08:07 -0600732
Bora Guvendike383b3d2021-11-22 16:12:48 -0800733 /*
734 * Insert a timestamp to represent the base time (start of coreboot),
735 * in case we have to rebase for negative timestamps below.
736 */
737 sorted_tst_p->entries[tst_p->num_entries].entry_id = 0;
738 sorted_tst_p->entries[tst_p->num_entries].entry_stamp = 0;
739 sorted_tst_p->num_entries += 1;
740
Raul E Rangeld4fec682018-05-11 11:08:07 -0600741 qsort(&sorted_tst_p->entries[0], sorted_tst_p->num_entries,
742 sizeof(struct timestamp_entry), compare_timestamp_entries);
743
Bora Guvendike383b3d2021-11-22 16:12:48 -0800744 /*
745 * If there are negative timestamp entries, rebase all of the
746 * timestamps to the lowest one in the list.
747 */
Bora Guvendikc79da5f2022-02-10 20:13:50 -0800748 if (sorted_tst_p->entries[0].entry_stamp < 0) {
Bora Guvendike383b3d2021-11-22 16:12:48 -0800749 sorted_tst_p->base_time = -sorted_tst_p->entries[0].entry_stamp;
Bora Guvendikc79da5f2022-02-10 20:13:50 -0800750 prev_stamp = 0;
751 } else {
752 prev_stamp = tst_p->base_time;
753 }
Bora Guvendike383b3d2021-11-22 16:12:48 -0800754
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100755 struct ts_range_stack range_stack[20];
756 range_stack[0].end = sorted_tst_p->num_entries;
757 int stacklvl = 0;
758
Jacob Garber414d5d82019-06-27 16:02:32 -0600759 for (uint32_t i = 0; i < sorted_tst_p->num_entries; i++) {
Aaron Durbin31540fb2015-07-11 12:44:10 -0500760 uint64_t stamp;
Raul E Rangeld4fec682018-05-11 11:08:07 -0600761 const struct timestamp_entry *tse = &sorted_tst_p->entries[i];
Aaron Durbin31540fb2015-07-11 12:44:10 -0500762
763 /* Make all timestamps absolute. */
Raul E Rangeld4fec682018-05-11 11:08:07 -0600764 stamp = tse->entry_stamp + sorted_tst_p->base_time;
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100765 if (output_type == TIMESTAMPS_PRINT_MACHINE_READABLE) {
766 timestamp_print_parseable_entry(tse->entry_id, stamp, prev_stamp);
767 } else if (output_type == TIMESTAMPS_PRINT_NORMAL) {
768 total_time += timestamp_print_entry(tse->entry_id, stamp, prev_stamp);
769 } else if (output_type == TIMESTAMPS_PRINT_STACKED) {
770 bool end_of_range = false;
771 /* Iterate over stacked entries to pop all ranges, which are closed by
772 current element. For example, assuming two ranges: (TS_A, TS_C),
773 (TS_B, TS_C) it will pop all of them instead of just last one. */
774 while (stacklvl > 0 && range_stack[stacklvl].end == i) {
775 end_of_range = true;
776 stacklvl--;
777 }
778
779 int match =
780 find_matching_end(sorted_tst_p, i, range_stack[stacklvl].end);
781 if (match != -1) {
782 const uint64_t match_stamp =
783 sorted_tst_p->entries[match].entry_stamp
784 + sorted_tst_p->base_time;
785 stacklvl++;
786 assert(stacklvl < (int)ARRAY_SIZE(range_stack));
787 range_stack[stacklvl].name = get_timestamp_name(tse->entry_id);
788 range_stack[stacklvl].end_name = get_timestamp_name(
789 sorted_tst_p->entries[match].entry_id);
790 range_stack[stacklvl].end = match;
791 print_with_path(range_stack, stacklvl, match_stamp - stamp,
792 NULL);
793 } else if (!end_of_range) {
794 print_with_path(range_stack, stacklvl, stamp - prev_stamp,
795 get_timestamp_name(tse->entry_id));
796 }
797 /* else: No match && end_of_range == true */
798 }
Aaron Durbin31540fb2015-07-11 12:44:10 -0500799 prev_stamp = stamp;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700800 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800801
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +0100802 if (output_type == TIMESTAMPS_PRINT_NORMAL) {
Aaron Durbinfbff3012015-08-30 22:00:12 -0500803 printf("\nTotal Time: ");
804 print_norm(total_time);
805 printf("\n");
806 }
Aaron Durbin799bf782015-08-06 13:52:08 -0500807
Aaron Durbin46300aa2017-09-26 16:22:38 -0600808 unmap_memory(&timestamp_mapping);
Raul E Rangeld4fec682018-05-11 11:08:07 -0600809 free(sorted_tst_p);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700810}
811
Mattias Nisslerecc165b2022-02-08 23:01:50 +0000812/* add a timestamp entry */
813static void timestamp_add_now(uint32_t timestamp_id)
814{
815 struct timestamp_table *tst_p;
816 struct mapping timestamp_mapping;
817
818 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
819 die("No timestamps found in coreboot table.\n");
820 }
821
822 tst_p = map_memory_with_prot(&timestamp_mapping, timestamps.cbmem_addr,
823 timestamps.size, PROT_READ | PROT_WRITE);
824 if (!tst_p)
825 die("Unable to map timestamp table\n");
826
827 /*
828 * Note that coreboot sizes the cbmem entry in the table according to
829 * max_entries, so it's OK to just add more entries if there's room.
830 */
831 if (tst_p->num_entries >= tst_p->max_entries) {
832 die("Not enough space to add timestamp.\n");
833 } else {
834 int64_t time =
835 timestamp_get(tst_p->tick_freq_mhz) - tst_p->base_time;
836 tst_p->entries[tst_p->num_entries].entry_id = timestamp_id;
837 tst_p->entries[tst_p->num_entries].entry_stamp = time;
838 tst_p->num_entries += 1;
839 }
840
841 unmap_memory(&timestamp_mapping);
842}
843
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200844/* dump the TPM CB log table */
845static void dump_tpm_cb_log(void)
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200846{
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200847 const struct tpm_cb_log_table *tclt_p;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200848 size_t size;
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200849 struct mapping log_mapping;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200850
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200851 if (tpm_cb_log.tag != LB_TAG_TPM_CB_LOG) {
852 fprintf(stderr, "No TPM log found in coreboot table.\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200853 return;
854 }
855
856 size = sizeof(*tclt_p);
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200857 tclt_p = map_memory(&log_mapping, tpm_cb_log.cbmem_addr, size);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200858 if (!tclt_p)
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200859 die("Unable to map TPM log header\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200860
861 size += tclt_p->num_entries * sizeof(tclt_p->entries[0]);
862
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200863 unmap_memory(&log_mapping);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200864
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200865 tclt_p = map_memory(&log_mapping, tpm_cb_log.cbmem_addr, size);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200866 if (!tclt_p)
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200867 die("Unable to map full TPM log table\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200868
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200869 printf("coreboot TPM log:\n\n");
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200870
Jacob Garber414d5d82019-06-27 16:02:32 -0600871 for (uint16_t i = 0; i < tclt_p->num_entries; i++) {
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200872 const struct tpm_cb_log_entry *tce = &tclt_p->entries[i];
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200873
Philipp Deppenwiesec9b7d1f2018-11-10 00:35:02 +0100874 printf(" PCR-%u ", tce->pcr);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200875
Jacob Garber414d5d82019-06-27 16:02:32 -0600876 for (uint32_t j = 0; j < tce->digest_length; j++)
Philipp Deppenwiesec9b7d1f2018-11-10 00:35:02 +0100877 printf("%02x", tce->digest[j]);
878
879 printf(" %s [%s]\n", tce->digest_type, tce->name);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200880 }
881
Sergii Dmytruk2710df72022-11-10 00:40:51 +0200882 unmap_memory(&log_mapping);
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +0200883}
884
Julius Wernerd67c6872017-02-02 17:32:00 -0800885struct cbmem_console {
886 u32 size;
887 u32 cursor;
888 u8 body[0];
889} __attribute__ ((__packed__));
890
891#define CBMC_CURSOR_MASK ((1 << 28) - 1)
892#define CBMC_OVERFLOW (1 << 31)
893
Julius Werner8202fc42021-09-08 16:10:15 -0700894enum console_print_type {
895 CONSOLE_PRINT_FULL = 0,
896 CONSOLE_PRINT_LAST,
897 CONSOLE_PRINT_PREVIOUS,
898};
899
Julius Wernerb8258bd2022-02-09 17:26:39 -0800900static int parse_loglevel(char *arg, int *print_unknown_logs)
901{
902 if (arg[0] == '+') {
903 *print_unknown_logs = 1;
904 arg++;
905 } else {
906 *print_unknown_logs = 0;
907 }
908
909 char *endptr;
910 int loglevel = strtol(arg, &endptr, 0);
911 if (*endptr == '\0' && loglevel >= BIOS_EMERG && loglevel <= BIOS_LOG_PREFIX_MAX_LEVEL)
912 return loglevel;
913
914 /* Only match first 3 characters so `NOTE` and `NOTICE` both match. */
915 for (int i = BIOS_EMERG; i <= BIOS_LOG_PREFIX_MAX_LEVEL; i++)
916 if (!strncasecmp(arg, bios_log_prefix[i], 3))
917 return i;
918
919 *print_unknown_logs = 1;
920 return BIOS_NEVER;
921}
922
Stefan Reinauer19f87562013-01-07 13:37:12 -0800923/* dump the cbmem console */
Julius Wernerb8258bd2022-02-09 17:26:39 -0800924static void dump_console(enum console_print_type type, int max_loglevel, int print_unknown_logs)
Stefan Reinauer19f87562013-01-07 13:37:12 -0800925{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600926 const struct cbmem_console *console_p;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800927 char *console_c;
Julius Werner8202fc42021-09-08 16:10:15 -0700928 size_t size, cursor, previous;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600929 struct mapping console_mapping;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800930
931 if (console.tag != LB_TAG_CBMEM_CONSOLE) {
932 fprintf(stderr, "No console found in coreboot table.\n");
933 return;
934 }
935
Julius Wernerd67c6872017-02-02 17:32:00 -0800936 size = sizeof(*console_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600937 console_p = map_memory(&console_mapping, console.cbmem_addr, size);
938 if (!console_p)
939 die("Unable to map console object.\n");
940
Julius Wernerd67c6872017-02-02 17:32:00 -0800941 cursor = console_p->cursor & CBMC_CURSOR_MASK;
942 if (!(console_p->cursor & CBMC_OVERFLOW) && cursor < console_p->size)
Vladimir Serbinenkof4a0d012013-03-30 12:15:12 +0100943 size = cursor;
Julius Wernerd67c6872017-02-02 17:32:00 -0800944 else
945 size = console_p->size;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600946 unmap_memory(&console_mapping);
Julius Wernerd67c6872017-02-02 17:32:00 -0800947
948 console_c = malloc(size + 1);
Stefan Reinauer19f87562013-01-07 13:37:12 -0800949 if (!console_c) {
950 fprintf(stderr, "Not enough memory for console.\n");
951 exit(1);
952 }
Julius Wernerd67c6872017-02-02 17:32:00 -0800953 console_c[size] = '\0';
Stefan Reinauer19f87562013-01-07 13:37:12 -0800954
Aaron Durbin46300aa2017-09-26 16:22:38 -0600955 console_p = map_memory(&console_mapping, console.cbmem_addr,
956 size + sizeof(*console_p));
957
958 if (!console_p)
959 die("Unable to map full console object.\n");
960
Julius Wernerd67c6872017-02-02 17:32:00 -0800961 if (console_p->cursor & CBMC_OVERFLOW) {
962 if (cursor >= size) {
963 printf("cbmem: ERROR: CBMEM console struct is illegal, "
964 "output may be corrupt or out of order!\n\n");
965 cursor = 0;
966 }
967 aligned_memcpy(console_c, console_p->body + cursor,
968 size - cursor);
969 aligned_memcpy(console_c + size - cursor,
970 console_p->body, cursor);
971 } else {
972 aligned_memcpy(console_c, console_p->body, size);
973 }
Stefan Reinauer19f87562013-01-07 13:37:12 -0800974
Julius Wernerd67c6872017-02-02 17:32:00 -0800975 /* Slight memory corruption may occur between reboots and give us a few
976 unprintable characters like '\0'. Replace them with '?' on output. */
977 for (cursor = 0; cursor < size; cursor++)
Julius Werner984d03c2022-01-21 15:33:47 -0800978 if (!isprint(console_c[cursor]) && !isspace(console_c[cursor])
979 && !BIOS_LOG_IS_MARKER(console_c[cursor]))
Julius Wernerd67c6872017-02-02 17:32:00 -0800980 console_c[cursor] = '?';
Stefan Reinauer19f87562013-01-07 13:37:12 -0800981
Julius Werner8202fc42021-09-08 16:10:15 -0700982 /* We detect the reboot cutoff by looking for a bootblock, romstage or
Julius Wernerb7b64a92017-04-28 16:31:46 -0700983 ramstage banner, in that order (to account for platforms without
984 CONFIG_BOOTBLOCK_CONSOLE and/or CONFIG_EARLY_CONSOLE). Once we find
Julius Werner8202fc42021-09-08 16:10:15 -0700985 a banner, store the last two matches for that stage and stop. */
986 cursor = previous = 0;
987 if (type != CONSOLE_PRINT_FULL) {
You-Cheng Syu1430b392019-07-01 16:40:25 +0800988#define BANNER_REGEX(stage) \
Julius Wernerce878322022-03-09 16:51:43 -0800989 "\n\n.?coreboot-[^\n]* " stage " starting.*\\.\\.\\.\n"
990#define OVERFLOW_REGEX(stage) "\n.?\\*\\*\\* Pre-CBMEM " stage " console overflow"
Kangheui Won4b5c8b52020-10-07 14:29:38 +1100991 const char *regex[] = { BANNER_REGEX("verstage-before-bootblock"),
992 BANNER_REGEX("bootblock"),
You-Cheng Syu1430b392019-07-01 16:40:25 +0800993 BANNER_REGEX("verstage"),
Julius Wernerd906bb62017-05-16 13:54:18 -0700994 OVERFLOW_REGEX("romstage"),
Furquan Shaikh35972de2018-02-16 16:12:02 -0800995 BANNER_REGEX("romstage"),
996 OVERFLOW_REGEX("ramstage"),
997 BANNER_REGEX("ramstage") };
Julius Wernerb7b64a92017-04-28 16:31:46 -0700998
Jacob Garber414d5d82019-06-27 16:02:32 -0600999 for (size_t i = 0; !cursor && i < ARRAY_SIZE(regex); i++) {
Julius Wernerb7b64a92017-04-28 16:31:46 -07001000 regex_t re;
1001 regmatch_t match;
Konrad Adamczyk8120cb42023-04-24 10:06:30 +00001002 int res = regcomp(&re, regex[i], REG_EXTENDED | REG_NEWLINE);
Julius Wernerce878322022-03-09 16:51:43 -08001003 assert(res == 0);
Julius Wernerb7b64a92017-04-28 16:31:46 -07001004
1005 /* Keep looking for matches so we find the last one. */
Julius Werner8202fc42021-09-08 16:10:15 -07001006 while (!regexec(&re, console_c + cursor, 1, &match, 0)) {
1007 previous = cursor;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001008 cursor += match.rm_so + 1;
Julius Werner8202fc42021-09-08 16:10:15 -07001009 }
Julius Wernerb7b64a92017-04-28 16:31:46 -07001010 regfree(&re);
1011 }
1012 }
1013
Julius Werner8202fc42021-09-08 16:10:15 -07001014 if (type == CONSOLE_PRINT_PREVIOUS) {
1015 console_c[cursor] = '\0';
1016 cursor = previous;
1017 }
1018
Julius Werner984d03c2022-01-21 15:33:47 -08001019 char c;
Julius Wernerb8258bd2022-02-09 17:26:39 -08001020 int suppressed = 0;
Julius Werner984d03c2022-01-21 15:33:47 -08001021 int tty = isatty(fileno(stdout));
1022 while ((c = console_c[cursor++])) {
1023 if (BIOS_LOG_IS_MARKER(c)) {
1024 int lvl = BIOS_LOG_MARKER_TO_LEVEL(c);
Julius Wernerb8258bd2022-02-09 17:26:39 -08001025 if (lvl > max_loglevel) {
1026 suppressed = 1;
1027 continue;
1028 }
1029 suppressed = 0;
Julius Werner984d03c2022-01-21 15:33:47 -08001030 if (tty)
1031 printf(BIOS_LOG_ESCAPE_PATTERN, bios_log_escape[lvl]);
1032 printf(BIOS_LOG_PREFIX_PATTERN, bios_log_prefix[lvl]);
1033 } else {
Julius Wernerb8258bd2022-02-09 17:26:39 -08001034 if (!suppressed)
1035 putchar(c);
1036 if (c == '\n') {
1037 if (tty && !suppressed)
1038 printf(BIOS_LOG_ESCAPE_RESET);
1039 suppressed = !print_unknown_logs;
1040 }
Julius Werner984d03c2022-01-21 15:33:47 -08001041 }
1042 }
1043 if (tty)
1044 printf(BIOS_LOG_ESCAPE_RESET);
1045
Stefan Reinauer19f87562013-01-07 13:37:12 -08001046 free(console_c);
Aaron Durbin46300aa2017-09-26 16:22:38 -06001047 unmap_memory(&console_mapping);
Stefan Reinauer19f87562013-01-07 13:37:12 -08001048}
1049
Stefan Reinauera9c83612013-07-16 17:47:35 -07001050static void hexdump(unsigned long memory, int length)
1051{
1052 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -06001053 const uint8_t *m;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001054 int all_zero = 0;
Aaron Durbin46300aa2017-09-26 16:22:38 -06001055 struct mapping hexdump_mapping;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001056
Aaron Durbin46300aa2017-09-26 16:22:38 -06001057 m = map_memory(&hexdump_mapping, memory, length);
1058 if (!m)
1059 die("Unable to map hexdump memory.\n");
Stefan Reinauera9c83612013-07-16 17:47:35 -07001060
1061 for (i = 0; i < length; i += 16) {
1062 int j;
1063
1064 all_zero++;
1065 for (j = 0; j < 16; j++) {
1066 if(m[i+j] != 0) {
1067 all_zero = 0;
1068 break;
1069 }
1070 }
1071
1072 if (all_zero < 2) {
1073 printf("%08lx:", memory + i);
1074 for (j = 0; j < 16; j++)
1075 printf(" %02x", m[i+j]);
1076 printf(" ");
1077 for (j = 0; j < 16; j++)
1078 printf("%c", isprint(m[i+j]) ? m[i+j] : '.');
1079 printf("\n");
1080 } else if (all_zero == 2) {
1081 printf("...\n");
1082 }
1083 }
1084
Aaron Durbin46300aa2017-09-26 16:22:38 -06001085 unmap_memory(&hexdump_mapping);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001086}
1087
1088static void dump_cbmem_hex(void)
1089{
1090 if (cbmem.type != LB_MEM_TABLE) {
1091 fprintf(stderr, "No coreboot CBMEM area found!\n");
1092 return;
1093 }
1094
Jianjun Wangb2537bd2022-04-08 16:57:28 +08001095 hexdump(cbmem.start, cbmem.size);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001096}
1097
Jacob Garber79a2f472019-06-27 17:23:25 -06001098static void rawdump(uint64_t base, uint64_t size)
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001099{
Aaron Durbinf2a38222017-09-26 17:44:28 -06001100 const uint8_t *m;
Aaron Durbin46300aa2017-09-26 16:22:38 -06001101 struct mapping dump_mapping;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001102
Aaron Durbin46300aa2017-09-26 16:22:38 -06001103 m = map_memory(&dump_mapping, base, size);
1104 if (!m)
1105 die("Unable to map rawdump memory\n");
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001106
Jacob Garber414d5d82019-06-27 16:02:32 -06001107 for (uint64_t i = 0 ; i < size; i++)
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001108 printf("%c", m[i]);
Aaron Durbin46300aa2017-09-26 16:22:38 -06001109
1110 unmap_memory(&dump_mapping);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001111}
1112
1113static void dump_cbmem_raw(unsigned int id)
1114{
Aaron Durbinf2a38222017-09-26 17:44:28 -06001115 const uint8_t *table;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001116 size_t offset;
1117 uint64_t base = 0;
1118 uint64_t size = 0;
1119
Aaron Durbin46300aa2017-09-26 16:22:38 -06001120 table = mapping_virt(&lbtable_mapping);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001121
1122 if (table == NULL)
1123 return;
1124
1125 offset = 0;
1126
Aaron Durbin46300aa2017-09-26 16:22:38 -06001127 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -06001128 const struct lb_record *lbr;
Yidi Lin0811a642022-09-15 15:47:59 +08001129 struct lb_cbmem_entry lbe;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001130
Aaron Durbinf2a38222017-09-26 17:44:28 -06001131 lbr = (const void *)(table + offset);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001132 offset += lbr->size;
1133
1134 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
1135 continue;
1136
Yidi Lin0811a642022-09-15 15:47:59 +08001137 aligned_memcpy(&lbe, lbr, sizeof(lbe));
1138 if (lbe.id == id) {
1139 debug("found id for raw dump %0x", lbe.id);
1140 base = lbe.address;
1141 size = lbe.entry_size;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001142 break;
1143 }
1144 }
1145
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001146 if (!base)
1147 fprintf(stderr, "id %0x not found in cbtable\n", id);
1148 else
1149 rawdump(base, size);
1150}
1151
Aaron Durbin0dff57d2015-03-05 21:18:33 -06001152struct cbmem_id_to_name {
1153 uint32_t id;
1154 const char *name;
1155};
Vadim Bendebury8b143c52014-05-14 10:12:55 -07001156static const struct cbmem_id_to_name cbmem_ids[] = { CBMEM_ID_TO_NAME_TABLE };
Stefan Reinauerc0199072013-01-07 16:26:10 -08001157
Kyösti Mälkkieab5c122017-09-04 11:10:17 +03001158#define MAX_STAGEx 10
Jacob Garber79a2f472019-06-27 17:23:25 -06001159static void cbmem_print_entry(int n, uint32_t id, uint64_t base, uint64_t size)
Stefan Reinauera9c83612013-07-16 17:47:35 -07001160{
Stefan Reinauera9c83612013-07-16 17:47:35 -07001161 const char *name;
Kyösti Mälkkieab5c122017-09-04 11:10:17 +03001162 char stage_x[20];
Stefan Reinauera9c83612013-07-16 17:47:35 -07001163
1164 name = NULL;
Jacob Garber414d5d82019-06-27 16:02:32 -06001165 for (size_t i = 0; i < ARRAY_SIZE(cbmem_ids); i++) {
Stefan Reinauera9c83612013-07-16 17:47:35 -07001166 if (cbmem_ids[i].id == id) {
1167 name = cbmem_ids[i].name;
1168 break;
1169 }
Kyösti Mälkkieab5c122017-09-04 11:10:17 +03001170 if (id >= CBMEM_ID_STAGEx_META &&
1171 id < CBMEM_ID_STAGEx_META + MAX_STAGEx) {
1172 snprintf(stage_x, sizeof(stage_x), "STAGE%d META",
1173 (id - CBMEM_ID_STAGEx_META));
1174 name = stage_x;
1175 }
1176 if (id >= CBMEM_ID_STAGEx_CACHE &&
1177 id < CBMEM_ID_STAGEx_CACHE + MAX_STAGEx) {
1178 snprintf(stage_x, sizeof(stage_x), "STAGE%d $ ",
1179 (id - CBMEM_ID_STAGEx_CACHE));
1180 name = stage_x;
1181 }
Stefan Reinauera9c83612013-07-16 17:47:35 -07001182 }
1183
1184 printf("%2d. ", n);
1185 if (name == NULL)
Martin Roth90e4f3d2022-10-20 21:11:30 -06001186 name = "(unknown)";
1187 printf("%-20s %08x", name, id);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001188 printf(" %08" PRIx64 " ", base);
Martin Roth90e4f3d2022-10-20 21:11:30 -06001189 printf(" %08" PRIx64 "\n", size);
Stefan Reinauera9c83612013-07-16 17:47:35 -07001190}
1191
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001192static void dump_cbmem_toc(void)
Stefan Reinauerc0199072013-01-07 16:26:10 -08001193{
Aaron Durbin09c0c112015-09-30 12:33:01 -05001194 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -06001195 const uint8_t *table;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001196 size_t offset;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001197
Aaron Durbin46300aa2017-09-26 16:22:38 -06001198 table = mapping_virt(&lbtable_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -05001199
1200 if (table == NULL)
Stefan Reinauerc0199072013-01-07 16:26:10 -08001201 return;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001202
1203 printf("CBMEM table of contents:\n");
Martin Roth90e4f3d2022-10-20 21:11:30 -06001204 printf(" %-20s %-8s %-8s %-8s\n", "NAME", "ID", "START",
1205 "LENGTH");
Aaron Durbin09c0c112015-09-30 12:33:01 -05001206
1207 i = 0;
1208 offset = 0;
1209
Aaron Durbin46300aa2017-09-26 16:22:38 -06001210 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -06001211 const struct lb_record *lbr;
Yidi Lin0811a642022-09-15 15:47:59 +08001212 struct lb_cbmem_entry lbe;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001213
Aaron Durbinf2a38222017-09-26 17:44:28 -06001214 lbr = (const void *)(table + offset);
Aaron Durbin09c0c112015-09-30 12:33:01 -05001215 offset += lbr->size;
1216
1217 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
1218 continue;
1219
Yidi Lin0811a642022-09-15 15:47:59 +08001220 aligned_memcpy(&lbe, lbr, sizeof(lbe));
1221 cbmem_print_entry(i, lbe.id, lbe.address, lbe.entry_size);
Aaron Durbin09c0c112015-09-30 12:33:01 -05001222 i++;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001223 }
Stefan Reinauerc0199072013-01-07 16:26:10 -08001224}
Stefan Reinauer19f87562013-01-07 13:37:12 -08001225
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001226#define COVERAGE_MAGIC 0x584d4153
1227struct file {
1228 uint32_t magic;
1229 uint32_t next;
1230 uint32_t filename;
1231 uint32_t data;
1232 int offset;
1233 int len;
1234};
1235
1236static int mkpath(char *path, mode_t mode)
1237{
1238 assert (path && *path);
1239 char *p;
1240 for (p = strchr(path+1, '/'); p; p = strchr(p + 1, '/')) {
1241 *p = '\0';
1242 if (mkdir(path, mode) == -1) {
1243 if (errno != EEXIST) {
1244 *p = '/';
1245 return -1;
1246 }
1247 }
1248 *p = '/';
1249 }
1250 return 0;
1251}
1252
1253static void dump_coverage(void)
1254{
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001255 uint64_t start;
Aaron Durbin09c0c112015-09-30 12:33:01 -05001256 size_t size;
Aaron Durbinf2a38222017-09-26 17:44:28 -06001257 const void *coverage;
Aaron Durbin46300aa2017-09-26 16:22:38 -06001258 struct mapping coverage_mapping;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001259 unsigned long phys_offset;
1260#define phys_to_virt(x) ((void *)(unsigned long)(x) + phys_offset)
1261
Aaron Durbin09c0c112015-09-30 12:33:01 -05001262 if (find_cbmem_entry(CBMEM_ID_COVERAGE, &start, &size)) {
1263 fprintf(stderr, "No coverage information found\n");
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001264 return;
1265 }
1266
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001267 /* Map coverage area */
Aaron Durbin46300aa2017-09-26 16:22:38 -06001268 coverage = map_memory(&coverage_mapping, start, size);
1269 if (!coverage)
1270 die("Unable to map coverage area.\n");
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001271 phys_offset = (unsigned long)coverage - (unsigned long)start;
1272
1273 printf("Dumping coverage data...\n");
1274
1275 struct file *file = (struct file *)coverage;
1276 while (file && file->magic == COVERAGE_MAGIC) {
1277 FILE *f;
1278 char *filename;
1279
1280 debug(" -> %s\n", (char *)phys_to_virt(file->filename));
1281 filename = strdup((char *)phys_to_virt(file->filename));
1282 if (mkpath(filename, 0755) == -1) {
1283 perror("Directory for coverage data could "
1284 "not be created");
1285 exit(1);
1286 }
1287 f = fopen(filename, "wb");
1288 if (!f) {
1289 printf("Could not open %s: %s\n",
1290 filename, strerror(errno));
1291 exit(1);
1292 }
1293 if (fwrite((void *)phys_to_virt(file->data),
1294 file->len, 1, f) != 1) {
1295 printf("Could not write to %s: %s\n",
1296 filename, strerror(errno));
1297 exit(1);
1298 }
1299 fclose(f);
1300 free(filename);
1301
1302 if (file->next)
1303 file = (struct file *)phys_to_virt(file->next);
1304 else
1305 file = NULL;
1306 }
Aaron Durbin46300aa2017-09-26 16:22:38 -06001307 unmap_memory(&coverage_mapping);
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001308}
1309
1310static void print_version(void)
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001311{
1312 printf("cbmem v%s -- ", CBMEM_VERSION);
1313 printf("Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.\n\n");
1314 printf(
1315 "This program is free software: you can redistribute it and/or modify\n"
1316 "it under the terms of the GNU General Public License as published by\n"
1317 "the Free Software Foundation, version 2 of the License.\n\n"
1318 "This program is distributed in the hope that it will be useful,\n"
1319 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1320 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
Martin Rotheb20e602016-01-12 13:30:50 -07001321 "GNU General Public License for more details.\n\n");
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001322}
1323
Martin Roth8448ac42016-09-11 15:43:22 -06001324static void print_usage(const char *name, int exit_code)
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001325{
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001326 printf("usage: %s [-cCltTLxVvh?]\n", name);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001327 printf("\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001328 " -c | --console: print cbmem console\n"
Julius Wernerb7b64a92017-04-28 16:31:46 -07001329 " -1 | --oneboot: print cbmem console for last boot only\n"
Julius Werner8202fc42021-09-08 16:10:15 -07001330 " -2 | --2ndtolast: print cbmem console for the boot that came before the last one only\n"
Julius Wernerb8258bd2022-02-09 17:26:39 -08001331 " -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 -08001332 " -C | --coverage: dump coverage information\n"
Stefan Reinauerc0199072013-01-07 16:26:10 -08001333 " -l | --list: print cbmem table of contents\n"
Stefan Reinauera9c83612013-07-16 17:47:35 -07001334 " -x | --hexdump: print hexdump of cbmem area\n"
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001335 " -r | --rawdump ID: print rawdump of specific ID (in hex) of cbtable\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001336 " -t | --timestamps: print timestamp information\n"
Aaron Durbinfbff3012015-08-30 22:00:12 -05001337 " -T | --parseable-timestamps: print parseable timestamps\n"
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001338 " -S | --stacked-timestamps: print stacked timestamps (e.g. for flame graph tools)\n"
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001339 " -a | --add-timestamp ID: append timestamp with ID\n"
Sergii Dmytruk2710df72022-11-10 00:40:51 +02001340 " -L | --tcpa-log print TPM log\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001341 " -V | --verbose: verbose (debugging) output\n"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001342 " -v | --version: print the version\n"
1343 " -h | --help: print this help\n"
1344 "\n");
Martin Roth8448ac42016-09-11 15:43:22 -06001345 exit(exit_code);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001346}
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001347
Adam Kallai66c22502018-11-30 11:31:50 +01001348#if defined(__arm__) || defined(__aarch64__)
Julius Werner337de4c2014-06-16 23:02:03 -07001349static void dt_update_cells(const char *name, int *addr_cells_ptr,
1350 int *size_cells_ptr)
1351{
1352 if (*addr_cells_ptr >= 0 && *size_cells_ptr >= 0)
1353 return;
1354
1355 int buffer;
1356 size_t nlen = strlen(name);
1357 char *prop = alloca(nlen + sizeof("/#address-cells"));
1358 strcpy(prop, name);
1359
1360 if (*addr_cells_ptr < 0) {
1361 strcpy(prop + nlen, "/#address-cells");
1362 int fd = open(prop, O_RDONLY);
1363 if (fd < 0 && errno != ENOENT) {
1364 perror(prop);
1365 } else if (fd >= 0) {
1366 if (read(fd, &buffer, sizeof(int)) < 0)
1367 perror(prop);
1368 else
1369 *addr_cells_ptr = ntohl(buffer);
1370 close(fd);
1371 }
1372 }
1373
1374 if (*size_cells_ptr < 0) {
1375 strcpy(prop + nlen, "/#size-cells");
1376 int fd = open(prop, O_RDONLY);
1377 if (fd < 0 && errno != ENOENT) {
1378 perror(prop);
1379 } else if (fd >= 0) {
1380 if (read(fd, &buffer, sizeof(int)) < 0)
1381 perror(prop);
1382 else
1383 *size_cells_ptr = ntohl(buffer);
1384 close(fd);
1385 }
1386 }
1387}
1388
1389static char *dt_find_compat(const char *parent, const char *compat,
1390 int *addr_cells_ptr, int *size_cells_ptr)
1391{
1392 char *ret = NULL;
1393 struct dirent *entry;
1394 DIR *dir;
1395
1396 if (!(dir = opendir(parent))) {
1397 perror(parent);
1398 return NULL;
1399 }
1400
1401 /* Loop through all files in the directory (DT node). */
1402 while ((entry = readdir(dir))) {
1403 /* We only care about compatible props or subnodes. */
1404 if (entry->d_name[0] == '.' || !((entry->d_type & DT_DIR) ||
1405 !strcmp(entry->d_name, "compatible")))
1406 continue;
1407
1408 /* Assemble the file name (on the stack, for speed). */
1409 size_t plen = strlen(parent);
1410 char *name = alloca(plen + strlen(entry->d_name) + 2);
1411
1412 strcpy(name, parent);
1413 name[plen] = '/';
1414 strcpy(name + plen + 1, entry->d_name);
1415
1416 /* If it's a subnode, recurse. */
1417 if (entry->d_type & DT_DIR) {
1418 ret = dt_find_compat(name, compat, addr_cells_ptr,
1419 size_cells_ptr);
1420
1421 /* There is only one matching node to find, abort. */
1422 if (ret) {
1423 /* Gather cells values on the way up. */
1424 dt_update_cells(parent, addr_cells_ptr,
1425 size_cells_ptr);
1426 break;
1427 }
1428 continue;
1429 }
1430
1431 /* If it's a compatible string, see if it's the right one. */
1432 int fd = open(name, O_RDONLY);
1433 int clen = strlen(compat);
1434 char *buffer = alloca(clen + 1);
1435
1436 if (fd < 0) {
1437 perror(name);
1438 continue;
1439 }
1440
1441 if (read(fd, buffer, clen + 1) < 0) {
1442 perror(name);
1443 close(fd);
1444 continue;
1445 }
1446 close(fd);
1447
1448 if (!strcmp(compat, buffer)) {
1449 /* Initialize these to "unset" for the way up. */
1450 *addr_cells_ptr = *size_cells_ptr = -1;
1451
1452 /* Can't leave string on the stack or we'll lose it! */
1453 ret = strdup(parent);
1454 break;
1455 }
1456 }
1457
1458 closedir(dir);
1459 return ret;
1460}
Adam Kallai66c22502018-11-30 11:31:50 +01001461#endif /* defined(__arm__) || defined(__aarch64__) */
Julius Werner337de4c2014-06-16 23:02:03 -07001462
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001463int main(int argc, char** argv)
1464{
Stefan Reinauer19f87562013-01-07 13:37:12 -08001465 int print_defaults = 1;
1466 int print_console = 0;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001467 int print_coverage = 0;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001468 int print_list = 0;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001469 int print_hexdump = 0;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001470 int print_rawdump = 0;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001471 int print_tcpa_log = 0;
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001472 enum timestamps_print_type timestamp_type = TIMESTAMPS_PRINT_NONE;
Julius Werner8202fc42021-09-08 16:10:15 -07001473 enum console_print_type console_type = CONSOLE_PRINT_FULL;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001474 unsigned int rawdump_id = 0;
Julius Wernerb8258bd2022-02-09 17:26:39 -08001475 int max_loglevel = BIOS_NEVER;
1476 int print_unknown_logs = 1;
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001477 uint32_t timestamp_id = 0;
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001478
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001479 int opt, option_index = 0;
1480 static struct option long_options[] = {
Stefan Reinauer19f87562013-01-07 13:37:12 -08001481 {"console", 0, 0, 'c'},
Julius Wernerb7b64a92017-04-28 16:31:46 -07001482 {"oneboot", 0, 0, '1'},
Julius Werner8202fc42021-09-08 16:10:15 -07001483 {"2ndtolast", 0, 0, '2'},
Julius Wernerb8258bd2022-02-09 17:26:39 -08001484 {"loglevel", required_argument, 0, 'B'},
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001485 {"coverage", 0, 0, 'C'},
Stefan Reinauerc0199072013-01-07 16:26:10 -08001486 {"list", 0, 0, 'l'},
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001487 {"tcpa-log", 0, 0, 'L'},
Stefan Reinauer19f87562013-01-07 13:37:12 -08001488 {"timestamps", 0, 0, 't'},
Aaron Durbinfbff3012015-08-30 22:00:12 -05001489 {"parseable-timestamps", 0, 0, 'T'},
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001490 {"stacked-timestamps", 0, 0, 'S'},
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001491 {"add-timestamp", required_argument, 0, 'a'},
Stefan Reinauera9c83612013-07-16 17:47:35 -07001492 {"hexdump", 0, 0, 'x'},
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001493 {"rawdump", required_argument, 0, 'r'},
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001494 {"verbose", 0, 0, 'V'},
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001495 {"version", 0, 0, 'v'},
1496 {"help", 0, 0, 'h'},
1497 {0, 0, 0, 0}
1498 };
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001499 while ((opt = getopt_long(argc, argv, "c12B:CltTSa:LxVvh?r:",
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001500 long_options, &option_index)) != EOF) {
1501 switch (opt) {
Stefan Reinauer19f87562013-01-07 13:37:12 -08001502 case 'c':
1503 print_console = 1;
1504 print_defaults = 0;
1505 break;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001506 case '1':
1507 print_console = 1;
Julius Werner8202fc42021-09-08 16:10:15 -07001508 console_type = CONSOLE_PRINT_LAST;
1509 print_defaults = 0;
1510 break;
1511 case '2':
1512 print_console = 1;
1513 console_type = CONSOLE_PRINT_PREVIOUS;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001514 print_defaults = 0;
1515 break;
Julius Wernerb8258bd2022-02-09 17:26:39 -08001516 case 'B':
1517 max_loglevel = parse_loglevel(optarg, &print_unknown_logs);
1518 break;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001519 case 'C':
1520 print_coverage = 1;
1521 print_defaults = 0;
1522 break;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001523 case 'l':
1524 print_list = 1;
1525 print_defaults = 0;
1526 break;
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001527 case 'L':
1528 print_tcpa_log = 1;
1529 print_defaults = 0;
1530 break;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001531 case 'x':
1532 print_hexdump = 1;
1533 print_defaults = 0;
1534 break;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001535 case 'r':
1536 print_rawdump = 1;
1537 print_defaults = 0;
1538 rawdump_id = strtoul(optarg, NULL, 16);
1539 break;
Stefan Reinauer19f87562013-01-07 13:37:12 -08001540 case 't':
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001541 timestamp_type = TIMESTAMPS_PRINT_NORMAL;
Stefan Reinauer19f87562013-01-07 13:37:12 -08001542 print_defaults = 0;
1543 break;
Aaron Durbinfbff3012015-08-30 22:00:12 -05001544 case 'T':
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001545 timestamp_type = TIMESTAMPS_PRINT_MACHINE_READABLE;
1546 print_defaults = 0;
1547 break;
1548 case 'S':
1549 timestamp_type = TIMESTAMPS_PRINT_STACKED;
Aaron Durbinfbff3012015-08-30 22:00:12 -05001550 print_defaults = 0;
1551 break;
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001552 case 'a':
1553 print_defaults = 0;
1554 timestamp_id = timestamp_enum_name_to_id(optarg);
1555 /* Parse numeric value if name is unknown */
1556 if (timestamp_id == 0)
1557 timestamp_id = strtoul(optarg, NULL, 0);
1558 break;
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001559 case 'V':
1560 verbose = 1;
1561 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001562 case 'v':
1563 print_version();
1564 exit(0);
1565 break;
1566 case 'h':
Martin Roth8448ac42016-09-11 15:43:22 -06001567 print_usage(argv[0], 0);
1568 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001569 case '?':
1570 default:
Martin Roth8448ac42016-09-11 15:43:22 -06001571 print_usage(argv[0], 1);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001572 break;
1573 }
1574 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001575
Arthur Heymans8cd17ea2018-08-01 17:21:32 +02001576 if (optind < argc) {
1577 fprintf(stderr, "Error: Extra parameter found.\n");
1578 print_usage(argv[0], 1);
1579 }
1580
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001581 mem_fd = open("/dev/mem", timestamp_id ? O_RDWR : O_RDONLY, 0);
Julius Werner337de4c2014-06-16 23:02:03 -07001582 if (mem_fd < 0) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001583 fprintf(stderr, "Failed to gain memory access: %s\n",
1584 strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001585 return 1;
1586 }
1587
Adam Kallai66c22502018-11-30 11:31:50 +01001588#if defined(__arm__) || defined(__aarch64__)
Julius Werner337de4c2014-06-16 23:02:03 -07001589 int addr_cells, size_cells;
1590 char *coreboot_node = dt_find_compat("/proc/device-tree", "coreboot",
1591 &addr_cells, &size_cells);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001592
Julius Werner337de4c2014-06-16 23:02:03 -07001593 if (!coreboot_node) {
1594 fprintf(stderr, "Could not find 'coreboot' compatible node!\n");
Stefan Reinauer7f681502013-06-19 15:39:09 -07001595 return 1;
1596 }
1597
Julius Werner337de4c2014-06-16 23:02:03 -07001598 if (addr_cells < 0) {
1599 fprintf(stderr, "Warning: no #address-cells node in tree!\n");
1600 addr_cells = 1;
1601 }
1602
1603 int nlen = strlen(coreboot_node);
1604 char *reg = alloca(nlen + sizeof("/reg"));
1605
1606 strcpy(reg, coreboot_node);
1607 strcpy(reg + nlen, "/reg");
1608 free(coreboot_node);
1609
1610 int fd = open(reg, O_RDONLY);
1611 if (fd < 0) {
1612 perror(reg);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001613 return 1;
1614 }
Stefan Reinauer7f681502013-06-19 15:39:09 -07001615
Julius Werner337de4c2014-06-16 23:02:03 -07001616 int i;
Aaron Durbinb58f9e32014-10-07 14:56:35 -05001617 size_t size_to_read = addr_cells * 4 + size_cells * 4;
1618 u8 *dtbuffer = alloca(size_to_read);
1619 if (read(fd, dtbuffer, size_to_read) < 0) {
Julius Werner337de4c2014-06-16 23:02:03 -07001620 perror(reg);
1621 return 1;
1622 }
1623 close(fd);
1624
1625 /* No variable-length byte swap function anywhere in C... how sad. */
1626 u64 baseaddr = 0;
1627 for (i = 0; i < addr_cells * 4; i++) {
1628 baseaddr <<= 8;
Aaron Durbinb58f9e32014-10-07 14:56:35 -05001629 baseaddr |= *dtbuffer;
1630 dtbuffer++;
1631 }
1632 u64 cb_table_size = 0;
1633 for (i = 0; i < size_cells * 4; i++) {
1634 cb_table_size <<= 8;
1635 cb_table_size |= *dtbuffer;
1636 dtbuffer++;
Julius Werner337de4c2014-06-16 23:02:03 -07001637 }
1638
Aaron Durbin46300aa2017-09-26 16:22:38 -06001639 parse_cbtable(baseaddr, cb_table_size);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001640#else
Aaron Durbin46300aa2017-09-26 16:22:38 -06001641 unsigned long long possible_base_addresses[] = { 0, 0xf0000 };
Stefan Reinauer7f681502013-06-19 15:39:09 -07001642
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001643 /* Find and parse coreboot table */
Jacob Garber414d5d82019-06-27 16:02:32 -06001644 for (size_t j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
Aaron Durbin46300aa2017-09-26 16:22:38 -06001645 if (!parse_cbtable(possible_base_addresses[j], 0))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001646 break;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001647 }
Stefan Reinauer7f681502013-06-19 15:39:09 -07001648#endif
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001649
Aaron Durbin46300aa2017-09-26 16:22:38 -06001650 if (mapping_virt(&lbtable_mapping) == NULL)
1651 die("Table not found.\n");
1652
Stefan Reinauer19f87562013-01-07 13:37:12 -08001653 if (print_console)
Julius Wernerb8258bd2022-02-09 17:26:39 -08001654 dump_console(console_type, max_loglevel, print_unknown_logs);
Stefan Reinauer19f87562013-01-07 13:37:12 -08001655
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001656 if (print_coverage)
1657 dump_coverage();
1658
Stefan Reinauerc0199072013-01-07 16:26:10 -08001659 if (print_list)
1660 dump_cbmem_toc();
1661
Stefan Reinauera9c83612013-07-16 17:47:35 -07001662 if (print_hexdump)
1663 dump_cbmem_hex();
1664
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001665 if (print_rawdump)
1666 dump_cbmem_raw(rawdump_id);
1667
Mattias Nisslerecc165b2022-02-08 23:01:50 +00001668 if (timestamp_id)
1669 timestamp_add_now(timestamp_id);
1670
Jakub Czapiga48f6c2b2022-02-28 16:23:02 +01001671 if (print_defaults)
1672 timestamp_type = TIMESTAMPS_PRINT_NORMAL;
1673
1674 if (timestamp_type != TIMESTAMPS_PRINT_NONE)
1675 dump_timestamps(timestamp_type);
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001676
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001677 if (print_tcpa_log)
Sergii Dmytruk2710df72022-11-10 00:40:51 +02001678 dump_tpm_cb_log();
Philipp Deppenwiesefa1f6ff2018-05-13 12:42:42 +02001679
Aaron Durbin46300aa2017-09-26 16:22:38 -06001680 unmap_memory(&lbtable_mapping);
1681
Julius Werner337de4c2014-06-16 23:02:03 -07001682 close(mem_fd);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001683 return 0;
1684}