diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/Makefile.inc | 12 | ||||
-rw-r--r-- | src/lib/cbmem.c | 36 | ||||
-rw-r--r-- | src/lib/cbmem_info.c | 69 | ||||
-rw-r--r-- | src/lib/dynamic_cbmem.c | 452 | ||||
-rw-r--r-- | src/lib/hardwaremain.c | 7 |
5 files changed, 548 insertions, 28 deletions
diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index 132e662d8c..1f2ad96735 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -42,7 +42,6 @@ romstage-y += cbfs.c romstage-y += lzma.c #romstage-y += lzmadecode.c romstage-$(CONFIG_CACHE_AS_RAM) += ramtest.c -romstage-$(CONFIG_HAVE_ACPI_RESUME) += cbmem.c romstage-$(CONFIG_CONSOLE_SERIAL8250) += uart8250.c romstage-$(CONFIG_CONSOLE_SERIAL8250MEM) += uart8250mem.c romstage-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c @@ -76,7 +75,6 @@ ramstage-y += lzma.c ramstage-y += stack.c ramstage-$(CONFIG_ARCH_X86) += gcc.c ramstage-y += clog2.c -ramstage-y += cbmem.c ramstage-$(CONFIG_CONSOLE_SERIAL8250) += uart8250.c ramstage-$(CONFIG_CONSOLE_SERIAL8250MEM) += uart8250mem.c ramstage-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c @@ -87,6 +85,16 @@ ramstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c ramstage-$(CONFIG_COVERAGE) += libgcov.c ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += edid.c +# The CBMEM implementations are chosen based on CONFIG_DYNAMIC_CBMEM. +ifeq ($(CONFIG_DYNAMIC_CBMEM),y) +ramstage-y += dynamic_cbmem.c +romstage-y += dynamic_cbmem.c +else +ramstage-y += cbmem.c +romstage-$(CONFIG_HAVE_ACPI_RESUME) += cbmem.c +endif # CONFIG_DYNAMIC_CBMEM +ramstage-y += cbmem_info.c + ramstage-$(CONFIG_CONSOLE_NE2K) += ne2k.c ifneq ($(CONFIG_HAVE_ARCH_MEMSET),y) diff --git a/src/lib/cbmem.c b/src/lib/cbmem.c index 5663cc85c4..ad98082861 100644 --- a/src/lib/cbmem.c +++ b/src/lib/cbmem.c @@ -232,6 +232,18 @@ int cbmem_initialize(void) #endif #ifndef __PRE_RAM__ +/* cbmem cannot be initialized before device drivers, but it can be initialized + * after the drivers have run. */ +void init_cbmem_pre_device(void) {} + +void init_cbmem_post_device(void) +{ + cbmem_initialize(); +#if CONFIG_CONSOLE_CBMEM + cbmemc_reinit(); +#endif +} + void cbmem_list(void) { struct cbmem_entry *cbmem_toc; @@ -245,28 +257,8 @@ void cbmem_list(void) if (cbmem_toc[i].magic != CBMEM_MAGIC) continue; - printk(BIOS_DEBUG, "%2d. ", i); - switch (cbmem_toc[i].id) { - case CBMEM_ID_FREESPACE: printk(BIOS_DEBUG, "FREE SPACE "); break; - case CBMEM_ID_GDT: printk(BIOS_DEBUG, "GDT "); break; - case CBMEM_ID_ACPI: printk(BIOS_DEBUG, "ACPI "); break; - case CBMEM_ID_CBTABLE: printk(BIOS_DEBUG, "COREBOOT "); break; - case CBMEM_ID_PIRQ: printk(BIOS_DEBUG, "IRQ TABLE "); break; - case CBMEM_ID_MPTABLE: printk(BIOS_DEBUG, "SMP TABLE "); break; - case CBMEM_ID_RESUME: printk(BIOS_DEBUG, "ACPI RESUME"); break; - case CBMEM_ID_RESUME_SCRATCH: printk(BIOS_DEBUG, "ACPISCRATCH"); break; - case CBMEM_ID_ACPI_GNVS: printk(BIOS_DEBUG, "ACPI GNVS "); break; - case CBMEM_ID_SMBIOS: printk(BIOS_DEBUG, "SMBIOS "); break; - case CBMEM_ID_TIMESTAMP: printk(BIOS_DEBUG, "TIME STAMP "); break; - case CBMEM_ID_MRCDATA: printk(BIOS_DEBUG, "MRC DATA "); break; - case CBMEM_ID_CONSOLE: printk(BIOS_DEBUG, "CONSOLE "); break; - case CBMEM_ID_ELOG: printk(BIOS_DEBUG, "ELOG "); break; - case CBMEM_ID_COVERAGE: printk(BIOS_DEBUG, "COVERAGE "); break; - case CBMEM_ID_ROMSTAGE_INFO: printk(BIOS_DEBUG, "ROMSTAGE "); break; - default: printk(BIOS_DEBUG, "%08x ", cbmem_toc[i].id); - } - printk(BIOS_DEBUG, "%08llx ", cbmem_toc[i].base); - printk(BIOS_DEBUG, "%08llx\n", cbmem_toc[i].size); + cbmem_print_entry(i, cbmem_toc[i].id, cbmem_toc[i].base, + cbmem_toc[i].size); } } #endif diff --git a/src/lib/cbmem_info.c b/src/lib/cbmem_info.c new file mode 100644 index 0000000000..aaf5840684 --- /dev/null +++ b/src/lib/cbmem_info.c @@ -0,0 +1,69 @@ +/* + * This file is part of the coreboot project. + * + * 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 wacbmem_entryanty 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <console/console.h> +#include <cbmem.h> +#include <stdlib.h> + +static struct cbmem_id_to_name { + u32 id; + const char *name; +} cbmem_ids[] = { + { CBMEM_ID_FREESPACE, "FREE SPACE " }, + { CBMEM_ID_GDT, "GDT " }, + { CBMEM_ID_ACPI, "ACPI " }, + { CBMEM_ID_CBTABLE, "COREBOOT " }, + { CBMEM_ID_PIRQ, "IRQ TABLE " }, + { CBMEM_ID_MPTABLE, "SMP TABLE " }, + { CBMEM_ID_RESUME, "ACPI RESUME" }, + { CBMEM_ID_RESUME_SCRATCH, "ACPISCRATCH" }, + { CBMEM_ID_ACPI_GNVS, "ACPI GNVS " }, + { CBMEM_ID_SMBIOS, "SMBIOS " }, + { CBMEM_ID_TIMESTAMP, "TIME STAMP " }, + { CBMEM_ID_MRCDATA, "MRC DATA " }, + { CBMEM_ID_CONSOLE, "CONSOLE " }, + { CBMEM_ID_ELOG, "ELOG " }, + { CBMEM_ID_COVERAGE, "COVERAGE " }, + { CBMEM_ID_ROMSTAGE_INFO, "ROMSTAGE " }, + { CBMEM_ID_ROMSTAGE_RAM_STACK, "ROMSTG STCK" }, + { CBMEM_ID_RAMSTAGE, "RAMSTAGE " }, + { CBMEM_ID_RAMSTAGE_CACHE, "RAMSTAGE $ " }, + { CBMEM_ID_ROOT, "CBMEM ROOT " }, +}; + +void cbmem_print_entry(int n, u32 id, u64 base, u64 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; + } + } + + if (name == NULL) + printk(BIOS_DEBUG, "%08x ", id); + else + printk(BIOS_DEBUG, "%s", name); + printk(BIOS_DEBUG, "%2d. ", n); + printk(BIOS_DEBUG, "%08llx ", base); + printk(BIOS_DEBUG, "%08llx\n", size); +} diff --git a/src/lib/dynamic_cbmem.c b/src/lib/dynamic_cbmem.c new file mode 100644 index 0000000000..ae6c87ac87 --- /dev/null +++ b/src/lib/dynamic_cbmem.c @@ -0,0 +1,452 @@ +/* + * This file is part of the coreboot project. + * + * 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 wacbmem_entryanty 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <boot/tables.h> +#include <console/console.h> +#include <cbmem.h> +#include <string.h> +#include <stdlib.h> +#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__) +#include <arch/acpi.h> +#endif + +#ifndef UINT_MAX +#define UINT_MAX 4294967295U +#endif + +/* ACPI resume needs to be cleared in the fail-to-recover case, but that + * condition is only handled during ramstage. */ +#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__) +static inline void cbmem_handle_acpi_resume(void) +{ + /* Something went wrong, our high memory area got wiped */ + if (acpi_slp_type == 3 || acpi_slp_type == 2) + acpi_slp_type = 0; +} +#else +static inline void cbmem_handle_acpi_resume(void) {} +#endif + +/* + * The dynamic cbmem code uses a root region. The root region boundary + * addresses are determined by cbmem_top() and ROOT_MIN_SIZE. Just below + * the address returned by cbmem_top() is a pointer that points to the + * root data structure. The root data structure provides the book keeping + * for each large entry. + */ + +/* The root region is at least DYN_CBMEM_ALIGN_SIZE . */ +#define ROOT_MIN_SIZE DYN_CBMEM_ALIGN_SIZE +#define CBMEM_POINTER_MAGIC 0xc0389479 +#define CBMEM_ENTRY_MAGIC ~(CBMEM_POINTER_MAGIC) + +/* The cbmem_root_pointer structure lives just below address returned + * from cbmem_top(). It points to the root data structure that + * maintains the entries. */ +struct cbmem_root_pointer { + u32 magic; + u32 root; +} __attribute__((packed)); + +struct cbmem_entry { + u32 magic; + u32 start; + u32 size; + u32 id; +} __attribute__((packed)); + +struct cbmem_root { + u32 max_entries; + u32 num_entries; + u32 locked; + u32 size; + struct cbmem_entry entries[0]; +} __attribute__((packed)); + + +static inline void *cbmem_top_cached(void) +{ +#if !defined(__PRE_RAM__) + static void *cached_cbmem_top; + + if (cached_cbmem_top == NULL) + cached_cbmem_top = cbmem_top(); + + return cached_cbmem_top; +#else + return cbmem_top(); +#endif +} + +static inline void *get_top_aligned(void) +{ + unsigned long top; + + /* Align down what is returned from cbmem_top(). */ + top = (unsigned long)cbmem_top_cached(); + top &= ~(DYN_CBMEM_ALIGN_SIZE - 1); + + return (void *)top; +} + +static inline void *get_root(void) +{ + unsigned long pointer_addr; + struct cbmem_root_pointer *pointer; + + pointer_addr = (unsigned long)get_top_aligned(); + pointer_addr -= sizeof(struct cbmem_root_pointer); + + pointer = (void *)pointer_addr; + if (pointer->magic != CBMEM_POINTER_MAGIC) + return NULL; + + return (void *)pointer->root; +} + +static inline void cbmem_entry_assign(struct cbmem_entry *entry, + u32 id, u32 start, u32 size) +{ + entry->magic = CBMEM_ENTRY_MAGIC; + entry->start = start; + entry->size = size; + entry->id = id; +} + +static inline const struct cbmem_entry * +cbmem_entry_append(struct cbmem_root *root, u32 id, u32 start, u32 size) +{ + struct cbmem_entry *cbmem_entry; + + cbmem_entry = &root->entries[root->num_entries]; + root->num_entries++; + + cbmem_entry_assign(cbmem_entry, id, start, size); + + return cbmem_entry; +} + +void cbmem_initialize_empty(void) +{ + unsigned long pointer_addr; + unsigned long root_addr; + unsigned long max_entries; + struct cbmem_root *root; + struct cbmem_root_pointer *pointer; + + /* Place the root pointer and the root. The number of entries is + * dictated by difference between the root address and the pointer + * where the root address is aligned down to + * DYN_CBMEM_ALIGN_SIZE. The pointer falls just below the + * address returned by get_top_aligned(). */ + pointer_addr = (unsigned long)get_top_aligned(); + root_addr = pointer_addr - ROOT_MIN_SIZE; + root_addr &= ~(DYN_CBMEM_ALIGN_SIZE - 1); + pointer_addr -= sizeof(struct cbmem_root_pointer); + + max_entries = (pointer_addr - (root_addr + sizeof(*root))) / + sizeof(struct cbmem_entry); + + pointer = (void *)pointer_addr; + pointer->magic = CBMEM_POINTER_MAGIC; + pointer->root = root_addr; + + root = (void *)root_addr; + root->max_entries = max_entries; + root->num_entries = 0; + root->locked = 0; + root->size = pointer_addr - root_addr + + sizeof(struct cbmem_root_pointer); + + /* Add an entry covering the root region. */ + cbmem_entry_append(root, CBMEM_ID_ROOT, root_addr, root->size); + + printk(BIOS_DEBUG, "CBMEM: root @ %p %d entries.\n", + root, root->max_entries); + + cbmem_arch_init(); +} + +static inline int cbmem_fail_recovery(void) +{ + cbmem_initialize_empty(); + cbmem_handle_acpi_resume(); + return 1; +} + +static int validate_entries(struct cbmem_root *root) +{ + unsigned int i; + u32 current_end; + + current_end = (u32)get_top_aligned(); + + printk(BIOS_DEBUG, "CBMEM: recovering %d/%d entries from root @ %p\n", + root->num_entries, root->max_entries, root); + + /* Check that all regions are properly aligned and are just below + * the previous entry */ + for (i = 0; i < root->num_entries; i++) { + struct cbmem_entry *entry = &root->entries[i]; + + if (entry->magic != CBMEM_ENTRY_MAGIC) + return -1; + + if (entry->start & (DYN_CBMEM_ALIGN_SIZE - 1)) + return -1; + + if (entry->start + entry->size != current_end) + return -1; + + current_end = entry->start; + } + + return 0; +} + +int cbmem_initialize(void) +{ + struct cbmem_root *root; + void *top_according_to_root; + + root = get_root(); + + /* No recovery possible since root couldn't be recovered. */ + if (root == NULL) + return cbmem_fail_recovery(); + + /* Sanity check the root. */ + top_according_to_root = (void *)(root->size + (unsigned long)root); + if (get_top_aligned() != top_according_to_root) + return cbmem_fail_recovery(); + + if (root->num_entries > root->max_entries) + return cbmem_fail_recovery(); + + if ((root->max_entries * sizeof(struct cbmem_entry)) > + (root->size - sizeof(struct cbmem_root_pointer) - sizeof(*root))) + return cbmem_fail_recovery(); + + /* Validate current entries. */ + if (validate_entries(root)) + return cbmem_fail_recovery(); + +#if defined(__PRE_RAM__) + /* Lock the root in the romstage on a recovery. The assumption is that + * recovery is called during romstage on the S3 resume path. */ + root->locked = 1; +#endif + + cbmem_arch_init(); + + /* Recovery successful. */ + return 0; +} + +static void *cbmem_base(void) +{ + struct cbmem_root *root; + u32 low_addr; + + root = get_root(); + + if (root == NULL) + return NULL; + + low_addr = (u32)root; + + /* Assume the lowest address is the last one added. */ + if (root->num_entries > 0) { + low_addr = root->entries[root->num_entries - 1].start; + } + + return (void *)low_addr; +} + + +const struct cbmem_entry *cbmem_entry_add(u32 id, u64 size64) +{ + struct cbmem_root *root; + const struct cbmem_entry *entry; + unsigned long base;; + u32 size; + u32 aligned_size; + + entry = cbmem_entry_find(id); + + if (entry != NULL) + return entry; + + /* Only handle sizes <= UINT_MAX internally. */ + if (size64 > (u64)UINT_MAX) + return NULL; + + size = size64; + + root = get_root(); + + if (root == NULL) + return NULL; + + /* Nothing can be added once it is locked down. */ + if (root->locked) + return NULL; + + if (root->max_entries == root->num_entries) + return NULL; + + aligned_size = ALIGN(size, DYN_CBMEM_ALIGN_SIZE); + base = (unsigned long)cbmem_base(); + base -= aligned_size; + + return cbmem_entry_append(root, id, base, aligned_size); +} + +void *cbmem_add(u32 id, u64 size) +{ + const struct cbmem_entry *entry; + + entry = cbmem_entry_add(id, size); + + if (entry == NULL) + return NULL; + + return cbmem_entry_start(entry); +} + +/* Retrieve a region provided a given id. */ +const struct cbmem_entry *cbmem_entry_find(u32 id) +{ + struct cbmem_root *root; + const struct cbmem_entry *entry; + unsigned int i; + + root = get_root(); + + if (root == NULL) + return NULL; + + entry = NULL; + + for (i = 0; i < root->num_entries; i++) { + if (root->entries[i].id == id) { + entry = &root->entries[i]; + break; + } + } + + return entry; +} + +void *cbmem_find(u32 id) +{ + const struct cbmem_entry *entry; + + entry = cbmem_entry_find(id); + + if (entry == NULL) + return NULL; + + return cbmem_entry_start(entry); +} + +/* Remove a reserved region. Returns 0 on success, < 0 on error. Note: A region + * cannot be removed unless it was the last one added. */ +int cbmem_entry_remove(const struct cbmem_entry *entry) +{ + unsigned long entry_num; + struct cbmem_root *root; + + root = get_root(); + + if (root == NULL) + return -1; + + if (root->num_entries == 0) + return -1; + + /* Nothing can be removed. */ + if (root->locked) + return -1; + + entry_num = entry - &root->entries[0]; + + /* If the entry is the last one in the root it can be removed. */ + if (entry_num == (root->num_entries - 1)) { + root->num_entries--; + return 0; + } + + return -1; +} + +u64 cbmem_entry_size(const struct cbmem_entry *entry) +{ + return entry->size; +} + +void *cbmem_entry_start(const struct cbmem_entry *entry) +{ + return (void *)entry->start; +} + + +#if !defined(__PRE_RAM__) +/* selected cbmem can be initialized early in ramstage. Additionally, that + * means cbmem console can be reinitialized early as well. The post_device + * function is empty since cbmem was initialized early in ramstage. */ +void init_cbmem_pre_device(void) +{ + cbmem_initialize(); +#if CONFIG_CONSOLE_CBMEM + cbmemc_reinit(); +#endif /* CONFIG_CONSOLE_CBMEM */ +} + +void init_cbmem_post_device(void) {} + +void cbmem_add_lb_mem(struct lb_memory *mem) +{ + unsigned long base; + unsigned long top; + + base = (unsigned long)cbmem_base(); + top = (unsigned long)get_top_aligned(); + lb_add_memory_range(mem, LB_MEM_TABLE, base, top - base); +} + +void cbmem_list(void) +{ + unsigned int i; + struct cbmem_root *root; + + root = get_root(); + + if (root == NULL) + return; + + for (i = 0; i < root->num_entries; i++) { + struct cbmem_entry *entry; + + entry = &root->entries[i]; + + cbmem_print_entry(i, entry->id, entry->start, entry->size); + } +} +#endif /* __PRE_RAM__ */ diff --git a/src/lib/hardwaremain.c b/src/lib/hardwaremain.c index b29cc93e79..bc18989ba0 100644 --- a/src/lib/hardwaremain.c +++ b/src/lib/hardwaremain.c @@ -94,6 +94,7 @@ void hardwaremain(int boot_complete) !cbmem_get_table_location(&high_tables_base, &high_tables_size)) cbmem_initialize(); #endif + init_cbmem_pre_device(); timestamp_stash(TS_DEVICE_ENUMERATE); @@ -121,10 +122,8 @@ void hardwaremain(int boot_complete) timestamp_stash(TS_DEVICE_DONE); - cbmem_initialize(); -#if CONFIG_CONSOLE_CBMEM - cbmemc_reinit(); -#endif + init_cbmem_post_device(); + timestamp_sync(); #if CONFIG_HAVE_ACPI_RESUME |