blob: 59d054046e568977ffac5735270d509dd61ed849 [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 Reinauerd7144dc2013-01-07 15:25:37 -0800161 debug(" Found timestamp table.\n");
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800162 timestamps = *(struct lb_cbmem_ref *) lbr_p;
163 continue;
164 }
165 case LB_TAG_CBMEM_CONSOLE: {
Stefan Reinauerd7144dc2013-01-07 15:25:37 -0800166 debug(" Found cbmem console.\n");
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800167 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;
Stefan Reinauerd7144dc2013-01-07 15:25:37 -0800177 debug(" Found forwarding entry.\n");
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800178 unmap_memory();
179 return parse_cbtable(lbf_p.forward);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700180 }
181 default:
182 break;
183 }
184
185 }
186 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800187 unmap_memory();
188
189 return found;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700190}
191
192/*
193 * read CPU frequency from a sysfs file, return an frequency in Kilohertz as
194 * an int or exit on any error.
195 */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800196static u64 get_cpu_freq_KHz(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700197{
198 FILE *cpuf;
199 char freqs[100];
200 int size;
201 char *endp;
202 u64 rv;
203
204 const char* freq_file =
205 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
206
207 cpuf = fopen(freq_file, "r");
208 if (!cpuf) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800209 fprintf(stderr, "Could not open %s: %s\n",
210 freq_file, strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700211 exit(1);
212 }
213
214 memset(freqs, 0, sizeof(freqs));
215 size = fread(freqs, 1, sizeof(freqs), cpuf);
216 if (!size || (size == sizeof(freqs))) {
217 fprintf(stderr, "Wrong number of bytes(%d) read from %s\n",
218 size, freq_file);
219 exit(1);
220 }
221 fclose(cpuf);
222 rv = strtoull(freqs, &endp, 10);
223
224 if (*endp == '\0' || *endp == '\n')
225 return rv;
226 fprintf(stderr, "Wrong formatted value ^%s^ read from %s\n",
227 freqs, freq_file);
228 exit(1);
229}
230
231/*
232 * Print an integer in 'normalized' form - with commas separating every three
233 * decimal orders. The 'comma' parameter indicates if a comma is needed after
234 * the value is printed.
235 */
236static void print_norm(u64 v, int comma)
237{
238 int first_triple = 1;
239
240 if (v > 1000) {
241 /* print the higher order sections first */
242 print_norm(v / 1000, 1);
243 first_triple = 0;
244 }
245 if (first_triple)
246 printf("%d", (u32)(v % 1000));
247 else
248 printf("%3.3d", (u32)(v % 1000));
249 if (comma)
250 printf(",");
251}
252
253/* dump the timestamp table */
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800254static void dump_timestamps(void)
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700255{
256 int i;
257 u64 cpu_freq_MHz = get_cpu_freq_KHz() / 1000;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800258 struct timestamp_table *tst_p;
259
260 if (timestamps.tag != LB_TAG_TIMESTAMPS) {
261 fprintf(stderr, "No timestamps found in coreboot table.\n");
262 return;
263 }
264
265 tst_p = (struct timestamp_table *)
266 map_memory((unsigned long)timestamps.cbmem_addr);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700267
268 printf("%d entries total:\n\n", tst_p->num_entries);
269 for (i = 0; i < tst_p->num_entries; i++) {
270 const struct timestamp_entry *tse_p = tst_p->entries + i;
271
272 printf("%4d:", tse_p->entry_id);
273 print_norm(tse_p->entry_stamp / cpu_freq_MHz, 0);
274 if (i) {
275 printf(" (");
276 print_norm((tse_p->entry_stamp -
277 tse_p[-1].entry_stamp) /
278 cpu_freq_MHz, 0);
279 printf(")");
280 }
281 printf("\n");
282 }
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800283
284 unmap_memory();
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700285}
286
Stefan Reinauer19f87562013-01-07 13:37:12 -0800287/* dump the cbmem console */
288static void dump_console(void)
289{
290 void *console_p;
291 char *console_c;
292 uint32_t size;
293
294 if (console.tag != LB_TAG_CBMEM_CONSOLE) {
295 fprintf(stderr, "No console found in coreboot table.\n");
296 return;
297 }
298
299 console_p = map_memory((unsigned long)console.cbmem_addr);
300 /* The in-memory format of the console area is:
301 * u32 size
302 * u32 cursor
303 * char console[size]
304 * Hence we have to add 8 to get to the actual console string.
305 */
306 size = *(uint32_t *)console_p;
307 console_c = malloc(size + 1);
308 if (!console_c) {
309 fprintf(stderr, "Not enough memory for console.\n");
310 exit(1);
311 }
312
313 memcpy(console_c, console_p + 8, size);
314 console_c[size] = 0;
315
316 printf("%s", console_c);
317
318 free(console_c);
319
320 unmap_memory();
321}
322
323
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800324void print_version(void)
325{
326 printf("cbmem v%s -- ", CBMEM_VERSION);
327 printf("Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.\n\n");
328 printf(
329 "This program is free software: you can redistribute it and/or modify\n"
330 "it under the terms of the GNU General Public License as published by\n"
331 "the Free Software Foundation, version 2 of the License.\n\n"
332 "This program is distributed in the hope that it will be useful,\n"
333 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
334 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
335 "GNU General Public License for more details.\n\n"
336 "You should have received a copy of the GNU General Public License\n"
337 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n");
338}
339
340void print_usage(const char *name)
341{
342 printf("usage: %s [-vh?]\n", name);
343 printf("\n"
Stefan Reinauer19f87562013-01-07 13:37:12 -0800344 " -c | --console: print cbmem console\n"
345 " -t | --timestamps: print timestamp information\n"
346 " -V | --verbose: verbose (debugging) output\n"
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800347 " -v | --version: print the version\n"
348 " -h | --help: print this help\n"
349 "\n");
350 exit(1);
351}
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700352
353int main(int argc, char** argv)
354{
355 int j;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800356 static const int possible_base_addresses[] = { 0, 0xf0000 };
357
Stefan Reinauer19f87562013-01-07 13:37:12 -0800358 int print_defaults = 1;
359 int print_console = 0;
360 int print_timestamps = 0;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800361
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800362 int opt, option_index = 0;
363 static struct option long_options[] = {
Stefan Reinauer19f87562013-01-07 13:37:12 -0800364 {"console", 0, 0, 'c'},
365 {"timestamps", 0, 0, 't'},
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800366 {"verbose", 0, 0, 'V'},
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800367 {"version", 0, 0, 'v'},
368 {"help", 0, 0, 'h'},
369 {0, 0, 0, 0}
370 };
Stefan Reinauer19f87562013-01-07 13:37:12 -0800371 while ((opt = getopt_long(argc, argv, "ctVvh?",
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800372 long_options, &option_index)) != EOF) {
373 switch (opt) {
Stefan Reinauer19f87562013-01-07 13:37:12 -0800374 case 'c':
375 print_console = 1;
376 print_defaults = 0;
377 break;
378 case 't':
379 print_timestamps = 1;
380 print_defaults = 0;
381 break;
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800382 case 'V':
383 verbose = 1;
384 break;
Stefan Reinauer1e0e5562013-01-02 15:43:56 -0800385 case 'v':
386 print_version();
387 exit(0);
388 break;
389 case 'h':
390 case '?':
391 default:
392 print_usage(argv[0]);
393 exit(0);
394 break;
395 }
396 }
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700397
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800398 fd = open("/dev/mem", O_RDONLY, 0);
399 if (fd < 0) {
400 fprintf(stderr, "Failed to gain memory access: %s\n",
401 strerror(errno));
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700402 return 1;
403 }
404
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800405 /* Find and parse coreboot table */
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700406 for (j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800407 if (parse_cbtable(possible_base_addresses[j]))
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700408 break;
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700409 }
410
Stefan Reinauer19f87562013-01-07 13:37:12 -0800411 if (print_console)
412 dump_console();
413
414 if (print_defaults || print_timestamps)
Stefan Reinauer05cbce62013-01-03 14:30:33 -0800415 dump_timestamps();
416
417 close(fd);
Vadim Bendebury6d18fd02012-09-27 19:24:07 -0700418 return 0;
419}