diff options
author | Aaron Durbin <adurbin@chromium.org> | 2013-02-08 17:28:04 -0600 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2013-03-21 22:28:28 +0100 |
commit | 8e4a355773cc64a89b3fc4d79981cfb02bda7e66 (patch) | |
tree | 01a9e6d5fc66568fd5c318e5edf17d47e73f65e8 /src | |
parent | 43e4a80a92bf00540e4d9b0734eb53f32044226f (diff) |
coreboot: introduce CONFIG_RELOCATABLE_RAMSTAGE
This patch adds an option to build the ramstage as a reloctable binary.
It uses the rmodule library for the relocation. The main changes
consist of the following:
1. The ramstage is loaded just under the cmbem space.
2. Payloads cannot be loaded over where ramstage is loaded. If a payload
is attempted to load where the relocatable ramstage resides the load
is aborted.
3. The memory occupied by the ramstage is reserved from the OS's usage
using the romstage_handoff structure stored in cbmem. This region is
communicated to ramstage by an CBMEM_ID_ROMSTAGE_INFO entry in cbmem.
4. There is no need to reserve cbmem space for the OS controlled memory for
the resume path because the ramsage region has been reserved in #3.
5. Since no memory needs to be preserved in the wake path, the loading
and begin of execution of a elf payload is straight forward.
Change-Id: Ia66cf1be65c29fa25ca7bd9ea6c8f11d7eee05f5
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/2792
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/Kconfig | 18 | ||||
-rw-r--r-- | src/arch/x86/Makefile.inc | 8 | ||||
-rw-r--r-- | src/arch/x86/boot/Makefile.inc | 1 | ||||
-rw-r--r-- | src/arch/x86/boot/acpi.c | 4 | ||||
-rw-r--r-- | src/arch/x86/boot/boot.c | 29 | ||||
-rw-r--r-- | src/arch/x86/boot/ramstage_module_header.c | 24 | ||||
-rw-r--r-- | src/arch/x86/boot/tables.c | 3 | ||||
-rw-r--r-- | src/include/cbmem.h | 5 | ||||
-rw-r--r-- | src/lib/Makefile.inc | 1 | ||||
-rw-r--r-- | src/lib/cbfs.c | 61 | ||||
-rw-r--r-- | src/lib/selfboot.c | 16 |
11 files changed, 170 insertions, 0 deletions
diff --git a/src/Kconfig b/src/Kconfig index f6a83e8a85..7a8985ce0f 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -307,7 +307,14 @@ config HAVE_INIT_TIMER config HIGH_SCRATCH_MEMORY_SIZE hex + default 0x5000 if RELOCATABLE_RAMSTAGE default 0x0 + help + The amount of extra memory to reserve from the OS. If + RELOCATABLE_RAMSTAGE is enabled a size of 20KiB is reserved. This is + for the use of a stack in romstage after memory has been initialized. + The stack size required in romstage can be large when needing to + decompress the ramstage. config USE_OPTION_TABLE bool @@ -374,6 +381,17 @@ config RELOCATABLE_MODULES building relocatable modules in the ram stage. Those modules can be loaded anywhere and all the relocations are handled automatically. +config RELOCATABLE_RAMSTAGE + depends on RELOCATABLE_MODULES + bool "Build the ramstage to be relocatable in 32-bit address space." + default n + help + The reloctable ramstage support allows for the ramstage to be built + as a relocatable module. The stage loader can identify a place + out of the OS way so that copying memory is unnecessary during an S3 + wake. When selecting this option the romstage is responsible for + determing a stack location to use for loading the ramstage. + config HAVE_ACPI_TABLES bool help diff --git a/src/arch/x86/Makefile.inc b/src/arch/x86/Makefile.inc index 433b434721..7cda5b6d8b 100644 --- a/src/arch/x86/Makefile.inc +++ b/src/arch/x86/Makefile.inc @@ -157,6 +157,12 @@ $(objcbfs)/%.elf: $(objcbfs)/%.debug ################################################################################ # Build the coreboot_ram (stage 2) +ifeq ($(CONFIG_RELOCATABLE_RAMSTAGE),y) + +$(eval $(call rmodule_link,$(objcbfs)/coreboot_ram.debug, $(obj)/arch/x86/boot/ramstage_module_header.ramstage.o $(objgenerated)/coreboot_ram.o, $(CONFIG_HEAP_SIZE))) + +else + $(objcbfs)/coreboot_ram.debug: $(objgenerated)/coreboot_ram.o $(src)/arch/x86/coreboot_ram.ld @printf " CC $(subst $(obj)/,,$(@))\n" ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) @@ -165,6 +171,8 @@ else $(CC) -nostdlib -nostartfiles -static -o $@ -L$(obj) -T $(src)/arch/x86/coreboot_ram.ld $< endif +endif + $(objgenerated)/coreboot_ram.o: $$(ramstage-objs) $(LIBGCC_FILE_NAME) @printf " CC $(subst $(obj)/,,$(@))\n" ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) diff --git a/src/arch/x86/boot/Makefile.inc b/src/arch/x86/boot/Makefile.inc index 3f11c01611..9c18043c07 100644 --- a/src/arch/x86/boot/Makefile.inc +++ b/src/arch/x86/boot/Makefile.inc @@ -9,6 +9,7 @@ ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c ramstage-$(CONFIG_GENERATE_SMBIOS_TABLES) += smbios.c ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpigen.c ramstage-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.S +ramstage-$(CONFIG_RELOCATABLE_RAMSTAGE) += ramstage_module_header.c $(obj)/arch/x86/boot/coreboot_table.ramstage.o : $(OPTION_TABLE_H) $(obj)/arch/x86/boot/smbios.ramstage.o: $(obj)/build.h diff --git a/src/arch/x86/boot/acpi.c b/src/arch/x86/boot/acpi.c index 327d175584..b04cbe52bf 100644 --- a/src/arch/x86/boot/acpi.c +++ b/src/arch/x86/boot/acpi.c @@ -759,6 +759,9 @@ extern unsigned int __wakeup_size; void acpi_jump_to_wakeup(void *vector) { +#if CONFIG_RELOCATABLE_RAMSTAGE + u32 acpi_backup_memory = 0; +#else u32 acpi_backup_memory = (u32)cbmem_find(CBMEM_ID_RESUME); if (!acpi_backup_memory) { @@ -766,6 +769,7 @@ void acpi_jump_to_wakeup(void *vector) "No S3 resume.\n"); return; } +#endif #if CONFIG_SMP // FIXME: This should go into the ACPI backup memory, too. No pork saussages. diff --git a/src/arch/x86/boot/boot.c b/src/arch/x86/boot/boot.c index d9cb02e776..4892c5e009 100644 --- a/src/arch/x86/boot/boot.c +++ b/src/arch/x86/boot/boot.c @@ -68,6 +68,34 @@ int elf_check_arch(Elf_ehdr *ehdr) } +#if CONFIG_RELOCATABLE_RAMSTAGE +/* When the ramstage is relocatable the elf loading ensures an elf image cannot + * be loaded over the ramstage code. */ +void jmp_to_elf_entry(void *entry, unsigned long unused1, unsigned long unused2) +{ + elf_boot_notes.hdr.b_checksum = + compute_ip_checksum(&elf_boot_notes, sizeof(elf_boot_notes)); + + /* Jump to kernel */ + __asm__ __volatile__( + " cld \n\t" + /* Now jump to the loaded image */ + " call *%0\n\t" + + /* The loaded image returned? */ + " cli \n\t" + " cld \n\t" + + :: + "r" (entry), +#if CONFIG_MULTIBOOT + "b"(mbi), "a" (MB_MAGIC2) +#else + "b"(&elf_boot_notes), "a" (0x0E1FB007) +#endif + ); +} +#else void jmp_to_elf_entry(void *entry, unsigned long buffer, unsigned long size) { extern unsigned char _ram_seg, _eram_seg; @@ -182,5 +210,6 @@ void jmp_to_elf_entry(void *entry, unsigned long buffer, unsigned long size) #endif ); } +#endif /* CONFIG_RELOCATABLE_RAMSTAGE */ diff --git a/src/arch/x86/boot/ramstage_module_header.c b/src/arch/x86/boot/ramstage_module_header.c new file mode 100644 index 0000000000..b958c16085 --- /dev/null +++ b/src/arch/x86/boot/ramstage_module_header.c @@ -0,0 +1,24 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 ChromeOS Authors + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <rmodule.h> + +extern char _start[]; + +DEFINE_RMODULE_HEADER(ramstage_module, _start, RMODULE_TYPE_STAGE); diff --git a/src/arch/x86/boot/tables.c b/src/arch/x86/boot/tables.c index 7f28861b56..294a10c46c 100644 --- a/src/arch/x86/boot/tables.c +++ b/src/arch/x86/boot/tables.c @@ -232,11 +232,14 @@ struct lb_memory *write_tables(void) post_code(0x9e); #if CONFIG_HAVE_ACPI_RESUME +/* Only add CBMEM_ID_RESUME when the ramstage isn't relocatable. */ +#if !CONFIG_RELOCATABLE_RAMSTAGE /* Let's prepare the ACPI S3 Resume area now already, so we can rely on * it begin there during reboot time. We don't need the pointer, nor * the result right now. If it fails, ACPI resume will be disabled. */ cbmem_add(CBMEM_ID_RESUME, HIGH_MEMORY_SAVE); +#endif #if CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY14 || CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_TN cbmem_add(CBMEM_ID_RESUME_SCRATCH, CONFIG_HIGH_SCRATCH_MEMORY_SIZE); #endif diff --git a/src/include/cbmem.h b/src/include/cbmem.h index 0aa9b305d7..d0f0c9a5cc 100644 --- a/src/include/cbmem.h +++ b/src/include/cbmem.h @@ -28,7 +28,12 @@ #endif #if CONFIG_HAVE_ACPI_RESUME +#if CONFIG_RELOCATABLE_RAMSTAGE +#define HIGH_MEMORY_SAVE 0 +#else #define HIGH_MEMORY_SAVE (CONFIG_RAMTOP - CONFIG_RAMBASE) +#endif + #define HIGH_MEMORY_SIZE (HIGH_MEMORY_SAVE + CONFIG_HIGH_SCRATCH_MEMORY_SIZE + HIGH_MEMORY_DEF_SIZE) /* Delegation of resume backup memory so we don't have to diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index d08ee7a4e0..97baaa2be0 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -110,6 +110,7 @@ $(obj)/lib/uart8250.smm.o : $(OPTION_TABLE_H) ifeq ($(CONFIG_RELOCATABLE_MODULES),y) ramstage-y += rmodule.c +romstage-$(CONFIG_RELOCATABLE_RAMSTAGE) += rmodule.c RMODULE_LDSCRIPT := $(src)/lib/rmodule.ld RMODULE_LDFLAGS := -nostartfiles -shared -z defs -nostdlib -Bsymbolic -T $(RMODULE_LDSCRIPT) diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index c56f550e17..4dac4bb5c2 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -36,6 +36,7 @@ #include <cbfs.h> #include <string.h> +#include <cbmem.h> #ifdef LIBPAYLOAD # include <stdio.h> @@ -114,6 +115,65 @@ void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor, return dest; } +#if CONFIG_RELOCATABLE_RAMSTAGE && defined(__PRE_RAM__) + +#include <rmodule.h> +#include <romstage_handoff.h> +/* When CONFIG_RELOCATABLE_RAMSTAGE is enabled and this file is being compiled + * for the romstage the rmodule loader is used. The ramstage is placed just + * below the cbemem location. */ + +void * cbfs_load_stage(struct cbfs_media *media, const char *name) +{ + struct cbfs_stage *stage; + struct rmodule ramstage; + void *cbmem_base; + void *ramstage_base; + void *decompression_loc; + void *ramstage_loc; + struct romstage_handoff *handoff; + + stage = (struct cbfs_stage *) + cbfs_get_file_content(media, name, CBFS_TYPE_STAGE); + + if (stage == NULL) + return (void *) -1; + + cbmem_base = get_cbmem_toc(); + if (cbmem_base == NULL) + return (void *) -1; + + ramstage_base = rmodule_find_region_below(cbmem_base, stage->memlen, + &ramstage_loc, + &decompression_loc); + + LOG("Decompressing stage %s @ 0x%p (%d bytes)\n", + name, decompression_loc, stage->memlen); + + if (cbfs_decompress(stage->compression, &stage[1], + decompression_loc, stage->len)) + return (void *) -1; + + if (rmodule_parse(decompression_loc, &ramstage)) + return (void *) -1; + + /* The ramstage is responsible for clearing its own bss. */ + if (rmodule_load(ramstage_loc, &ramstage)) + return (void *) -1; + + handoff = cbmem_add(CBMEM_ID_ROMSTAGE_INFO, sizeof(*handoff)); + if (handoff) { + handoff->reserve_base = (uint32_t)ramstage_base; + handoff->reserve_size = (uint32_t)cbmem_base - + (uint32_t)ramstage_base; + } else + LOG("Couldn't allocate romstage handoff.\n"); + + return rmodule_entry(&ramstage); +} + +#else + void * cbfs_load_stage(struct cbfs_media *media, const char *name) { struct cbfs_stage *stage = (struct cbfs_stage *) @@ -146,6 +206,7 @@ void * cbfs_load_stage(struct cbfs_media *media, const char *name) return (void *) entry; } +#endif /* CONFIG_RELOCATABLE_RAMSTAGE */ int cbfs_execute_stage(struct cbfs_media *media, const char *name) { diff --git a/src/lib/selfboot.c b/src/lib/selfboot.c index f32bb814fb..f933142144 100644 --- a/src/lib/selfboot.c +++ b/src/lib/selfboot.c @@ -78,6 +78,16 @@ struct segment { static unsigned long bounce_size, bounce_buffer; +#if CONFIG_RELOCATABLE_RAMSTAGE +static void get_bounce_buffer(struct lb_memory *mem, unsigned long req_size) +{ + /* When the ramstage is relocatable there is no need for a bounce + * buffer. All payloads should not overlap the ramstage. + */ + bounce_buffer = ~0UL; + bounce_size = 0; +} +#else static void get_bounce_buffer(struct lb_memory *mem, unsigned long req_size) { unsigned long lb_size; @@ -114,6 +124,7 @@ static void get_bounce_buffer(struct lb_memory *mem, unsigned long req_size) bounce_buffer = buffer; bounce_size = req_size; } +#endif /* CONFIG_RELOCATABLE_RAMSTAGE */ static int valid_area(struct lb_memory *mem, unsigned long buffer, unsigned long start, unsigned long len) @@ -394,8 +405,13 @@ static int load_self_segments( for(ptr = head->next; ptr != head; ptr = ptr->next) { if (!overlaps_coreboot(ptr)) continue; +#if CONFIG_RELOCATABLE_RAMSTAGE + /* payloads are required to not overlap ramstage. */ + return 0; +#else if (ptr->s_dstaddr + ptr->s_memsz > bounce_high) bounce_high = ptr->s_dstaddr + ptr->s_memsz; +#endif } get_bounce_buffer(mem, bounce_high - lb_start); if (!bounce_buffer) { |