blob: eae5da3390794b3bd0f1d322568d4c6be922d119 [file] [log] [blame]
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001/*
2 * This file is part of the coreboot project.
3 *
Stefan Reinauera9c83612013-07-16 17:47:35 -07004 * Copyright 2012 Google Inc.
Timothy Pearsonbea71402015-09-05 18:07:17 -05005 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070015 */
16
Nico Huber8e4bb922013-05-26 18:17:54 +020017#include <inttypes.h>
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070018#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
Stefan Reinauer1e0e5562013-01-02 15:43:56 -080021#include <unistd.h>
Stefan Reinauer8c594772013-04-19 14:22:29 -070022#include <inttypes.h>
Stefan Reinauer1e0e5562013-01-02 15:43:56 -080023#include <getopt.h>
Julius Werner337de4c2014-06-16 23:02:03 -070024#include <dirent.h>
Stefan Reinauer05cbce62013-01-03 14:30:33 -080025#include <errno.h>
26#include <fcntl.h>
Stefan Reinauera9c83612013-07-16 17:47:35 -070027#include <ctype.h>
Stefan Reinauer7f681502013-06-19 15:39:09 -070028#include <arpa/inet.h>
Stefan Reinauer05cbce62013-01-03 14:30:33 -080029#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/mman.h>
Stefan Reinauerd37ab452012-12-18 16:23:28 -080032#include <libgen.h>
33#include <assert.h>
Julius Wernerb7b64a92017-04-28 16:31:46 -070034#include <regex.h>
Aaron Durbindc9f5cd2015-09-08 13:34:43 -050035#include <commonlib/cbmem_id.h>
36#include <commonlib/timestamp_serialized.h>
37#include <commonlib/coreboot_tables.h>
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070038
Patrick Georgid00f1802015-07-13 16:53:50 +020039#ifdef __OpenBSD__
40#include <sys/param.h>
41#include <sys/sysctl.h>
42#endif
43
Stefan Reinauer05cbce62013-01-03 14:30:33 -080044#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070045
Julius Werner337de4c2014-06-16 23:02:03 -070046typedef uint8_t u8;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070047typedef uint16_t u16;
48typedef uint32_t u32;
49typedef uint64_t u64;
50
Aaron Durbin46300aa2017-09-26 16:22:38 -060051/* Return < 0 on error, 0 on success. */
52static int parse_cbtable(u64 address, size_t table_size);
53
54struct mapping {
55 void *virt;
56 size_t offset;
57 size_t virt_size;
58 unsigned long long phys;
59 size_t size;
60};
61
Stefan Reinauera9c83612013-07-16 17:47:35 -070062#define CBMEM_VERSION "1.1"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -080063
Stefan Reinauer05cbce62013-01-03 14:30:33 -080064/* verbose output? */
65static int verbose = 0;
66#define debug(x...) if(verbose) printf(x)
67
68/* File handle used to access /dev/mem */
Julius Werner337de4c2014-06-16 23:02:03 -070069static int mem_fd;
Aaron Durbin46300aa2017-09-26 16:22:38 -060070static struct mapping lbtable_mapping;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070071
Aaron Durbin46300aa2017-09-26 16:22:38 -060072static void die(const char *msg)
73{
74 if (msg)
75 fputs(msg, stderr);
76 exit(1);
77}
78
79static unsigned long long system_page_size(void)
80{
81 static unsigned long long page_size;
82
83 if (!page_size)
84 page_size = getpagesize();
85
86 return page_size;
87}
88
89static inline size_t size_to_mib(size_t sz)
90{
91 return sz >> 20;
92}
93
94/* Return mapping of physical address requested. */
95static const void *mapping_virt(const struct mapping *mapping)
96{
97 const char *v = mapping->virt;
98
99 if (v == NULL)
100 return NULL;
101
102 return v + mapping->offset;
103}
104
Aaron Durbincf20c902017-09-28 17:52:59 -0600105/* Returns virtual address on success, NULL on error. mapping is filled in. */
Aaron Durbin46300aa2017-09-26 16:22:38 -0600106static const void *map_memory(struct mapping *mapping, unsigned long long phys,
107 size_t sz)
108{
109 void *v;
110 unsigned long long page_size;
111
112 page_size = system_page_size();
113
114 mapping->virt = NULL;
115 mapping->offset = phys % page_size;
116 mapping->virt_size = sz + mapping->offset;
117 mapping->size = sz;
118 mapping->phys = phys;
119
120 if (size_to_mib(mapping->virt_size) == 0) {
121 debug("Mapping %zuB of physical memory at 0x%llx (requested 0x%llx).\n",
122 mapping->virt_size, phys - mapping->offset, phys);
123 } else {
124 debug("Mapping %zuMB of physical memory at 0x%llx (requested 0x%llx).\n",
125 size_to_mib(mapping->virt_size), phys - mapping->offset,
126 phys);
127 }
128
129 v = mmap(NULL, mapping->virt_size, PROT_READ, MAP_SHARED, mem_fd,
130 phys - mapping->offset);
131
132 if (v == MAP_FAILED) {
133 debug("Mapping failed %zuB of physical memory at 0x%llx.\n",
134 mapping->virt_size, phys - mapping->offset);
135 return NULL;
136 }
137
138 mapping->virt = v;
139
140 if (mapping->offset != 0)
141 debug(" ... padding virtual address with 0x%zx bytes.\n",
142 mapping->offset);
143
144 return mapping_virt(mapping);
145}
146
147/* Returns 0 on success, < 0 on error. mapping is cleared if successful. */
148static int unmap_memory(struct mapping *mapping)
149{
150 if (mapping->virt == NULL)
151 return -1;
152
153 munmap(mapping->virt, mapping->virt_size);
154 mapping->virt = NULL;
155 mapping->offset = 0;
156 mapping->virt_size = 0;
157
158 return 0;
159}
160
161/* Return size of physical address mapping requested. */
162static size_t mapping_size(const struct mapping *mapping)
163{
164 if (mapping->virt == NULL)
165 return 0;
166
167 return mapping->size;
168}
Timothy Pearsondf699d52015-05-16 14:55:54 -0500169
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700170/*
Julius Werner127a79e2017-02-03 12:50:03 -0800171 * Some architectures map /dev/mem memory in a way that doesn't support
172 * unaligned accesses. Most normal libc memcpy()s aren't safe to use in this
173 * case, so build our own which makes sure to never do unaligned accesses on
174 * *src (*dest is fine since we never map /dev/mem for writing).
175 */
176static void *aligned_memcpy(void *dest, const void *src, size_t n)
177{
178 u8 *d = dest;
179 const volatile u8 *s = src; /* volatile to prevent optimization */
180
181 while ((uintptr_t)s & (sizeof(size_t) - 1)) {
182 if (n-- == 0)
183 return dest;
184 *d++ = *s++;
185 }
186
187 while (n >= sizeof(size_t)) {
188 *(size_t *)d = *(const volatile size_t *)s;
189 d += sizeof(size_t);
190 s += sizeof(size_t);
191 n -= sizeof(size_t);
192 }
193
194 while (n-- > 0)
195 *d++ = *s++;
196
197 return dest;
198}
199
200/*
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700201 * calculate ip checksum (16 bit quantities) on a passed in buffer. In case
202 * the buffer length is odd last byte is excluded from the calculation
203 */
204static u16 ipchcksum(const void *addr, unsigned size)
205{
206 const u16 *p = addr;
207 unsigned i, n = size / 2; /* don't expect odd sized blocks */
208 u32 sum = 0;
209
210 for (i = 0; i < n; i++)
211 sum += p[i];
212
213 sum = (sum >> 16) + (sum & 0xffff);
214 sum += (sum >> 16);
215 sum = ~sum & 0xffff;
216 return (u16) sum;
217}
218
Aaron Durbin09c0c112015-09-30 12:33:01 -0500219/* Find the first cbmem entry filling in the details. */
220static int find_cbmem_entry(uint32_t id, uint64_t *addr, size_t *size)
221{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600222 const uint8_t *table;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500223 size_t offset;
224 int ret = -1;
225
Aaron Durbin46300aa2017-09-26 16:22:38 -0600226 table = mapping_virt(&lbtable_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500227
228 if (table == NULL)
229 return -1;
230
231 offset = 0;
232
Aaron Durbin46300aa2017-09-26 16:22:38 -0600233 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -0600234 const struct lb_record *lbr;
235 const struct lb_cbmem_entry *lbe;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500236
Aaron Durbinf2a38222017-09-26 17:44:28 -0600237 lbr = (const void *)(table + offset);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500238 offset += lbr->size;
239
240 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
241 continue;
242
Aaron Durbinf2a38222017-09-26 17:44:28 -0600243 lbe = (const void *)lbr;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500244 if (lbe->id != id)
245 continue;
246
247 *addr = lbe->address;
248 *size = lbe->entry_size;
249 ret = 0;
250 break;
251 }
252
Aaron Durbin09c0c112015-09-30 12:33:01 -0500253 return ret;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700254}
255
256/*
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800257 * Try finding the timestamp table and coreboot cbmem console starting from the
258 * passed in memory offset. Could be called recursively in case a forwarding
259 * entry is found.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700260 *
261 * Returns pointer to a memory buffer containg the timestamp table or zero if
262 * none found.
263 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800264
265static struct lb_cbmem_ref timestamps;
266static struct lb_cbmem_ref console;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800267static struct lb_memory_range cbmem;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800268
Stefan Reinauer8c594772013-04-19 14:22:29 -0700269/* This is a work-around for a nasty problem introduced by initially having
270 * pointer sized entries in the lb_cbmem_ref structures. This caused problems
271 * on 64bit x86 systems because coreboot is 32bit on those systems.
272 * When the problem was found, it was corrected, but there are a lot of
273 * systems out there with a firmware that does not produce the right
274 * lb_cbmem_ref structure. Hence we try to autocorrect this issue here.
275 */
Aaron Durbinf2a38222017-09-26 17:44:28 -0600276static struct lb_cbmem_ref parse_cbmem_ref(const struct lb_cbmem_ref *cbmem_ref)
Stefan Reinauer8c594772013-04-19 14:22:29 -0700277{
278 struct lb_cbmem_ref ret;
279
280 ret = *cbmem_ref;
281
282 if (cbmem_ref->size < sizeof(*cbmem_ref))
283 ret.cbmem_addr = (uint32_t)ret.cbmem_addr;
284
Stefan Reinauera9c83612013-07-16 17:47:35 -0700285 debug(" cbmem_addr = %" PRIx64 "\n", ret.cbmem_addr);
286
Stefan Reinauer8c594772013-04-19 14:22:29 -0700287 return ret;
288}
289
Aaron Durbin46300aa2017-09-26 16:22:38 -0600290static void parse_memory_tags(const struct lb_memory *mem)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700291{
Aaron Durbin46300aa2017-09-26 16:22:38 -0600292 int num_entries;
293 int i;
294
295 /* Peel off the header size and calculate the number of entries. */
296 num_entries = (mem->size - sizeof(*mem)) / sizeof(mem->map[0]);
297
298 for (i = 0; i < num_entries; i++) {
299 if (mem->map[i].type != LB_MEM_TABLE)
300 continue;
301 debug(" LB_MEM_TABLE found.\n");
302 /* The last one found is CBMEM */
303 cbmem = mem->map[i];
304 }
305}
306
307/* Return < 0 on error, 0 on success, 1 if forwarding table entry found. */
308static int parse_cbtable_entries(const struct mapping *table_mapping)
309{
310 size_t i;
311 const struct lb_record* lbr_p;
312 size_t table_size = mapping_size(table_mapping);
313 const void *lbtable = mapping_virt(table_mapping);
314 int forwarding_table_found = 0;
315
316 for (i = 0; i < table_size; i += lbr_p->size) {
317 lbr_p = lbtable + i;
318 debug(" coreboot table entry 0x%02x\n", lbr_p->tag);
319 switch (lbr_p->tag) {
320 case LB_TAG_MEMORY:
321 debug(" Found memory map.\n");
322 parse_memory_tags(lbtable + i);
323 continue;
324 case LB_TAG_TIMESTAMPS: {
325 debug(" Found timestamp table.\n");
326 timestamps = parse_cbmem_ref((struct lb_cbmem_ref *) lbr_p);
327 continue;
328 }
329 case LB_TAG_CBMEM_CONSOLE: {
330 debug(" Found cbmem console.\n");
331 console = parse_cbmem_ref((struct lb_cbmem_ref *) lbr_p);
332 continue;
333 }
334 case LB_TAG_FORWARD: {
335 int ret;
336 /*
337 * This is a forwarding entry - repeat the
338 * search at the new address.
339 */
340 struct lb_forward lbf_p =
341 *(const struct lb_forward *) lbr_p;
342 debug(" Found forwarding entry.\n");
343 ret = parse_cbtable(lbf_p.forward, 0);
344
345 /* Assume the forwarding entry is valid. If this fails
346 * then there's a total failure. */
347 if (ret < 0)
348 return -1;
349 forwarding_table_found = 1;
350 }
351 default:
352 break;
353 }
354 }
355
356 return forwarding_table_found;
357}
358
359/* Return < 0 on error, 0 on success. */
360static int parse_cbtable(u64 address, size_t table_size)
361{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600362 const void *buf;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600363 struct mapping header_mapping;
364 size_t req_size;
365 size_t i;
366
367 req_size = table_size;
368 /* Default to 4 KiB search space. */
369 if (req_size == 0)
370 req_size = 4 * 1024;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800371
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500372 debug("Looking for coreboot table at %" PRIx64 " %zd bytes.\n",
Aaron Durbin46300aa2017-09-26 16:22:38 -0600373 address, req_size);
374
375 buf = map_memory(&header_mapping, address, req_size);
376
Timothy Pearsonbea71402015-09-05 18:07:17 -0500377 if (!buf)
Aaron Durbin46300aa2017-09-26 16:22:38 -0600378 return -1;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700379
Aaron Durbin46300aa2017-09-26 16:22:38 -0600380 /* look at every 16 bytes */
Aaron Durbincf20c902017-09-28 17:52:59 -0600381 for (i = 0; i <= req_size - sizeof(struct lb_header); i += 16) {
Aaron Durbin46300aa2017-09-26 16:22:38 -0600382 int ret;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600383 const struct lb_header *lbh;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600384 struct mapping table_mapping;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700385
Aaron Durbin46300aa2017-09-26 16:22:38 -0600386 lbh = buf + i;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800387 if (memcmp(lbh->signature, "LBIO", sizeof(lbh->signature)) ||
388 !lbh->header_bytes ||
389 ipchcksum(lbh, sizeof(*lbh))) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700390 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700391 }
392
Aaron Durbin46300aa2017-09-26 16:22:38 -0600393 /* Map in the whole table to parse. */
394 if (!map_memory(&table_mapping, address + i + lbh->header_bytes,
395 lbh->table_bytes)) {
396 debug("Couldn't map in table\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700397 continue;
398 }
399
Aaron Durbin46300aa2017-09-26 16:22:38 -0600400 if (ipchcksum(mapping_virt(&table_mapping), lbh->table_bytes) !=
401 lbh->table_checksum) {
402 debug("Signature found, but wrong checksum.\n");
403 unmap_memory(&table_mapping);
404 continue;
405 }
406
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800407 debug("Found!\n");
408
Aaron Durbin46300aa2017-09-26 16:22:38 -0600409 ret = parse_cbtable_entries(&table_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500410
Aaron Durbin46300aa2017-09-26 16:22:38 -0600411 /* Table parsing failed. */
412 if (ret < 0) {
413 unmap_memory(&table_mapping);
414 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700415 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800416
Aaron Durbin46300aa2017-09-26 16:22:38 -0600417 /* Succeeded in parsing the table. Header not needed anymore. */
418 unmap_memory(&header_mapping);
419
420 /*
421 * Table parsing succeeded. If forwarding table not found update
422 * coreboot table mapping for future use.
423 */
424 if (ret == 0)
425 lbtable_mapping = table_mapping;
426 else
427 unmap_memory(&table_mapping);
428
429 return 0;
430 }
431
432 unmap_memory(&header_mapping);
433
434 return -1;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700435}
436
Patrick Georgid00f1802015-07-13 16:53:50 +0200437#if defined(linux) && (defined(__i386__) || defined(__x86_64__))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700438/*
Aaron Durbin08e920e2016-03-12 08:41:34 +0100439 * read CPU frequency from a sysfs file, return an frequency in Megahertz as
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700440 * an int or exit on any error.
441 */
Aaron Durbinc49014e2015-08-30 21:19:55 -0500442static unsigned long arch_tick_frequency(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700443{
444 FILE *cpuf;
445 char freqs[100];
446 int size;
447 char *endp;
448 u64 rv;
449
450 const char* freq_file =
451 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
452
453 cpuf = fopen(freq_file, "r");
454 if (!cpuf) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800455 fprintf(stderr, "Could not open %s: %s\n",
456 freq_file, strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700457 exit(1);
458 }
459
460 memset(freqs, 0, sizeof(freqs));
461 size = fread(freqs, 1, sizeof(freqs), cpuf);
462 if (!size || (size == sizeof(freqs))) {
463 fprintf(stderr, "Wrong number of bytes(%d) read from %s\n",
464 size, freq_file);
465 exit(1);
466 }
467 fclose(cpuf);
468 rv = strtoull(freqs, &endp, 10);
469
470 if (*endp == '\0' || *endp == '\n')
Aaron Durbin08e920e2016-03-12 08:41:34 +0100471 /* cpuinfo_max_freq is in kHz. Convert it to MHz. */
472 return rv / 1000;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700473 fprintf(stderr, "Wrong formatted value ^%s^ read from %s\n",
474 freqs, freq_file);
475 exit(1);
476}
Patrick Georgid00f1802015-07-13 16:53:50 +0200477#elif defined(__OpenBSD__) && (defined(__i386__) || defined(__x86_64__))
Aaron Durbinc49014e2015-08-30 21:19:55 -0500478static unsigned long arch_tick_frequency(void)
Patrick Georgid00f1802015-07-13 16:53:50 +0200479{
480 int mib[2] = { CTL_HW, HW_CPUSPEED };
481 static int value = 0;
482 size_t value_len = sizeof(value);
483
Aaron Durbinc49014e2015-08-30 21:19:55 -0500484 /* Return 1 MHz when sysctl fails. */
Patrick Georgid00f1802015-07-13 16:53:50 +0200485 if ((value == 0) && (sysctl(mib, 2, &value, &value_len, NULL, 0) == -1))
Aaron Durbinc49014e2015-08-30 21:19:55 -0500486 return 1;
Patrick Georgid00f1802015-07-13 16:53:50 +0200487
Aaron Durbinc49014e2015-08-30 21:19:55 -0500488 return value;
Patrick Georgid00f1802015-07-13 16:53:50 +0200489}
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700490#else
Aaron Durbinc49014e2015-08-30 21:19:55 -0500491static unsigned long arch_tick_frequency(void)
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700492{
Aaron Durbinc49014e2015-08-30 21:19:55 -0500493 /* 1 MHz = 1us. */
494 return 1;
Stefan Reinauerd8ef9e92013-07-31 15:44:37 -0700495}
496#endif
497
Aaron Durbinc49014e2015-08-30 21:19:55 -0500498static unsigned long tick_freq_mhz;
499
500static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz)
501{
502 tick_freq_mhz = table_tick_freq_mhz;
503
Martin Rothac9d6b82017-12-11 13:27:56 -0700504 /* Honor table frequency if present. */
505 if (!tick_freq_mhz)
506 tick_freq_mhz = arch_tick_frequency();
Aaron Durbinc49014e2015-08-30 21:19:55 -0500507
508 if (!tick_freq_mhz) {
509 fprintf(stderr, "Cannot determine timestamp tick frequency.\n");
510 exit(1);
511 }
Martin Rothac9d6b82017-12-11 13:27:56 -0700512
513 debug("Timestamp tick frequency: %ld MHz\n", tick_freq_mhz);
Aaron Durbinc49014e2015-08-30 21:19:55 -0500514}
515
516u64 arch_convert_raw_ts_entry(u64 ts)
517{
518 return ts / tick_freq_mhz;
519}
520
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700521/*
522 * Print an integer in 'normalized' form - with commas separating every three
Julius Wernera7d92442014-12-02 20:51:19 -0800523 * decimal orders.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700524 */
Julius Wernera7d92442014-12-02 20:51:19 -0800525static void print_norm(u64 v)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700526{
Julius Wernera7d92442014-12-02 20:51:19 -0800527 if (v >= 1000) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700528 /* print the higher order sections first */
Julius Wernera7d92442014-12-02 20:51:19 -0800529 print_norm(v / 1000);
530 printf(",%3.3u", (u32)(v % 1000));
531 } else {
532 printf("%u", (u32)(v % 1000));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700533 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700534}
535
Aaron Durbinfbff3012015-08-30 22:00:12 -0500536static const char *timestamp_name(uint32_t id)
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700537{
538 int i;
Aaron Durbinfbff3012015-08-30 22:00:12 -0500539
540 for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
541 if (timestamp_ids[i].id == id)
542 return timestamp_ids[i].name;
543 }
544 return "<unknown>";
545}
546
547static uint64_t timestamp_print_parseable_entry(uint32_t id, uint64_t stamp,
548 uint64_t prev_stamp)
549{
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700550 const char *name;
Aaron Durbin799bf782015-08-06 13:52:08 -0500551 uint64_t step_time;
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700552
Aaron Durbinfbff3012015-08-30 22:00:12 -0500553 name = timestamp_name(id);
554
555 step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
556
557 /* ID<tab>absolute time<tab>relative time<tab>description */
558 printf("%d\t", id);
559 printf("%llu\t", (long long)arch_convert_raw_ts_entry(stamp));
560 printf("%llu\t", (long long)step_time);
561 printf("%s\n", name);
562
563 return step_time;
564}
565
566uint64_t timestamp_print_entry(uint32_t id, uint64_t stamp, uint64_t prev_stamp)
567{
568 const char *name;
569 uint64_t step_time;
570
571 name = timestamp_name(id);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700572
573 printf("%4d:", id);
Julius Wernera7d92442014-12-02 20:51:19 -0800574 printf("%-50s", name);
575 print_norm(arch_convert_raw_ts_entry(stamp));
Aaron Durbin799bf782015-08-06 13:52:08 -0500576 step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700577 if (prev_stamp) {
578 printf(" (");
Aaron Durbin799bf782015-08-06 13:52:08 -0500579 print_norm(step_time);
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700580 printf(")");
581 }
582 printf("\n");
Aaron Durbin799bf782015-08-06 13:52:08 -0500583
584 return step_time;
Stefan Reinauer0db924d2013-08-09 11:06:11 -0700585}
586
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700587/* dump the timestamp table */
Aaron Durbinfbff3012015-08-30 22:00:12 -0500588static void dump_timestamps(int mach_readable)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700589{
590 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600591 const struct timestamp_table *tst_p;
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500592 size_t size;
Aaron Durbin31540fb2015-07-11 12:44:10 -0500593 uint64_t prev_stamp;
Aaron Durbin799bf782015-08-06 13:52:08 -0500594 uint64_t total_time;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600595 struct mapping timestamp_mapping;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800596
597 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
598 fprintf(stderr, "No timestamps found in coreboot table.\n");
599 return;
600 }
601
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500602 size = sizeof(*tst_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600603 tst_p = map_memory(&timestamp_mapping, timestamps.cbmem_addr, size);
604 if (!tst_p)
605 die("Unable to map timestamp header\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700606
Aaron Durbinc49014e2015-08-30 21:19:55 -0500607 timestamp_set_tick_freq(tst_p->tick_freq_mhz);
608
Aaron Durbinfbff3012015-08-30 22:00:12 -0500609 if (!mach_readable)
610 printf("%d entries total:\n\n", tst_p->num_entries);
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500611 size += tst_p->num_entries * sizeof(tst_p->entries[0]);
612
Aaron Durbin46300aa2017-09-26 16:22:38 -0600613 unmap_memory(&timestamp_mapping);
614
615 tst_p = map_memory(&timestamp_mapping, timestamps.cbmem_addr, size);
616 if (!tst_p)
617 die("Unable to map full timestamp table\n");
Aaron Durbinb58f9e32014-10-07 14:56:35 -0500618
Aaron Durbin31540fb2015-07-11 12:44:10 -0500619 /* Report the base time within the table. */
620 prev_stamp = 0;
Aaron Durbinfbff3012015-08-30 22:00:12 -0500621 if (mach_readable)
622 timestamp_print_parseable_entry(0, tst_p->base_time,
623 prev_stamp);
624 else
625 timestamp_print_entry(0, tst_p->base_time, prev_stamp);
Aaron Durbin31540fb2015-07-11 12:44:10 -0500626 prev_stamp = tst_p->base_time;
627
Aaron Durbin799bf782015-08-06 13:52:08 -0500628 total_time = 0;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700629 for (i = 0; i < tst_p->num_entries; i++) {
Aaron Durbin31540fb2015-07-11 12:44:10 -0500630 uint64_t stamp;
631 const struct timestamp_entry *tse = &tst_p->entries[i];
632
633 /* Make all timestamps absolute. */
634 stamp = tse->entry_stamp + tst_p->base_time;
Aaron Durbinfbff3012015-08-30 22:00:12 -0500635 if (mach_readable)
636 total_time +=
637 timestamp_print_parseable_entry(tse->entry_id,
638 stamp, prev_stamp);
639 else
640 total_time += timestamp_print_entry(tse->entry_id,
Aaron Durbin799bf782015-08-06 13:52:08 -0500641 stamp, prev_stamp);
Aaron Durbin31540fb2015-07-11 12:44:10 -0500642 prev_stamp = stamp;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700643 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800644
Aaron Durbinfbff3012015-08-30 22:00:12 -0500645 if (!mach_readable) {
646 printf("\nTotal Time: ");
647 print_norm(total_time);
648 printf("\n");
649 }
Aaron Durbin799bf782015-08-06 13:52:08 -0500650
Aaron Durbin46300aa2017-09-26 16:22:38 -0600651 unmap_memory(&timestamp_mapping);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700652}
653
Julius Wernerd67c6872017-02-02 17:32:00 -0800654struct cbmem_console {
655 u32 size;
656 u32 cursor;
657 u8 body[0];
658} __attribute__ ((__packed__));
659
660#define CBMC_CURSOR_MASK ((1 << 28) - 1)
661#define CBMC_OVERFLOW (1 << 31)
662
Stefan Reinauer19f87562013-01-07 13:37:12 -0800663/* dump the cbmem console */
Julius Wernerb7b64a92017-04-28 16:31:46 -0700664static void dump_console(int one_boot_only)
Stefan Reinauer19f87562013-01-07 13:37:12 -0800665{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600666 const struct cbmem_console *console_p;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800667 char *console_c;
Julius Wernerd67c6872017-02-02 17:32:00 -0800668 size_t size, cursor;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600669 struct mapping console_mapping;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800670
671 if (console.tag != LB_TAG_CBMEM_CONSOLE) {
672 fprintf(stderr, "No console found in coreboot table.\n");
673 return;
674 }
675
Julius Wernerd67c6872017-02-02 17:32:00 -0800676 size = sizeof(*console_p);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600677 console_p = map_memory(&console_mapping, console.cbmem_addr, size);
678 if (!console_p)
679 die("Unable to map console object.\n");
680
Julius Wernerd67c6872017-02-02 17:32:00 -0800681 cursor = console_p->cursor & CBMC_CURSOR_MASK;
682 if (!(console_p->cursor & CBMC_OVERFLOW) && cursor < console_p->size)
Vladimir Serbinenkof4a0d012013-03-30 12:15:12 +0100683 size = cursor;
Julius Wernerd67c6872017-02-02 17:32:00 -0800684 else
685 size = console_p->size;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600686 unmap_memory(&console_mapping);
Julius Wernerd67c6872017-02-02 17:32:00 -0800687
688 console_c = malloc(size + 1);
Stefan Reinauer19f87562013-01-07 13:37:12 -0800689 if (!console_c) {
690 fprintf(stderr, "Not enough memory for console.\n");
691 exit(1);
692 }
Julius Wernerd67c6872017-02-02 17:32:00 -0800693 console_c[size] = '\0';
Stefan Reinauer19f87562013-01-07 13:37:12 -0800694
Aaron Durbin46300aa2017-09-26 16:22:38 -0600695 console_p = map_memory(&console_mapping, console.cbmem_addr,
696 size + sizeof(*console_p));
697
698 if (!console_p)
699 die("Unable to map full console object.\n");
700
Julius Wernerd67c6872017-02-02 17:32:00 -0800701 if (console_p->cursor & CBMC_OVERFLOW) {
702 if (cursor >= size) {
703 printf("cbmem: ERROR: CBMEM console struct is illegal, "
704 "output may be corrupt or out of order!\n\n");
705 cursor = 0;
706 }
707 aligned_memcpy(console_c, console_p->body + cursor,
708 size - cursor);
709 aligned_memcpy(console_c + size - cursor,
710 console_p->body, cursor);
711 } else {
712 aligned_memcpy(console_c, console_p->body, size);
713 }
Stefan Reinauer19f87562013-01-07 13:37:12 -0800714
Julius Wernerd67c6872017-02-02 17:32:00 -0800715 /* Slight memory corruption may occur between reboots and give us a few
716 unprintable characters like '\0'. Replace them with '?' on output. */
717 for (cursor = 0; cursor < size; cursor++)
718 if (!isprint(console_c[cursor]) && !isspace(console_c[cursor]))
719 console_c[cursor] = '?';
Stefan Reinauer19f87562013-01-07 13:37:12 -0800720
Julius Wernerb7b64a92017-04-28 16:31:46 -0700721 /* We detect the last boot by looking for a bootblock, romstage or
722 ramstage banner, in that order (to account for platforms without
723 CONFIG_BOOTBLOCK_CONSOLE and/or CONFIG_EARLY_CONSOLE). Once we find
724 a banner, store the last match for that stage in cursor and stop. */
725 cursor = 0;
726 if (one_boot_only) {
727#define BANNER_REGEX(stage) "\n\ncoreboot-[^\n]* " stage " starting\\.\\.\\.\n"
Julius Wernerbe3aa042017-06-12 17:35:15 -0700728#define OVERFLOW_REGEX(stage) "\n\\*\\*\\* Pre-CBMEM " stage " console overflow"
Julius Wernerb7b64a92017-04-28 16:31:46 -0700729 const char *regex[] = { BANNER_REGEX("bootblock"),
Julius Wernerd906bb62017-05-16 13:54:18 -0700730 OVERFLOW_REGEX("romstage"),
Furquan Shaikh35972de2018-02-16 16:12:02 -0800731 BANNER_REGEX("romstage"),
732 OVERFLOW_REGEX("ramstage"),
733 BANNER_REGEX("ramstage") };
Julius Wernerb7b64a92017-04-28 16:31:46 -0700734 int i;
735
736 for (i = 0; !cursor && i < ARRAY_SIZE(regex); i++) {
737 regex_t re;
738 regmatch_t match;
739 assert(!regcomp(&re, regex[i], 0));
740
741 /* Keep looking for matches so we find the last one. */
742 while (!regexec(&re, console_c + cursor, 1, &match, 0))
743 cursor += match.rm_so + 1;
744 regfree(&re);
745 }
746 }
747
748 puts(console_c + cursor);
Stefan Reinauer19f87562013-01-07 13:37:12 -0800749 free(console_c);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600750 unmap_memory(&console_mapping);
Stefan Reinauer19f87562013-01-07 13:37:12 -0800751}
752
Stefan Reinauera9c83612013-07-16 17:47:35 -0700753static void hexdump(unsigned long memory, int length)
754{
755 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600756 const uint8_t *m;
Stefan Reinauera9c83612013-07-16 17:47:35 -0700757 int all_zero = 0;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600758 struct mapping hexdump_mapping;
Stefan Reinauera9c83612013-07-16 17:47:35 -0700759
Aaron Durbin46300aa2017-09-26 16:22:38 -0600760 m = map_memory(&hexdump_mapping, memory, length);
761 if (!m)
762 die("Unable to map hexdump memory.\n");
Stefan Reinauera9c83612013-07-16 17:47:35 -0700763
764 for (i = 0; i < length; i += 16) {
765 int j;
766
767 all_zero++;
768 for (j = 0; j < 16; j++) {
769 if(m[i+j] != 0) {
770 all_zero = 0;
771 break;
772 }
773 }
774
775 if (all_zero < 2) {
776 printf("%08lx:", memory + i);
777 for (j = 0; j < 16; j++)
778 printf(" %02x", m[i+j]);
779 printf(" ");
780 for (j = 0; j < 16; j++)
781 printf("%c", isprint(m[i+j]) ? m[i+j] : '.');
782 printf("\n");
783 } else if (all_zero == 2) {
784 printf("...\n");
785 }
786 }
787
Aaron Durbin46300aa2017-09-26 16:22:38 -0600788 unmap_memory(&hexdump_mapping);
Stefan Reinauera9c83612013-07-16 17:47:35 -0700789}
790
791static void dump_cbmem_hex(void)
792{
793 if (cbmem.type != LB_MEM_TABLE) {
794 fprintf(stderr, "No coreboot CBMEM area found!\n");
795 return;
796 }
797
798 hexdump(unpack_lb64(cbmem.start), unpack_lb64(cbmem.size));
799}
800
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700801void rawdump(uint64_t base, uint64_t size)
802{
803 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600804 const uint8_t *m;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600805 struct mapping dump_mapping;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700806
Aaron Durbin46300aa2017-09-26 16:22:38 -0600807 m = map_memory(&dump_mapping, base, size);
808 if (!m)
809 die("Unable to map rawdump memory\n");
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700810
811 for (i = 0 ; i < size; i++)
812 printf("%c", m[i]);
Aaron Durbin46300aa2017-09-26 16:22:38 -0600813
814 unmap_memory(&dump_mapping);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700815}
816
817static void dump_cbmem_raw(unsigned int id)
818{
Aaron Durbinf2a38222017-09-26 17:44:28 -0600819 const uint8_t *table;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700820 size_t offset;
821 uint64_t base = 0;
822 uint64_t size = 0;
823
Aaron Durbin46300aa2017-09-26 16:22:38 -0600824 table = mapping_virt(&lbtable_mapping);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700825
826 if (table == NULL)
827 return;
828
829 offset = 0;
830
Aaron Durbin46300aa2017-09-26 16:22:38 -0600831 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -0600832 const struct lb_record *lbr;
833 const struct lb_cbmem_entry *lbe;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700834
Aaron Durbinf2a38222017-09-26 17:44:28 -0600835 lbr = (const void *)(table + offset);
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700836 offset += lbr->size;
837
838 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
839 continue;
840
Aaron Durbinf2a38222017-09-26 17:44:28 -0600841 lbe = (const void *)lbr;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700842 if (lbe->id == id) {
843 debug("found id for raw dump %0x", lbe->id);
844 base = lbe->address;
845 size = lbe->entry_size;
846 break;
847 }
848 }
849
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700850 if (!base)
851 fprintf(stderr, "id %0x not found in cbtable\n", id);
852 else
853 rawdump(base, size);
854}
855
Aaron Durbin0dff57d2015-03-05 21:18:33 -0600856struct cbmem_id_to_name {
857 uint32_t id;
858 const char *name;
859};
Vadim Bendebury8b143c52014-05-14 10:12:55 -0700860static const struct cbmem_id_to_name cbmem_ids[] = { CBMEM_ID_TO_NAME_TABLE };
Stefan Reinauerc0199072013-01-07 16:26:10 -0800861
Kyösti Mälkkieab5c122017-09-04 11:10:17 +0300862#define MAX_STAGEx 10
Stefan Reinauera9c83612013-07-16 17:47:35 -0700863void cbmem_print_entry(int n, uint32_t id, uint64_t base, uint64_t size)
864{
865 int i;
866 const char *name;
Kyösti Mälkkieab5c122017-09-04 11:10:17 +0300867 char stage_x[20];
Stefan Reinauera9c83612013-07-16 17:47:35 -0700868
869 name = NULL;
870 for (i = 0; i < ARRAY_SIZE(cbmem_ids); i++) {
871 if (cbmem_ids[i].id == id) {
872 name = cbmem_ids[i].name;
873 break;
874 }
Kyösti Mälkkieab5c122017-09-04 11:10:17 +0300875 if (id >= CBMEM_ID_STAGEx_META &&
876 id < CBMEM_ID_STAGEx_META + MAX_STAGEx) {
877 snprintf(stage_x, sizeof(stage_x), "STAGE%d META",
878 (id - CBMEM_ID_STAGEx_META));
879 name = stage_x;
880 }
881 if (id >= CBMEM_ID_STAGEx_CACHE &&
882 id < CBMEM_ID_STAGEx_CACHE + MAX_STAGEx) {
883 snprintf(stage_x, sizeof(stage_x), "STAGE%d $ ",
884 (id - CBMEM_ID_STAGEx_CACHE));
885 name = stage_x;
886 }
Stefan Reinauera9c83612013-07-16 17:47:35 -0700887 }
888
889 printf("%2d. ", n);
890 if (name == NULL)
Kyösti Mälkkieab5c122017-09-04 11:10:17 +0300891 printf("\t\t%08x", id);
Stefan Reinauera9c83612013-07-16 17:47:35 -0700892 else
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700893 printf("%s\t%08x", name, id);
Stefan Reinauera9c83612013-07-16 17:47:35 -0700894 printf(" %08" PRIx64 " ", base);
895 printf(" %08" PRIx64 "\n", size);
896}
897
Stefan Reinauerd37ab452012-12-18 16:23:28 -0800898static void dump_cbmem_toc(void)
Stefan Reinauerc0199072013-01-07 16:26:10 -0800899{
Aaron Durbin09c0c112015-09-30 12:33:01 -0500900 int i;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600901 const uint8_t *table;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500902 size_t offset;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800903
Aaron Durbin46300aa2017-09-26 16:22:38 -0600904 table = mapping_virt(&lbtable_mapping);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500905
906 if (table == NULL)
Stefan Reinauerc0199072013-01-07 16:26:10 -0800907 return;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500908
909 printf("CBMEM table of contents:\n");
Pratik Prajapatic29e57d2015-09-03 12:58:44 -0700910 printf(" NAME ID START LENGTH\n");
Aaron Durbin09c0c112015-09-30 12:33:01 -0500911
912 i = 0;
913 offset = 0;
914
Aaron Durbin46300aa2017-09-26 16:22:38 -0600915 while (offset < mapping_size(&lbtable_mapping)) {
Aaron Durbinf2a38222017-09-26 17:44:28 -0600916 const struct lb_record *lbr;
917 const struct lb_cbmem_entry *lbe;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500918
Aaron Durbinf2a38222017-09-26 17:44:28 -0600919 lbr = (const void *)(table + offset);
Aaron Durbin09c0c112015-09-30 12:33:01 -0500920 offset += lbr->size;
921
922 if (lbr->tag != LB_TAG_CBMEM_ENTRY)
923 continue;
924
Aaron Durbinf2a38222017-09-26 17:44:28 -0600925 lbe = (const void *)lbr;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500926 cbmem_print_entry(i, lbe->id, lbe->address, lbe->entry_size);
927 i++;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800928 }
Stefan Reinauerc0199072013-01-07 16:26:10 -0800929}
Stefan Reinauer19f87562013-01-07 13:37:12 -0800930
Stefan Reinauerd37ab452012-12-18 16:23:28 -0800931#define COVERAGE_MAGIC 0x584d4153
932struct file {
933 uint32_t magic;
934 uint32_t next;
935 uint32_t filename;
936 uint32_t data;
937 int offset;
938 int len;
939};
940
941static int mkpath(char *path, mode_t mode)
942{
943 assert (path && *path);
944 char *p;
945 for (p = strchr(path+1, '/'); p; p = strchr(p + 1, '/')) {
946 *p = '\0';
947 if (mkdir(path, mode) == -1) {
948 if (errno != EEXIST) {
949 *p = '/';
950 return -1;
951 }
952 }
953 *p = '/';
954 }
955 return 0;
956}
957
958static void dump_coverage(void)
959{
Stefan Reinauerd37ab452012-12-18 16:23:28 -0800960 uint64_t start;
Aaron Durbin09c0c112015-09-30 12:33:01 -0500961 size_t size;
Aaron Durbinf2a38222017-09-26 17:44:28 -0600962 const void *coverage;
Aaron Durbin46300aa2017-09-26 16:22:38 -0600963 struct mapping coverage_mapping;
Stefan Reinauerd37ab452012-12-18 16:23:28 -0800964 unsigned long phys_offset;
965#define phys_to_virt(x) ((void *)(unsigned long)(x) + phys_offset)
966
Aaron Durbin09c0c112015-09-30 12:33:01 -0500967 if (find_cbmem_entry(CBMEM_ID_COVERAGE, &start, &size)) {
968 fprintf(stderr, "No coverage information found\n");
Stefan Reinauerd37ab452012-12-18 16:23:28 -0800969 return;
970 }
971
Stefan Reinauerd37ab452012-12-18 16:23:28 -0800972 /* Map coverage area */
Aaron Durbin46300aa2017-09-26 16:22:38 -0600973 coverage = map_memory(&coverage_mapping, start, size);
974 if (!coverage)
975 die("Unable to map coverage area.\n");
Stefan Reinauerd37ab452012-12-18 16:23:28 -0800976 phys_offset = (unsigned long)coverage - (unsigned long)start;
977
978 printf("Dumping coverage data...\n");
979
980 struct file *file = (struct file *)coverage;
981 while (file && file->magic == COVERAGE_MAGIC) {
982 FILE *f;
983 char *filename;
984
985 debug(" -> %s\n", (char *)phys_to_virt(file->filename));
986 filename = strdup((char *)phys_to_virt(file->filename));
987 if (mkpath(filename, 0755) == -1) {
988 perror("Directory for coverage data could "
989 "not be created");
990 exit(1);
991 }
992 f = fopen(filename, "wb");
993 if (!f) {
994 printf("Could not open %s: %s\n",
995 filename, strerror(errno));
996 exit(1);
997 }
998 if (fwrite((void *)phys_to_virt(file->data),
999 file->len, 1, f) != 1) {
1000 printf("Could not write to %s: %s\n",
1001 filename, strerror(errno));
1002 exit(1);
1003 }
1004 fclose(f);
1005 free(filename);
1006
1007 if (file->next)
1008 file = (struct file *)phys_to_virt(file->next);
1009 else
1010 file = NULL;
1011 }
Aaron Durbin46300aa2017-09-26 16:22:38 -06001012 unmap_memory(&coverage_mapping);
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001013}
1014
1015static void print_version(void)
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001016{
1017 printf("cbmem v%s -- ", CBMEM_VERSION);
1018 printf("Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.\n\n");
1019 printf(
1020 "This program is free software: you can redistribute it and/or modify\n"
1021 "it under the terms of the GNU General Public License as published by\n"
1022 "the Free Software Foundation, version 2 of the License.\n\n"
1023 "This program is distributed in the hope that it will be useful,\n"
1024 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1025 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
Martin Rotheb20e602016-01-12 13:30:50 -07001026 "GNU General Public License for more details.\n\n");
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001027}
1028
Martin Roth8448ac42016-09-11 15:43:22 -06001029static void print_usage(const char *name, int exit_code)
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001030{
Aaron Durbinfbff3012015-08-30 22:00:12 -05001031 printf("usage: %s [-cCltTxVvh?]\n", name);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001032 printf("\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001033 " -c | --console: print cbmem console\n"
Julius Wernerb7b64a92017-04-28 16:31:46 -07001034 " -1 | --oneboot: print cbmem console for last boot only\n"
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001035 " -C | --coverage: dump coverage information\n"
Stefan Reinauerc0199072013-01-07 16:26:10 -08001036 " -l | --list: print cbmem table of contents\n"
Stefan Reinauera9c83612013-07-16 17:47:35 -07001037 " -x | --hexdump: print hexdump of cbmem area\n"
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001038 " -r | --rawdump ID: print rawdump of specific ID (in hex) of cbtable\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001039 " -t | --timestamps: print timestamp information\n"
Aaron Durbinfbff3012015-08-30 22:00:12 -05001040 " -T | --parseable-timestamps: print parseable timestamps\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -08001041 " -V | --verbose: verbose (debugging) output\n"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001042 " -v | --version: print the version\n"
1043 " -h | --help: print this help\n"
1044 "\n");
Martin Roth8448ac42016-09-11 15:43:22 -06001045 exit(exit_code);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001046}
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001047
Julius Werner337de4c2014-06-16 23:02:03 -07001048#ifdef __arm__
1049static void dt_update_cells(const char *name, int *addr_cells_ptr,
1050 int *size_cells_ptr)
1051{
1052 if (*addr_cells_ptr >= 0 && *size_cells_ptr >= 0)
1053 return;
1054
1055 int buffer;
1056 size_t nlen = strlen(name);
1057 char *prop = alloca(nlen + sizeof("/#address-cells"));
1058 strcpy(prop, name);
1059
1060 if (*addr_cells_ptr < 0) {
1061 strcpy(prop + nlen, "/#address-cells");
1062 int fd = open(prop, O_RDONLY);
1063 if (fd < 0 && errno != ENOENT) {
1064 perror(prop);
1065 } else if (fd >= 0) {
1066 if (read(fd, &buffer, sizeof(int)) < 0)
1067 perror(prop);
1068 else
1069 *addr_cells_ptr = ntohl(buffer);
1070 close(fd);
1071 }
1072 }
1073
1074 if (*size_cells_ptr < 0) {
1075 strcpy(prop + nlen, "/#size-cells");
1076 int fd = open(prop, O_RDONLY);
1077 if (fd < 0 && errno != ENOENT) {
1078 perror(prop);
1079 } else if (fd >= 0) {
1080 if (read(fd, &buffer, sizeof(int)) < 0)
1081 perror(prop);
1082 else
1083 *size_cells_ptr = ntohl(buffer);
1084 close(fd);
1085 }
1086 }
1087}
1088
1089static char *dt_find_compat(const char *parent, const char *compat,
1090 int *addr_cells_ptr, int *size_cells_ptr)
1091{
1092 char *ret = NULL;
1093 struct dirent *entry;
1094 DIR *dir;
1095
1096 if (!(dir = opendir(parent))) {
1097 perror(parent);
1098 return NULL;
1099 }
1100
1101 /* Loop through all files in the directory (DT node). */
1102 while ((entry = readdir(dir))) {
1103 /* We only care about compatible props or subnodes. */
1104 if (entry->d_name[0] == '.' || !((entry->d_type & DT_DIR) ||
1105 !strcmp(entry->d_name, "compatible")))
1106 continue;
1107
1108 /* Assemble the file name (on the stack, for speed). */
1109 size_t plen = strlen(parent);
1110 char *name = alloca(plen + strlen(entry->d_name) + 2);
1111
1112 strcpy(name, parent);
1113 name[plen] = '/';
1114 strcpy(name + plen + 1, entry->d_name);
1115
1116 /* If it's a subnode, recurse. */
1117 if (entry->d_type & DT_DIR) {
1118 ret = dt_find_compat(name, compat, addr_cells_ptr,
1119 size_cells_ptr);
1120
1121 /* There is only one matching node to find, abort. */
1122 if (ret) {
1123 /* Gather cells values on the way up. */
1124 dt_update_cells(parent, addr_cells_ptr,
1125 size_cells_ptr);
1126 break;
1127 }
1128 continue;
1129 }
1130
1131 /* If it's a compatible string, see if it's the right one. */
1132 int fd = open(name, O_RDONLY);
1133 int clen = strlen(compat);
1134 char *buffer = alloca(clen + 1);
1135
1136 if (fd < 0) {
1137 perror(name);
1138 continue;
1139 }
1140
1141 if (read(fd, buffer, clen + 1) < 0) {
1142 perror(name);
1143 close(fd);
1144 continue;
1145 }
1146 close(fd);
1147
1148 if (!strcmp(compat, buffer)) {
1149 /* Initialize these to "unset" for the way up. */
1150 *addr_cells_ptr = *size_cells_ptr = -1;
1151
1152 /* Can't leave string on the stack or we'll lose it! */
1153 ret = strdup(parent);
1154 break;
1155 }
1156 }
1157
1158 closedir(dir);
1159 return ret;
1160}
1161#endif /* __arm__ */
1162
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001163int main(int argc, char** argv)
1164{
Stefan Reinauer19f87562013-01-07 13:37:12 -08001165 int print_defaults = 1;
1166 int print_console = 0;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001167 int print_coverage = 0;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001168 int print_list = 0;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001169 int print_hexdump = 0;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001170 int print_rawdump = 0;
Stefan Reinauer19f87562013-01-07 13:37:12 -08001171 int print_timestamps = 0;
Aaron Durbinfbff3012015-08-30 22:00:12 -05001172 int machine_readable_timestamps = 0;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001173 int one_boot_only = 0;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001174 unsigned int rawdump_id = 0;
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001175
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001176 int opt, option_index = 0;
1177 static struct option long_options[] = {
Stefan Reinauer19f87562013-01-07 13:37:12 -08001178 {"console", 0, 0, 'c'},
Julius Wernerb7b64a92017-04-28 16:31:46 -07001179 {"oneboot", 0, 0, '1'},
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001180 {"coverage", 0, 0, 'C'},
Stefan Reinauerc0199072013-01-07 16:26:10 -08001181 {"list", 0, 0, 'l'},
Stefan Reinauer19f87562013-01-07 13:37:12 -08001182 {"timestamps", 0, 0, 't'},
Aaron Durbinfbff3012015-08-30 22:00:12 -05001183 {"parseable-timestamps", 0, 0, 'T'},
Stefan Reinauera9c83612013-07-16 17:47:35 -07001184 {"hexdump", 0, 0, 'x'},
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001185 {"rawdump", required_argument, 0, 'r'},
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001186 {"verbose", 0, 0, 'V'},
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001187 {"version", 0, 0, 'v'},
1188 {"help", 0, 0, 'h'},
1189 {0, 0, 0, 0}
1190 };
Julius Wernerb7b64a92017-04-28 16:31:46 -07001191 while ((opt = getopt_long(argc, argv, "c1CltTxVvh?r:",
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001192 long_options, &option_index)) != EOF) {
1193 switch (opt) {
Stefan Reinauer19f87562013-01-07 13:37:12 -08001194 case 'c':
1195 print_console = 1;
1196 print_defaults = 0;
1197 break;
Julius Wernerb7b64a92017-04-28 16:31:46 -07001198 case '1':
1199 print_console = 1;
1200 one_boot_only = 1;
1201 print_defaults = 0;
1202 break;
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001203 case 'C':
1204 print_coverage = 1;
1205 print_defaults = 0;
1206 break;
Stefan Reinauerc0199072013-01-07 16:26:10 -08001207 case 'l':
1208 print_list = 1;
1209 print_defaults = 0;
1210 break;
Stefan Reinauera9c83612013-07-16 17:47:35 -07001211 case 'x':
1212 print_hexdump = 1;
1213 print_defaults = 0;
1214 break;
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001215 case 'r':
1216 print_rawdump = 1;
1217 print_defaults = 0;
1218 rawdump_id = strtoul(optarg, NULL, 16);
1219 break;
Stefan Reinauer19f87562013-01-07 13:37:12 -08001220 case 't':
1221 print_timestamps = 1;
1222 print_defaults = 0;
1223 break;
Aaron Durbinfbff3012015-08-30 22:00:12 -05001224 case 'T':
1225 print_timestamps = 1;
1226 machine_readable_timestamps = 1;
1227 print_defaults = 0;
1228 break;
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001229 case 'V':
1230 verbose = 1;
1231 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001232 case 'v':
1233 print_version();
1234 exit(0);
1235 break;
1236 case 'h':
Martin Roth8448ac42016-09-11 15:43:22 -06001237 print_usage(argv[0], 0);
1238 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001239 case '?':
1240 default:
Martin Roth8448ac42016-09-11 15:43:22 -06001241 print_usage(argv[0], 1);
Stefan Reinauer1e0e5562013-01-02 15:43:56 -08001242 break;
1243 }
1244 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001245
Julius Werner337de4c2014-06-16 23:02:03 -07001246 mem_fd = open("/dev/mem", O_RDONLY, 0);
1247 if (mem_fd < 0) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001248 fprintf(stderr, "Failed to gain memory access: %s\n",
1249 strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001250 return 1;
1251 }
1252
Stefan Reinauer7f681502013-06-19 15:39:09 -07001253#ifdef __arm__
Julius Werner337de4c2014-06-16 23:02:03 -07001254 int addr_cells, size_cells;
1255 char *coreboot_node = dt_find_compat("/proc/device-tree", "coreboot",
1256 &addr_cells, &size_cells);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001257
Julius Werner337de4c2014-06-16 23:02:03 -07001258 if (!coreboot_node) {
1259 fprintf(stderr, "Could not find 'coreboot' compatible node!\n");
Stefan Reinauer7f681502013-06-19 15:39:09 -07001260 return 1;
1261 }
1262
Julius Werner337de4c2014-06-16 23:02:03 -07001263 if (addr_cells < 0) {
1264 fprintf(stderr, "Warning: no #address-cells node in tree!\n");
1265 addr_cells = 1;
1266 }
1267
1268 int nlen = strlen(coreboot_node);
1269 char *reg = alloca(nlen + sizeof("/reg"));
1270
1271 strcpy(reg, coreboot_node);
1272 strcpy(reg + nlen, "/reg");
1273 free(coreboot_node);
1274
1275 int fd = open(reg, O_RDONLY);
1276 if (fd < 0) {
1277 perror(reg);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001278 return 1;
1279 }
Stefan Reinauer7f681502013-06-19 15:39:09 -07001280
Julius Werner337de4c2014-06-16 23:02:03 -07001281 int i;
Aaron Durbinb58f9e32014-10-07 14:56:35 -05001282 size_t size_to_read = addr_cells * 4 + size_cells * 4;
1283 u8 *dtbuffer = alloca(size_to_read);
1284 if (read(fd, dtbuffer, size_to_read) < 0) {
Julius Werner337de4c2014-06-16 23:02:03 -07001285 perror(reg);
1286 return 1;
1287 }
1288 close(fd);
1289
1290 /* No variable-length byte swap function anywhere in C... how sad. */
1291 u64 baseaddr = 0;
1292 for (i = 0; i < addr_cells * 4; i++) {
1293 baseaddr <<= 8;
Aaron Durbinb58f9e32014-10-07 14:56:35 -05001294 baseaddr |= *dtbuffer;
1295 dtbuffer++;
1296 }
1297 u64 cb_table_size = 0;
1298 for (i = 0; i < size_cells * 4; i++) {
1299 cb_table_size <<= 8;
1300 cb_table_size |= *dtbuffer;
1301 dtbuffer++;
Julius Werner337de4c2014-06-16 23:02:03 -07001302 }
1303
Aaron Durbin46300aa2017-09-26 16:22:38 -06001304 parse_cbtable(baseaddr, cb_table_size);
Stefan Reinauer7f681502013-06-19 15:39:09 -07001305#else
1306 int j;
Aaron Durbin46300aa2017-09-26 16:22:38 -06001307 unsigned long long possible_base_addresses[] = { 0, 0xf0000 };
Stefan Reinauer7f681502013-06-19 15:39:09 -07001308
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001309 /* Find and parse coreboot table */
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001310 for (j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
Aaron Durbin46300aa2017-09-26 16:22:38 -06001311 if (!parse_cbtable(possible_base_addresses[j], 0))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001312 break;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001313 }
Stefan Reinauer7f681502013-06-19 15:39:09 -07001314#endif
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001315
Aaron Durbin46300aa2017-09-26 16:22:38 -06001316 if (mapping_virt(&lbtable_mapping) == NULL)
1317 die("Table not found.\n");
1318
Stefan Reinauer19f87562013-01-07 13:37:12 -08001319 if (print_console)
Julius Wernerb7b64a92017-04-28 16:31:46 -07001320 dump_console(one_boot_only);
Stefan Reinauer19f87562013-01-07 13:37:12 -08001321
Stefan Reinauerd37ab452012-12-18 16:23:28 -08001322 if (print_coverage)
1323 dump_coverage();
1324
Stefan Reinauerc0199072013-01-07 16:26:10 -08001325 if (print_list)
1326 dump_cbmem_toc();
1327
Stefan Reinauera9c83612013-07-16 17:47:35 -07001328 if (print_hexdump)
1329 dump_cbmem_hex();
1330
Pratik Prajapatic29e57d2015-09-03 12:58:44 -07001331 if (print_rawdump)
1332 dump_cbmem_raw(rawdump_id);
1333
Stefan Reinauer19f87562013-01-07 13:37:12 -08001334 if (print_defaults || print_timestamps)
Aaron Durbinfbff3012015-08-30 22:00:12 -05001335 dump_timestamps(machine_readable_timestamps);
Stefan Reinauer05cbce62013-01-03 14:30:33 -08001336
Aaron Durbin46300aa2017-09-26 16:22:38 -06001337 unmap_memory(&lbtable_mapping);
1338
Julius Werner337de4c2014-06-16 23:02:03 -07001339 close(mem_fd);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001340 return 0;
1341}