blob: a777a8f3027682c13816b4e1118c3b7e3cc95843 [file] [log] [blame]
Vadim Bendebury6d18fd02012-09-27 19:24:07 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070020#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
Stefan Reinauer1e0e5562013-01-02 15:43:56 -080023#include <unistd.h>
24#include <getopt.h>
Stefan Reinauer05cbce62013-01-03 14:30:33 -080025#include <errno.h>
26#include <fcntl.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <sys/mman.h>
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070030
Stefan Reinauer05cbce62013-01-03 14:30:33 -080031#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
32#define MAP_BYTES (1024*1024)
33
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070034#include "boot/coreboot_tables.h"
35
36typedef uint16_t u16;
37typedef uint32_t u32;
38typedef uint64_t u64;
39
40#include "cbmem.h"
41#include "timestamp.h"
42
Stefan Reinauer1e0e5562013-01-02 15:43:56 -080043#define CBMEM_VERSION "1.0"
44
Stefan Reinauer05cbce62013-01-03 14:30:33 -080045/* verbose output? */
46static int verbose = 0;
47#define debug(x...) if(verbose) printf(x)
48
49/* File handle used to access /dev/mem */
50static int fd;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070051
52/*
53 * calculate ip checksum (16 bit quantities) on a passed in buffer. In case
54 * the buffer length is odd last byte is excluded from the calculation
55 */
56static u16 ipchcksum(const void *addr, unsigned size)
57{
58 const u16 *p = addr;
59 unsigned i, n = size / 2; /* don't expect odd sized blocks */
60 u32 sum = 0;
61
62 for (i = 0; i < n; i++)
63 sum += p[i];
64
65 sum = (sum >> 16) + (sum & 0xffff);
66 sum += (sum >> 16);
67 sum = ~sum & 0xffff;
68 return (u16) sum;
69}
70
71/*
Stefan Reinauer05cbce62013-01-03 14:30:33 -080072 * Functions to map / unmap physical memory into virtual address space. These
73 * functions always maps 1MB at a time and can only map one area at once.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070074 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -080075static void *mapped_virtual;
76static void *map_memory(u64 physical)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070077{
Stefan Reinauer05cbce62013-01-03 14:30:33 -080078 void *v;
79 off_t p;
80 int page = getpagesize();
81
82 /* Mapped memory must be aligned to page size */
83 p = physical & ~(page - 1);
84
85 debug("Mapping 1MB of physical memory at %zx.\n", p);
86
87 v = mmap(NULL, MAP_BYTES, PROT_READ, MAP_SHARED, fd, p);
88
89 if (v == MAP_FAILED) {
90 fprintf(stderr, "Failed to mmap /dev/mem: %s\n",
91 strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -070092 exit(1);
93 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -080094
95 /* Remember what we actually mapped ... */
96 mapped_virtual = v;
97
98 /* ... but return address to the physical memory that was requested */
99 v += physical & (page-1);
100
101 return v;
102}
103
104static void unmap_memory(void)
105{
106 debug("Unmapping 1MB of virtual memory at %p.\n", mapped_virtual);
107 munmap(mapped_virtual, MAP_BYTES);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700108}
109
110/*
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800111 * Try finding the timestamp table and coreboot cbmem console starting from the
112 * passed in memory offset. Could be called recursively in case a forwarding
113 * entry is found.
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700114 *
115 * Returns pointer to a memory buffer containg the timestamp table or zero if
116 * none found.
117 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800118
119static struct lb_cbmem_ref timestamps;
120static struct lb_cbmem_ref console;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800121static struct lb_memory_range cbmem;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800122
123static int parse_cbtable(u64 address)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700124{
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800125 int i, found = 0;
126 void *buf;
127
128 debug("Looking for coreboot table at %lx\n", address);
129 buf = map_memory(address);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700130
131 /* look at every 16 bytes within 4K of the base */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800132
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700133 for (i = 0; i < 0x1000; i += 0x10) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800134 struct lb_header *lbh;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700135 struct lb_record* lbr_p;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800136 void *lbtable;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700137 int j;
138
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800139 lbh = (struct lb_header *)(buf + i);
140 if (memcmp(lbh->signature, "LBIO", sizeof(lbh->signature)) ||
141 !lbh->header_bytes ||
142 ipchcksum(lbh, sizeof(*lbh))) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700143 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700144 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800145 lbtable = buf + i + lbh->header_bytes;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700146
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800147 if (ipchcksum(lbtable, lbh->table_bytes) !=
148 lbh->table_checksum) {
149 debug("Signature found, but wrong checksum.\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700150 continue;
151 }
152
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800153 found = 1;
154 debug("Found!\n");
155
156 for (j = 0; j < lbh->table_bytes; j += lbr_p->size) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700157 /* look for the timestamp table */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800158 lbr_p = (struct lb_record*) ((char *)lbtable + j);
159 debug(" coreboot table entry 0x%02x\n", lbr_p->tag);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700160 switch (lbr_p->tag) {
Stefan Reinauerc0199072013-01-07 16:26:10 -0800161 case LB_TAG_MEMORY: {
162 int i = 0;
163 debug(" Found memory map.\n");
164 struct lb_memory *memory =
165 (struct lb_memory *)lbr_p;
166 while ((char *)&memory->map[i] < ((char *)lbtable
167 + lbr_p->size)) {
168 if (memory->map[i].type == LB_MEM_TABLE) {
169 debug(" LB_MEM_TABLE found.\n");
170 /* The last one found is CBMEM */
171 cbmem = memory->map[i];
172 }
173 i++;
174 }
175 continue;
176 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700177 case LB_TAG_TIMESTAMPS: {
Stefan Reinauerd7144dc2013-01-07 15:25:37 -0800178 debug(" Found timestamp table.\n");
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800179 timestamps = *(struct lb_cbmem_ref *) lbr_p;
180 continue;
181 }
182 case LB_TAG_CBMEM_CONSOLE: {
Stefan Reinauerd7144dc2013-01-07 15:25:37 -0800183 debug(" Found cbmem console.\n");
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800184 console = *(struct lb_cbmem_ref *) lbr_p;
185 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700186 }
187 case LB_TAG_FORWARD: {
188 /*
189 * This is a forwarding entry - repeat the
190 * search at the new address.
191 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800192 struct lb_forward lbf_p =
193 *(struct lb_forward *) lbr_p;
Stefan Reinauerd7144dc2013-01-07 15:25:37 -0800194 debug(" Found forwarding entry.\n");
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800195 unmap_memory();
196 return parse_cbtable(lbf_p.forward);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700197 }
198 default:
199 break;
200 }
201
202 }
203 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800204 unmap_memory();
205
206 return found;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700207}
208
209/*
210 * read CPU frequency from a sysfs file, return an frequency in Kilohertz as
211 * an int or exit on any error.
212 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800213static u64 get_cpu_freq_KHz(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700214{
215 FILE *cpuf;
216 char freqs[100];
217 int size;
218 char *endp;
219 u64 rv;
220
221 const char* freq_file =
222 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
223
224 cpuf = fopen(freq_file, "r");
225 if (!cpuf) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800226 fprintf(stderr, "Could not open %s: %s\n",
227 freq_file, strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700228 exit(1);
229 }
230
231 memset(freqs, 0, sizeof(freqs));
232 size = fread(freqs, 1, sizeof(freqs), cpuf);
233 if (!size || (size == sizeof(freqs))) {
234 fprintf(stderr, "Wrong number of bytes(%d) read from %s\n",
235 size, freq_file);
236 exit(1);
237 }
238 fclose(cpuf);
239 rv = strtoull(freqs, &endp, 10);
240
241 if (*endp == '\0' || *endp == '\n')
242 return rv;
243 fprintf(stderr, "Wrong formatted value ^%s^ read from %s\n",
244 freqs, freq_file);
245 exit(1);
246}
247
248/*
249 * Print an integer in 'normalized' form - with commas separating every three
250 * decimal orders. The 'comma' parameter indicates if a comma is needed after
251 * the value is printed.
252 */
253static void print_norm(u64 v, int comma)
254{
255 int first_triple = 1;
256
257 if (v > 1000) {
258 /* print the higher order sections first */
259 print_norm(v / 1000, 1);
260 first_triple = 0;
261 }
262 if (first_triple)
263 printf("%d", (u32)(v % 1000));
264 else
265 printf("%3.3d", (u32)(v % 1000));
266 if (comma)
267 printf(",");
268}
269
270/* dump the timestamp table */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800271static void dump_timestamps(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700272{
273 int i;
274 u64 cpu_freq_MHz = get_cpu_freq_KHz() / 1000;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800275 struct timestamp_table *tst_p;
276
277 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
278 fprintf(stderr, "No timestamps found in coreboot table.\n");
279 return;
280 }
281
282 tst_p = (struct timestamp_table *)
283 map_memory((unsigned long)timestamps.cbmem_addr);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700284
285 printf("%d entries total:\n\n", tst_p->num_entries);
286 for (i = 0; i < tst_p->num_entries; i++) {
287 const struct timestamp_entry *tse_p = tst_p->entries + i;
288
289 printf("%4d:", tse_p->entry_id);
290 print_norm(tse_p->entry_stamp / cpu_freq_MHz, 0);
291 if (i) {
292 printf(" (");
293 print_norm((tse_p->entry_stamp -
294 tse_p[-1].entry_stamp) /
295 cpu_freq_MHz, 0);
296 printf(")");
297 }
298 printf("\n");
299 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800300
301 unmap_memory();
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700302}
303
Stefan Reinauer19f87562013-01-07 13:37:12 -0800304/* dump the cbmem console */
305static void dump_console(void)
306{
307 void *console_p;
308 char *console_c;
309 uint32_t size;
310
311 if (console.tag != LB_TAG_CBMEM_CONSOLE) {
312 fprintf(stderr, "No console found in coreboot table.\n");
313 return;
314 }
315
316 console_p = map_memory((unsigned long)console.cbmem_addr);
317 /* The in-memory format of the console area is:
318 * u32 size
319 * u32 cursor
320 * char console[size]
321 * Hence we have to add 8 to get to the actual console string.
322 */
323 size = *(uint32_t *)console_p;
324 console_c = malloc(size + 1);
325 if (!console_c) {
326 fprintf(stderr, "Not enough memory for console.\n");
327 exit(1);
328 }
329
330 memcpy(console_c, console_p + 8, size);
331 console_c[size] = 0;
332
333 printf("%s", console_c);
334
335 free(console_c);
336
337 unmap_memory();
338}
339
Stefan Reinauerc0199072013-01-07 16:26:10 -0800340#define CBMEM_MAGIC 0x434f5245
341#define MAX_CBMEM_ENTRIES 16
342
343struct cbmem_entry {
344 uint32_t magic;
345 uint32_t id;
346 uint64_t base;
347 uint64_t size;
348};
349
350void dump_cbmem_toc(void)
351{
352 int i;
353 uint64_t start;
354 struct cbmem_entry *entries;
355
356 if (cbmem.type != LB_MEM_TABLE) {
357 fprintf(stderr, "No coreboot table area found!\n");
358 return;
359 }
360
361 start = unpack_lb64(cbmem.start);
362
363 entries = (struct cbmem_entry *)map_memory(start);
364
365 printf("CBMEM table of contents:\n");
366 printf(" ID START LENGTH\n");
367 for (i=0; i<MAX_CBMEM_ENTRIES; i++) {
368 if (entries[i].magic != CBMEM_MAGIC)
369 break;
370
371 printf("%2d. ", i);
372 switch (entries[i].id) {
373 case CBMEM_ID_FREESPACE: printf("FREE SPACE "); break;
374 case CBMEM_ID_GDT: printf("GDT "); break;
375 case CBMEM_ID_ACPI: printf("ACPI "); break;
376 case CBMEM_ID_ACPI_GNVS: printf("ACPI GNVS "); break;
377 case CBMEM_ID_CBTABLE: printf("COREBOOTE "); break;
378 case CBMEM_ID_PIRQ: printf("IRQ TABLE "); break;
379 case CBMEM_ID_MPTABLE: printf("SMP TABLE "); break;
380 case CBMEM_ID_RESUME: printf("ACPI RESUME "); break;
381 case CBMEM_ID_RESUME_SCRATCH: printf("ACPI SCRATCH"); break;
382 case CBMEM_ID_SMBIOS: printf("SMBIOS "); break;
383 case CBMEM_ID_TIMESTAMP: printf("TIME STAMP "); break;
384 case CBMEM_ID_MRCDATA: printf("MRC DATA "); break;
385 case CBMEM_ID_CONSOLE: printf("CONSOLE "); break;
386 case CBMEM_ID_ELOG: printf("ELOG "); break;
387 default: printf("%08x ",
388 entries[i].id); break;
389 }
390 printf(" 0x%08jx 0x%08jx\n", (uintmax_t)entries[i].base,
391 (uintmax_t)entries[i].size);
392 }
393 unmap_memory();
394}
Stefan Reinauer19f87562013-01-07 13:37:12 -0800395
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800396void print_version(void)
397{
398 printf("cbmem v%s -- ", CBMEM_VERSION);
399 printf("Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.\n\n");
400 printf(
401 "This program is free software: you can redistribute it and/or modify\n"
402 "it under the terms of the GNU General Public License as published by\n"
403 "the Free Software Foundation, version 2 of the License.\n\n"
404 "This program is distributed in the hope that it will be useful,\n"
405 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
406 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
407 "GNU General Public License for more details.\n\n"
408 "You should have received a copy of the GNU General Public License\n"
409 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n");
410}
411
412void print_usage(const char *name)
413{
414 printf("usage: %s [-vh?]\n", name);
415 printf("\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -0800416 " -c | --console: print cbmem console\n"
Stefan Reinauerc0199072013-01-07 16:26:10 -0800417 " -l | --list: print cbmem table of contents\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -0800418 " -t | --timestamps: print timestamp information\n"
419 " -V | --verbose: verbose (debugging) output\n"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800420 " -v | --version: print the version\n"
421 " -h | --help: print this help\n"
422 "\n");
423 exit(1);
424}
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700425
426int main(int argc, char** argv)
427{
428 int j;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800429 static const int possible_base_addresses[] = { 0, 0xf0000 };
430
Stefan Reinauer19f87562013-01-07 13:37:12 -0800431 int print_defaults = 1;
432 int print_console = 0;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800433 int print_list = 0;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800434 int print_timestamps = 0;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800435
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800436 int opt, option_index = 0;
437 static struct option long_options[] = {
Stefan Reinauer19f87562013-01-07 13:37:12 -0800438 {"console", 0, 0, 'c'},
Stefan Reinauerc0199072013-01-07 16:26:10 -0800439 {"list", 0, 0, 'l'},
Stefan Reinauer19f87562013-01-07 13:37:12 -0800440 {"timestamps", 0, 0, 't'},
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800441 {"verbose", 0, 0, 'V'},
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800442 {"version", 0, 0, 'v'},
443 {"help", 0, 0, 'h'},
444 {0, 0, 0, 0}
445 };
Stefan Reinauerc0199072013-01-07 16:26:10 -0800446 while ((opt = getopt_long(argc, argv, "cltVvh?",
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800447 long_options, &option_index)) != EOF) {
448 switch (opt) {
Stefan Reinauer19f87562013-01-07 13:37:12 -0800449 case 'c':
450 print_console = 1;
451 print_defaults = 0;
452 break;
Stefan Reinauerc0199072013-01-07 16:26:10 -0800453 case 'l':
454 print_list = 1;
455 print_defaults = 0;
456 break;
Stefan Reinauer19f87562013-01-07 13:37:12 -0800457 case 't':
458 print_timestamps = 1;
459 print_defaults = 0;
460 break;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800461 case 'V':
462 verbose = 1;
463 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800464 case 'v':
465 print_version();
466 exit(0);
467 break;
468 case 'h':
469 case '?':
470 default:
471 print_usage(argv[0]);
472 exit(0);
473 break;
474 }
475 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700476
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800477 fd = open("/dev/mem", O_RDONLY, 0);
478 if (fd < 0) {
479 fprintf(stderr, "Failed to gain memory access: %s\n",
480 strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700481 return 1;
482 }
483
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800484 /* Find and parse coreboot table */
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700485 for (j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800486 if (parse_cbtable(possible_base_addresses[j]))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700487 break;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700488 }
489
Stefan Reinauer19f87562013-01-07 13:37:12 -0800490 if (print_console)
491 dump_console();
492
Stefan Reinauerc0199072013-01-07 16:26:10 -0800493 if (print_list)
494 dump_cbmem_toc();
495
Stefan Reinauer19f87562013-01-07 13:37:12 -0800496 if (print_defaults || print_timestamps)
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800497 dump_timestamps();
498
499 close(fd);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700500 return 0;
501}