diff --git a/board/coreboot/packages/cbmem/Makefile b/board/coreboot/packages/cbmem/Makefile new file mode 100644 index 0000000..48f50dc --- /dev/null +++ b/board/coreboot/packages/cbmem/Makefile @@ -0,0 +1,44 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc. +## + +PROGRAM = cbmem +#ROOT = ../../src +CC ?= $(CROSS_COMPILE)gcc +CFLAGS ?= -Os +CFLAGS += -Wall -Werror +#CPPFLAGS += -iquote $(ROOT)/include -iquote $(ROOT)/src/arch/x86 + +OBJS = $(PROGRAM).o + +all: $(PROGRAM) + +$(PROGRAM): $(OBJS) + +clean: + rm -f $(PROGRAM) *.o *~ + +distclean: clean + rm -f .dependencies + +.dependencies: + @$(CC) $(CFLAGS) $(CPPFLAGS) -MM *.c > .dependencies + +.PHONY: all clean distclean + +-include .dependencies diff --git a/board/coreboot/packages/cbmem/cbmem.c b/board/coreboot/packages/cbmem/cbmem.c new file mode 100644 index 0000000..892ed0a --- /dev/null +++ b/board/coreboot/packages/cbmem/cbmem.c @@ -0,0 +1,1240 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __OpenBSD__ +#include +#include +#endif + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define MAP_BYTES (1024*1024) +#define IS_ENABLED(x) (defined (x) && (x)) + +#include "coreboot_tables.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#include "cbmem_id.h" +#include "timestamp.h" + +#define CBMEM_VERSION "1.1" + +/* verbose output? */ +static int verbose = 0; +#define debug(x...) if(verbose) printf(x) + +/* File handle used to access /dev/mem */ +static int mem_fd; + +/* IMD root pointer location */ +static uint64_t rootptr = 0; + +/* + * calculate ip checksum (16 bit quantities) on a passed in buffer. In case + * the buffer length is odd last byte is excluded from the calculation + */ +static u16 ipchcksum(const void *addr, unsigned size) +{ + const u16 *p = addr; + unsigned i, n = size / 2; /* don't expect odd sized blocks */ + u32 sum = 0; + + for (i = 0; i < n; i++) + sum += p[i]; + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + sum = ~sum & 0xffff; + return (u16) sum; +} + +/* + * Functions to map / unmap physical memory into virtual address space. These + * functions always maps 1MB at a time and can only map one area at once. + */ +static void *mapped_virtual; +static size_t mapped_size; + +static inline size_t size_to_mib(size_t sz) +{ + return sz >> 20; +} + +static void unmap_memory(void) +{ + if (mapped_virtual == NULL) { + fprintf(stderr, "Error unmapping memory\n"); + return; + } + if (size_to_mib(mapped_size) == 0) { + debug("Unmapping %zuMB of virtual memory at %p.\n", + size_to_mib(mapped_size), mapped_virtual); + } + else { + debug("Unmapping %zuMB of virtual memory at %p.\n", + size_to_mib(mapped_size), mapped_virtual); + } + munmap(mapped_virtual, mapped_size); + mapped_virtual = NULL; + mapped_size = 0; +} + +static void *map_memory_size(u64 physical, size_t size) +{ + void *v; + off_t p; + u64 page = getpagesize(); + size_t padding; + + if (mapped_virtual != NULL) + unmap_memory(); + + /* Mapped memory must be aligned to page size */ + p = physical & ~(page - 1); + padding = physical & (page-1); + size += padding; + + if (size_to_mib(size) == 0) { + debug("Mapping %zuB of physical memory at 0x%jx (requested 0x%jx).\n", + size, (intmax_t)p, (intmax_t)physical); + } + else { + debug("Mapping %zuMB of physical memory at 0x%jx (requested 0x%jx).\n", + size_to_mib(size), (intmax_t)p, (intmax_t)physical); + } + + v = mmap(NULL, size, PROT_READ, MAP_SHARED, mem_fd, p); + + if (v == MAP_FAILED) { + /* The mapped area may have overrun the upper cbmem boundary when trying to + * align to the page size. Try growing down instead of up... + */ + p -= page; + padding += page; + size &= ~(page - 1); + size = size + (page - 1); + v = mmap(NULL, size, PROT_READ, MAP_SHARED, mem_fd, p); + debug(" ... failed. Mapping %zuB of physical memory at 0x%jx.\n", + size, (intmax_t)p); + } + + if (v == MAP_FAILED) { + fprintf(stderr, "Failed to mmap /dev/mem: %s\n", + strerror(errno)); + exit(1); + } + + /* Remember what we actually mapped ... */ + mapped_virtual = v; + mapped_size = size; + + /* ... but return address to the physical memory that was requested */ + if (padding) + debug(" ... padding virtual address with 0x%zx bytes.\n", + padding); + v += padding; + + return v; +} + +static void *map_memory(u64 physical) +{ + return map_memory_size(physical, MAP_BYTES); +} + +/* + * Try finding the timestamp table and coreboot cbmem console starting from the + * passed in memory offset. Could be called recursively in case a forwarding + * entry is found. + * + * Returns pointer to a memory buffer containg the timestamp table or zero if + * none found. + */ + +static struct lb_cbmem_ref timestamps; +static struct lb_cbmem_ref console; +static struct lb_memory_range cbmem; + +/* This is a work-around for a nasty problem introduced by initially having + * pointer sized entries in the lb_cbmem_ref structures. This caused problems + * on 64bit x86 systems because coreboot is 32bit on those systems. + * When the problem was found, it was corrected, but there are a lot of + * systems out there with a firmware that does not produce the right + * lb_cbmem_ref structure. Hence we try to autocorrect this issue here. + */ +static struct lb_cbmem_ref parse_cbmem_ref(struct lb_cbmem_ref *cbmem_ref) +{ + struct lb_cbmem_ref ret; + + ret = *cbmem_ref; + + if (cbmem_ref->size < sizeof(*cbmem_ref)) + ret.cbmem_addr = (uint32_t)ret.cbmem_addr; + + debug(" cbmem_addr = %" PRIx64 "\n", ret.cbmem_addr); + + return ret; +} + +static int parse_cbtable(u64 address, size_t table_size) +{ + int i, found = 0; + void *buf; + + debug("Looking for coreboot table at %" PRIx64 " %zd bytes.\n", + address, table_size); + buf = map_memory_size(address, table_size); + + /* look at every 16 bytes within 4K of the base */ + + for (i = 0; i < 0x1000; i += 0x10) { + struct lb_header *lbh; + struct lb_record* lbr_p; + void *lbtable; + int j; + + lbh = (struct lb_header *)(buf + i); + if (memcmp(lbh->signature, "LBIO", sizeof(lbh->signature)) || + !lbh->header_bytes || + ipchcksum(lbh, sizeof(*lbh))) { + continue; + } + lbtable = buf + i + lbh->header_bytes; + + if (ipchcksum(lbtable, lbh->table_bytes) != + lbh->table_checksum) { + debug("Signature found, but wrong checksum.\n"); + continue; + } + + found = 1; + debug("Found!\n"); + + for (j = 0; j < lbh->table_bytes; j += lbr_p->size) { + lbr_p = (struct lb_record*) ((char *)lbtable + j); + debug(" coreboot table entry 0x%02x\n", lbr_p->tag); + switch (lbr_p->tag) { + case LB_TAG_MEMORY: { + int i = 0; + debug(" Found memory map.\n"); + struct lb_memory *memory = + (struct lb_memory *)lbr_p; + while ((char *)&memory->map[i] < ((char *)lbr_p + + lbr_p->size)) { + if (memory->map[i].type == LB_MEM_TABLE) { + debug(" LB_MEM_TABLE found.\n"); + /* The last one found is CBMEM */ + cbmem = memory->map[i]; + } + i++; + } + continue; + } + case LB_TAG_TIMESTAMPS: { + debug(" Found timestamp table.\n"); + timestamps = parse_cbmem_ref((struct lb_cbmem_ref *) lbr_p); + continue; + } + case LB_TAG_CBMEM_CONSOLE: { + debug(" Found cbmem console.\n"); + console = parse_cbmem_ref((struct lb_cbmem_ref *) lbr_p); + continue; + } + case LB_TAG_FORWARD: { + /* + * This is a forwarding entry - repeat the + * search at the new address. + */ + struct lb_forward lbf_p = + *(struct lb_forward *) lbr_p; + debug(" Found forwarding entry.\n"); + unmap_memory(); + return parse_cbtable(lbf_p.forward, table_size); + } + default: + break; + } + + } + } + unmap_memory(); + + return found; +} + +#if defined(linux) && (defined(__i386__) || defined(__x86_64__)) +/* + * read CPU frequency from a sysfs file, return an frequency in Kilohertz as + * an int or exit on any error. + */ +static unsigned long arch_tick_frequency(void) +{ + FILE *cpuf; + char freqs[100]; + int size; + char *endp; + u64 rv; + + const char* freq_file = + "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; + + cpuf = fopen(freq_file, "r"); + if (!cpuf) { + fprintf(stderr, "Could not open %s: %s\n", + freq_file, strerror(errno)); + exit(1); + } + + memset(freqs, 0, sizeof(freqs)); + size = fread(freqs, 1, sizeof(freqs), cpuf); + if (!size || (size == sizeof(freqs))) { + fprintf(stderr, "Wrong number of bytes(%d) read from %s\n", + size, freq_file); + exit(1); + } + fclose(cpuf); + rv = strtoull(freqs, &endp, 10); + + if (*endp == '\0' || *endp == '\n') + return rv; + fprintf(stderr, "Wrong formatted value ^%s^ read from %s\n", + freqs, freq_file); + exit(1); +} +#elif defined(__OpenBSD__) && (defined(__i386__) || defined(__x86_64__)) +static unsigned long arch_tick_frequency(void) +{ + int mib[2] = { CTL_HW, HW_CPUSPEED }; + static int value = 0; + size_t value_len = sizeof(value); + + /* Return 1 MHz when sysctl fails. */ + if ((value == 0) && (sysctl(mib, 2, &value, &value_len, NULL, 0) == -1)) + return 1; + + return value; +} +#else +static unsigned long arch_tick_frequency(void) +{ + /* 1 MHz = 1us. */ + return 1; +} +#endif + +static unsigned long tick_freq_mhz; + +static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz) +{ + tick_freq_mhz = table_tick_freq_mhz; + + /* Honor table frequency. */ + if (tick_freq_mhz) + return; + + tick_freq_mhz = arch_tick_frequency(); + + if (!tick_freq_mhz) { + fprintf(stderr, "Cannot determine timestamp tick frequency.\n"); + exit(1); + } +} + +u64 arch_convert_raw_ts_entry(u64 ts) +{ + return ts / tick_freq_mhz; +} + +/* + * Print an integer in 'normalized' form - with commas separating every three + * decimal orders. + */ +static void print_norm(u64 v) +{ + if (v >= 1000) { + /* print the higher order sections first */ + print_norm(v / 1000); + printf(",%3.3u", (u32)(v % 1000)); + } else { + printf("%u", (u32)(v % 1000)); + } +} + +enum additional_timestamp_id { + // Depthcharge entry IDs start at 1000. + TS_DC_START = 1000, + + TS_RO_PARAMS_INIT = 1001, + TS_RO_VB_INIT = 1002, + TS_RO_VB_SELECT_FIRMWARE = 1003, + TS_RO_VB_SELECT_AND_LOAD_KERNEL = 1004, + + TS_RW_VB_SELECT_AND_LOAD_KERNEL = 1010, + + TS_VB_SELECT_AND_LOAD_KERNEL = 1020, + + TS_CROSSYSTEM_DATA = 1100, + TS_START_KERNEL = 1101 +}; + +static const struct timestamp_id_to_name { + u32 id; + const char *name; +} timestamp_ids[] = { + /* Marker to report base_time. */ + { 0, "1st timestamp" }, + { TS_START_ROMSTAGE, "start of rom stage" }, + { TS_BEFORE_INITRAM, "before ram initialization" }, + { TS_AFTER_INITRAM, "after ram initialization" }, + { TS_END_ROMSTAGE, "end of romstage" }, + { TS_START_VBOOT, "start of verified boot" }, + { TS_END_VBOOT, "end of verified boot" }, + { TS_START_COPYRAM, "starting to load ramstage" }, + { TS_END_COPYRAM, "finished loading ramstage" }, + { TS_START_RAMSTAGE, "start of ramstage" }, + { TS_START_BOOTBLOCK, "start of bootblock" }, + { TS_END_BOOTBLOCK, "end of bootblock" }, + { TS_START_COPYROM, "starting to load romstage" }, + { TS_END_COPYROM, "finished loading romstage" }, + { TS_START_ULZMA, "starting LZMA decompress (ignore for x86)" }, + { TS_END_ULZMA, "finished LZMA decompress (ignore for x86)" }, + { TS_DEVICE_ENUMERATE, "device enumeration" }, + { TS_DEVICE_CONFIGURE, "device configuration" }, + { TS_DEVICE_ENABLE, "device enable" }, + { TS_DEVICE_INITIALIZE, "device initialization" }, + { TS_DEVICE_DONE, "device setup done" }, + { TS_CBMEM_POST, "cbmem post" }, + { TS_WRITE_TABLES, "write tables" }, + { TS_LOAD_PAYLOAD, "load payload" }, + { TS_ACPI_WAKE_JUMP, "ACPI wake jump" }, + { TS_SELFBOOT_JUMP, "selfboot jump" }, + + { TS_START_COPYVER, "starting to load verstage" }, + { TS_END_COPYVER, "finished loading verstage" }, + { TS_START_TPMINIT, "starting to initialize TPM" }, + { TS_END_TPMINIT, "finished TPM initialization" }, + { TS_START_VERIFY_SLOT, "starting to verify keyblock/preamble (RSA)" }, + { TS_END_VERIFY_SLOT, "finished verifying keyblock/preamble (RSA)" }, + { TS_START_HASH_BODY, "starting to verify body (load+SHA2+RSA) " }, + { TS_DONE_LOADING, "finished loading body (ignore for x86)" }, + { TS_DONE_HASHING, "finished calculating body hash (SHA2)" }, + { TS_END_HASH_BODY, "finished verifying body signature (RSA)" }, + + { TS_DC_START, "depthcharge start" }, + { TS_RO_PARAMS_INIT, "RO parameter init" }, + { TS_RO_VB_INIT, "RO vboot init" }, + { TS_RO_VB_SELECT_FIRMWARE, "RO vboot select firmware" }, + { TS_RO_VB_SELECT_AND_LOAD_KERNEL, "RO vboot select&load kernel" }, + { TS_RW_VB_SELECT_AND_LOAD_KERNEL, "RW vboot select&load kernel" }, + { TS_VB_SELECT_AND_LOAD_KERNEL, "vboot select&load kernel" }, + { TS_CROSSYSTEM_DATA, "crossystem data" }, + { TS_START_KERNEL, "start kernel" }, + + /* FSP related timestamps */ + { TS_FSP_MEMORY_INIT_START, "calling FspMemoryInit" }, + { TS_FSP_MEMORY_INIT_END, "returning from FspMemoryInit" }, + { TS_FSP_TEMP_RAM_EXIT_START, "calling FspTempRamExit" }, + { TS_FSP_TEMP_RAM_EXIT_END, "returning from FspTempRamExit" }, + { TS_FSP_SILICON_INIT_START, "calling FspSiliconInit" }, + { TS_FSP_SILICON_INIT_END, "returning from FspSiliconInit" }, + { TS_FSP_BEFORE_ENUMERATE, "calling FspNotify(AfterPciEnumeration)" }, + { TS_FSP_AFTER_ENUMERATE, + "returning from FspNotify(AfterPciEnumeration)" }, + { TS_FSP_BEFORE_FINALIZE, "calling FspNotify(ReadyToBoot)" }, + { TS_FSP_AFTER_FINALIZE, "returning from FspNotify(ReadyToBoot)" } +}; + +static const char *timestamp_name(uint32_t id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) { + if (timestamp_ids[i].id == id) + return timestamp_ids[i].name; + } + return ""; +} + +static uint64_t timestamp_print_parseable_entry(uint32_t id, uint64_t stamp, + uint64_t prev_stamp) +{ + const char *name; + uint64_t step_time; + + name = timestamp_name(id); + + step_time = arch_convert_raw_ts_entry(stamp - prev_stamp); + + /* IDabsolute timerelative timedescription */ + printf("%d\t", id); + printf("%llu\t", (long long)arch_convert_raw_ts_entry(stamp)); + printf("%llu\t", (long long)step_time); + printf("%s\n", name); + + return step_time; +} + +uint64_t timestamp_print_entry(uint32_t id, uint64_t stamp, uint64_t prev_stamp) +{ + const char *name; + uint64_t step_time; + + name = timestamp_name(id); + + printf("%4d:", id); + printf("%-50s", name); + print_norm(arch_convert_raw_ts_entry(stamp)); + step_time = arch_convert_raw_ts_entry(stamp - prev_stamp); + if (prev_stamp) { + printf(" ("); + print_norm(step_time); + printf(")"); + } + printf("\n"); + + return step_time; +} + +/* dump the timestamp table */ +static void dump_timestamps(int mach_readable) +{ + int i; + struct timestamp_table *tst_p; + size_t size; + uint64_t prev_stamp; + uint64_t total_time; + + if (timestamps.tag != LB_TAG_TIMESTAMPS) { + fprintf(stderr, "No timestamps found in coreboot table.\n"); + return; + } + + size = sizeof(*tst_p); + tst_p = map_memory_size((unsigned long)timestamps.cbmem_addr, size); + + timestamp_set_tick_freq(tst_p->tick_freq_mhz); + + if (!mach_readable) + printf("%d entries total:\n\n", tst_p->num_entries); + size += tst_p->num_entries * sizeof(tst_p->entries[0]); + + unmap_memory(); + tst_p = map_memory_size((unsigned long)timestamps.cbmem_addr, size); + + /* Report the base time within the table. */ + prev_stamp = 0; + if (mach_readable) + timestamp_print_parseable_entry(0, tst_p->base_time, + prev_stamp); + else + timestamp_print_entry(0, tst_p->base_time, prev_stamp); + prev_stamp = tst_p->base_time; + + total_time = 0; + for (i = 0; i < tst_p->num_entries; i++) { + uint64_t stamp; + const struct timestamp_entry *tse = &tst_p->entries[i]; + + /* Make all timestamps absolute. */ + stamp = tse->entry_stamp + tst_p->base_time; + if (mach_readable) + total_time += + timestamp_print_parseable_entry(tse->entry_id, + stamp, prev_stamp); + else + total_time += timestamp_print_entry(tse->entry_id, + stamp, prev_stamp); + prev_stamp = stamp; + } + + if (!mach_readable) { + printf("\nTotal Time: "); + print_norm(total_time); + printf("\n"); + } + + unmap_memory(); +} + +/* dump the cbmem console */ +static void dump_console(void) +{ + void *console_p; + char *console_c; + uint32_t size; + uint32_t cursor; + + if (console.tag != LB_TAG_CBMEM_CONSOLE) { + fprintf(stderr, "No console found in coreboot table.\n"); + return; + } + + console_p = map_memory_size((unsigned long)console.cbmem_addr, + 2 * sizeof(uint32_t)); + /* The in-memory format of the console area is: + * u32 size + * u32 cursor + * char console[size] + * Hence we have to add 8 to get to the actual console string. + */ + size = ((uint32_t *)console_p)[0]; + cursor = ((uint32_t *)console_p)[1]; + /* Cursor continues to go on even after no more data fits in + * the buffer but the data is dropped in this case. + */ + if (size > cursor) + size = cursor; + console_c = malloc(size + 1); + unmap_memory(); + if (!console_c) { + fprintf(stderr, "Not enough memory for console.\n"); + exit(1); + } + + console_p = map_memory_size((unsigned long)console.cbmem_addr, + size + sizeof(size) + sizeof(cursor)); + memcpy(console_c, console_p + 8, size); + console_c[size] = 0; + console_c[cursor] = 0; + + printf("%s\n", console_c); + if (size < cursor) + printf("%d %s lost\n", cursor - size, + (cursor - size) == 1 ? "byte":"bytes"); + + free(console_c); + + unmap_memory(); +} + +static void hexdump(unsigned long memory, int length) +{ + int i; + uint8_t *m; + int all_zero = 0; + + m = map_memory_size((intptr_t)memory, length); + + if (length > MAP_BYTES) { + printf("Truncating hex dump from %d to %d bytes\n\n", + length, MAP_BYTES); + length = MAP_BYTES; + } + + for (i = 0; i < length; i += 16) { + int j; + + all_zero++; + for (j = 0; j < 16; j++) { + if(m[i+j] != 0) { + all_zero = 0; + break; + } + } + + if (all_zero < 2) { + printf("%08lx:", memory + i); + for (j = 0; j < 16; j++) + printf(" %02x", m[i+j]); + printf(" "); + for (j = 0; j < 16; j++) + printf("%c", isprint(m[i+j]) ? m[i+j] : '.'); + printf("\n"); + } else if (all_zero == 2) { + printf("...\n"); + } + } + + unmap_memory(); +} + +static void dump_cbmem_hex(void) +{ + if (cbmem.type != LB_MEM_TABLE) { + fprintf(stderr, "No coreboot CBMEM area found!\n"); + return; + } + + hexdump(unpack_lb64(cbmem.start), unpack_lb64(cbmem.size)); +} + +/* The root region is at least DYN_CBMEM_ALIGN_SIZE . */ +#define DYN_CBMEM_ALIGN_SIZE (4096) +#define ROOT_MIN_SIZE DYN_CBMEM_ALIGN_SIZE +#define CBMEM_POINTER_MAGIC 0xc0389481 +#define CBMEM_ENTRY_MAGIC ~(CBMEM_POINTER_MAGIC) + +struct cbmem_root_pointer { + uint32_t magic; + /* Relative to upper limit/offset. */ + int32_t root_offset; +} __attribute__((packed)); + +struct dynamic_cbmem_entry { + uint32_t magic; + int32_t start_offset; + uint32_t size; + uint32_t id; +} __attribute__((packed)); + +struct cbmem_root { + uint32_t max_entries; + uint32_t num_entries; + uint32_t flags; + uint32_t entry_align; + int32_t max_offset; + struct dynamic_cbmem_entry entries[0]; +} __attribute__((packed)); + +#define CBMEM_MAGIC 0x434f5245 +#define MAX_CBMEM_ENTRIES 16 + +struct cbmem_entry { + uint32_t magic; + uint32_t id; + uint64_t base; + uint64_t size; +} __attribute__((packed)); + +struct cbmem_id_to_name { + uint32_t id; + const char *name; +}; +static const struct cbmem_id_to_name cbmem_ids[] = { CBMEM_ID_TO_NAME_TABLE }; + +void cbmem_print_entry(int n, uint32_t id, uint64_t base, uint64_t size) +{ + int i; + const char *name; + + name = NULL; + for (i = 0; i < ARRAY_SIZE(cbmem_ids); i++) { + if (cbmem_ids[i].id == id) { + name = cbmem_ids[i].name; + break; + } + } + + printf("%2d. ", n); + if (name == NULL) + printf("%08x ", id); + else + printf("%s", name); + printf(" %08" PRIx64 " ", base); + printf(" %08" PRIx64 "\n", size); +} + +static void dump_static_cbmem_toc(struct cbmem_entry *entries) +{ + int i; + + printf("CBMEM table of contents:\n"); + printf(" ID START LENGTH\n"); + + for (i=0; imax_entries, root->num_entries, root->flags, root->entry_align, root->max_offset); + + printf("CBMEM table of contents:\n"); + printf(" ID START LENGTH\n"); + + for (i = 0; i < root->num_entries; i++) { + if(root->entries[i].magic != CBMEM_ENTRY_MAGIC) + break; + cbmem_print_entry(i, root->entries[i].id, + rootptr + root->entries[i].start_offset, root->entries[i].size); + } +} + +static void dump_cbmem_toc(void) +{ + uint64_t start; + void *cbmem_area; + struct cbmem_entry *entries; + + if (cbmem.type != LB_MEM_TABLE) { + fprintf(stderr, "No coreboot CBMEM area found!\n"); + return; + } + + start = unpack_lb64(cbmem.start); + + cbmem_area = map_memory_size(start, unpack_lb64(cbmem.size)); + entries = (struct cbmem_entry *)cbmem_area; + + if (entries[0].magic == CBMEM_MAGIC) { + dump_static_cbmem_toc(entries); + } else { + rootptr = unpack_lb64(cbmem.start) + unpack_lb64(cbmem.size); + rootptr &= ~(DYN_CBMEM_ALIGN_SIZE - 1); + rootptr -= sizeof(struct cbmem_root_pointer); + unmap_memory(); + struct cbmem_root_pointer *r = + map_memory_size(rootptr, sizeof(*r)); + if (r->magic == CBMEM_POINTER_MAGIC) { + struct cbmem_root *root; + uint64_t rootaddr = rootptr + r->root_offset; + unmap_memory(); + root = map_memory_size(rootaddr, ROOT_MIN_SIZE); + dump_dynamic_cbmem_toc(root); + } else + fprintf(stderr, "No valid coreboot CBMEM root pointer found.\n"); + } + + unmap_memory(); +} + +#define COVERAGE_MAGIC 0x584d4153 +struct file { + uint32_t magic; + uint32_t next; + uint32_t filename; + uint32_t data; + int offset; + int len; +}; + +static int mkpath(char *path, mode_t mode) +{ + assert (path && *path); + char *p; + for (p = strchr(path+1, '/'); p; p = strchr(p + 1, '/')) { + *p = '\0'; + if (mkdir(path, mode) == -1) { + if (errno != EEXIST) { + *p = '/'; + return -1; + } + } + *p = '/'; + } + return 0; +} + +static void dump_coverage(void) +{ + int i, found = 0; + uint64_t start; + struct cbmem_entry *entries; + void *coverage; + unsigned long phys_offset; +#define phys_to_virt(x) ((void *)(unsigned long)(x) + phys_offset) + + if (cbmem.type != LB_MEM_TABLE) { + fprintf(stderr, "No coreboot table area found!\n"); + return; + } + + start = unpack_lb64(cbmem.start); + + entries = (struct cbmem_entry *)map_memory(start); + + for (i=0; imagic == COVERAGE_MAGIC) { + FILE *f; + char *filename; + + debug(" -> %s\n", (char *)phys_to_virt(file->filename)); + filename = strdup((char *)phys_to_virt(file->filename)); + if (mkpath(filename, 0755) == -1) { + perror("Directory for coverage data could " + "not be created"); + exit(1); + } + f = fopen(filename, "wb"); + if (!f) { + printf("Could not open %s: %s\n", + filename, strerror(errno)); + exit(1); + } + if (fwrite((void *)phys_to_virt(file->data), + file->len, 1, f) != 1) { + printf("Could not write to %s: %s\n", + filename, strerror(errno)); + exit(1); + } + fclose(f); + free(filename); + + if (file->next) + file = (struct file *)phys_to_virt(file->next); + else + file = NULL; + } + unmap_memory(); +} + +static void print_version(void) +{ + printf("cbmem v%s -- ", CBMEM_VERSION); + printf("Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.\n\n"); + printf( + "This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, version 2 of the License.\n\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see .\n\n"); +} + +static void print_usage(const char *name) +{ + printf("usage: %s [-cCltTxVvh?]\n", name); + printf("\n" + " -c | --console: print cbmem console\n" + " -C | --coverage: dump coverage information\n" + " -l | --list: print cbmem table of contents\n" + " -x | --hexdump: print hexdump of cbmem area\n" + " -t | --timestamps: print timestamp information\n" + " -T | --parseable-timestamps: print parseable timestamps\n" + " -V | --verbose: verbose (debugging) output\n" + " -v | --version: print the version\n" + " -h | --help: print this help\n" + "\n"); + exit(1); +} + +#ifdef __arm__ +static void dt_update_cells(const char *name, int *addr_cells_ptr, + int *size_cells_ptr) +{ + if (*addr_cells_ptr >= 0 && *size_cells_ptr >= 0) + return; + + int buffer; + size_t nlen = strlen(name); + char *prop = alloca(nlen + sizeof("/#address-cells")); + strcpy(prop, name); + + if (*addr_cells_ptr < 0) { + strcpy(prop + nlen, "/#address-cells"); + int fd = open(prop, O_RDONLY); + if (fd < 0 && errno != ENOENT) { + perror(prop); + } else if (fd >= 0) { + if (read(fd, &buffer, sizeof(int)) < 0) + perror(prop); + else + *addr_cells_ptr = ntohl(buffer); + close(fd); + } + } + + if (*size_cells_ptr < 0) { + strcpy(prop + nlen, "/#size-cells"); + int fd = open(prop, O_RDONLY); + if (fd < 0 && errno != ENOENT) { + perror(prop); + } else if (fd >= 0) { + if (read(fd, &buffer, sizeof(int)) < 0) + perror(prop); + else + *size_cells_ptr = ntohl(buffer); + close(fd); + } + } +} + +static char *dt_find_compat(const char *parent, const char *compat, + int *addr_cells_ptr, int *size_cells_ptr) +{ + char *ret = NULL; + struct dirent *entry; + DIR *dir; + + if (!(dir = opendir(parent))) { + perror(parent); + return NULL; + } + + /* Loop through all files in the directory (DT node). */ + while ((entry = readdir(dir))) { + /* We only care about compatible props or subnodes. */ + if (entry->d_name[0] == '.' || !((entry->d_type & DT_DIR) || + !strcmp(entry->d_name, "compatible"))) + continue; + + /* Assemble the file name (on the stack, for speed). */ + size_t plen = strlen(parent); + char *name = alloca(plen + strlen(entry->d_name) + 2); + + strcpy(name, parent); + name[plen] = '/'; + strcpy(name + plen + 1, entry->d_name); + + /* If it's a subnode, recurse. */ + if (entry->d_type & DT_DIR) { + ret = dt_find_compat(name, compat, addr_cells_ptr, + size_cells_ptr); + + /* There is only one matching node to find, abort. */ + if (ret) { + /* Gather cells values on the way up. */ + dt_update_cells(parent, addr_cells_ptr, + size_cells_ptr); + break; + } + continue; + } + + /* If it's a compatible string, see if it's the right one. */ + int fd = open(name, O_RDONLY); + int clen = strlen(compat); + char *buffer = alloca(clen + 1); + + if (fd < 0) { + perror(name); + continue; + } + + if (read(fd, buffer, clen + 1) < 0) { + perror(name); + close(fd); + continue; + } + close(fd); + + if (!strcmp(compat, buffer)) { + /* Initialize these to "unset" for the way up. */ + *addr_cells_ptr = *size_cells_ptr = -1; + + /* Can't leave string on the stack or we'll lose it! */ + ret = strdup(parent); + break; + } + } + + closedir(dir); + return ret; +} +#endif /* __arm__ */ + +int main(int argc, char** argv) +{ + int print_defaults = 1; + int print_console = 0; + int print_coverage = 0; + int print_list = 0; + int print_hexdump = 0; + int print_timestamps = 0; + int machine_readable_timestamps = 0; + + int opt, option_index = 0; + static struct option long_options[] = { + {"console", 0, 0, 'c'}, + {"coverage", 0, 0, 'C'}, + {"list", 0, 0, 'l'}, + {"timestamps", 0, 0, 't'}, + {"parseable-timestamps", 0, 0, 'T'}, + {"hexdump", 0, 0, 'x'}, + {"verbose", 0, 0, 'V'}, + {"version", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + while ((opt = getopt_long(argc, argv, "cCltTxVvh?", + long_options, &option_index)) != EOF) { + switch (opt) { + case 'c': + print_console = 1; + print_defaults = 0; + break; + case 'C': + print_coverage = 1; + print_defaults = 0; + break; + case 'l': + print_list = 1; + print_defaults = 0; + break; + case 'x': + print_hexdump = 1; + print_defaults = 0; + break; + case 't': + print_timestamps = 1; + print_defaults = 0; + break; + case 'T': + print_timestamps = 1; + machine_readable_timestamps = 1; + print_defaults = 0; + break; + case 'V': + verbose = 1; + break; + case 'v': + print_version(); + exit(0); + break; + case 'h': + case '?': + default: + print_usage(argv[0]); + exit(0); + break; + } + } + + mem_fd = open("/dev/mem", O_RDONLY, 0); + if (mem_fd < 0) { + fprintf(stderr, "Failed to gain memory access: %s\n", + strerror(errno)); + return 1; + } + +#ifdef __arm__ + int addr_cells, size_cells; + char *coreboot_node = dt_find_compat("/proc/device-tree", "coreboot", + &addr_cells, &size_cells); + + if (!coreboot_node) { + fprintf(stderr, "Could not find 'coreboot' compatible node!\n"); + return 1; + } + + if (addr_cells < 0) { + fprintf(stderr, "Warning: no #address-cells node in tree!\n"); + addr_cells = 1; + } + + int nlen = strlen(coreboot_node); + char *reg = alloca(nlen + sizeof("/reg")); + + strcpy(reg, coreboot_node); + strcpy(reg + nlen, "/reg"); + free(coreboot_node); + + int fd = open(reg, O_RDONLY); + if (fd < 0) { + perror(reg); + return 1; + } + + int i; + size_t size_to_read = addr_cells * 4 + size_cells * 4; + u8 *dtbuffer = alloca(size_to_read); + if (read(fd, dtbuffer, size_to_read) < 0) { + perror(reg); + return 1; + } + close(fd); + + /* No variable-length byte swap function anywhere in C... how sad. */ + u64 baseaddr = 0; + for (i = 0; i < addr_cells * 4; i++) { + baseaddr <<= 8; + baseaddr |= *dtbuffer; + dtbuffer++; + } + u64 cb_table_size = 0; + for (i = 0; i < size_cells * 4; i++) { + cb_table_size <<= 8; + cb_table_size |= *dtbuffer; + dtbuffer++; + } + + parse_cbtable(baseaddr, cb_table_size); +#else + int j; + static const int possible_base_addresses[] = { 0, 0xf0000 }; + + /* Find and parse coreboot table */ + for (j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) { + if (parse_cbtable(possible_base_addresses[j], MAP_BYTES)) + break; + } +#endif + + if (print_console) + dump_console(); + + if (print_coverage) + dump_coverage(); + + if (print_list) + dump_cbmem_toc(); + + if (print_hexdump) + dump_cbmem_hex(); + + if (print_defaults || print_timestamps) + dump_timestamps(machine_readable_timestamps); + + close(mem_fd); + return 0; +} diff --git a/board/coreboot/packages/cbmem/cbmem_id.h b/board/coreboot/packages/cbmem/cbmem_id.h new file mode 100644 index 0000000..6812c41 --- /dev/null +++ b/board/coreboot/packages/cbmem/cbmem_id.h @@ -0,0 +1,113 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 coresystems GmbH + * Copyright (C) 2013 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc. + */ + +#ifndef _CBMEM_ID_H_ +#define _CBMEM_ID_H_ + +#define CBMEM_ID_ACPI 0x41435049 +#define CBMEM_ID_ACPI_GNVS 0x474e5653 +#define CBMEM_ID_ACPI_GNVS_PTR 0x474e5650 +#define CBMEM_ID_AGESA_RUNTIME 0x41474553 +#define CBMEM_ID_AMDMCT_MEMINFO 0x494D454E +#define CBMEM_ID_CAR_GLOBALS 0xcac4e6a3 +#define CBMEM_ID_CBTABLE 0x43425442 +#define CBMEM_ID_CONSOLE 0x434f4e53 +#define CBMEM_ID_COVERAGE 0x47434f56 +#define CBMEM_ID_EHCI_DEBUG 0xe4c1deb9 +#define CBMEM_ID_ELOG 0x454c4f47 +#define CBMEM_ID_FREESPACE 0x46524545 +#define CBMEM_ID_FSP_RESERVED_MEMORY 0x46535052 +#define CBMEM_ID_FSP_RUNTIME 0x52505346 +#define CBMEM_ID_GDT 0x4c474454 +#define CBMEM_ID_HOB_POINTER 0x484f4221 +#define CBMEM_ID_IGD_OPREGION 0x4f444749 +#define CBMEM_ID_IMD_ROOT 0xff4017ff +#define CBMEM_ID_IMD_SMALL 0x53a11439 +#define CBMEM_ID_MEMINFO 0x494D454D +#define CBMEM_ID_MPTABLE 0x534d5054 +#define CBMEM_ID_MRCDATA 0x4d524344 +#define CBMEM_ID_MTC 0xcb31d31c +#define CBMEM_ID_NONE 0x00000000 +#define CBMEM_ID_PIRQ 0x49525154 +#define CBMEM_ID_POWER_STATE 0x50535454 +#define CBMEM_ID_RAM_OOPS 0x05430095 +#define CBMEM_ID_RAMSTAGE 0x9a357a9e +#define CBMEM_ID_RAMSTAGE_CACHE 0x9a3ca54e +#define CBMEM_ID_REFCODE 0x04efc0de +#define CBMEM_ID_REFCODE_CACHE 0x4efc0de5 +#define CBMEM_ID_RESUME 0x5245534d +#define CBMEM_ID_RESUME_SCRATCH 0x52455343 +#define CBMEM_ID_ROMSTAGE_INFO 0x47545352 +#define CBMEM_ID_ROMSTAGE_RAM_STACK 0x90357ac4 +#define CBMEM_ID_ROOT 0xff4007ff +#define CBMEM_ID_SMBIOS 0x534d4254 +#define CBMEM_ID_SMM_SAVE_SPACE 0x07e9acee +#define CBMEM_ID_SPINTABLE 0x59175917 +#define CBMEM_ID_STAGEx_META 0x57a9e000 +#define CBMEM_ID_STAGEx_CACHE 0x57a9e100 +#define CBMEM_ID_TCPA_LOG 0x54435041 +#define CBMEM_ID_TIMESTAMP 0x54494d45 +#define CBMEM_ID_VBOOT_HANDOFF 0x780074f0 +#define CBMEM_ID_VBOOT_WORKBUF 0x78007343 +#define CBMEM_ID_WIFI_CALIBRATION 0x57494649 + +#define CBMEM_ID_TO_NAME_TABLE \ + { CBMEM_ID_ACPI, "ACPI " }, \ + { CBMEM_ID_ACPI_GNVS, "ACPI GNVS " }, \ + { CBMEM_ID_ACPI_GNVS_PTR, "GNVS PTR " }, \ + { CBMEM_ID_AGESA_RUNTIME, "AGESA RSVD " }, \ + { CBMEM_ID_AMDMCT_MEMINFO, "AMDMEM INFO" }, \ + { CBMEM_ID_CAR_GLOBALS, "CAR GLOBALS" }, \ + { CBMEM_ID_CBTABLE, "COREBOOT " }, \ + { CBMEM_ID_CONSOLE, "CONSOLE " }, \ + { CBMEM_ID_COVERAGE, "COVERAGE " }, \ + { CBMEM_ID_EHCI_DEBUG, "USBDEBUG " }, \ + { CBMEM_ID_ELOG, "ELOG " }, \ + { CBMEM_ID_FREESPACE, "FREE SPACE " }, \ + { CBMEM_ID_FSP_RESERVED_MEMORY, "FSP MEMORY " }, \ + { CBMEM_ID_FSP_RUNTIME, "FSP RUNTIME" }, \ + { CBMEM_ID_GDT, "GDT " }, \ + { CBMEM_ID_IMD_ROOT, "IMD ROOT " }, \ + { CBMEM_ID_IMD_SMALL, "IMD SMALL " }, \ + { CBMEM_ID_MEMINFO, "MEM INFO " }, \ + { CBMEM_ID_MPTABLE, "SMP TABLE " }, \ + { CBMEM_ID_MRCDATA, "MRC DATA " }, \ + { CBMEM_ID_MTC, "MTC " }, \ + { CBMEM_ID_PIRQ, "IRQ TABLE " }, \ + { CBMEM_ID_POWER_STATE, "POWER STATE" }, \ + { CBMEM_ID_RAM_OOPS, "RAMOOPS " }, \ + { CBMEM_ID_RAMSTAGE_CACHE, "RAMSTAGE $ " }, \ + { CBMEM_ID_RAMSTAGE, "RAMSTAGE " }, \ + { CBMEM_ID_REFCODE_CACHE, "REFCODE $ " }, \ + { CBMEM_ID_REFCODE, "REFCODE " }, \ + { CBMEM_ID_RESUME, "ACPI RESUME" }, \ + { CBMEM_ID_RESUME_SCRATCH, "ACPISCRATCH" }, \ + { CBMEM_ID_ROMSTAGE_INFO, "ROMSTAGE " }, \ + { CBMEM_ID_ROMSTAGE_RAM_STACK, "ROMSTG STCK" }, \ + { CBMEM_ID_ROOT, "CBMEM ROOT " }, \ + { CBMEM_ID_SMBIOS, "SMBIOS " }, \ + { CBMEM_ID_SMM_SAVE_SPACE, "SMM BACKUP " }, \ + { CBMEM_ID_SPINTABLE, "SPIN TABLE " }, \ + { CBMEM_ID_TCPA_LOG, "TCPA LOG " }, \ + { CBMEM_ID_TIMESTAMP, "TIME STAMP " }, \ + { CBMEM_ID_VBOOT_HANDOFF, "VBOOT " }, \ + { CBMEM_ID_VBOOT_WORKBUF, "VBOOT WORK " }, \ + { CBMEM_ID_WIFI_CALIBRATION, "WIFI CLBR " }, +#endif /* _CBMEM_ID_H_ */ diff --git a/board/coreboot/packages/cbmem/coreboot_tables.h b/board/coreboot/packages/cbmem/coreboot_tables.h new file mode 100644 index 0000000..3dddde5 --- /dev/null +++ b/board/coreboot/packages/cbmem/coreboot_tables.h @@ -0,0 +1,414 @@ +#ifndef COREBOOT_TABLES_H +#define COREBOOT_TABLES_H + +#include + +/* The coreboot table information is for conveying information + * from the firmware to the loaded OS image. Primarily this + * is expected to be information that cannot be discovered by + * other means, such as querying the hardware directly. + * + * All of the information should be Position Independent Data. + * That is it should be safe to relocated any of the information + * without it's meaning/correctness changing. For table that + * can reasonably be used on multiple architectures the data + * size should be fixed. This should ease the transition between + * 32 bit and 64 bit architectures etc. + * + * The completeness test for the information in this table is: + * - Can all of the hardware be detected? + * - Are the per motherboard constants available? + * - Is there enough to allow a kernel to run that was written before + * a particular motherboard is constructed? (Assuming the kernel + * has drivers for all of the hardware but it does not have + * assumptions on how the hardware is connected together). + * + * With this test it should be straight forward to determine if a + * table entry is required or not. This should remove much of the + * long term compatibility burden as table entries which are + * irrelevant or have been replaced by better alternatives may be + * dropped. Of course it is polite and expedite to include extra + * table entries and be backwards compatible, but it is not required. + */ + +/* Since coreboot is usually compiled 32bit, gcc will align 64bit + * types to 32bit boundaries. If the coreboot table is dumped on a + * 64bit system, a uint64_t would be aligned to 64bit boundaries, + * breaking the table format. + * + * lb_uint64 will keep 64bit coreboot table values aligned to 32bit + * to ensure compatibility. They can be accessed with the two functions + * below: unpack_lb64() and pack_lb64() + * + * See also: util/lbtdump/lbtdump.c + */ + +struct lb_uint64 { + uint32_t lo; + uint32_t hi; +}; + +static inline uint64_t unpack_lb64(struct lb_uint64 value) +{ + uint64_t result; + result = value.hi; + result = (result << 32) + value.lo; + return result; +} + +static inline struct lb_uint64 pack_lb64(uint64_t value) +{ + struct lb_uint64 result; + result.lo = (value >> 0) & 0xffffffff; + result.hi = (value >> 32) & 0xffffffff; + return result; +} + +struct lb_header +{ + uint8_t signature[4]; /* LBIO */ + uint32_t header_bytes; + uint32_t header_checksum; + uint32_t table_bytes; + uint32_t table_checksum; + uint32_t table_entries; +}; + +/* Every entry in the boot environment list will correspond to a boot + * info record. Encoding both type and size. The type is obviously + * so you can tell what it is. The size allows you to skip that + * boot environment record if you don't know what it is. This allows + * forward compatibility with records not yet defined. + */ +struct lb_record { + uint32_t tag; /* tag ID */ + uint32_t size; /* size of record (in bytes) */ +}; + +#define LB_TAG_UNUSED 0x0000 + +#define LB_TAG_MEMORY 0x0001 + +struct lb_memory_range { + struct lb_uint64 start; + struct lb_uint64 size; + uint32_t type; +#define LB_MEM_RAM 1 /* Memory anyone can use */ +#define LB_MEM_RESERVED 2 /* Don't use this memory region */ +#define LB_MEM_ACPI 3 /* ACPI Tables */ +#define LB_MEM_NVS 4 /* ACPI NVS Memory */ +#define LB_MEM_UNUSABLE 5 /* Unusable address space */ +#define LB_MEM_VENDOR_RSVD 6 /* Vendor Reserved */ +#define LB_MEM_TABLE 16 /* Ram configuration tables are kept in */ +}; + +struct lb_memory { + uint32_t tag; + uint32_t size; + struct lb_memory_range map[0]; +}; + +#define LB_TAG_HWRPB 0x0002 +struct lb_hwrpb { + uint32_t tag; + uint32_t size; + uint64_t hwrpb; +}; + +#define LB_TAG_MAINBOARD 0x0003 +struct lb_mainboard { + uint32_t tag; + uint32_t size; + uint8_t vendor_idx; + uint8_t part_number_idx; + uint8_t strings[0]; +}; + +#define LB_TAG_VERSION 0x0004 +#define LB_TAG_EXTRA_VERSION 0x0005 +#define LB_TAG_BUILD 0x0006 +#define LB_TAG_COMPILE_TIME 0x0007 +#define LB_TAG_COMPILE_BY 0x0008 +#define LB_TAG_COMPILE_HOST 0x0009 +#define LB_TAG_COMPILE_DOMAIN 0x000a +#define LB_TAG_COMPILER 0x000b +#define LB_TAG_LINKER 0x000c +#define LB_TAG_ASSEMBLER 0x000d +struct lb_string { + uint32_t tag; + uint32_t size; + uint8_t string[0]; +}; + +#define LB_TAG_VERSION_TIMESTAMP 0x0026 +struct lb_timestamp { + uint32_t tag; + uint32_t size; + uint32_t timestamp; +}; + + +/* 0xe is taken by v3 */ + +#define LB_TAG_SERIAL 0x000f +struct lb_serial { + uint32_t tag; + uint32_t size; +#define LB_SERIAL_TYPE_IO_MAPPED 1 +#define LB_SERIAL_TYPE_MEMORY_MAPPED 2 + uint32_t type; + uint32_t baseaddr; + uint32_t baud; + uint32_t regwidth; +}; + +#define LB_TAG_CONSOLE 0x0010 +struct lb_console { + uint32_t tag; + uint32_t size; + uint16_t type; +}; + +#define LB_TAG_CONSOLE_SERIAL8250 0 +#define LB_TAG_CONSOLE_VGA 1 // OBSOLETE +#define LB_TAG_CONSOLE_BTEXT 2 // OBSOLETE +#define LB_TAG_CONSOLE_LOGBUF 3 // OBSOLETE +#define LB_TAG_CONSOLE_SROM 4 // OBSOLETE +#define LB_TAG_CONSOLE_EHCI 5 +#define LB_TAG_CONSOLE_SERIAL8250MEM 6 + +#define LB_TAG_FORWARD 0x0011 +struct lb_forward { + uint32_t tag; + uint32_t size; + uint64_t forward; +}; + +#define LB_TAG_FRAMEBUFFER 0x0012 +struct lb_framebuffer { + uint32_t tag; + uint32_t size; + + uint64_t physical_address; + uint32_t x_resolution; + uint32_t y_resolution; + uint32_t bytes_per_line; + uint8_t bits_per_pixel; + uint8_t red_mask_pos; + uint8_t red_mask_size; + uint8_t green_mask_pos; + uint8_t green_mask_size; + uint8_t blue_mask_pos; + uint8_t blue_mask_size; + uint8_t reserved_mask_pos; + uint8_t reserved_mask_size; +}; + +#define LB_TAG_GPIO 0x0013 + +struct lb_gpio { + uint32_t port; + uint32_t polarity; +#define ACTIVE_LOW 0 +#define ACTIVE_HIGH 1 + uint32_t value; +#define GPIO_MAX_NAME_LENGTH 16 + uint8_t name[GPIO_MAX_NAME_LENGTH]; +}; + +struct lb_gpios { + uint32_t tag; + uint32_t size; + + uint32_t count; + struct lb_gpio gpios[0]; +}; + +#define LB_TAG_VDAT 0x0015 +#define LB_TAG_VBNV 0x0019 +#define LB_TAB_VBOOT_HANDOFF 0x0020 +#define LB_TAB_DMA 0x0022 +#define LB_TAG_RAM_OOPS 0x0023 +#define LB_TAG_MTC 0x002b +struct lb_range { + uint32_t tag; + uint32_t size; + + uint64_t range_start; + uint32_t range_size; +}; + +void lb_ramoops(struct lb_header *header); + +#define LB_TAG_TIMESTAMPS 0x0016 +#define LB_TAG_CBMEM_CONSOLE 0x0017 +#define LB_TAG_MRC_CACHE 0x0018 +#define LB_TAG_ACPI_GNVS 0x0024 +#define LB_TAG_WIFI_CALIBRATION 0x0027 +struct lb_cbmem_ref { + uint32_t tag; + uint32_t size; + + uint64_t cbmem_addr; +}; + +#define LB_TAG_X86_ROM_MTRR 0x0021 +struct lb_x86_rom_mtrr { + uint32_t tag; + uint32_t size; + /* The variable range MTRR index covering the ROM. */ + uint32_t index; +}; + +#define LB_TAG_BOARD_ID 0x0025 +struct lb_board_id { + uint32_t tag; + uint32_t size; + /* Board ID as retrieved from the board revision GPIOs. */ + uint32_t board_id; +}; + +#define LB_TAG_MAC_ADDRS 0x0026 +struct mac_address { + uint8_t mac_addr[6]; + uint8_t pad[2]; /* Pad it to 8 bytes to keep it simple. */ +}; + +struct lb_macs { + uint32_t tag; + uint32_t size; + uint32_t count; + struct mac_address mac_addrs[0]; +}; + +#define LB_TAG_RAM_CODE 0x0028 +struct lb_ram_code { + uint32_t tag; + uint32_t size; + uint32_t ram_code; +}; + +#define LB_TAG_SPI_FLASH 0x0029 +struct lb_spi_flash { + uint32_t tag; + uint32_t size; + uint32_t flash_size; + uint32_t sector_size; + uint32_t erase_cmd; +}; + +#define LB_TAG_BOOT_MEDIA_PARAMS 0x0030 +struct lb_boot_media_params { + uint32_t tag; + uint32_t size; + /* offsets are relative to start of boot media */ + uint64_t fmap_offset; + uint64_t cbfs_offset; + uint64_t cbfs_size; + uint64_t boot_media_size; +}; + +#define LB_TAG_SERIALNO 0x002a +#define MAX_SERIALNO_LENGTH 32 + +/* The following structures are for the cmos definitions table */ +#define LB_TAG_CMOS_OPTION_TABLE 200 +/* cmos header record */ +struct cmos_option_table { + uint32_t tag; /* CMOS definitions table type */ + uint32_t size; /* size of the entire table */ + uint32_t header_length; /* length of header */ +}; + +/* cmos entry record + This record is variable length. The name field may be + shorter than CMOS_MAX_NAME_LENGTH. The entry may start + anywhere in the byte, but can not span bytes unless it + starts at the beginning of the byte and the length is + fills complete bytes. +*/ +#define LB_TAG_OPTION 201 +struct cmos_entries { + uint32_t tag; /* entry type */ + uint32_t size; /* length of this record */ + uint32_t bit; /* starting bit from start of image */ + uint32_t length; /* length of field in bits */ + uint32_t config; /* e=enumeration, h=hex, r=reserved */ + uint32_t config_id; /* a number linking to an enumeration record */ +#define CMOS_MAX_NAME_LENGTH 32 + uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name of entry in ascii, + variable length int aligned */ +}; + + +/* cmos enumerations record + This record is variable length. The text field may be + shorter than CMOS_MAX_TEXT_LENGTH. +*/ +#define LB_TAG_OPTION_ENUM 202 +struct cmos_enums { + uint32_t tag; /* enumeration type */ + uint32_t size; /* length of this record */ + uint32_t config_id; /* a number identifying the config id */ + uint32_t value; /* the value associated with the text */ +#define CMOS_MAX_TEXT_LENGTH 32 + uint8_t text[CMOS_MAX_TEXT_LENGTH]; /* enum description in ascii, + variable length int aligned */ +}; + +/* cmos defaults record + This record contains default settings for the cmos ram. +*/ +#define LB_TAG_OPTION_DEFAULTS 203 +struct cmos_defaults { + uint32_t tag; /* default type */ + uint32_t size; /* length of this record */ + uint32_t name_length; /* length of the following name field */ + uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name identifying the default */ +#define CMOS_IMAGE_BUFFER_SIZE 256 + uint8_t default_set[CMOS_IMAGE_BUFFER_SIZE]; /* default settings */ +}; + +#define LB_TAG_OPTION_CHECKSUM 204 +struct cmos_checksum { + uint32_t tag; + uint32_t size; + /* In practice everything is byte aligned, but things are measured + * in bits to be consistent. + */ + uint32_t range_start; /* First bit that is checksummed (byte aligned) */ + uint32_t range_end; /* Last bit that is checksummed (byte aligned) */ + uint32_t location; /* First bit of the checksum (byte aligned) */ + uint32_t type; /* Checksum algorithm that is used */ +#define CHECKSUM_NONE 0 +#define CHECKSUM_PCBIOS 1 +}; + +/* function prototypes for building the coreboot table */ + +unsigned long write_coreboot_table( + unsigned long low_table_start, unsigned long low_table_end, + unsigned long rom_table_start, unsigned long rom_table_end); + +void fill_lb_gpios(struct lb_gpios *gpios); +void fill_lb_gpio(struct lb_gpio *gpio, int num, + int polarity, const char *name, int value); + +void uart_fill_lb(void *data); +void lb_add_serial(struct lb_serial *serial, void *data); +void lb_add_console(uint16_t consoletype, void *data); + +/* Define this in mainboard.c to add board-specific table entries. */ +void lb_board(struct lb_header *header); + +/* + * Function to retrieve MAC address(es) from the VPD and store them in the + * coreboot table. + */ +void lb_table_add_macs_from_vpd(struct lb_header *header); + +void lb_table_add_serialno_from_vpd(struct lb_header *header); + +struct lb_record *lb_new_record(struct lb_header *header); + +#endif /* COREBOOT_TABLES_H */ diff --git a/board/coreboot/packages/cbmem/timestamp.h b/board/coreboot/packages/cbmem/timestamp.h new file mode 100644 index 0000000..be33b0a --- /dev/null +++ b/board/coreboot/packages/cbmem/timestamp.h @@ -0,0 +1,121 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc. + */ + +#ifndef __TIMESTAMP_H__ +#define __TIMESTAMP_H__ + +#include + +struct timestamp_entry { + uint32_t entry_id; + uint64_t entry_stamp; +} __attribute__((packed)); + +struct timestamp_table { + uint64_t base_time; + uint16_t max_entries; + uint16_t tick_freq_mhz; + uint32_t num_entries; + struct timestamp_entry entries[0]; /* Variable number of entries */ +} __attribute__((packed)); + +enum timestamp_id { + TS_START_ROMSTAGE = 1, + TS_BEFORE_INITRAM = 2, + TS_AFTER_INITRAM = 3, + TS_END_ROMSTAGE = 4, + TS_START_VBOOT = 5, + TS_END_VBOOT = 6, + TS_START_COPYRAM = 8, + TS_END_COPYRAM = 9, + TS_START_RAMSTAGE = 10, + TS_START_BOOTBLOCK = 11, + TS_END_BOOTBLOCK = 12, + TS_START_COPYROM = 13, + TS_END_COPYROM = 14, + TS_START_ULZMA = 15, + TS_END_ULZMA = 16, + TS_DEVICE_ENUMERATE = 30, + TS_DEVICE_CONFIGURE = 40, + TS_DEVICE_ENABLE = 50, + TS_DEVICE_INITIALIZE = 60, + TS_DEVICE_DONE = 70, + TS_CBMEM_POST = 75, + TS_WRITE_TABLES = 80, + TS_LOAD_PAYLOAD = 90, + TS_ACPI_WAKE_JUMP = 98, + TS_SELFBOOT_JUMP = 99, + + /* 500+ reserved for vendorcode extensions (500-600: google/chromeos) */ + TS_START_COPYVER = 501, + TS_END_COPYVER = 502, + TS_START_TPMINIT = 503, + TS_END_TPMINIT = 504, + TS_START_VERIFY_SLOT = 505, + TS_END_VERIFY_SLOT = 506, + TS_START_HASH_BODY = 507, + TS_DONE_LOADING = 508, + TS_DONE_HASHING = 509, + TS_END_HASH_BODY = 510, + + /* 950+ reserved for vendorcode extensions (950-999: intel/fsp) */ + TS_FSP_MEMORY_INIT_START = 950, + TS_FSP_MEMORY_INIT_END = 951, + TS_FSP_TEMP_RAM_EXIT_START = 952, + TS_FSP_TEMP_RAM_EXIT_END = 953, + TS_FSP_SILICON_INIT_START = 954, + TS_FSP_SILICON_INIT_END = 955, + TS_FSP_BEFORE_ENUMERATE = 956, + TS_FSP_AFTER_ENUMERATE = 957, + TS_FSP_BEFORE_FINALIZE = 958, + TS_FSP_AFTER_FINALIZE = 959, + + /* 1000+ reserved for payloads (1000-1200: ChromeOS depthcharge) */ +}; + +#if CONFIG_COLLECT_TIMESTAMPS && (CONFIG_EARLY_CBMEM_INIT || !defined(__PRE_RAM__)) +/* + * timestamp_init() needs to be called once for each of these cases: + * 1. __PRE_RAM__ (bootblock, romstage, verstage, etc) and + * 2. !__PRE_RAM__ (ramstage) + * The latter is taken care of by the generic coreboot infrastructure so + * it's up to the chipset/arch to call timestamp_init() in *one* of + * the __PRE_RAM__ stages. If multiple calls are made timestamps will be lost. + */ +void timestamp_init(uint64_t base); +/* + * Add a new timestamp. Depending on cbmem is available or not, this timestamp + * will be stored to cbmem / timestamp cache. + */ +void timestamp_add(enum timestamp_id id, uint64_t ts_time); +/* Calls timestamp_add with current timestamp. */ +void timestamp_add_now(enum timestamp_id id); +#else +#define timestamp_init(base) +#define timestamp_add(id, time) +#define timestamp_add_now(id) +#endif + +/* Implemented by the architecture code */ +uint64_t timestamp_get(void); +uint64_t get_initial_timestamp(void); +/* Returns timestamp tick frequency in MHz. */ +int timestamp_tick_freq_mhz(void); + +#endif diff --git a/configs/coreboot_defconfig b/configs/coreboot_defconfig index 35ad9d4..68e8e8c 100644 --- a/configs/coreboot_defconfig +++ b/configs/coreboot_defconfig @@ -58,6 +58,7 @@ BR2_TARGET_ROOTFS_CPIO_XZ=y # BR2_TARGET_ROOTFS_TAR is not set BR2_PACKAGE_CONNECTPROXY=y BR2_PACKAGE_CBFSTOOL=y +BR2_PACKAGE_CBMEM=y BR2_PACKAGE_IFDTOOL=y BR2_PACKAGE_NVRAMTOOL=y BR2_DEFCONFIG="$(BR2_EXTERNAL)/configs/coreboot_defconfig" diff --git a/package/Config.in b/package/Config.in index 6240976..af463cc 100644 --- a/package/Config.in +++ b/package/Config.in @@ -5,6 +5,7 @@ endmenu menu "Coreboot" source "$BR2_EXTERNAL/package/coreboot/cbfstool/Config.in" + source "$BR2_EXTERNAL/package/coreboot/cbmem/Config.in" source "$BR2_EXTERNAL/package/coreboot/ifdtool/Config.in" source "$BR2_EXTERNAL/package/coreboot/nvramtool/Config.in" endmenu diff --git a/package/coreboot/cbmem/Config.in b/package/coreboot/cbmem/Config.in new file mode 100644 index 0000000..084b883 --- /dev/null +++ b/package/coreboot/cbmem/Config.in @@ -0,0 +1,7 @@ +config BR2_PACKAGE_CBMEM + bool "cbmem" + depends on BR2_i386 || BR2_x86_64 + help + cbmem is a tool for reading coreboot log dumps in memory + + http://www.coreboot.org/ diff --git a/package/coreboot/cbmem/cbmem.mk b/package/coreboot/cbmem/cbmem.mk new file mode 100644 index 0000000..7ccb1df --- /dev/null +++ b/package/coreboot/cbmem/cbmem.mk @@ -0,0 +1,22 @@ +################################################################################ +# +# cbmem +# +################################################################################ + +CBMEM_VERSION = 1.1 +CBMEM_SITE = $(BR2_EXTERNAL)/board/coreboot/packages/cbmem +CBMEM_SITE_METHOD = local +CBMEM_LICENSE = GPLv2 +CBMEM_LICENSE_FILES = COPYING +CBMEM_CFLAGS = $(TARGET_CFLAGS) -I$(@D) + +define CBMEM_BUILD_CMDS + $(MAKE) $(TARGET_CONFIGURE_OPTS) CC="$(TARGET_CC)" -C "$(@D)" +endef + +define CBMEM_INSTALL_TARGET_CMDS + $(INSTALL) -m 0755 -D $(@D)/cbmem $(TARGET_DIR)/usr/sbin/cbmem +endef + +$(eval $(generic-package))