aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Kconfig8
-rw-r--r--src/include/rmodule.h126
-rw-r--r--src/lib/Makefile.inc18
-rw-r--r--src/lib/rmodule.c245
-rw-r--r--src/lib/rmodule.ld115
5 files changed, 512 insertions, 0 deletions
diff --git a/src/Kconfig b/src/Kconfig
index 3e249679b8..7206878b28 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -357,6 +357,14 @@ config GFXUMA
help
Enable Unified Memory Architecture for graphics.
+config RELOCATABLE_MODULES
+ bool "Relocatable Modules"
+ default n
+ help
+ If RELOCATABLE_MODULES is selected then support is enabled for
+ building relocatable modules in the ram stage. Those modules can be
+ loaded anywhere and all the relocations are handled automatically.
+
config HAVE_ACPI_TABLES
bool
help
diff --git a/src/include/rmodule.h b/src/include/rmodule.h
new file mode 100644
index 0000000000..b51700cc3a
--- /dev/null
+++ b/src/include/rmodule.h
@@ -0,0 +1,126 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 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
+ */
+#ifndef RMODULE_H
+#define RMODULE_H
+
+#include <stdint.h>
+
+#define RMODULE_MAGIC 0xf8fe
+#define RMODULE_VERSION_1 1
+
+enum {
+ RMODULE_TYPE_SMM,
+};
+
+struct rmodule;
+
+/* Public API for loading rmdoules. */
+int rmodule_parse(void *ptr, struct rmodule *m);
+void *rmodule_parameters(const struct rmodule *m);
+void *rmodule_entry(const struct rmodule *m);
+int rmodule_entry_offset(const struct rmodule *m);
+int rmodule_memory_size(const struct rmodule *m);
+int rmodule_load(void *loc, struct rmodule *m);
+int rmodule_load_alignment(const struct rmodule *m);
+
+#define FIELD_ENTRY(x_) ((u32)&x_)
+#define RMODULE_HEADER(entry_, type_) \
+{ \
+ .magic = RMODULE_MAGIC, \
+ .version = RMODULE_VERSION_1, \
+ .type = type_, \
+ .payload_begin_offset = FIELD_ENTRY(_payload_begin_offset), \
+ .payload_end_offset = FIELD_ENTRY(_payload_end_offset), \
+ .relocations_begin_offset = \
+ FIELD_ENTRY(_relocations_begin_offset), \
+ .relocations_end_offset = \
+ FIELD_ENTRY(_relocations_end_offset), \
+ .module_link_start_address = \
+ FIELD_ENTRY(_module_link_start_addr), \
+ .module_program_size = FIELD_ENTRY(_module_program_size), \
+ .module_entry_point = FIELD_ENTRY(entry_), \
+ .parameters_begin = FIELD_ENTRY(_module_params_begin), \
+ .parameters_end = FIELD_ENTRY(_module_params_end), \
+ .bss_begin = FIELD_ENTRY(_bss_begin), \
+ .bss_end = FIELD_ENTRY(_bss_end), \
+}
+
+#define DEFINE_RMODULE_HEADER(name_, entry_, type_) \
+ struct rmodule_header name_ \
+ __attribute__ ((section (".module_header"))) = \
+ RMODULE_HEADER(entry_, type_)
+
+
+/* Private data structures below should not be used directly. */
+
+/* All fields with '_offset' in the name are byte offsets into the flat blob.
+ * The linker and the linker script takes are of assigning the values. */
+struct rmodule_header {
+ u16 magic;
+ u8 version;
+ u8 type;
+ /* The payload represents the program's loadable code and data. */
+ u32 payload_begin_offset;
+ u32 payload_end_offset;
+ /* Begin and of relocation information about the program module. */
+ u32 relocations_begin_offset;
+ u32 relocations_end_offset;
+ /* The starting address of the linked program. This address is vital
+ * for determining relocation offsets as the reloction info and other
+ * symbols (bss, entry point) need this value as a basis to calculate
+ * the offsets.
+ */
+ u32 module_link_start_address;
+ /* The module_program_size is the size of memory used while running
+ * the program. The program is assumed to consume a contiguos amount
+ * of memory. */
+ u32 module_program_size;
+ /* This is program's execution entry point. */
+ u32 module_entry_point;
+ /* Optional paramter structure that can be used to pass data into
+ * the module. */
+ u32 parameters_begin;
+ u32 parameters_end;
+ /* BSS section information so the loader can clear the bss. */
+ u32 bss_begin;
+ u32 bss_end;
+} __attribute__ ((packed));
+
+struct rmodule {
+ void *location;
+ struct rmodule_header *header;
+ const void *payload;
+ int payload_size;
+ void *relocations;
+};
+
+/* These are the symbols assumed that every module contains. The linker script
+ * provides these symbols. */
+extern char _relocations_begin_offset[];
+extern char _relocations_end_offset[];
+extern char _payload_end_offset[];
+extern char _payload_begin_offset[];
+extern char _bss_begin[];
+extern char _bss_end[];
+extern char _module_program_size[];
+extern char _module_link_start_addr[];
+extern char _module_params_begin[];
+extern char _module_params_end[];
+
+#endif /* RMODULE_H */
diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc
index be57f29411..160fce6714 100644
--- a/src/lib/Makefile.inc
+++ b/src/lib/Makefile.inc
@@ -107,3 +107,21 @@ endif
$(obj)/lib/uart8250mem.smm.o : $(OPTION_TABLE_H)
$(obj)/lib/uart8250.smm.o : $(OPTION_TABLE_H)
+ifeq ($(CONFIG_RELOCATABLE_MODULES),y)
+ramstage-y += rmodule.c
+
+RMODULE_LDSCRIPT := $(src)/lib/rmodule.ld
+RMODULE_LDFLAGS := -nostartfiles -shared -z defs -nostdlib -Bsymbolic -T $(RMODULE_LDSCRIPT)
+
+# rmodule_link_rules is a function that should be called with:
+# (1) the object name to link
+# (2) the dependencies
+# (3) heap size of the relocatable module
+# It will create the necessary Make rules.
+define rmodule_link
+$(strip $(1)): $(strip $(2)) $$(RMODULE_LDSCRIPT) $$(obj)/ldoptions
+ $$(LD) $$(RMODULE_LDFLAGS) --defsym=__heap_size=$(strip $(3)) -o $$@ $(strip $(2))
+ $$(NM) -n $$@ > $$(basename $$@).map
+endef
+
+endif
diff --git a/src/lib/rmodule.c b/src/lib/rmodule.c
new file mode 100644
index 0000000000..56d7c6d646
--- /dev/null
+++ b/src/lib/rmodule.c
@@ -0,0 +1,245 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 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 <stdint.h>
+#include <string.h>
+#include <console/console.h>
+#include <rmodule.h>
+
+/* Change this define to get more verbose debugging for module loading. */
+#define PK_ADJ_LEVEL BIOS_NEVER
+
+#if CONFIG_ARCH_X86
+/*
+ * On X86, the only relocations currently allowed are R_386_RELATIVE which
+ * have '0' for the symbol info in the relocation metadata (in r_info).
+ * The reason is that the module is fully linked and just has the relocations'
+ * locations.
+ */
+typedef struct {
+ u32 r_offset;
+ u32 r_info;
+} Elf32_Rel;
+
+#define R_386_RELATIVE 8
+
+#define RELOCTION_ENTRY_SIZE sizeof(Elf32_Rel)
+static inline int rmodule_reloc_offset(const void *reloc)
+{
+ const Elf32_Rel *rel = reloc;
+ return rel->r_offset;
+}
+
+static inline int rmodule_reloc_valid(const void *reloc)
+{
+ const Elf32_Rel *rel = reloc;
+ return (rel->r_info == R_386_RELATIVE);
+}
+
+static inline void *remodule_next_reloc(const void *reloc)
+{
+ const Elf32_Rel *rel = reloc;
+ rel++;
+ return (void *)rel;
+}
+
+#else
+#error Arch needs to add relocation information support for RMODULE
+#endif
+
+static inline int rmodule_is_loaded(const struct rmodule *module)
+{
+ return module->location != NULL;
+}
+
+/* Calculate a loaded program address based on the blob address. */
+static inline void *rmodule_load_addr(const struct rmodule *module,
+ u32 blob_addr)
+{
+ char *loc = module->location;
+ return &loc[blob_addr - module->header->module_link_start_address];
+}
+
+/* Initialize a rmodule structure based on raw data. */
+int rmodule_parse(void *ptr, struct rmodule *module)
+{
+ char *base;
+ struct rmodule_header *rhdr;
+
+ base = ptr;
+ rhdr = ptr;
+
+ if (rhdr == NULL)
+ return -1;
+
+ /* Sanity check the raw data. */
+ if (rhdr->magic != RMODULE_MAGIC)
+ return -1;
+ if (rhdr->version != RMODULE_VERSION_1)
+ return -1;
+
+ /* Indicate the module hasn't been loaded yet. */
+ module->location = NULL;
+
+ /* The rmodule only needs a reference to the reloc_header. */
+ module->header = rhdr;
+
+ /* The payload lives after the header. */
+ module->payload = &base[rhdr->payload_begin_offset];
+ module->payload_size = rhdr->payload_end_offset -
+ rhdr->payload_begin_offset;
+ module->relocations = &base[rhdr->relocations_begin_offset];
+
+ return 0;
+}
+
+int rmodule_memory_size(const struct rmodule *module)
+{
+ return module->header->module_program_size;
+}
+
+void *rmodule_parameters(const struct rmodule *module)
+{
+ if (!rmodule_is_loaded(module))
+ return NULL;
+
+ /* Indicate if there are no parameters. */
+ if (module->header->parameters_begin == module->header->parameters_end)
+ return NULL;
+
+ return rmodule_load_addr(module, module->header->parameters_begin);
+}
+
+int rmodule_entry_offset(const struct rmodule *module)
+{
+ return module->header->module_entry_point -
+ module->header->module_link_start_address;
+}
+
+void *rmodule_entry(const struct rmodule *module)
+{
+ if (!rmodule_is_loaded(module))
+ return NULL;
+
+ return rmodule_load_addr(module, module->header->module_entry_point);
+}
+
+static void rmodule_clear_bss(struct rmodule *module)
+{
+ char *begin;
+ int size;
+
+ begin = rmodule_load_addr(module, module->header->bss_begin);
+ size = module->header->bss_end - module->header->bss_begin;
+ memset(begin, 0, size);
+}
+
+static inline int rmodule_number_relocations(const struct rmodule *module)
+{
+ int r;
+
+ r = module->header->relocations_end_offset;
+ r -= module->header->relocations_begin_offset;
+ r /= RELOCTION_ENTRY_SIZE;
+ return r;
+}
+
+static void rmodule_copy_payload(const struct rmodule *module)
+{
+ printk(BIOS_DEBUG, "Loading module at %p with entry %p. "
+ "filesize: 0x%x memsize: 0x%x\n",
+ module->location, rmodule_entry(module),
+ module->payload_size, rmodule_memory_size(module));
+ memcpy(module->location, module->payload, module->payload_size);
+}
+
+static inline u32 *rmodule_adjustment_location(const struct rmodule *module,
+ const void *reloc)
+{
+ int reloc_offset;
+
+ /* Don't relocate header field entries -- only program relocations. */
+ reloc_offset = rmodule_reloc_offset(reloc);
+ if (reloc_offset < module->header->module_link_start_address)
+ return NULL;
+
+ return rmodule_load_addr(module, reloc_offset);
+}
+
+static int rmodule_relocate(const struct rmodule *module)
+{
+ int num_relocations;
+ const void *reloc;
+ u32 adjustment;
+
+ /* Each relocation needs to be adjusted relative to the beginning of
+ * the loaded program. */
+ adjustment = (u32)rmodule_load_addr(module, 0);
+
+ reloc = module->relocations;
+ num_relocations = rmodule_number_relocations(module);
+
+ printk(BIOS_DEBUG, "Processing %d relocs with adjust value of 0x%08x\n",
+ num_relocations, adjustment);
+
+ while (num_relocations > 0) {
+ u32 *adjust_loc;
+
+ if (!rmodule_reloc_valid(reloc))
+ return -1;
+
+ /* If the adjustment location is non-NULL adjust it. */
+ adjust_loc = rmodule_adjustment_location(module, reloc);
+ if (adjust_loc != NULL) {
+ printk(PK_ADJ_LEVEL, "Adjusting %p: 0x%08x -> 0x%08x\n",
+ adjust_loc, *adjust_loc,
+ *adjust_loc + adjustment);
+ *adjust_loc += adjustment;
+ }
+
+ reloc = remodule_next_reloc(reloc);
+ num_relocations--;
+ }
+
+ return 0;
+}
+
+int rmodule_load_alignment(const struct rmodule *module)
+{
+ /* The load alignment is the start of the program's linked address.
+ * The base address where the program is loaded needs to be a multiple
+ * of the program's starting link address. That way all data alignment
+ * in the program is presered. */
+ return module->header->module_link_start_address;
+}
+
+int rmodule_load(void *base, struct rmodule *module)
+{
+ /*
+ * In order to load the module at a given address, the following steps
+ * take place:
+ * 1. Copy payload to base address.
+ * 2. Clear the bss segment.
+ * 3. Adjust relocations within the module to new base address.
+ */
+ module->location = base;
+ rmodule_copy_payload(module);
+ rmodule_clear_bss(module);
+ return rmodule_relocate(module);
+}
+
diff --git a/src/lib/rmodule.ld b/src/lib/rmodule.ld
new file mode 100644
index 0000000000..4c13c84666
--- /dev/null
+++ b/src/lib/rmodule.ld
@@ -0,0 +1,115 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+/*
+ * This linker script is used to link rmodules (relocatable modules). It
+ * links at zero so that relocation fixups are easy when placing the binaries
+ * anywhere in the address space.
+ *
+ * NOTE: The program's loadable sections (text, module_params, and data) are
+ * packed into the flat blob using the AT directive. The rmodule loader assumes
+ * the entire program resides in one contiguous address space. Therefore,
+ * alignment for a given section (if required) needs to be done at the end of
+ * the preceeding section. e.g. if the data section should be aligned to an 8
+ * byte address the text section should have ALIGN(8) at the end of its section.
+ * Otherwise there won't be a consistent mapping between the flat blob and the
+ * loaded program.
+ */
+
+BASE_ADDRESS = 0x00000;
+
+SECTIONS
+{
+ . = BASE_ADDRESS;
+
+ .header : AT (0) {
+ *(.module_header);
+ . = ALIGN(8);
+ }
+
+ /* Align the start of the module program to a large enough alignment
+ * so that any data in the program with an alignement property is met.
+ * Essentially, this alignment is the maximum possible data alignment
+ * property a program can have. */
+ . = ALIGN(4096);
+ _module_link_start_addr = .;
+ _payload_begin_offset = LOADADDR(.header) + SIZEOF(.header);
+
+ .text : AT (_payload_begin_offset) {
+ /* C code of the module. */
+ *(.text);
+ *(.text.*);
+ /* C read-only data. */
+ . = ALIGN(16);
+ *(.rodata);
+ *(.rodata.*);
+ . = ALIGN(4);
+ }
+
+ .module_params : AT (LOADADDR(.text) + SIZEOF(.text)) {
+ /* The parameters section can be used to pass parameters
+ * to a module, however there has to be an prior agreement
+ * on how to interpret the parameters. */
+ _module_params_begin = .;
+ *(.module_parameters);
+ _module_params_end = .;
+ . = ALIGN(4);
+ }
+
+ .data : AT (LOADADDR(.module_params) + SIZEOF(.module_params)) {
+ _sdata = .;
+ *(.data);
+ . = ALIGN(4);
+ _edata = .;
+ }
+
+ /* _payload_end marks the end of the module's code and data. */
+ _payload_end_offset = LOADADDR(.data) + SIZEOF(.data);
+
+ .bss (NOLOAD) : {
+ /* C uninitialized data of the SMM handler */
+ _bss_begin = .;
+ *(.bss);
+ *(.sbss);
+ *(COMMON);
+ . = ALIGN(8);
+ _bss_end = .;
+ }
+
+ .heap (NOLOAD) : {
+ /*
+ * Place the heap after BSS. The heap size is passed in by
+ * by way of ld --defsym=__heap_size=<>
+ */
+ _heap = .;
+ . = . + __heap_size;
+ _eheap = .;
+ }
+
+ /* _module_program_size is the total memory used by the program. */
+ _module_program_size = _eheap - _module_link_start_addr;
+
+ /* The relocation information is linked on top of the BSS section
+ * because the BSS section takes no space on disk. The relocation data
+ * resides directly after the data section in the flat binary. */
+ .relocations ADDR(.bss) : AT (_payload_end_offset) {
+ *(.rel.*);
+ }
+ _relocations_begin_offset = LOADADDR(.relocations);
+ _relocations_end_offset = _relocations_begin_offset +
+ SIZEOF(.relocations);
+
+ /DISCARD/ : {
+ /* Drop unnecessary sections. Since these modules are linked
+ * as shared objects there are dynamic sections. These sections
+ * aren't needed so drop them. */
+ *(.comment);
+ *(.note);
+ *(.note.*);
+ *(.dynamic);
+ *(.dynsym);
+ *(.dynstr);
+ *(.gnu.hash);
+ *(.eh_frame);
+ }
+}