blob: 4c0bd6837f5105e3338087c84fc62b7d85eddc7c [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;
121
122static int parse_cbtable(u64 address)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700123{
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800124 int i, found = 0;
125 void *buf;
126
127 debug("Looking for coreboot table at %lx\n", address);
128 buf = map_memory(address);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700129
130 /* look at every 16 bytes within 4K of the base */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800131
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700132 for (i = 0; i < 0x1000; i += 0x10) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800133 struct lb_header *lbh;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700134 struct lb_record* lbr_p;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800135 void *lbtable;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700136 int j;
137
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800138 lbh = (struct lb_header *)(buf + i);
139 if (memcmp(lbh->signature, "LBIO", sizeof(lbh->signature)) ||
140 !lbh->header_bytes ||
141 ipchcksum(lbh, sizeof(*lbh))) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700142 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700143 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800144 lbtable = buf + i + lbh->header_bytes;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700145
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800146 if (ipchcksum(lbtable, lbh->table_bytes) !=
147 lbh->table_checksum) {
148 debug("Signature found, but wrong checksum.\n");
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700149 continue;
150 }
151
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800152 found = 1;
153 debug("Found!\n");
154
155 for (j = 0; j < lbh->table_bytes; j += lbr_p->size) {
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700156 /* look for the timestamp table */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800157 lbr_p = (struct lb_record*) ((char *)lbtable + j);
158 debug(" coreboot table entry 0x%02x\n", lbr_p->tag);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700159 switch (lbr_p->tag) {
160 case LB_TAG_TIMESTAMPS: {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800161 debug("Found timestamp table\n");
162 timestamps = *(struct lb_cbmem_ref *) lbr_p;
163 continue;
164 }
165 case LB_TAG_CBMEM_CONSOLE: {
166 debug("Found cbmem console\n");
167 console = *(struct lb_cbmem_ref *) lbr_p;
168 continue;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700169 }
170 case LB_TAG_FORWARD: {
171 /*
172 * This is a forwarding entry - repeat the
173 * search at the new address.
174 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800175 struct lb_forward lbf_p =
176 *(struct lb_forward *) lbr_p;
177 unmap_memory();
178 return parse_cbtable(lbf_p.forward);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700179 }
180 default:
181 break;
182 }
183
184 }
185 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800186 unmap_memory();
187
188 return found;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700189}
190
191/*
192 * read CPU frequency from a sysfs file, return an frequency in Kilohertz as
193 * an int or exit on any error.
194 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800195static u64 get_cpu_freq_KHz(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700196{
197 FILE *cpuf;
198 char freqs[100];
199 int size;
200 char *endp;
201 u64 rv;
202
203 const char* freq_file =
204 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
205
206 cpuf = fopen(freq_file, "r");
207 if (!cpuf) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800208 fprintf(stderr, "Could not open %s: %s\n",
209 freq_file, strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700210 exit(1);
211 }
212
213 memset(freqs, 0, sizeof(freqs));
214 size = fread(freqs, 1, sizeof(freqs), cpuf);
215 if (!size || (size == sizeof(freqs))) {
216 fprintf(stderr, "Wrong number of bytes(%d) read from %s\n",
217 size, freq_file);
218 exit(1);
219 }
220 fclose(cpuf);
221 rv = strtoull(freqs, &endp, 10);
222
223 if (*endp == '\0' || *endp == '\n')
224 return rv;
225 fprintf(stderr, "Wrong formatted value ^%s^ read from %s\n",
226 freqs, freq_file);
227 exit(1);
228}
229
230/*
231 * Print an integer in 'normalized' form - with commas separating every three
232 * decimal orders. The 'comma' parameter indicates if a comma is needed after
233 * the value is printed.
234 */
235static void print_norm(u64 v, int comma)
236{
237 int first_triple = 1;
238
239 if (v > 1000) {
240 /* print the higher order sections first */
241 print_norm(v / 1000, 1);
242 first_triple = 0;
243 }
244 if (first_triple)
245 printf("%d", (u32)(v % 1000));
246 else
247 printf("%3.3d", (u32)(v % 1000));
248 if (comma)
249 printf(",");
250}
251
252/* dump the timestamp table */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800253static void dump_timestamps(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700254{
255 int i;
256 u64 cpu_freq_MHz = get_cpu_freq_KHz() / 1000;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800257 struct timestamp_table *tst_p;
258
259 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
260 fprintf(stderr, "No timestamps found in coreboot table.\n");
261 return;
262 }
263
264 tst_p = (struct timestamp_table *)
265 map_memory((unsigned long)timestamps.cbmem_addr);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700266
267 printf("%d entries total:\n\n", tst_p->num_entries);
268 for (i = 0; i < tst_p->num_entries; i++) {
269 const struct timestamp_entry *tse_p = tst_p->entries + i;
270
271 printf("%4d:", tse_p->entry_id);
272 print_norm(tse_p->entry_stamp / cpu_freq_MHz, 0);
273 if (i) {
274 printf(" (");
275 print_norm((tse_p->entry_stamp -
276 tse_p[-1].entry_stamp) /
277 cpu_freq_MHz, 0);
278 printf(")");
279 }
280 printf("\n");
281 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800282
283 unmap_memory();
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700284}
285
Stefan Reinauer19f87562013-01-07 13:37:12 -0800286/* dump the cbmem console */
287static void dump_console(void)
288{
289 void *console_p;
290 char *console_c;
291 uint32_t size;
292
293 if (console.tag != LB_TAG_CBMEM_CONSOLE) {
294 fprintf(stderr, "No console found in coreboot table.\n");
295 return;
296 }
297
298 console_p = map_memory((unsigned long)console.cbmem_addr);
299 /* The in-memory format of the console area is:
300 * u32 size
301 * u32 cursor
302 * char console[size]
303 * Hence we have to add 8 to get to the actual console string.
304 */
305 size = *(uint32_t *)console_p;
306 console_c = malloc(size + 1);
307 if (!console_c) {
308 fprintf(stderr, "Not enough memory for console.\n");
309 exit(1);
310 }
311
312 memcpy(console_c, console_p + 8, size);
313 console_c[size] = 0;
314
315 printf("%s", console_c);
316
317 free(console_c);
318
319 unmap_memory();
320}
321
322
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800323void print_version(void)
324{
325 printf("cbmem v%s -- ", CBMEM_VERSION);
326 printf("Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.\n\n");
327 printf(
328 "This program is free software: you can redistribute it and/or modify\n"
329 "it under the terms of the GNU General Public License as published by\n"
330 "the Free Software Foundation, version 2 of the License.\n\n"
331 "This program is distributed in the hope that it will be useful,\n"
332 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
333 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
334 "GNU General Public License for more details.\n\n"
335 "You should have received a copy of the GNU General Public License\n"
336 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n");
337}
338
339void print_usage(const char *name)
340{
341 printf("usage: %s [-vh?]\n", name);
342 printf("\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -0800343 " -c | --console: print cbmem console\n"
344 " -t | --timestamps: print timestamp information\n"
345 " -V | --verbose: verbose (debugging) output\n"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800346 " -v | --version: print the version\n"
347 " -h | --help: print this help\n"
348 "\n");
349 exit(1);
350}
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700351
352int main(int argc, char** argv)
353{
354 int j;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800355 static const int possible_base_addresses[] = { 0, 0xf0000 };
356
Stefan Reinauer19f87562013-01-07 13:37:12 -0800357 int print_defaults = 1;
358 int print_console = 0;
359 int print_timestamps = 0;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800360
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800361 int opt, option_index = 0;
362 static struct option long_options[] = {
Stefan Reinauer19f87562013-01-07 13:37:12 -0800363 {"console", 0, 0, 'c'},
364 {"timestamps", 0, 0, 't'},
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800365 {"verbose", 0, 0, 'V'},
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800366 {"version", 0, 0, 'v'},
367 {"help", 0, 0, 'h'},
368 {0, 0, 0, 0}
369 };
Stefan Reinauer19f87562013-01-07 13:37:12 -0800370 while ((opt = getopt_long(argc, argv, "ctVvh?",
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800371 long_options, &option_index)) != EOF) {
372 switch (opt) {
Stefan Reinauer19f87562013-01-07 13:37:12 -0800373 case 'c':
374 print_console = 1;
375 print_defaults = 0;
376 break;
377 case 't':
378 print_timestamps = 1;
379 print_defaults = 0;
380 break;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800381 case 'V':
382 verbose = 1;
383 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800384 case 'v':
385 print_version();
386 exit(0);
387 break;
388 case 'h':
389 case '?':
390 default:
391 print_usage(argv[0]);
392 exit(0);
393 break;
394 }
395 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700396
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800397 fd = open("/dev/mem", O_RDONLY, 0);
398 if (fd < 0) {
399 fprintf(stderr, "Failed to gain memory access: %s\n",
400 strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700401 return 1;
402 }
403
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800404 /* Find and parse coreboot table */
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700405 for (j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800406 if (parse_cbtable(possible_base_addresses[j]))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700407 break;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700408 }
409
Stefan Reinauer19f87562013-01-07 13:37:12 -0800410 if (print_console)
411 dump_console();
412
413 if (print_defaults || print_timestamps)
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800414 dump_timestamps();
415
416 close(fd);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700417 return 0;
418}