diff --git a/.gitignore b/.gitignore index 70da2c81ef..417e06fcf4 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ tarballs/ util/*/.dependencies util/*/.test util/cbfstool/cbfstool +util/cbfstool/rmodtool util/cbmem/.dependencies util/cbmem/cbmem util/dumpmmcr/dumpmmcr diff --git a/Makefile.inc b/Makefile.inc index 49a39d7b5e..a37456d778 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -255,10 +255,14 @@ build-dirs: ####################################################################### # Build the tools CBFSTOOL:=$(obj)/cbfstool +RMODTOOL:=$(obj)/rmodtool $(CBFSTOOL): $(objutil)/cbfstool/cbfstool cp $< $@ +$(RMODTOOL): $(objutil)/cbfstool/rmodtool + cp $< $@ + _WINCHECK=$(shell uname -o 2> /dev/null) STACK= ifeq ($(_WINCHECK),Msys) @@ -376,5 +380,5 @@ crosstools: clean-for-update crossgcc-clean: clean-for-update $(MAKE) -C util/crossgcc clean -tools: $(objutil)/kconfig/conf $(objutil)/cbfstool/cbfstool $(objutil)/nvramtool/nvramtool $(objutil)/romcc/romcc $(objutil)/sconfig/sconfig +tools: $(objutil)/kconfig/conf $(objutil)/cbfstool/cbfstool $(objutil)/cbfstool/rmodtool $(objutil)/nvramtool/nvramtool $(objutil)/romcc/romcc $(objutil)/sconfig/sconfig diff --git a/src/arch/x86/Makefile.inc b/src/arch/x86/Makefile.inc index c121d98b83..96a4e6ff86 100644 --- a/src/arch/x86/Makefile.inc +++ b/src/arch/x86/Makefile.inc @@ -74,7 +74,13 @@ $(obj)/coreboot.pre1: $(CBFSTOOL) mv $(obj)/coreboot.rom $@ endif -$(obj)/coreboot.rom: $(obj)/coreboot.pre $(objcbfs)/coreboot_ram.elf $(CBFSTOOL) $(call strip_quotes,$(COREBOOT_ROM_DEPENDENCIES)) $$(INTERMEDIATE) $$(VBOOT_STUB_ELF) +ifeq ($(CONFIG_HAVE_REFCODE_BLOB),y) +REFCODE_BLOB=$(obj)/refcode.rmod +$(REFCODE_BLOB): $(RMODTOOL) + $(RMODTOOL) -i $(CONFIG_REFCODE_BLOB_FILE) -o $@ +endif + +$(obj)/coreboot.rom: $(obj)/coreboot.pre $(objcbfs)/coreboot_ram.elf $(CBFSTOOL) $(call strip_quotes,$(COREBOOT_ROM_DEPENDENCIES)) $$(INTERMEDIATE) $$(VBOOT_STUB) $(REFCODE_BLOB) @printf " CBFS $(subst $(obj)/,,$(@))\n" cp $(obj)/coreboot.pre $@.tmp $(CBFSTOOL) $@.tmp add-stage -f $(objcbfs)/coreboot_ram.elf -n $(CONFIG_CBFS_PREFIX)/coreboot_ram -c $(CBFS_COMPRESS_FLAG) @@ -105,10 +111,10 @@ ifeq ($(CONFIG_INCLUDE_CONFIG_FILE),y) $(CBFSTOOL) $@.tmp add -f $(obj)/config.tmp -n config -t raw; rm -f $(obj)/config.tmp ; fi endif ifeq ($(CONFIG_VBOOT_VERIFY_FIRMWARE),y) - $(CBFSTOOL) $@.tmp add-stage -f $(VBOOT_STUB_ELF) -n $(CONFIG_CBFS_PREFIX)/vboot -c $(CBFS_COMPRESS_FLAG) + $(CBFSTOOL) $@.tmp add-stage -f $(VBOOT_STUB) -n $(CONFIG_CBFS_PREFIX)/vboot -c $(CBFS_COMPRESS_FLAG) endif ifeq ($(CONFIG_HAVE_REFCODE_BLOB),y) - $(CBFSTOOL) $@.tmp add-stage -f $(CONFIG_REFCODE_BLOB_FILE) -n $(CONFIG_CBFS_PREFIX)/refcode -c $(CBFS_COMPRESS_FLAG) + $(CBFSTOOL) $@.tmp add-stage -f $(REFCODE_BLOB) -n $(CONFIG_CBFS_PREFIX)/refcode -c $(CBFS_COMPRESS_FLAG) endif ifeq ($(CONFIG_PXE_ROM),y) $(CBFSTOOL) $@.tmp add -f $(CONFIG_PXE_ROM_FILE) -n pci$(CONFIG_PXE_ROM_ID).rom -t raw @@ -170,6 +176,10 @@ ifeq ($(CONFIG_RELOCATABLE_RAMSTAGE),y) $(eval $(call rmodule_link,$(objcbfs)/coreboot_ram.debug, $(objgenerated)/coreboot_ram.o, $(CONFIG_HEAP_SIZE))) +# The rmodule_link defintion creates an elf file with .rmod extension. +$(objcbfs)/coreboot_ram.elf: $(objcbfs)/coreboot_ram.debug.rmod + cp $< $@ + else $(objcbfs)/coreboot_ram.debug: $(objgenerated)/coreboot_ram.o $(src)/arch/x86/coreboot_ram.ld diff --git a/src/arch/x86/boot/Makefile.inc b/src/arch/x86/boot/Makefile.inc index 7b67e49c1c..6a00bde2a5 100644 --- a/src/arch/x86/boot/Makefile.inc +++ b/src/arch/x86/boot/Makefile.inc @@ -8,7 +8,6 @@ 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/lib/c_start.S b/src/arch/x86/lib/c_start.S index 01ffa7c42c..faea22d6a6 100644 --- a/src/arch/x86/lib/c_start.S +++ b/src/arch/x86/lib/c_start.S @@ -19,6 +19,8 @@ thread_stacks: .section ".textfirst", "ax", @progbits .code32 .globl _start + .globl __rmodule_entry +__rmodule_entry: _start: cli lgdt %cs:gdtaddr diff --git a/src/cpu/intel/haswell/Makefile.inc b/src/cpu/intel/haswell/Makefile.inc index 60c061ddd7..63c1939537 100644 --- a/src/cpu/intel/haswell/Makefile.inc +++ b/src/cpu/intel/haswell/Makefile.inc @@ -25,12 +25,12 @@ ramstage-srcs += $(SIPI_BIN) rmodules-y += sipi_vector.S rmodules-y += sipi_header.c -$(SIPI_DOTO): $(dir $(SIPI_ELF))sipi_vector.rmodules.o $(dir $(SIPI_ELF))sipi_header.rmodules.o +$(SIPI_DOTO): $(dir $(SIPI_ELF))sipi_vector.rmodules.o $(CC) $(LDFLAGS) -nostdlib -r -o $@ $^ -$(eval $(call rmodule_link,$(SIPI_ELF), $(SIPI_ELF:.elf=.o), 0)) +$(eval $(call rmodule_link,$(SIPI_ELF), $(SIPI_DOTO), 0)) -$(SIPI_BIN): $(SIPI_ELF) +$(SIPI_BIN): $(SIPI_ELF).rmod $(OBJCOPY) -O binary $< $@ $(SIPI_BIN).ramstage.o: $(SIPI_BIN) diff --git a/src/cpu/intel/haswell/sipi_header.c b/src/cpu/intel/haswell/sipi_header.c deleted file mode 100644 index 846a82d7c2..0000000000 --- a/src/cpu/intel/haswell/sipi_header.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - - -extern void *ap_start; - -DEFINE_RMODULE_HEADER(sipi_vector_header, ap_start, RMODULE_TYPE_SIPI_VECTOR); diff --git a/src/cpu/intel/haswell/sipi_vector.S b/src/cpu/intel/haswell/sipi_vector.S index 664a9ee8ee..e0abb7c9b2 100644 --- a/src/cpu/intel/haswell/sipi_vector.S +++ b/src/cpu/intel/haswell/sipi_vector.S @@ -58,6 +58,8 @@ apic_to_cpu_num: .text .code16 .global ap_start +.global __rmodule_entry +__rmodule_entry: ap_start: cli xorl %eax, %eax diff --git a/src/cpu/intel/haswell/smmrelocate.c b/src/cpu/intel/haswell/smmrelocate.c index 4fe6489be2..3020e2d9a8 100644 --- a/src/cpu/intel/haswell/smmrelocate.c +++ b/src/cpu/intel/haswell/smmrelocate.c @@ -164,11 +164,18 @@ static int bsp_setup_msr_save_state(struct smm_relocation_params *relo_params) /* The relocation work is actually performed in SMM context, but the code * resides in the ramstage module. This occurs by trampolining from the default * SMRAM entry point to here. */ -static void asmlinkage -cpu_smm_do_relocation(void *arg, int cpu, const struct smm_runtime *runtime) +static void asmlinkage cpu_smm_do_relocation(void *arg) { msr_t mtrr_cap; - struct smm_relocation_params *relo_params = arg; + struct smm_relocation_params *relo_params; + const struct smm_module_params *p; + const struct smm_runtime *runtime; + int cpu; + + p = arg; + runtime = p->runtime; + relo_params = p->arg; + cpu = p->cpu; if (cpu >= CONFIG_MAX_CPUS) { printk(BIOS_CRIT, diff --git a/src/cpu/x86/Makefile.inc b/src/cpu/x86/Makefile.inc index 2122ed76c9..11fbbb33cb 100644 --- a/src/cpu/x86/Makefile.inc +++ b/src/cpu/x86/Makefile.inc @@ -4,6 +4,7 @@ subdirs-$(CONFIG_PARALLEL_MP) += name ramstage-$(CONFIG_PARALLEL_MP) += mp_init.c SIPI_ELF=$(obj)/cpu/x86/sipi_vector.elf +SIPI_RMOD=$(SIPI_ELF).rmod SIPI_BIN=$(SIPI_ELF:.elf=) SIPI_DOTO=$(SIPI_ELF:.elf=.o) @@ -11,14 +12,13 @@ ifeq ($(CONFIG_PARALLEL_MP),y) ramstage-srcs += $(SIPI_BIN) endif rmodules-$(CONFIG_PARALLEL_MP) += sipi_vector.S -rmodules-$(CONFIG_PARALLEL_MP) += sipi_header.c -$(SIPI_DOTO): $(dir $(SIPI_ELF))sipi_vector.rmodules.o $(dir $(SIPI_ELF))sipi_header.rmodules.o +$(SIPI_DOTO): $(dir $(SIPI_ELF))sipi_vector.rmodules.o $(CC) $(LDFLAGS) -nostdlib -r -o $@ $^ $(eval $(call rmodule_link,$(SIPI_ELF), $(SIPI_ELF:.elf=.o), 0)) -$(SIPI_BIN): $(SIPI_ELF) +$(SIPI_BIN): $(SIPI_RMOD) $(OBJCOPY) -O binary $< $@ $(SIPI_BIN).ramstage.o: $(SIPI_BIN) diff --git a/src/cpu/x86/sipi_header.c b/src/cpu/x86/sipi_header.c deleted file mode 100644 index 846a82d7c2..0000000000 --- a/src/cpu/x86/sipi_header.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - - -extern void *ap_start; - -DEFINE_RMODULE_HEADER(sipi_vector_header, ap_start, RMODULE_TYPE_SIPI_VECTOR); diff --git a/src/cpu/x86/sipi_vector.S b/src/cpu/x86/sipi_vector.S index 52b12d0314..c08c3911df 100644 --- a/src/cpu/x86/sipi_vector.S +++ b/src/cpu/x86/sipi_vector.S @@ -58,6 +58,8 @@ ap_count: .text .code16 .global ap_start +.global __rmodule_entry +__rmodule_entry: ap_start: cli xorl %eax, %eax diff --git a/src/cpu/x86/smm/Makefile.inc b/src/cpu/x86/smm/Makefile.inc index 8dcd130117..bb9c11d83b 100644 --- a/src/cpu/x86/smm/Makefile.inc +++ b/src/cpu/x86/smm/Makefile.inc @@ -21,10 +21,8 @@ ramstage-$(CONFIG_BACKUP_DEFAULT_SMM_REGION) += backup_default_smm.c ifeq ($(CONFIG_SMM_MODULES),y) smmstub-y += smm_stub.S -smmstub-y += smm_module_header.c smm-y += smiutil.c -smm-y += smm_module_header.c smm-y += smm_module_handler.c ramstage-y += smm_module_loader.c @@ -40,7 +38,7 @@ $(obj)/cpu/x86/smm/smmstub.o: $$(smmstub-objs) # Link the SMM stub module with a 0-byte heap. $(eval $(call rmodule_link,$(obj)/cpu/x86/smm/smmstub.elf, $(obj)/cpu/x86/smm/smmstub.o, 0)) -$(obj)/cpu/x86/smm/smmstub: $(obj)/cpu/x86/smm/smmstub.elf +$(obj)/cpu/x86/smm/smmstub: $(obj)/cpu/x86/smm/smmstub.elf.rmod $(OBJCOPY) -O binary $< $@ $(obj)/cpu/x86/smm/smmstub.ramstage.o: $(obj)/cpu/x86/smm/smmstub @@ -55,7 +53,7 @@ $(obj)/cpu/x86/smm/smm.o: $$(smm-objs) $(LIBGCC_FILE_NAME) $(eval $(call rmodule_link,$(obj)/cpu/x86/smm/smm.elf, $(obj)/cpu/x86/smm/smm.o, $(CONFIG_SMM_MODULE_HEAP_SIZE))) -$(obj)/cpu/x86/smm/smm: $(obj)/cpu/x86/smm/smm.elf +$(obj)/cpu/x86/smm/smm: $(obj)/cpu/x86/smm/smm.elf.rmod $(OBJCOPY) -O binary $< $@ $(obj)/cpu/x86/smm/smm.ramstage.o: $(obj)/cpu/x86/smm/smm diff --git a/src/cpu/x86/smm/smm_module_handler.c b/src/cpu/x86/smm/smm_module_handler.c index 444e3352c7..79863d8fef 100644 --- a/src/cpu/x86/smm/smm_module_handler.c +++ b/src/cpu/x86/smm/smm_module_handler.c @@ -20,6 +20,7 @@ #include #include #include +#include typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore; @@ -113,8 +114,16 @@ void *smm_get_save_state(int cpu) return base; } -void smm_handler_start(void *arg, int cpu, const struct smm_runtime *runtime) +void asmlinkage smm_handler_start(void *arg) { + const struct smm_module_params *p; + const struct smm_runtime *runtime; + int cpu; + + p = arg; + runtime = p->runtime; + cpu = p->cpu; + /* Make sure to set the global runtime. It's OK to race as the value * will be the same across CPUs as well as multiple SMIs. */ if (smm_runtime == NULL) @@ -157,6 +166,8 @@ void smm_handler_start(void *arg, int cpu, const struct smm_runtime *runtime) smi_set_eos(); } +RMODULE_ENTRY(smm_handler_start); + /* Provide a default implementation for all weak handlers so that relocation * entries in the modules make sense. Without default implementations the * weak relocations w/o a symbol have a 0 address which is where the modules diff --git a/src/cpu/x86/smm/smm_module_header.c b/src/cpu/x86/smm/smm_module_header.c deleted file mode 100644 index 3ee654f6cd..0000000000 --- a/src/cpu/x86/smm/smm_module_header.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 - -extern char smm_handler_start[]; - -DEFINE_RMODULE_HEADER(smm_module, smm_handler_start, RMODULE_TYPE_SMM); diff --git a/src/cpu/x86/smm/smm_stub.S b/src/cpu/x86/smm/smm_stub.S index 07eb5dcb6d..083cb577b6 100644 --- a/src/cpu/x86/smm/smm_stub.S +++ b/src/cpu/x86/smm/smm_stub.S @@ -61,6 +61,8 @@ fallback_stack_top: .text .code16 .global smm_handler_start +.global __rmodule_entry +__rmodule_entry: smm_handler_start: movl $(smm_relocate_gdt), %ebx data32 lgdt (%ebx) @@ -132,11 +134,13 @@ smm_trampoline32: 2: /* Call into the c-based SMM relocation function with the platform * parameters. Equivalent to: - * c_handler(c_handler_params, cpu_num, smm_runtime); + * struct arg = { c_handler_params, cpu_num, smm_runtime {; + * c_handler(&arg) */ push $(smm_runtime) push %ecx push c_handler_arg + push %esp mov c_handler, %eax call *%eax diff --git a/src/include/cpu/x86/smm.h b/src/include/cpu/x86/smm.h index d938aa4f65..a84b496b50 100644 --- a/src/include/cpu/x86/smm.h +++ b/src/include/cpu/x86/smm.h @@ -517,14 +517,20 @@ struct smm_runtime { u8 apic_id_to_cpu[CONFIG_MAX_CPUS]; } __attribute__ ((packed)); -typedef void asmlinkage (*smm_handler_t)(void *arg, int cpu, - const struct smm_runtime *runtime); +struct smm_module_params { + void *arg; + int cpu; + const struct smm_runtime *runtime; +}; + +/* smm_handler_t is called with arg of smm_module_params pointer. */ +typedef void asmlinkage (*smm_handler_t)(void *); #ifdef __SMM__ /* SMM Runtime helpers. */ /* Entry point for SMM modules. */ -void smm_handler_start(void *arg, int cpu, const struct smm_runtime *runtime); +void asmlinkage smm_handler_start(void *params); /* Retrieve SMM save state for a given CPU. WARNING: This does not take into * account CPUs which are configured to not save their state to RAM. */ diff --git a/src/include/rmodule-defs.h b/src/include/rmodule-defs.h new file mode 100644 index 0000000000..4139ef3d41 --- /dev/null +++ b/src/include/rmodule-defs.h @@ -0,0 +1,63 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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 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_DEFS_H +#define RMODULE_DEFS_H + +#include +#include + +#define RMODULE_MAGIC 0xf8fe +#define RMODULE_VERSION_1 1 + +/* 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 { + uint16_t magic; + uint8_t version; + uint8_t type; + /* The payload represents the program's loadable code and data. */ + uint32_t payload_begin_offset; + uint32_t payload_end_offset; + /* Begin and of relocation information about the program module. */ + uint32_t relocations_begin_offset; + uint32_t relocations_end_offset; + /* The starting address of the linked program. This address is vital + * for determining relocation offsets as the relocation info and other + * symbols (bss, entry point) need this value as a basis to calculate + * the offsets. + */ + uint32_t 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 contiguous amount + * of memory. */ + uint32_t module_program_size; + /* This is program's execution entry point. */ + uint32_t module_entry_point; + /* Optional parameter structure that can be used to pass data into + * the module. */ + uint32_t parameters_begin; + uint32_t parameters_end; + /* BSS section information so the loader can clear the bss. */ + uint32_t bss_begin; + uint32_t bss_end; + /* Add some room for growth. */ + uint32_t padding[4]; +} __attribute__ ((packed)); + +#endif /* RMODULE_DEFS_H */ diff --git a/src/include/rmodule.h b/src/include/rmodule.h index 5c3b3792ba..d229cf816a 100644 --- a/src/include/rmodule.h +++ b/src/include/rmodule.h @@ -21,9 +21,8 @@ #include #include - -#define RMODULE_MAGIC 0xf8fe -#define RMODULE_VERSION_1 1 +#include +#include enum { RMODULE_TYPE_SMM, @@ -52,34 +51,7 @@ int rmodule_load_alignment(const struct rmodule *m); int rmodule_calc_region(unsigned int region_alignment, size_t rmodule_size, size_t *region_size, int *load_offset); -#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), \ - .bss_end = FIELD_ENTRY(_ebss), \ -} - -#define DEFINE_RMODULE_HEADER(name_, entry_, type_) \ - struct rmodule_header name_ \ - __attribute__ ((section (".module_header"))) = \ - RMODULE_HEADER(entry_, type_) - -/* Support for loading rmodule stages. This API is only available wwhen +/* Support for loading rmodule stages. This API is only available when * using dynamic cbmem because it uses the dynamic cbmem API to obtain * the backing store region for the stage. */ #if CONFIG_DYNAMIC_CBMEM @@ -100,43 +72,6 @@ int rmodule_stage_load(struct rmod_stage_load *rsl, struct cbfs_stage *stage); int rmodule_stage_load_from_cbfs(struct rmod_stage_load *rsl); #endif -/* 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; - /* Add some room for growth. */ - u32 padding[4]; -} __attribute__ ((packed)); - struct rmodule { void *location; struct rmodule_header *header; @@ -145,17 +80,12 @@ struct rmodule { 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[]; -extern char _ebss[]; -extern char _module_program_size[]; -extern char _module_link_start_addr[]; -extern char _module_params_begin[]; -extern char _module_params_end[]; +#if IS_ENABLED(CONFIG_RELOCATABLE_MODULES) +/* Rmodules have an entry point of named __rmodule_entry. */ +#define RMODULE_ENTRY(entry_) \ + void __rmodule_entry(void *) __attribute__((alias (STRINGIFY(entry_)))) +#else +#define RMODULE_ENTRY(entry_) +#endif #endif /* RMODULE_H */ diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index fa00ba9c18..c0481f1292 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -150,31 +150,41 @@ romstage-$(CONFIG_RELOCATABLE_RAMSTAGE) += rmodule.c RMODULE_LDSCRIPT := $(src)/lib/rmodule.ld ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) -RMODULE_LDFLAGS := -nostartfiles -shared -z defs -nostdlib -Bsymbolic -T $(RMODULE_LDSCRIPT) +RMODULE_LDFLAGS := -nostartfiles -Wl,--emit-relocs -Wl,-z,defs -Wl,-Bsymbolic -Wl,-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. +# It will create the necessary Make rules to create a rmodule. The resulting +# rmdoule is named $(1).rmod define rmodule_link $(strip $(1)): $(strip $(2)) $$(RMODULE_LDSCRIPT) $$(obj)/ldoptions $$(LD) $$(RMODULE_LDFLAGS) --defsym=__heap_size=$(strip $(3)) -o $$@ --start-group $(strip $(2)) $$(LIBGCC_FILE_NAME) --end-group $$(NM) -n $$@ > $$(basename $$@).map + +$(strip $(1)).rmod: $(strip $(1)) + $$(RMODTOOL) -i $$^ -o $$@ + endef else -RMODULE_LDFLAGS := -nostartfiles -shared -Wl,-z,defs -Wl,-Bsymbolic -Wl,-T,$(RMODULE_LDSCRIPT) +RMODULE_LDFLAGS := -nostartfiles -Wl,--emit-relocs -Wl,-z,defs -Wl,-Bsymbolic -Wl,-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. +# It will create the necessary Make rules to create a rmodule. The resulting +# rmdoule is named $(1).rmod define rmodule_link -$(strip $(1)): $(strip $(2)) $$(RMODULE_LDSCRIPT) $$(obj)/ldoptions +$(strip $(1)): $(strip $(2)) $$(RMODULE_LDSCRIPT) $$(obj)/ldoptions $$(RMODTOOL) $$(CC) $$(CFLAGS) $$(RMODULE_LDFLAGS) -Wl,--defsym=__heap_size=$(strip $(3)) -o $$@ -Wl,--start-group $(strip $(2)) $$(LIBGCC_FILE_NAME) -Wl,--end-group $$(NM) -n $$@ > $$(basename $$@).map + +$(strip $(1)).rmod: $(strip $(1)) + $$(RMODTOOL) -i $$^ -o $$@ + endef endif diff --git a/src/lib/rmodule.c b/src/lib/rmodule.c index bfac939566..11af0bd45a 100644 --- a/src/lib/rmodule.c +++ b/src/lib/rmodule.c @@ -26,78 +26,6 @@ /* 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; -} - -#elif CONFIG_ARCH_ARM -/* - * On ARM, the only relocations currently allowed are R_ARM_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_ARM_RELATIVE 23 - -#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_ARM_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; @@ -105,7 +33,7 @@ static inline int rmodule_is_loaded(const struct rmodule *module) /* Calculate a loaded program address based on the blob address. */ static inline void *rmodule_load_addr(const struct rmodule *module, - u32 blob_addr) + uintptr_t blob_addr) { char *loc = module->location; return &loc[blob_addr - module->header->module_link_start_address]; @@ -185,13 +113,13 @@ static void rmodule_clear_bss(struct rmodule *module) memset(begin, 0, size); } -static inline int rmodule_number_relocations(const struct rmodule *module) +static inline size_t rmodule_number_relocations(const struct rmodule *module) { - int r; + size_t r; r = module->header->relocations_end_offset; r -= module->header->relocations_begin_offset; - r /= RELOCTION_ENTRY_SIZE; + r /= sizeof(uintptr_t); return r; } @@ -210,51 +138,33 @@ static void rmodule_copy_payload(const struct rmodule *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; + size_t num_relocations; + const uintptr_t *reloc; + uintptr_t adjustment; /* Each relocation needs to be adjusted relative to the beginning of * the loaded program. */ - adjustment = (u32)rmodule_load_addr(module, 0); + adjustment = (uintptr_t)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", + printk(BIOS_DEBUG, "Processing %zu relocs. Offset value of 0x%08x\n", num_relocations, adjustment); while (num_relocations > 0) { - u32 *adjust_loc; - - if (!rmodule_reloc_valid(reloc)) - return -1; + uintptr_t *adjust_loc; /* 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 = rmodule_load_addr(module, *reloc); + 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); + reloc++; num_relocations--; } @@ -266,8 +176,8 @@ 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; + * in the program is preserved. Default to 4KiB. */ + return 4096; } int rmodule_load(void *base, struct rmodule *module) diff --git a/src/lib/rmodule.ld b/src/lib/rmodule.ld index 2d3621a3d6..401f65fb3a 100644 --- a/src/lib/rmodule.ld +++ b/src/lib/rmodule.ld @@ -4,13 +4,12 @@ * 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. + * packed into the flat blob. 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; @@ -19,21 +18,9 @@ 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); - - .payload : AT (_payload_begin_offset) { + .payload : { /* C code of the module. */ + _ram_seg = .; *(.textfirst); *(.text); *(.text.*); @@ -79,20 +66,20 @@ SECTIONS /* Data section. */ _sdata = .; *(.data); + *(.data.*); . = ALIGN(4); _edata = .; . = ALIGN(8); } - /* _payload_end marks the end of the module's code and data. */ - _payload_end_offset = LOADADDR(.payload) + SIZEOF(.payload); - .bss (NOLOAD) : { /* C uninitialized data of the module. */ _bss = .; *(.bss); - *(.sbss); + *(.bss.*) + *(.sbss) + *(.sbss.*) *(COMMON); . = ALIGN(8); _ebss = .; @@ -104,38 +91,11 @@ SECTIONS _heap = .; . = . + __heap_size; _eheap = .; + _eram_seg = .; } - /* _module_program_size is the total memory used by the program. */ - _module_program_size = _eheap - _module_link_start_addr; - - /* coreboot's ramstage uses the _ram_seg and _eram_seg symbols - * for determining its load location. Provide those to help it out. - * It's a nop for any non-ramstage rmodule. */ - _ram_seg = _module_link_start_addr; - _eram_seg = _module_link_start_addr + _module_program_size; - - /* 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); + /* Drop unnecessary sections. */ *(.eh_frame); } } diff --git a/src/soc/intel/baytrail/cpu.c b/src/soc/intel/baytrail/cpu.c index fb2e1c0b75..97d1f04b97 100644 --- a/src/soc/intel/baytrail/cpu.c +++ b/src/soc/intel/baytrail/cpu.c @@ -200,11 +200,17 @@ static void adjust_apic_id_map(struct smm_loader_params *smm_params) runtime->apic_id_to_cpu[i] = mp_get_apic_id(i); } -static void asmlinkage -cpu_smm_do_relocation(void *arg, int cpu, const struct smm_runtime *runtime) +static void asmlinkage cpu_smm_do_relocation(void *arg) { msr_t smrr; em64t100_smm_state_save_area_t *smm_state; + const struct smm_module_params *p; + const struct smm_runtime *runtime; + int cpu; + + p = arg; + runtime = p->runtime; + cpu = p->cpu; if (cpu >= CONFIG_MAX_CPUS) { printk(BIOS_CRIT, diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc index 7ae5d4e823..af0021e905 100644 --- a/src/vendorcode/google/chromeos/Makefile.inc +++ b/src/vendorcode/google/chromeos/Makefile.inc @@ -55,6 +55,7 @@ VB_INCLUDES += -I$(VB_SOURCE)/firmware/include INCLUDES += $(VB_INCLUDES) VBOOT_STUB_ELF = $(obj)/vendorcode/google/chromeos/vbootstub.elf +VBOOT_STUB = $(VBOOT_STUB_ELF).rmod VBOOT_STUB_DOTO = $(VBOOT_STUB_ELF:.elf=.o) # Dependency for the vboot rmodules. Ordering matters. diff --git a/src/vendorcode/google/chromeos/vboot_wrapper.c b/src/vendorcode/google/chromeos/vboot_wrapper.c index 436496eabf..d008de17cb 100644 --- a/src/vendorcode/google/chromeos/vboot_wrapper.c +++ b/src/vendorcode/google/chromeos/vboot_wrapper.c @@ -28,10 +28,6 @@ #include "vboot_context.h" #include "vboot_handoff.h" -static void vboot_wrapper(struct vboot_context *context); - -DEFINE_RMODULE_HEADER(vboot_wrapper_header, vboot_wrapper, RMODULE_TYPE_VBOOT); - /* Keep a global context pointer around for the callbacks to use. */ static struct vboot_context *gcontext; @@ -67,12 +63,14 @@ static void parse_component(const struct components *components, int num, fw->size = (uint32_t)components->entries[num].size; } -static void vboot_wrapper(struct vboot_context *context) +static void vboot_wrapper(void *arg) { int i; VbError_t res; const struct components *components; + struct vboot_context *context; + context = arg; gcontext = context; VbExDebug("Calling VbInit()\n"); @@ -276,3 +274,4 @@ VbError_t VbExTpmSendReceive(const uint8_t *request, uint32_t request_length, return VBERROR_SUCCESS; } +RMODULE_ENTRY(vboot_wrapper); diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index 67bbd8f1c0..7783f9fdbc 100644 --- a/util/cbfstool/Makefile +++ b/util/cbfstool/Makefile @@ -8,28 +8,37 @@ CFLAGS += -Wwrite-strings -Wredundant-decls -Wno-trigraphs CFLAGS += -Wstrict-aliasing -Wshadow -Werror LDFLAGS += -g -BINARY:=$(obj)/cbfstool +CBFSTOOL_BINARY:=$(obj)/cbfstool -COMMON:=cbfstool.o common.o cbfs_image.o compress.o fit.o -COMMON+=elfheaders.o cbfs-mkstage.o cbfs-mkpayload.o xdr.o +CBFSTOOL_COMMON:=cbfstool.o common.o cbfs_image.o compress.o fit.o +CBFSTOOL_COMMON+=elfheaders.o cbfs-mkstage.o cbfs-mkpayload.o xdr.o # LZMA -COMMON+=lzma/lzma.o -COMMON+=lzma/C/LzFind.o lzma/C/LzmaDec.o lzma/C/LzmaEnc.o +CBFSTOOL_COMMON+=lzma/lzma.o +CBFSTOOL_COMMON+=lzma/C/LzFind.o lzma/C/LzmaDec.o lzma/C/LzmaEnc.o -COMMON:=$(addprefix $(obj)/,$(COMMON)) +CBFSTOOL_COMMON:=$(addprefix $(obj)/,$(CBFSTOOL_COMMON)) -all: dep $(BINARY) +RMODTOOL_BINARY:=$(obj)/rmodtool +RMODTOOL_COMMON:=rmodtool.o rmodule.o common.o elfheaders.o xdr.o + +RMODTOOL_COMMON:=$(addprefix $(obj)/,$(RMODTOOL_COMMON)) + +all: dep $(CBFSTOOL_BINARY) $(RMODTOOL_BINARY) $(obj)/%.o: %.c $(HOSTCC) $(CFLAGS) -c -o $@ $< clean: - rm -f $(COMMON) $(BINARY) + rm -f $(CBFSTOOL_COMMON) $(CBFSTOOL_BINARY) + rm -f $(RMODTOOL_COMMON) $(RMODTOOL_BINARY) tags: ctags *.[ch] -$(obj)/cbfstool:$(COMMON) +$(obj)/cbfstool:$(CBFSTOOL_COMMON) + $(HOSTCC) $(CFLAGS) -o $@ $^ + +$(obj)/rmodtool:$(RMODTOOL_COMMON) $(HOSTCC) $(CFLAGS) -o $@ $^ dep: diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index c0b1994c30..00b68db45e 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -14,10 +14,16 @@ cbfsobj += LzFind.o cbfsobj += LzmaDec.o cbfsobj += LzmaEnc.o -CBFSTOOLFLAGS=-D_7ZIP_ST -g +rmodobj += rmodtool.o +rmodobj += rmodule.o +rmodobj += common.o +rmodobj += elfheaders.o +rmodobj += xdr.o + +TOOLFLAGS=-D_7ZIP_ST -g ifeq ($(shell uname -s | cut -c-7 2>/dev/null), MINGW32) -CBFSTOOLFLAGS+=-mno-ms-bitfields +TOOLFLAGS+=-mno-ms-bitfields endif $(objutil)/cbfstool: @@ -25,17 +31,21 @@ $(objutil)/cbfstool: $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/%.c printf " HOSTCC $(subst $(objutil)/,,$(@))\n" - $(HOSTCC) $(CBFSTOOLFLAGS) $(HOSTCFLAGS) -c -o $@ $< + $(HOSTCC) $(TOOLFLAGS) $(HOSTCFLAGS) -c -o $@ $< $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/lzma/%.c printf " HOSTCC $(subst $(objutil)/,,$(@))\n" - $(HOSTCC) $(CBFSTOOLFLAGS) $(HOSTCFLAGS) -c -o $@ $< + $(HOSTCC) $(TOOLFLAGS) $(HOSTCFLAGS) -c -o $@ $< $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/lzma/C/%.c printf " HOSTCC $(subst $(objutil)/,,$(@))\n" - $(HOSTCC) $(CBFSTOOLFLAGS) $(HOSTCFLAGS) -c -o $@ $< + $(HOSTCC) $(TOOLFLAGS) $(HOSTCFLAGS) -c -o $@ $< $(objutil)/cbfstool/cbfstool: $(objutil)/cbfstool $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" - $(HOSTCC) $(CBFSTOOLFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) + $(HOSTCC) $(TOOLFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) + +$(objutil)/cbfstool/rmodtool: $(objutil)/cbfstool $(addprefix $(objutil)/cbfstool/,$(rmodobj)) + printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" + $(HOSTCC) $(TOOLFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(rmodobj)) diff --git a/util/cbfstool/cbfs-mkpayload.c b/util/cbfstool/cbfs-mkpayload.c index 7baeadf205..d9e543004a 100644 --- a/util/cbfstool/cbfs-mkpayload.c +++ b/util/cbfstool/cbfs-mkpayload.c @@ -23,7 +23,7 @@ #include #include -#include "elf.h" +#include "elfparsing.h" #include "common.h" #include "cbfs.h" #include "fv.h" diff --git a/util/cbfstool/cbfs-mkstage.c b/util/cbfstool/cbfs-mkstage.c index c24593c934..59e80652e8 100644 --- a/util/cbfstool/cbfs-mkstage.c +++ b/util/cbfstool/cbfs-mkstage.c @@ -24,7 +24,7 @@ #include #include -#include "elf.h" +#include "elfparsing.h" #include "common.h" #include "cbfs.h" diff --git a/util/cbfstool/cbfs.h b/util/cbfstool/cbfs.h index e078b1e1f5..bcc83e21ae 100644 --- a/util/cbfstool/cbfs.h +++ b/util/cbfstool/cbfs.h @@ -122,11 +122,7 @@ struct cbfs_payload { #define CBFS_NAME(_c) (((char *) (_c)) + sizeof(struct cbfs_file)) #define CBFS_SUBHEADER(_p) ( (void *) ((((uint8_t *) (_p)) + ntohl((_p)->offset))) ) -/* elfheaders.c */ -int -elf_headers(const struct buffer *pinput, - Elf64_Ehdr *ehdr, - Elf64_Phdr **pphdr, - Elf64_Shdr **pshdr); +/* common.c */ +void cbfs_file_get_header(struct buffer *buf, struct cbfs_file *file); #endif diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c index 0eefd1519c..a70edb8951 100644 --- a/util/cbfstool/cbfs_image.c +++ b/util/cbfstool/cbfs_image.c @@ -24,7 +24,6 @@ #include #include "common.h" -#include "elf.h" #include "cbfs_image.h" /* The file name align is not defined in CBFS spec -- only a preference by diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c index 386e96583e..72799a4da8 100644 --- a/util/cbfstool/cbfstool.c +++ b/util/cbfstool/cbfstool.c @@ -26,7 +26,6 @@ #include #include #include "common.h" -#include "elf.h" #include "cbfs.h" #include "cbfs_image.h" #include "fit.h" @@ -37,7 +36,6 @@ struct command { int (*function) (void); }; -int verbose = 0; static struct param { char *cbfs_name; char *name; diff --git a/util/cbfstool/common.c b/util/cbfstool/common.c index 724524b53d..d1e1c9f1d1 100644 --- a/util/cbfstool/common.c +++ b/util/cbfstool/common.c @@ -23,11 +23,11 @@ #include #include #include -#include "elf.h" #include "common.h" #include "cbfs.h" /* Utilities */ +int verbose = 0; /* Small, OS/libc independent runtime check for endianess */ int is_big_endian(void) @@ -109,6 +109,15 @@ void buffer_delete(struct buffer *buffer) uint32_t arch = CBFS_ARCHITECTURE_UNKNOWN; +void cbfs_file_get_header(struct buffer *buf, struct cbfs_file *file) +{ + bgets(buf, &file->magic, sizeof(file->magic)); + file->len = xdr_be.get32(buf); + file->type = xdr_be.get32(buf); + file->checksum = xdr_be.get32(buf); + file->offset = xdr_be.get32(buf); +} + static struct { uint32_t arch; const char *name; @@ -150,12 +159,6 @@ const char *arch_to_string(uint32_t a) } -int iself(unsigned char *input) -{ - Elf32_Ehdr *ehdr = (Elf32_Ehdr *) input; - return !memcmp(ehdr->e_ident, ELFMAG, 4); -} - static const struct filetypes_t { uint32_t type; const char *name; diff --git a/util/cbfstool/common.h b/util/cbfstool/common.h index f882b86e54..9988f84035 100644 --- a/util/cbfstool/common.h +++ b/util/cbfstool/common.h @@ -54,6 +54,49 @@ struct buffer { size_t size; }; +static inline void *buffer_get(const struct buffer *b) +{ + return b->data; +} + +static inline size_t buffer_size(const struct buffer *b) +{ + return b->size; +} + +static inline void buffer_set_size(struct buffer *b, size_t size) +{ + b->size = size; +} + +/* + * Splice a buffer into another buffer. If size is zero the entire buffer + * is spliced while if size is non-zero the buffer is spliced starting at + * offset for size bytes. Note that it's up to caller to bounds check. + */ +static inline void buffer_splice(struct buffer *dest, const struct buffer *src, + size_t offset, size_t size) +{ + dest->name = src->name; + dest->data = src->data; + dest->size = src->size; + if (size != 0) { + dest->data += offset; + buffer_set_size(dest, size); + } +} + +static inline void buffer_clone(struct buffer *dest, const struct buffer *src) +{ + buffer_splice(dest, src, 0, 0); +} + +static inline void buffer_seek(struct buffer *b, size_t size) +{ + b->size -= size; + b->data += size; +} + /* Creates an empty memory buffer with given size. * Returns 0 on success, otherwise non-zero. */ int buffer_create(struct buffer *buffer, size_t size, const char *name); @@ -74,8 +117,6 @@ extern uint32_t arch; const char *arch_to_string(uint32_t a); uint32_t string_to_arch(const char *arch_string); -int iself(unsigned char *input); - typedef int (*comp_func_ptr) (char *, int, char *, int *); typedef enum { CBFS_COMPRESS_NONE = 0, CBFS_COMPRESS_LZMA = 1 } comp_algo; @@ -104,9 +145,11 @@ int do_lzma_compress(char *in, int in_len, char *out, int *out_len); int do_lzma_uncompress(char *dst, int dst_len, char *src, int src_len); /* xdr.c */ struct xdr { + uint8_t (*get8)(struct buffer *input); uint16_t (*get16)(struct buffer *input); uint32_t (*get32)(struct buffer *input); uint64_t (*get64)(struct buffer *input); + void (*put8)(struct buffer *input, uint8_t val); void (*put16)(struct buffer *input, uint16_t val); void (*put32)(struct buffer *input, uint32_t val); void (*put64)(struct buffer *input, uint64_t val); @@ -114,5 +157,7 @@ struct xdr { /* xdr.c */ extern struct xdr xdr_le, xdr_be; +size_t bgets(struct buffer *input, void *output, size_t len); +size_t bputs(struct buffer *b, const void *data, size_t len); #endif diff --git a/util/cbfstool/elfheaders.c b/util/cbfstool/elfheaders.c index dd047f24df..eca89eeb27 100644 --- a/util/cbfstool/elfheaders.c +++ b/util/cbfstool/elfheaders.c @@ -21,7 +21,7 @@ #include #include -#include "elf.h" +#include "elfparsing.h" #include "common.h" #include "cbfs.h" @@ -121,6 +121,13 @@ * in the buffer struct. */ + +static int iself(const void *input) +{ + const Elf32_Ehdr *ehdr = input; + return !memcmp(ehdr->e_ident, ELFMAG, 4); +} + /* Get the ident array, so we can figure out * endian-ness, word size, and in future other useful * parameters @@ -128,12 +135,25 @@ static void elf_eident(struct buffer *input, Elf64_Ehdr *ehdr) { - memmove(ehdr->e_ident, input->data, sizeof(ehdr->e_ident)); - input->data += sizeof(ehdr->e_ident); - input->size -= sizeof(ehdr->e_ident); + bgets(input, ehdr->e_ident, sizeof(ehdr->e_ident)); } +static int +check_size(const struct buffer *b, size_t offset, size_t size, const char *desc) +{ + if (size == 0) + return 0; + + if (offset >= buffer_size(b) || (offset + size) > buffer_size(b)) { + ERROR("The file is not large enough for the '%s'. " + "%ld bytes @ offset %zu, input %zu bytes.\n", + desc, size, offset, buffer_size(b)); + return -1; + } + return 0; +} + static void elf_ehdr(struct buffer *input, Elf64_Ehdr *ehdr, struct xdr *xdr, int bit64) { @@ -169,7 +189,8 @@ elf_phdr(struct buffer *pinput, Elf64_Phdr *phdr, * pointer the full entsize; rather than get tricky * we just advance it below. */ - struct buffer input = *pinput; + struct buffer input; + buffer_clone(&input, pinput); if (bit64){ phdr->p_type = xdr->get32(&input); phdr->p_flags = xdr->get32(&input); @@ -189,8 +210,7 @@ elf_phdr(struct buffer *pinput, Elf64_Phdr *phdr, phdr->p_flags = xdr->get32(&input); phdr->p_align = xdr->get32(&input); } - pinput->size -= entsize; - pinput->data += entsize; + buffer_seek(pinput, entsize); } static void @@ -228,8 +248,332 @@ elf_shdr(struct buffer *pinput, Elf64_Shdr *shdr, shdr->sh_addralign = xdr->get32(&input); shdr->sh_entsize = xdr->get32(&input); } - pinput->size -= entsize; - pinput->data += entsize; + buffer_seek(pinput, entsize); +} + +static int +phdr_read(const struct buffer *in, struct parsed_elf *pelf, + struct xdr *xdr, int bit64) +{ + struct buffer b; + Elf64_Phdr *phdr; + Elf64_Ehdr *ehdr; + int i; + + ehdr = &pelf->ehdr; + /* cons up an input buffer for the headers. + * Note that the program headers can be anywhere, + * per the ELF spec, You'd be surprised how many ELF + * readers miss this little detail. + */ + buffer_splice(&b, in, ehdr->e_phoff, ehdr->e_phentsize * ehdr->e_phnum); + if (check_size(in, ehdr->e_phoff, buffer_size(&b), "program headers")) + return -1; + + /* gather up all the phdrs. + * We do them all at once because there is more + * than one loop over all the phdrs. + */ + phdr = calloc(ehdr->e_phnum, sizeof(*phdr)); + for (i = 0; i < ehdr->e_phnum; i++) { + DEBUG("Parsing segment %d\n", i); + elf_phdr(&b, &phdr[i], ehdr->e_phentsize, xdr, bit64); + + /* Ensure the contents are valid within the elf file. */ + if (check_size(in, phdr[i].p_offset, phdr[i].p_filesz, + "segment contents")) + return -1; + } + + pelf->phdr = phdr; + + return 0; +} + +static int +shdr_read(const struct buffer *in, struct parsed_elf *pelf, + struct xdr *xdr, int bit64) +{ + struct buffer b; + Elf64_Shdr *shdr; + Elf64_Ehdr *ehdr; + int i; + + ehdr = &pelf->ehdr; + + /* cons up an input buffer for the section headers. + * Note that the section headers can be anywhere, + * per the ELF spec, You'd be surprised how many ELF + * readers miss this little detail. + */ + buffer_splice(&b, in, ehdr->e_shoff, ehdr->e_shentsize * ehdr->e_shnum); + if (check_size(in, ehdr->e_shoff, buffer_size(&b), "section headers")) + return -1; + + /* gather up all the shdrs. */ + shdr = calloc(ehdr->e_shnum, sizeof(*shdr)); + for (i = 0; i < ehdr->e_shnum; i++) { + DEBUG("Parsing section %d\n", i); + elf_shdr(&b, &shdr[i], ehdr->e_shentsize, xdr, bit64); + } + + pelf->shdr = shdr; + + return 0; +} + +static int +reloc_read(const struct buffer *in, struct parsed_elf *pelf, + struct xdr *xdr, int bit64) +{ + struct buffer b; + Elf64_Word i; + Elf64_Ehdr *ehdr; + + ehdr = &pelf->ehdr; + pelf->relocs = calloc(ehdr->e_shnum, sizeof(Elf64_Rela *)); + + /* Allocate array for each section that contains relocation entries. */ + for (i = 0; i < ehdr->e_shnum; i++) { + Elf64_Shdr *shdr; + Elf64_Rela *rela; + Elf64_Xword j; + Elf64_Xword nrelocs; + int is_rela; + + shdr = &pelf->shdr[i]; + + /* Only process REL and RELA sections. */ + if (shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) + continue; + + DEBUG("Checking relocation section %u\n", i); + + /* Ensure the section that relocations apply is a valid. */ + if (shdr->sh_info >= ehdr->e_shnum || + shdr->sh_info == SHN_UNDEF) { + ERROR("Relocations apply to an invalid section: %u\n", + shdr[i].sh_info); + return -1; + } + + is_rela = shdr->sh_type == SHT_RELA; + + /* Determine the number relocations in this section. */ + nrelocs = shdr->sh_size / shdr->sh_entsize; + + pelf->relocs[i] = calloc(nrelocs, sizeof(Elf64_Rela)); + + buffer_splice(&b, in, shdr->sh_offset, shdr->sh_size); + if (check_size(in, shdr->sh_offset, buffer_size(&b), + "relocation section")) { + ERROR("Relocation section %u failed.\n", i); + return -1; + } + + rela = pelf->relocs[i]; + for (j = 0; j < nrelocs; j++) { + if (bit64) { + rela->r_offset = xdr->get64(&b); + rela->r_info = xdr->get64(&b); + if (is_rela) + rela->r_addend = xdr->get64(&b); + } else { + uint32_t r_info; + + rela->r_offset = xdr->get32(&b); + r_info = xdr->get32(&b); + rela->r_info = ELF64_R_INFO(ELF32_R_SYM(r_info), + ELF32_R_TYPE(r_info)); + if (is_rela) + rela->r_addend = xdr->get32(&b); + } + rela++; + } + } + + return 0; +} + +static int strtab_read(const struct buffer *in, struct parsed_elf *pelf) +{ + Elf64_Ehdr *ehdr; + Elf64_Word i; + + ehdr = &pelf->ehdr; + + if (ehdr->e_shstrndx >= ehdr->e_shnum) { + ERROR("Section header string table index out of range: %d\n", + ehdr->e_shstrndx); + return -1; + } + + /* For each section of type SHT_STRTAB create a symtab buffer. */ + pelf->strtabs = calloc(ehdr->e_shnum, sizeof(struct buffer *)); + + for (i = 0; i < ehdr->e_shnum; i++) { + struct buffer *b; + Elf64_Shdr *shdr = &pelf->shdr[i]; + + if (shdr->sh_type != SHT_STRTAB) + continue; + + b = calloc(1, sizeof(*b)); + buffer_splice(b, in, shdr->sh_offset, shdr->sh_size); + if (check_size(in, shdr->sh_offset, buffer_size(b), "strtab")) { + ERROR("STRTAB section not within bounds: %d\n", i); + return -1; + } + pelf->strtabs[i] = b; + } + + return 0; +} + +static int +symtab_read(const struct buffer *in, struct parsed_elf *pelf, + struct xdr *xdr, int bit64) +{ + Elf64_Ehdr *ehdr; + Elf64_Shdr *shdr; + Elf64_Half i; + Elf64_Xword nsyms; + Elf64_Sym *sym; + struct buffer b; + + ehdr = &pelf->ehdr; + + shdr = NULL; + for (i = 0; i < ehdr->e_shnum; i++) { + if (pelf->shdr[i].sh_type != SHT_SYMTAB) + continue; + + if (shdr != NULL) { + ERROR("Multiple symbol sections found. %u and %u\n", + (unsigned int)(shdr - pelf->shdr), i); + return -1; + } + + shdr = &pelf->shdr[i]; + } + + if (shdr == NULL) { + ERROR("No symbol table found.\n"); + return -1; + } + + buffer_splice(&b, in, shdr->sh_offset, shdr->sh_size); + if (check_size(in, shdr->sh_offset, buffer_size(&b), "symtab")) + return -1; + + nsyms = shdr->sh_size / shdr->sh_entsize; + + pelf->syms = calloc(nsyms, sizeof(Elf64_Sym)); + + for (i = 0; i < nsyms; i++) { + sym = &pelf->syms[i]; + + if (bit64) { + sym->st_name = xdr->get32(&b); + sym->st_info = xdr->get8(&b); + sym->st_other = xdr->get8(&b); + sym->st_shndx = xdr->get16(&b); + sym->st_value = xdr->get64(&b); + sym->st_size = xdr->get64(&b); + } else { + sym->st_name = xdr->get32(&b); + sym->st_value = xdr->get32(&b); + sym->st_size = xdr->get32(&b); + sym->st_info = xdr->get8(&b); + sym->st_other = xdr->get8(&b); + sym->st_shndx = xdr->get16(&b); + } + } + + return 0; +} + +int parse_elf(const struct buffer *pinput, struct parsed_elf *pelf, int flags) +{ + struct xdr *xdr = &xdr_le; + int bit64 = 0; + struct buffer input; + Elf64_Ehdr *ehdr; + + /* Zero out the parsed elf structure. */ + memset(pelf, 0, sizeof(*pelf)); + + if (!iself(buffer_get(pinput))) { + ERROR("The stage file is not in ELF format!\n"); + return -1; + } + + buffer_clone(&input, pinput); + ehdr = &pelf->ehdr; + elf_eident(&input, ehdr); + bit64 = ehdr->e_ident[EI_CLASS] == ELFCLASS64; + /* Assume LE unless we are sure otherwise. + * We're not going to take on the task of + * fully validating the ELF file. That way + * lies madness. + */ + if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + xdr = &xdr_be; + + elf_ehdr(&input, ehdr, xdr, bit64); + + /* Relocation processing requires section header parsing. */ + if (flags & ELF_PARSE_RELOC) + flags |= ELF_PARSE_SHDR; + + /* String table processing requires section header parsing. */ + if (flags & ELF_PARSE_STRTAB) + flags |= ELF_PARSE_SHDR; + + /* Symbole table processing requires section header parsing. */ + if (flags & ELF_PARSE_SYMTAB) + flags |= ELF_PARSE_SHDR; + + if ((flags & ELF_PARSE_PHDR) && phdr_read(pinput, pelf, xdr, bit64)) + goto fail; + + if ((flags & ELF_PARSE_SHDR) && shdr_read(pinput, pelf, xdr, bit64)) + goto fail; + + if ((flags & ELF_PARSE_RELOC) && reloc_read(pinput, pelf, xdr, bit64)) + goto fail; + + if ((flags & ELF_PARSE_STRTAB) && strtab_read(pinput, pelf)) + goto fail; + + if ((flags & ELF_PARSE_SYMTAB) && symtab_read(pinput, pelf, xdr, bit64)) + goto fail; + + return 0; + +fail: + parsed_elf_destroy(pelf); + return -1; +} + +void parsed_elf_destroy(struct parsed_elf *pelf) +{ + Elf64_Half i; + + free(pelf->phdr); + free(pelf->shdr); + if (pelf->relocs != NULL) { + for (i = 0; i < pelf->ehdr.e_shnum; i++) + free(pelf->relocs[i]); + } + free(pelf->relocs); + + if (pelf->strtabs != NULL) { + for (i = 0; i < pelf->ehdr.e_shnum; i++) + free(pelf->strtabs[i]); + } + free(pelf->strtabs); + free(pelf->syms); } /* Get the headers from the buffer. @@ -245,31 +589,20 @@ elf_headers(const struct buffer *pinput, Elf64_Phdr **pphdr, Elf64_Shdr **pshdr) { - int i; - struct xdr *xdr = &xdr_le; - int bit64 = 0; - struct buffer input = *(struct buffer *)pinput; - struct buffer phdr_buf; - struct buffer shdr_buf; - Elf64_Phdr *phdr; - Elf64_Shdr *shdr; - if (!iself((unsigned char *)pinput->data)) { - ERROR("The stage file is not in ELF format!\n"); + struct parsed_elf pelf; + int flags; + + flags = ELF_PARSE_PHDR; + + if (pshdr != NULL) + flags |= ELF_PARSE_SHDR; + + if (parse_elf(pinput, &pelf, flags)) return -1; - } - elf_eident(&input, ehdr); - bit64 = ehdr->e_ident[EI_CLASS] == ELFCLASS64; - /* Assume LE unless we are sure otherwise. - * We're not going to take on the task of - * fully validating the ELF file. That way - * lies madness. - */ - if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) - xdr = &xdr_be; - - elf_ehdr(&input, ehdr, xdr, bit64); + /* Copy out the parsed elf header. */ + memcpy(ehdr, &pelf.ehdr, sizeof(*ehdr)); // The tool may work in architecture-independent way. if (arch != CBFS_ARCHITECTURE_UNKNOWN && @@ -280,65 +613,334 @@ elf_headers(const struct buffer *pinput, return -1; } - if (pinput->size < ehdr->e_phoff){ - ERROR("The program header offset is greater than " - "the remaining file size." - "%ld bytes left, program header offset is %ld \n", - pinput->size, ehdr->e_phoff); - return -1; - } - /* cons up an input buffer for the headers. - * Note that the program headers can be anywhere, - * per the ELF spec, You'd be surprised how many ELF - * readers miss this little detail. - */ - phdr_buf.data = &pinput->data[ehdr->e_phoff]; - phdr_buf.size = ehdr->e_phentsize * ehdr->e_phnum; - if (phdr_buf.size > (pinput->size - ehdr->e_phoff)){ - ERROR("The file is not large enough for the program headers." - "%ld bytes left, %ld bytes of headers\n", - pinput->size - ehdr->e_phoff, phdr_buf.size); - return -1; - } - /* gather up all the phdrs. - * We do them all at once because there is more - * than one loop over all the phdrs. - */ - phdr = calloc(sizeof(*phdr), ehdr->e_phnum); - for (i = 0; i < ehdr->e_phnum; i++) - elf_phdr(&phdr_buf, &phdr[i], ehdr->e_phentsize, xdr, bit64); - *pphdr = phdr; + *pphdr = calloc(ehdr->e_phnum, sizeof(Elf64_Phdr)); + memcpy(*pphdr, pelf.phdr, ehdr->e_phnum * sizeof(Elf64_Phdr)); - if (!pshdr) - return 0; - - if (pinput->size < ehdr->e_shoff){ - ERROR("The section header offset is greater than " - "the remaining file size." - "%ld bytes left, program header offset is %ld \n", - pinput->size, ehdr->e_shoff); - return -1; + if (pshdr != NULL) { + *pshdr = calloc(ehdr->e_shnum, sizeof(Elf64_Shdr)); + memcpy(*pshdr, pelf.shdr, ehdr->e_shnum * sizeof(Elf64_Shdr)); } - /* cons up an input buffer for the section headers. - * Note that the section headers can be anywhere, - * per the ELF spec, You'd be surprised how many ELF - * readers miss this little detail. - */ - shdr_buf.data = &pinput->data[ehdr->e_shoff]; - shdr_buf.size = ehdr->e_shentsize * ehdr->e_shnum; - if (shdr_buf.size > (pinput->size - ehdr->e_shoff)){ - ERROR("The file is not large enough for the section headers." - "%ld bytes left, %ld bytes of headers\n", - pinput->size - ehdr->e_shoff, shdr_buf.size); - return -1; - } - /* gather up all the shdrs. */ - shdr = calloc(sizeof(*shdr), ehdr->e_shnum); - for (i = 0; i < ehdr->e_shnum; i++) - elf_shdr(&shdr_buf, &shdr[i], ehdr->e_shentsize, xdr, bit64); - *pshdr = shdr; + parsed_elf_destroy(&pelf); return 0; } +/* ELF Writing Support + * + * The ELF file is written according to the following layout: + * +------------------+ + * | ELF Header | + * +------------------+ + * | Section Headers | + * +------------------+ + * | Program Headers | + * +------------------+ + * | String table | + * +------------------+ <- 4KiB Aligned + * | Code/Data | + * +------------------+ + */ + +/* Arbitray maximum number of sections. */ +#define MAX_SECTIONS 16 +struct elf_writer_section { + Elf64_Shdr shdr; + struct buffer content; + const char *name; +}; + +struct elf_writer +{ + Elf64_Ehdr ehdr; + struct xdr *xdr; + size_t num_secs; + struct elf_writer_section sections[MAX_SECTIONS]; + Elf64_Phdr *phdrs; + struct elf_writer_section *shstrtab; + int bit64; +}; + +struct elf_writer *elf_writer_init(const Elf64_Ehdr *ehdr) +{ + struct elf_writer *ew; + Elf64_Shdr shdr; + struct buffer empty_buffer; + + if (!iself(ehdr)) + return NULL; + + ew = calloc(1, sizeof(*ew)); + + memcpy(&ew->ehdr, ehdr, sizeof(ew->ehdr)); + + ew->bit64 = ew->ehdr.e_ident[EI_CLASS] == ELFCLASS64; + + /* Set the endinan ops. */ + if (ew->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) + ew->xdr = &xdr_be; + else + ew->xdr = &xdr_le; + + /* Reset count and offsets */ + ew->ehdr.e_phoff = 0; + ew->ehdr.e_shoff = 0; + ew->ehdr.e_shnum = 0; + ew->ehdr.e_phnum = 0; + + memset(&empty_buffer, 0, sizeof(empty_buffer)); + memset(&shdr, 0, sizeof(shdr)); + + /* Add SHT_NULL section header. */ + shdr.sh_type = SHT_NULL; + elf_writer_add_section(ew, &shdr, &empty_buffer, NULL); + + /* Add section header string table and maintain reference to it. */ + shdr.sh_type = SHT_STRTAB; + elf_writer_add_section(ew, &shdr, &empty_buffer, ".shstrtab"); + ew->ehdr.e_shstrndx = ew->num_secs - 1; + ew->shstrtab = &ew->sections[ew->ehdr.e_shstrndx]; + + return ew; +} + +/* + * Clean up any internal state represented by ew. Aftewards the elf_writer + * is invalid. + */ +void elf_writer_destroy(struct elf_writer *ew) +{ + if (ew->phdrs != NULL) + free(ew->phdrs); + free(ew); +} + +/* + * Add a section to the ELF file. Section type, flags, and memsize are + * maintained from the passed in Elf64_Shdr. The buffer represents the + * content of the section while the name is the name of section itself. + * Returns < 0 on error, 0 on success. + */ +int elf_writer_add_section(struct elf_writer *ew, const Elf64_Shdr *shdr, + struct buffer *contents, const char *name) +{ + struct elf_writer_section *newsh; + + if (ew->num_secs == MAX_SECTIONS) + return -1; + + newsh = &ew->sections[ew->num_secs]; + ew->num_secs++; + + memcpy(&newsh->shdr, shdr, sizeof(newsh->shdr)); + newsh->shdr.sh_offset = 0; + + newsh->name = name; + if (contents != NULL) + buffer_clone(&newsh->content, contents); + + return 0; +} + +static void ehdr_write(struct elf_writer *ew, struct buffer *m) +{ + int i; + + for (i = 0; i < EI_NIDENT; i++) + ew->xdr->put8(m, ew->ehdr.e_ident[i]); + ew->xdr->put16(m, ew->ehdr.e_type); + ew->xdr->put16(m, ew->ehdr.e_machine); + ew->xdr->put32(m, ew->ehdr.e_version); + if (ew->bit64) { + ew->xdr->put64(m, ew->ehdr.e_entry); + ew->xdr->put64(m, ew->ehdr.e_phoff); + ew->xdr->put64(m, ew->ehdr.e_shoff); + } else { + ew->xdr->put32(m, ew->ehdr.e_entry); + ew->xdr->put32(m, ew->ehdr.e_phoff); + ew->xdr->put32(m, ew->ehdr.e_shoff); + } + ew->xdr->put32(m, ew->ehdr.e_flags); + ew->xdr->put16(m, ew->ehdr.e_ehsize); + ew->xdr->put16(m, ew->ehdr.e_phentsize); + ew->xdr->put16(m, ew->ehdr.e_phnum); + ew->xdr->put16(m, ew->ehdr.e_shentsize); + ew->xdr->put16(m, ew->ehdr.e_shnum); + ew->xdr->put16(m, ew->ehdr.e_shstrndx); +} + +static void shdr_write(struct elf_writer *ew, size_t n, struct buffer *m) +{ + struct xdr *xdr = ew->xdr; + int bit64 = ew->bit64; + struct elf_writer_section *sec = &ew->sections[n]; + Elf64_Shdr *shdr = &sec->shdr; + + xdr->put32(m, shdr->sh_name); + xdr->put32(m, shdr->sh_type); + xdr->put32(m, shdr->sh_flags); + if (bit64) { + xdr->put64(m, shdr->sh_addr); + xdr->put64(m, shdr->sh_offset); + xdr->put64(m, shdr->sh_size); + xdr->put32(m, shdr->sh_link); + xdr->put32(m, shdr->sh_info); + xdr->put64(m, shdr->sh_addralign); + xdr->put64(m, shdr->sh_entsize); + } else { + xdr->put32(m, shdr->sh_addr); + xdr->put32(m, shdr->sh_offset); + xdr->put32(m, shdr->sh_size); + xdr->put32(m, shdr->sh_link); + xdr->put32(m, shdr->sh_info); + xdr->put32(m, shdr->sh_addralign); + xdr->put32(m, shdr->sh_entsize); + } +} + +static void +phdr_write(struct elf_writer *ew, struct buffer *m, Elf64_Phdr *phdr) +{ + if (ew->bit64) { + ew->xdr->put32(m, phdr->p_type); + ew->xdr->put32(m, phdr->p_flags); + ew->xdr->put64(m, phdr->p_offset); + ew->xdr->put64(m, phdr->p_vaddr); + ew->xdr->put64(m, phdr->p_paddr); + ew->xdr->put64(m, phdr->p_filesz); + ew->xdr->put64(m, phdr->p_memsz); + ew->xdr->put64(m, phdr->p_align); + } else { + ew->xdr->put32(m, phdr->p_type); + ew->xdr->put32(m, phdr->p_offset); + ew->xdr->put32(m, phdr->p_vaddr); + ew->xdr->put32(m, phdr->p_paddr); + ew->xdr->put32(m, phdr->p_filesz); + ew->xdr->put32(m, phdr->p_memsz); + ew->xdr->put32(m, phdr->p_flags); + ew->xdr->put32(m, phdr->p_align); + } + +} + +/* + * Serialize the ELF file to the output buffer. Return < 0 on error, + * 0 on success. + */ +int elf_writer_serialize(struct elf_writer *ew, struct buffer *out) +{ + Elf64_Half i; + Elf64_Xword metadata_size; + Elf64_Xword program_size; + Elf64_Off shstroffset; + size_t shstrlen; + struct buffer metadata; + struct buffer phdrs; + struct buffer data; + struct buffer *strtab; + + INFO("Writing %zu sections.\n", ew->num_secs); + + /* Determine size of sections to be written. */ + program_size = 0; + /* Start with 1 byte for first byte of section header string table. */ + shstrlen = 1; + for (i = 0; i < ew->num_secs; i++) { + struct elf_writer_section *sec = &ew->sections[i]; + + if (sec->shdr.sh_flags & SHF_ALLOC) + ew->ehdr.e_phnum++; + + program_size += buffer_size(&sec->content); + + /* Keep track of the length sections' names. */ + if (sec->name != NULL) { + sec->shdr.sh_name = shstrlen; + shstrlen += strlen(sec->name) + 1; + } + } + ew->ehdr.e_shnum = ew->num_secs; + metadata_size = 0; + metadata_size += ew->ehdr.e_ehsize; + metadata_size += ew->ehdr.e_shnum * ew->ehdr.e_shentsize; + metadata_size += ew->ehdr.e_phnum * ew->ehdr.e_phentsize; + shstroffset = metadata_size; + /* Align up section header string size and metadata size to 4KiB */ + metadata_size = ALIGN(metadata_size + shstrlen, 4096); + + if (buffer_create(out, metadata_size + program_size, "elfout")) { + ERROR("Could not create output buffer for ELF.\n"); + return -1; + } + + INFO("Created %zu output buffer for ELF file.\n", buffer_size(out)); + + /* + * Write out ELF header. Section headers come right after ELF header + * followed by the program headers. Buffers need to be created first + * to do the writing. + */ + ew->ehdr.e_shoff = ew->ehdr.e_ehsize; + ew->ehdr.e_phoff = ew->ehdr.e_shoff + + ew->ehdr.e_shnum * ew->ehdr.e_shentsize; + + buffer_splice(&metadata, out, 0, metadata_size); + buffer_splice(&phdrs, out, ew->ehdr.e_phoff, + ew->ehdr.e_phnum * ew->ehdr.e_phentsize); + buffer_splice(&data, out, metadata_size, program_size); + /* Set up the section header string table contents. */ + strtab = &ew->shstrtab->content; + buffer_splice(strtab, out, shstroffset, shstrlen); + ew->shstrtab->shdr.sh_size = shstrlen; + + /* Reset current locations. */ + buffer_set_size(&metadata, 0); + buffer_set_size(&data, 0); + buffer_set_size(&phdrs, 0); + buffer_set_size(strtab, 0); + + /* ELF Header */ + ehdr_write(ew, &metadata); + + /* Write out section headers, section strings, section content, and + * program headers. */ + ew->xdr->put8(strtab, 0); + for (i = 0; i < ew->num_secs; i++) { + Elf64_Phdr phdr; + struct elf_writer_section *sec = &ew->sections[i]; + + /* Update section offsets. Be sure to not update SHT_NULL. */ + if (sec == ew->shstrtab) + sec->shdr.sh_offset = shstroffset; + else if (i != 0) + sec->shdr.sh_offset = buffer_size(&data) + + metadata_size; + shdr_write(ew, i, &metadata); + + /* Add section name to string table. */ + if (sec->name != NULL) + bputs(strtab, sec->name, strlen(sec->name) + 1); + + if (!(sec->shdr.sh_flags & SHF_ALLOC)) + continue; + + bputs(&data, buffer_get(&sec->content), + buffer_size(&sec->content)); + + phdr.p_type = PT_LOAD; + phdr.p_offset = sec->shdr.sh_offset; + phdr.p_vaddr = sec->shdr.sh_addr; + phdr.p_paddr = sec->shdr.sh_addr; + phdr.p_filesz = buffer_size(&sec->content); + phdr.p_memsz = sec->shdr.sh_size; + phdr.p_flags = 0; + if (sec->shdr.sh_flags & SHF_EXECINSTR) + phdr.p_flags |= PF_X | PF_R; + if (sec->shdr.sh_flags & SHF_WRITE) + phdr.p_flags |= PF_W; + phdr.p_align = sec->shdr.sh_addralign; + phdr_write(ew, &phdrs, &phdr); + } + + return 0; +} diff --git a/util/cbfstool/elfparsing.h b/util/cbfstool/elfparsing.h new file mode 100644 index 0000000000..0db1c595a2 --- /dev/null +++ b/util/cbfstool/elfparsing.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 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 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 ELFPARSING_H +#define ELFPARSING_H + +#include "elf.h" +#include "common.h" + +struct parsed_elf { + Elf64_Ehdr ehdr; + Elf64_Phdr *phdr; + Elf64_Shdr *shdr; + /* + * The relocs array contains pointers to arrays of relocation + * structures. Each index into the relocs array corresponds to its + * corresponding section index. i.e. if a section i is of type SHT_REL + * or SHT_RELA then the corresponding index into the relocs array will + * contain the associated relocations. Otherwise thee entry will be + * NULL. + */ + Elf64_Rela **relocs; + /* + * Similarly to the relocs array the strtabs array consists of an + * array of pointers where each entry represents a potential struct + * buffer pointer. Only setions of type SHT_STRTAB will have a non-NULL + * entry. + */ + struct buffer **strtabs; + /* Parsed symbols. */ + Elf64_Sym *syms; +}; + +#define ELF_PARSE_PHDR (1 << 0) +#define ELF_PARSE_SHDR (1 << 1) +#define ELF_PARSE_RELOC (1 << 2) +#define ELF_PARSE_STRTAB (1 << 3) +#define ELF_PARSE_SYMTAB (1 << 4) + +#define ELF_PARSE_ALL (-1) + +/* + * Parse an ELF file contained within provide struct buffer. The ELF header + * is always parsed while the flags value containing the ELF_PARSE_* values + * determine if other parts of the ELF file will be parsed as well. + * Returns 0 on success, < 0 error. + */ +int parse_elf(const struct buffer *pinput, struct parsed_elf *pelf, int flags); + +/* + * Clean up memory associated with parsed_elf. + */ +void parsed_elf_destroy(struct parsed_elf *pelf); + + +int +elf_headers(const struct buffer *pinput, + Elf64_Ehdr *ehdr, + Elf64_Phdr **pphdr, + Elf64_Shdr **pshdr); + +/* ELF writing support. */ +struct elf_writer; + +/* + * Initialize a new ELF writer. Deafult machine type, endianness, etc is + * copied from the passed in Elf64_Ehdr. Returns NULL on failure, valid + * pointer on success. + */ +struct elf_writer *elf_writer_init(const Elf64_Ehdr *ehdr); + +/* + * Clean up any internal state represented by ew. Aftewards the elf_writer + * is invalid. + */ +void elf_writer_destroy(struct elf_writer *ew); + +/* + * Add a section to the ELF file. Section type, flags, and memsize are + * maintained from the passed in Elf64_Shdr. The buffer represents the + * content of the section while the name is the name of section itself. + * Returns < 0 on error, 0 on success. + */ +int elf_writer_add_section(struct elf_writer *ew, const Elf64_Shdr *shdr, + struct buffer *contents, const char *name); + +/* + * Serialize the ELF file to the output buffer. Return < 0 on error, + * 0 on success. + */ +int elf_writer_serialize(struct elf_writer *ew, struct buffer *out); + +#endif /* ELFPARSING_H */ diff --git a/util/cbfstool/fit.c b/util/cbfstool/fit.c index 4762518d05..ed136a6cff 100644 --- a/util/cbfstool/fit.c +++ b/util/cbfstool/fit.c @@ -23,7 +23,6 @@ #include #include "common.h" -#include "elf.h" #include "cbfs.h" #include "cbfs_image.h" #include "fit.h" @@ -188,17 +187,28 @@ static void add_microcodde_entries(struct cbfs_image *image, } } +static int fit_header(void *ptr, uint32_t *current_offset, uint32_t *file_length) +{ + struct buffer buf; + struct cbfs_file header; + buf.data = ptr; + buf.size = sizeof(header); + cbfs_file_get_header(&buf, &header); + *current_offset = header.offset; + *file_length = header.len; + return 0; +} + static int parse_microcode_blob(struct cbfs_image *image, struct cbfs_file *mcode_file, struct microcode_entry *mcus, int *total_mcus) { int num_mcus; - int current_offset; - int file_length; + uint32_t current_offset; + uint32_t file_length; current_offset = (int)((char *)mcode_file - image->buffer.data); - current_offset += ntohl(mcode_file->offset); - file_length = ntohl(mcode_file->len); + fit_header(mcode_file, ¤t_offset, &file_length); num_mcus = 0; while (file_length > sizeof(struct microcode_header)) diff --git a/util/cbfstool/rmodtool.c b/util/cbfstool/rmodtool.c new file mode 100644 index 0000000000..1455cd9a3b --- /dev/null +++ b/util/cbfstool/rmodtool.c @@ -0,0 +1,106 @@ +/* + * cbfstool, CLI utility for creating rmodules + * + * Copyright (C) 2014 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 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 +#include +#include +#include +#include +#include "common.h" +#include "rmodule.h" + +static const char *optstring = "i:o:vh?"; +static struct option long_options[] = { + {"inelf", required_argument, 0, 'i' }, + {"outelf", required_argument, 0, 'o' }, + {"verbose", no_argument, 0, 'v' }, + {"help", no_argument, 0, 'h' }, + {NULL, 0, 0, 0 } +}; + +static void usage(char *name) +{ + printf( + "rmodtool: utility for creating rmodules\n\n" + "USAGE: %s [-h] [-v] <-i|--inelf name> <-o|--outelf name>\n", + name + ); +} + +int main(int argc, char *argv[]) +{ + int c; + struct buffer elfin; + struct buffer elfout; + const char *input_file = NULL; + const char *output_file = NULL; + + if (argc < 3) { + usage(argv[0]); + return 1; + } + + while (1) { + int optindex = 0; + + c = getopt_long(argc, argv, optstring, long_options, &optindex); + + if (c == -1) + break; + + switch (c) { + case 'i': + input_file = optarg; + break; + case 'h': + usage(argv[0]); + return 1; + case 'o': + output_file = optarg; + break; + case 'v': + verbose++; + break; + default: + break; + } + } + + if (input_file == NULL || output_file == NULL) { + usage(argv[0]); + return 1; + } + + if (buffer_from_file(&elfin, input_file)) { + ERROR("Couldn't read in file '%s'.\n", input_file); + return 1; + } + + if (rmodule_create(&elfin, &elfout)) { + ERROR("Unable to create rmodule from '%s'.\n", input_file); + return 1; + } + + if (buffer_write_file(&elfout, output_file)) { + ERROR("Unable to write rmodule elf '%s'.\n", output_file); + return 1; + } + + return 0; +} diff --git a/util/cbfstool/rmodule.c b/util/cbfstool/rmodule.c new file mode 100644 index 0000000000..ef4571328b --- /dev/null +++ b/util/cbfstool/rmodule.c @@ -0,0 +1,631 @@ +/* + ;* Copyright (C) 2014 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 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 +#include +#include + +#include "elfparsing.h" +#include "rmodule.h" +#include "../../src/include/rmodule-defs.h" + +struct rmod_context; + +struct arch_ops { + int arch; + /* Determine if relocation is a valid type for the architecture. */ + int (*valid_type)(struct rmod_context *ctx, Elf64_Rela *rel); + /* Determine if relocation should be emitted. */ + int (*should_emit)(struct rmod_context *ctx, Elf64_Rela *rel); +}; + +struct rmod_context { + /* Ops to process relocations. */ + struct arch_ops *ops; + + /* endian conversion ops */ + struct xdr *xdr; + + /* Parsed ELF sturcture. */ + struct parsed_elf pelf; + /* Program segment. */ + Elf64_Phdr *phdr; + + /* Collection of relocation addresses fixup in the module. */ + Elf64_Xword nrelocs; + Elf64_Addr *emitted_relocs; + + /* The following fields are addresses within the linked program. */ + Elf64_Addr link_addr; + Elf64_Addr entry; + Elf64_Addr parameters_begin; + Elf64_Addr parameters_end; + Elf64_Addr bss_begin; + Elf64_Addr bss_end; + Elf64_Xword size; +}; + +/* + * Architecture specific support operations. + */ +static int valid_reloc_386(struct rmod_context *ctx, Elf64_Rela *rel) +{ + int type; + + type = ELF64_R_TYPE(rel->r_info); + + /* Only these 2 relocations are expected to be found. */ + return (type == R_386_32 || type == R_386_PC32); +} + +static int should_emit_386(struct rmod_context *ctx, Elf64_Rela *rel) +{ + int type; + + type = ELF64_R_TYPE(rel->r_info); + + /* R_386_32 relocations are absolute. Must emit these. */ + return (type == R_386_32); +} + +static struct arch_ops reloc_ops[] = { + { + .arch = EM_386, + .valid_type = valid_reloc_386, + .should_emit = should_emit_386, + }, +}; + +/* + * Relocation processing loops. + */ + +static int for_each_reloc(struct rmod_context *ctx, int do_emit) +{ + Elf64_Half i; + struct parsed_elf *pelf = &ctx->pelf; + + for (i = 0; i < pelf->ehdr.e_shnum; i++) { + Elf64_Shdr *shdr; + Elf64_Rela *relocs; + Elf64_Xword nrelocs; + Elf64_Xword j; + + relocs = pelf->relocs[i]; + + /* No relocations in this section. */ + if (relocs == NULL) + continue; + + shdr = &pelf->shdr[i]; + nrelocs = shdr->sh_size / shdr->sh_entsize; + + for (j = 0; j < nrelocs; j++) { + Elf64_Rela *r = &relocs[j]; + + if (!ctx->ops->valid_type(ctx, r)) { + ERROR("Invalid reloc type: %u\n", + (unsigned int)ELF64_R_TYPE(r->r_info)); + return -1; + } + + if (ctx->ops->should_emit(ctx, r)) { + int n = ctx->nrelocs; + if (do_emit) + ctx->emitted_relocs[n] = r->r_offset; + ctx->nrelocs++; + } + } + } + + return 0; +} + +static int find_program_segment(struct rmod_context *ctx) +{ + int i; + int nsegments; + struct parsed_elf *pelf; + Elf64_Phdr *phdr; + + pelf = &ctx->pelf; + + /* Silence 'may be used unitilized in this function' warnings. */ + phdr = NULL; + + /* There should only be a single loadable segment. */ + nsegments = 0; + for (i = 0; i < pelf->ehdr.e_phnum; i++) { + if (pelf->phdr[i].p_type != PT_LOAD) + continue; + phdr = &pelf->phdr[i]; + nsegments++; + } + + if (nsegments != 1) { + ERROR("Unexepcted number of loadable segments: %d.\n", + nsegments); + return -1; + } + + INFO("Segment at 0x%0llx, file size 0x%0llx, mem size 0x%0llx.\n", + (long long)phdr->p_vaddr, (long long)phdr->p_filesz, + (long long)phdr->p_memsz); + + ctx->phdr = phdr; + + return 0; +} + +static int +filter_relocation_sections(struct rmod_context *ctx) +{ + int i; + const char *shstrtab; + struct parsed_elf *pelf; + const Elf64_Phdr *phdr; + + pelf = &ctx->pelf; + phdr = ctx->phdr; + shstrtab = buffer_get(pelf->strtabs[pelf->ehdr.e_shstrndx]); + + /* + * Find all relocation sections that contain relocation entries + * for sections that fall within the bounds of the segment. For + * easier processing the pointer to the relocation array for the + * sections that don't fall within the loadable program are NULL'd + * out. + */ + for (i = 0; i < pelf->ehdr.e_shnum; i++) { + Elf64_Shdr *shdr; + Elf64_Word sh_info; + const char *section_name; + + shdr = &pelf->shdr[i]; + + /* Ignore non-relocation sections. */ + if (shdr->sh_type != SHT_RELA && shdr->sh_type != SHT_REL) + continue; + + /* Obtain section which relocations apply. */ + sh_info = shdr->sh_info; + shdr = &pelf->shdr[sh_info]; + + section_name = &shstrtab[shdr->sh_name]; + DEBUG("Relocation section found for '%s' section.\n", + section_name); + + /* Do not process relocations for debug sections. */ + if (strstr(section_name, ".debug") != NULL) { + pelf->relocs[i] = NULL; + continue; + } + + /* + * If relocations apply to a non program section ignore the + * relocations for future processing. + */ + if (shdr->sh_type != SHT_PROGBITS) { + pelf->relocs[i] = NULL; + continue; + } + + if (shdr->sh_addr < phdr->p_vaddr || + ((shdr->sh_addr + shdr->sh_size) > + (phdr->p_vaddr + phdr->p_memsz))) { + ERROR("Relocations being applied to section %d not " + "within segment region.\n", sh_info); + return -1; + } + } + + return 0; +} + +static int vaddr_cmp(const void *a, const void *b) +{ + const Elf64_Addr *pa = a; + const Elf64_Addr *pb = b; + + if (*pa < *pb) + return -1; + if (*pa > *pb) + return 1; + return 0; +} + +static int collect_relocations(struct rmod_context *ctx) +{ + int nrelocs; + + /* + * The relocs array in the pelf should only contain relocations that + * apply to the program. Count the number relocations. Then collect + * them into the allocated buffer. + */ + if (for_each_reloc(ctx, 0)) + return -1; + + nrelocs = ctx->nrelocs; + INFO("%d relocations to be emitted.\n", nrelocs); + if (!nrelocs) { + ERROR("No valid relocations in file.\n"); + return -1; + } + + /* Reset the counter for indexing into the array. */ + ctx->nrelocs = 0; + ctx->emitted_relocs = calloc(nrelocs, sizeof(Elf64_Addr)); + /* Write out the relocations into the emitted_relocs array. */ + if (for_each_reloc(ctx, 1)) + return -1; + + if (ctx->nrelocs != nrelocs) { + ERROR("Mismatch counted and emitted relocations: %zu vs %zu.\n", + (size_t)nrelocs, (size_t)ctx->nrelocs); + return -1; + } + + /* Sort the relocations by their address. */ + qsort(ctx->emitted_relocs, nrelocs, sizeof(Elf64_Addr), vaddr_cmp); + + return 0; +} + +static int +populate_sym(struct rmod_context *ctx, const char *sym_name, Elf64_Addr *addr, + int nsyms, const char *strtab) +{ + int i; + Elf64_Sym *syms; + + syms = ctx->pelf.syms; + + for (i = 0; i < nsyms; i++) { + if (syms[i].st_name == 0) + continue; + if (strcmp(sym_name, &strtab[syms[i].st_name])) + continue; + DEBUG("%s -> 0x%llx\n", sym_name, (long long)syms[i].st_value); + *addr = syms[i].st_value; + return 0; + } + ERROR("symbol '%s' not found.\n", sym_name); + return -1; +} + +static int populate_program_info(struct rmod_context *ctx) +{ + int i; + const char *strtab; + struct parsed_elf *pelf; + Elf64_Ehdr *ehdr; + int nsyms; + + pelf = &ctx->pelf; + ehdr = &pelf->ehdr; + + /* Obtain the string table. */ + strtab = NULL; + for (i = 0; i < ehdr->e_shnum; i++) { + if (ctx->pelf.strtabs[i] == NULL) + continue; + /* Don't use the section headers' string table. */ + if (i == ehdr->e_shstrndx) + continue; + strtab = buffer_get(ctx->pelf.strtabs[i]); + break; + } + + if (strtab == NULL) { + ERROR("No string table found.\n"); + return -1; + } + + /* Determine number of symbols. */ + nsyms = 0; + for (i = 0; i < ehdr->e_shnum; i++) { + if (pelf->shdr[i].sh_type != SHT_SYMTAB) + continue; + + nsyms = pelf->shdr[i].sh_size / pelf->shdr[i].sh_entsize; + break; + } + + if (populate_sym(ctx, "_module_params_begin", &ctx->parameters_begin, + nsyms, strtab)) + return -1; + + if (populate_sym(ctx, "_module_params_end", &ctx->parameters_end, + nsyms, strtab)) + return -1; + + if (populate_sym(ctx, "_bss", &ctx->bss_begin, nsyms, strtab)) + return -1; + + if (populate_sym(ctx, "_ebss", &ctx->bss_end, nsyms, strtab)) + return -1; + + if (populate_sym(ctx, "__rmodule_entry", &ctx->entry, nsyms, strtab)) + return -1; + + /* Link address is the virtual address of the program segment. */ + ctx->link_addr = ctx->phdr->p_vaddr; + + /* The program size is the memsz of the program segment. */ + ctx->size = ctx->phdr->p_memsz; + + return 0; +} + +static int +add_section(struct elf_writer *ew, struct buffer *data, const char *name, + Elf64_Addr addr, Elf64_Word size) +{ + Elf64_Shdr shdr; + int ret; + + memset(&shdr, 0, sizeof(shdr)); + if (data != NULL) { + shdr.sh_type = SHT_PROGBITS; + shdr.sh_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; + } else { + shdr.sh_type = SHT_NOBITS; + shdr.sh_flags = SHF_ALLOC; + } + shdr.sh_addr = addr; + shdr.sh_offset = addr; + shdr.sh_size = size; + + ret = elf_writer_add_section(ew, &shdr, data, name); + + if (ret) + ERROR("Could not add '%s' section.\n", name); + + return ret; +} + +static int +write_elf(const struct rmod_context *ctx, const struct buffer *in, + struct buffer *out) +{ + int i; + int ret; + int bit64; + size_t loc; + size_t rmod_data_size; + struct elf_writer *ew; + struct buffer rmod_data; + struct buffer rmod_header; + struct buffer program; + struct buffer relocs; + Elf64_Xword total_size; + Elf64_Addr addr; + Elf64_Ehdr ehdr; + + bit64 = ctx->pelf.ehdr.e_ident[EI_CLASS] == ELFCLASS64; + + /* + * 3 sections will be added to the ELF file. + * +------------------+ + * | rmodule header | + * +------------------+ + * | program | + * +------------------+ + * | relocations | + * +------------------+ + */ + + /* Create buffer for header and relocations. */ + rmod_data_size = sizeof(struct rmodule_header); + if (bit64) + rmod_data_size += ctx->nrelocs * sizeof(Elf64_Addr); + else + rmod_data_size += ctx->nrelocs * sizeof(Elf32_Addr); + + if (buffer_create(&rmod_data, rmod_data_size, "rmod")) + return -1; + + buffer_splice(&rmod_header, &rmod_data, + 0, sizeof(struct rmodule_header)); + buffer_clone(&relocs, &rmod_data); + buffer_seek(&relocs, sizeof(struct rmodule_header)); + + /* Reset current location. */ + buffer_set_size(&rmod_header, 0); + buffer_set_size(&relocs, 0); + + /* Program contents. */ + buffer_splice(&program, in, ctx->phdr->p_offset, ctx->phdr->p_filesz); + + /* Create ELF writer with modified entry point. */ + memcpy(&ehdr, &ctx->pelf.ehdr, sizeof(ehdr)); + ehdr.e_entry = ctx->entry; + ew = elf_writer_init(&ehdr); + + if (ew == NULL) { + ERROR("Failed to create ELF writer.\n"); + buffer_delete(&rmod_data); + return -1; + } + + /* Write out rmodule_header. */ + ctx->xdr->put16(&rmod_header, RMODULE_MAGIC); + ctx->xdr->put8(&rmod_header, RMODULE_VERSION_1); + ctx->xdr->put8(&rmod_header, 0); + /* payload_begin_offset */ + loc = sizeof(struct rmodule_header); + ctx->xdr->put32(&rmod_header, loc); + /* payload_end_offset */ + loc += ctx->phdr->p_filesz; + ctx->xdr->put32(&rmod_header, loc); + /* relocations_begin_offset */ + ctx->xdr->put32(&rmod_header, loc); + /* relocations_end_offset */ + if (bit64) + loc += ctx->nrelocs * sizeof(Elf64_Addr); + else + loc += ctx->nrelocs * sizeof(Elf32_Addr); + ctx->xdr->put32(&rmod_header, loc); + /* module_link_start_address */ + ctx->xdr->put32(&rmod_header, ctx->link_addr); + /* module_program_size */ + ctx->xdr->put32(&rmod_header, ctx->size); + /* module_entry_point */ + ctx->xdr->put32(&rmod_header, ctx->entry); + /* parameters_begin */ + ctx->xdr->put32(&rmod_header, ctx->parameters_begin); + /* parameters_end */ + ctx->xdr->put32(&rmod_header, ctx->parameters_end); + /* bss_begin */ + ctx->xdr->put32(&rmod_header, ctx->bss_begin); + /* bss_end */ + ctx->xdr->put32(&rmod_header, ctx->bss_end); + /* padding[4] */ + ctx->xdr->put32(&rmod_header, 0); + ctx->xdr->put32(&rmod_header, 0); + ctx->xdr->put32(&rmod_header, 0); + ctx->xdr->put32(&rmod_header, 0); + + /* Write the relocations. */ + for (i = 0; i < ctx->nrelocs; i++) { + if (bit64) + ctx->xdr->put64(&relocs, ctx->emitted_relocs[i]); + else + ctx->xdr->put32(&relocs, ctx->emitted_relocs[i]); + } + + total_size = 0; + addr = 0; + + /* + * There are 2 cases to deal with. The program has a large NOBITS + * section and the relocations can fit entirely within occupied memory + * region for the program. The other is that the relocations increase + * the memory footprint of the program if it was loaded directly into + * the region it would run. The rmdoule header is a fixed cost that + * is considered a part of the program. + */ + total_size += buffer_size(&rmod_header); + total_size += ctx->phdr->p_memsz; + if (buffer_size(&relocs) + ctx->phdr->p_filesz > total_size) { + total_size -= ctx->phdr->p_memsz; + total_size += buffer_size(&relocs); + total_size += ctx->phdr->p_filesz; + } + + ret = add_section(ew, &rmod_header, ".header", addr, + buffer_size(&rmod_header)); + if (ret < 0) + goto out; + addr += buffer_size(&rmod_header); + + ret = add_section(ew, &program, ".program", addr, ctx->phdr->p_filesz); + if (ret < 0) + goto out; + addr += ctx->phdr->p_filesz; + + ret = add_section(ew, &relocs, ".relocs", addr, buffer_size(&relocs)); + if (ret < 0) + goto out; + addr += buffer_size(&relocs); + + if (total_size != addr) { + ret = add_section(ew, NULL, ".empty", addr, total_size - addr); + if (ret < 0) + goto out; + } + + /* + * Ensure last section has a memory usage that meets the required + * total size of the program in memory. + */ + + ret = elf_writer_serialize(ew, out); + if (ret < 0) + ERROR("Failed to serialize ELF to buffer.\n"); + +out: + buffer_delete(&rmod_data); + elf_writer_destroy(ew); + + return ret; +} + +int rmodule_create(const struct buffer *elfin, struct buffer *elfout) +{ + struct rmod_context ctx; + struct parsed_elf *pelf; + int i; + int ret; + + ret = -1; + memset(&ctx, 0, sizeof(ctx)); + pelf = &ctx.pelf; + + if (parse_elf(elfin, pelf, ELF_PARSE_ALL)) { + ERROR("Couldn't parse ELF!\n"); + return -1; + } + + /* Only allow executables to be turned into rmodules. */ + if (pelf->ehdr.e_type != ET_EXEC) { + ERROR("ELF is not an executable: %u.\n", pelf->ehdr.e_type); + goto out; + } + + /* Determine if architecture is supported. */ + for (i = 0; i < ARRAY_SIZE(reloc_ops); i++) { + if (reloc_ops[i].arch == pelf->ehdr.e_machine) { + ctx.ops = &reloc_ops[i]; + break; + } + } + + if (ctx.ops == NULL) { + ERROR("ELF is unsupported arch: %u.\n", pelf->ehdr.e_machine); + goto out; + } + + /* Set the endian ops. */ + if (ctx.pelf.ehdr.e_ident[EI_DATA] == ELFDATA2MSB) + ctx.xdr = &xdr_be; + else + ctx.xdr = &xdr_le; + + if (find_program_segment(&ctx)) + goto out; + + if (filter_relocation_sections(&ctx)) + goto out; + + if (collect_relocations(&ctx)) + goto out; + + if (populate_program_info(&ctx)) + goto out; + + if (write_elf(&ctx, elfin, elfout)) + goto out; + + ret = 0; + +out: + free(ctx.emitted_relocs); + parsed_elf_destroy(pelf); + return ret; +} diff --git a/src/arch/x86/boot/ramstage_module_header.c b/util/cbfstool/rmodule.h similarity index 58% rename from src/arch/x86/boot/ramstage_module_header.c rename to util/cbfstool/rmodule.h index b958c16085..94f8f3d064 100644 --- a/src/arch/x86/boot/ramstage_module_header.c +++ b/util/cbfstool/rmodule.h @@ -1,7 +1,5 @@ /* - * This file is part of the coreboot project. - * - * Copyright (C) 2013 ChromeOS Authors + * Copyright (C) 2014 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 @@ -14,11 +12,19 @@ * * 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 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA */ -#include +#ifndef TOOL_RMODULE_H +#define TOOL_RMODULE_H -extern char _start[]; +#include "elf.h" +#include "common.h" -DEFINE_RMODULE_HEADER(ramstage_module, _start, RMODULE_TYPE_STAGE); +/* + * Parse an ELF file within the elfin buffer and fill in the elfout buffer + * with a created rmodule in ELF format. Return 0 on success, < 0 on error. + */ +int rmodule_create(const struct buffer *elfin, struct buffer *elfout); + +#endif /* TOOL_RMODULE_H */ diff --git a/util/cbfstool/xdr.c b/util/cbfstool/xdr.c index c819c9c565..c29e538279 100644 --- a/util/cbfstool/xdr.c +++ b/util/cbfstool/xdr.c @@ -25,6 +25,22 @@ #include #include "common.h" +size_t bgets(struct buffer *input, void *output, size_t len) +{ + len = input->size < len ? input->size : len; + memmove(output, input->data, len); + input->data += len; + input->size -= len; + return len; +} + +size_t bputs(struct buffer *b, const void *data, size_t len) +{ + memmove(&b->data[b->size], data, len); + b->size += len; + return len; +} + /* The assumption in all this code is that we're given a pointer to enough data. * Hence, we do not check for underflow. */ @@ -130,12 +146,12 @@ static void put64le(struct buffer *input, uint64_t val) } struct xdr xdr_be = { - get16be, get32be, get64be, - put16be, put32be, put64be + get8, get16be, get32be, get64be, + put8, put16be, put32be, put64be }; struct xdr xdr_le = { - get16le, get32le, get64le, - put16le, put32le, put64le + get8, get16le, get32le, get64le, + put8, put16le, put32le, put64le };