diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Kconfig | 8 | ||||
-rw-r--r-- | src/arch/armv7/boot/coreboot_table.c | 2 | ||||
-rw-r--r-- | src/arch/armv7/include/arch/coreboot_tables.h | 3 | ||||
-rw-r--r-- | src/arch/x86/boot/coreboot_table.c | 24 | ||||
-rw-r--r-- | src/arch/x86/boot/tables.c | 2 | ||||
-rw-r--r-- | src/arch/x86/include/arch/coreboot_tables.h | 3 | ||||
-rw-r--r-- | src/include/cbmem.h | 104 | ||||
-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 |
12 files changed, 677 insertions, 45 deletions
diff --git a/src/Kconfig b/src/Kconfig index 7a8985ce0f..0297970173 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -179,6 +179,14 @@ config EARLY_CBMEM_INIT some, for instance, execution timestamps. It needs support in romstage.c and should be enabled by the board's Kconfig. +config DYNAMIC_CBMEM + bool "The CBMEM space is dynamically grown." + default n + help + Instead of reserving a static amount of CBMEM space the CBMEM + area grows dynamically. CBMEM can be used both in romstage (after + memory initialization) and ramstage. + config COLLECT_TIMESTAMPS bool "Create a table of timestamps collected during boot" depends on EARLY_CBMEM_INIT diff --git a/src/arch/armv7/boot/coreboot_table.c b/src/arch/armv7/boot/coreboot_table.c index 47aad3601b..e5aa6ed4c5 100644 --- a/src/arch/armv7/boot/coreboot_table.c +++ b/src/arch/armv7/boot/coreboot_table.c @@ -484,7 +484,7 @@ static void lb_remove_memory_range(struct lb_memory *mem, } } -static void lb_add_memory_range(struct lb_memory *mem, +void lb_add_memory_range(struct lb_memory *mem, uint32_t type, uint64_t start, uint64_t size) { lb_remove_memory_range(mem, start, size); diff --git a/src/arch/armv7/include/arch/coreboot_tables.h b/src/arch/armv7/include/arch/coreboot_tables.h index 4c2a01312d..c5eacf8020 100644 --- a/src/arch/armv7/include/arch/coreboot_tables.h +++ b/src/arch/armv7/include/arch/coreboot_tables.h @@ -12,6 +12,9 @@ unsigned long write_coreboot_table( void lb_memory_range(struct lb_memory *mem, uint32_t type, uint64_t start, uint64_t size); +void lb_add_memory_range(struct lb_memory *mem, + uint32_t type, uint64_t start, uint64_t size); + void fill_lb_gpios(struct lb_gpios *gpios); /* Routines to extract part so the coreboot table or information diff --git a/src/arch/x86/boot/coreboot_table.c b/src/arch/x86/boot/coreboot_table.c index 463f723097..617fab29cc 100644 --- a/src/arch/x86/boot/coreboot_table.c +++ b/src/arch/x86/boot/coreboot_table.c @@ -355,6 +355,9 @@ static void lb_memory_range(struct lb_memory *mem, static void lb_reserve_table_memory(struct lb_header *head) { +/* Dynamic cbmem has already reserved the memory where the coreboot tables + * reside. Therefore, there is nothing to fix up. */ +#if !CONFIG_DYNAMIC_CBMEM struct lb_record *last_rec; struct lb_memory *mem; uint64_t start; @@ -383,6 +386,7 @@ static void lb_reserve_table_memory(struct lb_header *head) mem->map[i].size = pack_lb64(map_end - end); } } +#endif } static unsigned long lb_table_fini(struct lb_header *head, int fixup) @@ -507,7 +511,7 @@ static void lb_remove_memory_range(struct lb_memory *mem, } } -static void lb_add_memory_range(struct lb_memory *mem, +void lb_add_memory_range(struct lb_memory *mem, uint32_t type, uint64_t start, uint64_t size) { lb_remove_memory_range(mem, start, size); @@ -664,14 +668,20 @@ unsigned long write_coreboot_table( lb_add_memory_range(mem, LB_MEM_TABLE, low_table_start, low_table_end - low_table_start); - /* Record the pirq table, acpi tables, and maybe the mptable */ - lb_add_memory_range(mem, LB_MEM_TABLE, - rom_table_start, rom_table_end-rom_table_start); - - printk(BIOS_DEBUG, "Adding high table area\n"); - // should this be LB_MEM_ACPI? + /* Record the pirq table, acpi tables, and maybe the mptable. However, + * these only need to be added when the rom_table is sitting below + * 1MiB. If it isn't that means high tables are being written. + * The code below handles high tables correctly. */ + if (rom_table_end <= (1 << 20)) + lb_add_memory_range(mem, LB_MEM_TABLE, + rom_table_start, rom_table_end-rom_table_start); + +#if CONFIG_DYNAMIC_CBMEM + cbmem_add_lb_mem(mem); +#else /* CONFIG_DYNAMIC_CBMEM */ lb_add_memory_range(mem, LB_MEM_TABLE, high_tables_base, high_tables_size); +#endif /* CONFIG_DYNAMIC_CBMEM */ /* Add reserved regions */ add_lb_reserved(mem); diff --git a/src/arch/x86/boot/tables.c b/src/arch/x86/boot/tables.c index 294a10c46c..d842e73f6e 100644 --- a/src/arch/x86/boot/tables.c +++ b/src/arch/x86/boot/tables.c @@ -53,6 +53,7 @@ struct lb_memory *write_tables(void) */ unsigned long high_table_pointer; +#if !CONFIG_DYNAMIC_CBMEM if (!high_tables_base) { printk(BIOS_ERR, "ERROR: High Tables Base is not set.\n"); // Are there any boards without? @@ -60,6 +61,7 @@ struct lb_memory *write_tables(void) } printk(BIOS_DEBUG, "High Tables Base is %llx.\n", high_tables_base); +#endif rom_table_start = 0xf0000; rom_table_end = 0xf0000; diff --git a/src/arch/x86/include/arch/coreboot_tables.h b/src/arch/x86/include/arch/coreboot_tables.h index e9790db17a..a8deeeddb3 100644 --- a/src/arch/x86/include/arch/coreboot_tables.h +++ b/src/arch/x86/include/arch/coreboot_tables.h @@ -8,6 +8,9 @@ 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 lb_add_memory_range(struct lb_memory *mem, + uint32_t type, uint64_t start, uint64_t size); + /* Routines to extract part so the coreboot table or information * from the coreboot table. */ diff --git a/src/include/cbmem.h b/src/include/cbmem.h index 1212cb2655..41f5971c80 100644 --- a/src/include/cbmem.h +++ b/src/include/cbmem.h @@ -2,6 +2,7 @@ * 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 @@ -62,9 +63,72 @@ #define CBMEM_ID_ELOG 0x454c4f47 #define CBMEM_ID_COVERAGE 0x47434f56 #define CBMEM_ID_ROMSTAGE_INFO 0x47545352 +#define CBMEM_ID_ROMSTAGE_RAM_STACK 0x90357ac4 +#define CBMEM_ID_RAMSTAGE 0x9a357a9e +#define CBMEM_ID_RAMSTAGE_CACHE 0x9a3ca54e +#define CBMEM_ID_ROOT 0xff4007ff #define CBMEM_ID_NONE 0x00000000 #ifndef __ASSEMBLER__ +#include <stdint.h> + +struct cbmem_entry; + +#if CONFIG_DYNAMIC_CBMEM + +/* + * The dynamic cbmem infrastructure allows for growing cbmem dynamically as + * things are added. It requires an external function, cbmem_top(), to be + * implemented by the board or chipset to define the upper address where + * cbmem lives. This address is required to be a 32-bit address. Additionally, + * the address needs to be consistent in both romstage and ramstage. The + * dynamic cbmem infrasturue allocates new regions below the last allocated + * region. Regions are defined by a cbmem_entry struct that is opaque. Regions + * may be removed, but the last one added is the only that can be removed. + * + * Dynamic cbmem has two allocators within it. All allocators use a top down + * allocation scheme. However, there are 2 modes for each allocation depending + * on the requested size. There are large allocations and small allocations. + * An allocation is considered to be small when it is less than or equal to + * DYN_CBMEM_ALIGN_SIZE / 2. The smaller allocations are fit into a larger + * allocation region. + */ + +#define DYN_CBMEM_ALIGN_SIZE (4096) + +/* Initialze cbmem to be empty. */ +void cbmem_initialize_empty(void); + +/* Return the top address for dynamic cbmem. The address returned needs to + * be consistent across romstage and ramstage, and it is required to be + * below 4GiB. */ +void *cbmem_top(void); + +/* Add a cbmem entry of a given size and id. These return NULL on failure. The + * add function performs a find first and do not check against the original + * size. */ +const struct cbmem_entry *cbmem_entry_add(u32 id, u64 size); + +/* Find a cbmem entry of a given id. These return NULL on failure. */ +const struct cbmem_entry *cbmem_entry_find(u32 id); + +/* Remove a region defined by a cbmem_entry. Returns 0 on success, < 0 on + * error. Note: A cbmem_entry cannot be removed unless it was the last one + * added. */ +int cbmem_entry_remove(const struct cbmem_entry *entry); + +/* cbmem_entry accessors to get pointer and size of a cbmem_entry. */ +void *cbmem_entry_start(const struct cbmem_entry *entry); +u64 cbmem_entry_size(const struct cbmem_entry *entry); + +#ifndef __PRE_RAM__ +/* Add the cbmem memory used to the memory tables. */ +struct lb_memory; +void cbmem_add_lb_mem(struct lb_memory *mem); +#endif /* __PRE_RAM__ */ + +#else /* !CONFIG_DYNAMIC_CBMEM */ + #ifndef __PRE_RAM__ extern uint64_t high_tables_base, high_tables_size; #if CONFIG_EARLY_CBMEM_INIT @@ -72,22 +136,44 @@ extern uint64_t high_tables_base, high_tables_size; int __attribute__((weak)) cbmem_get_table_location(uint64_t *tables_base, uint64_t *tables_size); #endif +void set_cbmem_toc(struct cbmem_entry *); #endif -int cbmem_initialize(void); - void cbmem_init(u64 baseaddr, u64 size); int cbmem_reinit(u64 baseaddr); + +extern struct cbmem_entry *get_cbmem_toc(void); + +#endif /* CONFIG_DYNAMIC_CBMEM */ + +/* Common API between cbmem and dynamic cbmem. */ + +/* By default cbmem is attempted to be recovered. Returns 0 if cbmem was + * recovered or 1 if cbmem had to be reinitialized. */ +int cbmem_initialize(void); +/* Add a cbmem entry of a given size and id. These return NULL on failure. The + * add function performs a find first and do not check against the original + * size. */ void *cbmem_add(u32 id, u64 size); +/* Find a cbmem entry of a given id. These return NULL on failure. */ void *cbmem_find(u32 id); + +#ifndef __PRE_RAM__ +/* Ramstage only functions. */ void cbmem_list(void); void cbmem_arch_init(void); +void __attribute__((weak)) cbmem_post_handling(void); +void cbmem_print_entry(int n, u32 id, u64 start, u64 size); +/* The pre|post device cbmem initialization functions are for the + * ramstage main to call. When cbmem is actually initialized depends on + * the cbmem implementation. */ +void init_cbmem_pre_device(void); +void init_cbmem_post_device(void); +#else +static inline void cbmem_arch_init(void) {} +#endif /* __PRE_RAM__ */ -extern struct cbmem_entry *get_cbmem_toc(void); +#endif /* __ASSEMBLER__ */ -#ifndef __PRE_RAM__ -void set_cbmem_toc(struct cbmem_entry *); -void __attribute__((weak)) cbmem_post_handling(void); -#endif -#endif -#endif + +#endif /* _CBMEM_H_ */ 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 |