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>
This commit is contained in:
Aaron Durbin 2013-02-08 17:28:04 -06:00 committed by Stefan Reinauer
parent 43e4a80a92
commit 8e4a355773
11 changed files with 170 additions and 0 deletions

View File

@ -307,7 +307,14 @@ config HAVE_INIT_TIMER
config HIGH_SCRATCH_MEMORY_SIZE config HIGH_SCRATCH_MEMORY_SIZE
hex hex
default 0x5000 if RELOCATABLE_RAMSTAGE
default 0x0 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 config USE_OPTION_TABLE
bool bool
@ -374,6 +381,17 @@ config RELOCATABLE_MODULES
building relocatable modules in the ram stage. Those modules can be building relocatable modules in the ram stage. Those modules can be
loaded anywhere and all the relocations are handled automatically. 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 config HAVE_ACPI_TABLES
bool bool
help help

View File

@ -157,6 +157,12 @@ $(objcbfs)/%.elf: $(objcbfs)/%.debug
################################################################################ ################################################################################
# Build the coreboot_ram (stage 2) # 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 $(objcbfs)/coreboot_ram.debug: $(objgenerated)/coreboot_ram.o $(src)/arch/x86/coreboot_ram.ld
@printf " CC $(subst $(obj)/,,$(@))\n" @printf " CC $(subst $(obj)/,,$(@))\n"
ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) 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 $< $(CC) -nostdlib -nostartfiles -static -o $@ -L$(obj) -T $(src)/arch/x86/coreboot_ram.ld $<
endif endif
endif
$(objgenerated)/coreboot_ram.o: $$(ramstage-objs) $(LIBGCC_FILE_NAME) $(objgenerated)/coreboot_ram.o: $$(ramstage-objs) $(LIBGCC_FILE_NAME)
@printf " CC $(subst $(obj)/,,$(@))\n" @printf " CC $(subst $(obj)/,,$(@))\n"
ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y)

View File

@ -9,6 +9,7 @@ ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c
ramstage-$(CONFIG_GENERATE_SMBIOS_TABLES) += smbios.c ramstage-$(CONFIG_GENERATE_SMBIOS_TABLES) += smbios.c
ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpigen.c ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpigen.c
ramstage-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.S 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/coreboot_table.ramstage.o : $(OPTION_TABLE_H)
$(obj)/arch/x86/boot/smbios.ramstage.o: $(obj)/build.h $(obj)/arch/x86/boot/smbios.ramstage.o: $(obj)/build.h

View File

@ -759,6 +759,9 @@ extern unsigned int __wakeup_size;
void acpi_jump_to_wakeup(void *vector) 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); u32 acpi_backup_memory = (u32)cbmem_find(CBMEM_ID_RESUME);
if (!acpi_backup_memory) { if (!acpi_backup_memory) {
@ -766,6 +769,7 @@ void acpi_jump_to_wakeup(void *vector)
"No S3 resume.\n"); "No S3 resume.\n");
return; return;
} }
#endif
#if CONFIG_SMP #if CONFIG_SMP
// FIXME: This should go into the ACPI backup memory, too. No pork saussages. // FIXME: This should go into the ACPI backup memory, too. No pork saussages.

View File

@ -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) void jmp_to_elf_entry(void *entry, unsigned long buffer, unsigned long size)
{ {
extern unsigned char _ram_seg, _eram_seg; 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
); );
} }
#endif /* CONFIG_RELOCATABLE_RAMSTAGE */

View File

@ -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);

View File

@ -232,11 +232,14 @@ struct lb_memory *write_tables(void)
post_code(0x9e); post_code(0x9e);
#if CONFIG_HAVE_ACPI_RESUME #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 /* 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 * 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. * the result right now. If it fails, ACPI resume will be disabled.
*/ */
cbmem_add(CBMEM_ID_RESUME, HIGH_MEMORY_SAVE); cbmem_add(CBMEM_ID_RESUME, HIGH_MEMORY_SAVE);
#endif
#if CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY14 || CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_TN #if CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY14 || CONFIG_NORTHBRIDGE_AMD_AGESA_FAMILY15_TN
cbmem_add(CBMEM_ID_RESUME_SCRATCH, CONFIG_HIGH_SCRATCH_MEMORY_SIZE); cbmem_add(CBMEM_ID_RESUME_SCRATCH, CONFIG_HIGH_SCRATCH_MEMORY_SIZE);
#endif #endif

View File

@ -28,7 +28,12 @@
#endif #endif
#if CONFIG_HAVE_ACPI_RESUME #if CONFIG_HAVE_ACPI_RESUME
#if CONFIG_RELOCATABLE_RAMSTAGE
#define HIGH_MEMORY_SAVE 0
#else
#define HIGH_MEMORY_SAVE (CONFIG_RAMTOP - CONFIG_RAMBASE) #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) #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 /* Delegation of resume backup memory so we don't have to

View File

@ -110,6 +110,7 @@ $(obj)/lib/uart8250.smm.o : $(OPTION_TABLE_H)
ifeq ($(CONFIG_RELOCATABLE_MODULES),y) ifeq ($(CONFIG_RELOCATABLE_MODULES),y)
ramstage-y += rmodule.c ramstage-y += rmodule.c
romstage-$(CONFIG_RELOCATABLE_RAMSTAGE) += rmodule.c
RMODULE_LDSCRIPT := $(src)/lib/rmodule.ld RMODULE_LDSCRIPT := $(src)/lib/rmodule.ld
RMODULE_LDFLAGS := -nostartfiles -shared -z defs -nostdlib -Bsymbolic -T $(RMODULE_LDSCRIPT) RMODULE_LDFLAGS := -nostartfiles -shared -z defs -nostdlib -Bsymbolic -T $(RMODULE_LDSCRIPT)

View File

@ -36,6 +36,7 @@
#include <cbfs.h> #include <cbfs.h>
#include <string.h> #include <string.h>
#include <cbmem.h>
#ifdef LIBPAYLOAD #ifdef LIBPAYLOAD
# include <stdio.h> # include <stdio.h>
@ -114,6 +115,65 @@ void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor,
return dest; 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) void * cbfs_load_stage(struct cbfs_media *media, const char *name)
{ {
struct cbfs_stage *stage = (struct cbfs_stage *) 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; return (void *) entry;
} }
#endif /* CONFIG_RELOCATABLE_RAMSTAGE */
int cbfs_execute_stage(struct cbfs_media *media, const char *name) int cbfs_execute_stage(struct cbfs_media *media, const char *name)
{ {

View File

@ -78,6 +78,16 @@ struct segment {
static unsigned long bounce_size, bounce_buffer; 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) static void get_bounce_buffer(struct lb_memory *mem, unsigned long req_size)
{ {
unsigned long lb_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_buffer = buffer;
bounce_size = req_size; bounce_size = req_size;
} }
#endif /* CONFIG_RELOCATABLE_RAMSTAGE */
static int valid_area(struct lb_memory *mem, unsigned long buffer, static int valid_area(struct lb_memory *mem, unsigned long buffer,
unsigned long start, unsigned long len) unsigned long start, unsigned long len)
@ -394,8 +405,13 @@ static int load_self_segments(
for(ptr = head->next; ptr != head; ptr = ptr->next) { for(ptr = head->next; ptr != head; ptr = ptr->next) {
if (!overlaps_coreboot(ptr)) if (!overlaps_coreboot(ptr))
continue; continue;
#if CONFIG_RELOCATABLE_RAMSTAGE
/* payloads are required to not overlap ramstage. */
return 0;
#else
if (ptr->s_dstaddr + ptr->s_memsz > bounce_high) if (ptr->s_dstaddr + ptr->s_memsz > bounce_high)
bounce_high = ptr->s_dstaddr + ptr->s_memsz; bounce_high = ptr->s_dstaddr + ptr->s_memsz;
#endif
} }
get_bounce_buffer(mem, bounce_high - lb_start); get_bounce_buffer(mem, bounce_high - lb_start);
if (!bounce_buffer) { if (!bounce_buffer) {