From patchwork Mon Aug 29 13:03:34 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Belloni X-Patchwork-Id: 9303985 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D81B760756 for ; Mon, 29 Aug 2016 13:06:25 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C81CB2865F for ; Mon, 29 Aug 2016 13:06:25 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BC7C328670; Mon, 29 Aug 2016 13:06:25 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id CD7592865F for ; Mon, 29 Aug 2016 13:06:23 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1beMFG-0000fF-0e; Mon, 29 Aug 2016 13:04:58 +0000 Received: from down.free-electrons.com ([37.187.137.238] helo=mail.free-electrons.com) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1beMEh-0000Wu-9o for linux-arm-kernel@lists.infradead.org; Mon, 29 Aug 2016 13:04:27 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id E570C355; Mon, 29 Aug 2016 15:04:02 +0200 (CEST) Received: from localhost (unknown [88.191.26.124]) by mail.free-electrons.com (Postfix) with ESMTPSA id 6D8331C2; Mon, 29 Aug 2016 15:03:52 +0200 (CEST) From: Alexandre Belloni To: Russell King - ARM Linux Subject: [PATCH v3 1/2] ARM: PIE infrastructure Date: Mon, 29 Aug 2016 15:03:34 +0200 Message-Id: <20160829130335.12089-2-alexandre.belloni@free-electrons.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20160829130335.12089-1-alexandre.belloni@free-electrons.com> References: <20160829130335.12089-1-alexandre.belloni@free-electrons.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160829_060423_738882_35A1733C X-CRM114-Status: GOOD ( 26.74 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Heiko Stuebner , Nicolas Ferre , Doug Anderson , linux-kernel@vger.kernel.org, Alexandre Belloni , Dave Martin , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Add support for embedding position independent executables (PIE) in the kernel. Those PIEs can then be loaded into memory allocated using genalloc. For example, this allows running code from SRAM which is usually needed for suspend/resume or to change the DDR timings. That code is usually written in assembly and can now be developed in C. Signed-off-by: Alexandre Belloni --- arch/arm/Kconfig | 2 + arch/arm/Makefile | 1 + arch/arm/pie/Kconfig | 8 +++ arch/arm/pie/Makefile | 1 + arch/arm/pie/Makefile.pie | 86 +++++++++++++++++++++++++++ arch/arm/pie/lib/empty.c | 15 +++++ arch/arm/pie/pie.c | 97 ++++++++++++++++++++++++++++++ arch/arm/pie/pie.lds.S | 40 +++++++++++++ include/linux/pie.h | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 396 insertions(+) create mode 100644 arch/arm/pie/Kconfig create mode 100644 arch/arm/pie/Makefile create mode 100644 arch/arm/pie/Makefile.pie create mode 100644 arch/arm/pie/lib/empty.c create mode 100644 arch/arm/pie/pie.c create mode 100644 arch/arm/pie/pie.lds.S create mode 100644 include/linux/pie.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 2d601d769a1c..f7118c7f4634 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2207,3 +2207,5 @@ endif source "lib/Kconfig" source "arch/arm/kvm/Kconfig" + +source "arch/arm/pie/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 56ea5c60b318..2660722c7e59 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -281,6 +281,7 @@ core-$(CONFIG_VFP) += arch/arm/vfp/ core-$(CONFIG_XEN) += arch/arm/xen/ core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ core-$(CONFIG_VDSO) += arch/arm/vdso/ +core-$(CONFIG_PIE) += arch/arm/pie/ # If we have a machine-specific directory, then include it in the build. core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ diff --git a/arch/arm/pie/Kconfig b/arch/arm/pie/Kconfig new file mode 100644 index 000000000000..d76122140561 --- /dev/null +++ b/arch/arm/pie/Kconfig @@ -0,0 +1,8 @@ +config PIE + bool + help + This option adds support for embedding position indepentant (PIE) + executables into the kernel. The PIEs can then be copied into + genalloc regions such as SRAM and executed. Some platforms require + this for suspend/resume support. + diff --git a/arch/arm/pie/Makefile b/arch/arm/pie/Makefile new file mode 100644 index 000000000000..d1a6a823e431 --- /dev/null +++ b/arch/arm/pie/Makefile @@ -0,0 +1 @@ +obj-y += pie.o diff --git a/arch/arm/pie/Makefile.pie b/arch/arm/pie/Makefile.pie new file mode 100644 index 000000000000..88a003595f0e --- /dev/null +++ b/arch/arm/pie/Makefile.pie @@ -0,0 +1,86 @@ +obj-y := pie.bin.elf + +# Report unresolved symbol references +ldflags-y += --no-undefined +# Delete all temporary local symbols +ldflags-y += -X + +GCOV_PROFILE := n + +KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING + +ifeq ($(CONFIG_FUNCTION_TRACER),y) +ORIG_CFLAGS := $(KBUILD_CFLAGS) +KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS)) +endif + +# Reset objcopy flags +OBJCOPYFLAGS = + +$(obj)/empty.o: arch/arm/pie/lib/empty.c FORCE + $(call if_changed_rule,cc_o_c) + +# Reference gcc builtins for use in PIE with __pie_ +$(obj)/pie_rename.syms: $(obj)/empty.o + @$(NM) $^ | awk '{if ($$3) print $$3,"__pie_"$(PIE_NAME)$$3}' > $@ + +# For embedding address of the symbols copied from the PIE into the kernel +$(obj)/pie.syms: $(obj)/pie.elf + @$(NM) $^ | awk '{if ($$3 && $$2 == toupper($$2)) print $$3,"=","0x"$$1";"}' > $@ + +# Collect together the libpie objects +LDFLAGS_libpie_stage1.o += -r + +$(obj)/libpie_stage1.o: $(obj)/empty.o + $(call if_changed,ld) + +# Rename the libpie gcc builtins with a __pie_ prefix +OBJCOPYFLAGS_libpie_stage2.o += --redefine-syms=$(obj)/pie_rename.syms + +$(obj)/libpie_stage2.o: $(obj)/libpie_stage1.o + $(call if_changed,objcopy) + +CFLAGS_$(PIE_NAME).o += -fPIE + +OBJCOPYFLAGS_pie_stage1.o += --redefine-syms=$(obj)/pie_rename.syms +$(obj)/pie_stage1.o: $(obj)/$(PIE_NAME).o $(obj)/pie_rename.syms + $(call if_changed,objcopy) + +LDFLAGS_pie_stage2.o += -r + +$(obj)/pie_stage2.o: $(obj)/pie_stage1.o $(obj)/libpie_stage2.o + $(call if_changed,ld) + +SEDFLAGS_lds = s/PIE_NAME/$(PIE_NAME)/ +$(obj)/pie.lds.S: arch/arm/pie/pie.lds.S + @sed "$(SEDFLAGS_lds)" < $< > $@ + +# Create the position independent executable +LDFLAGS_pie.elf += -Bstatic -T $(obj)/pie.lds + +$(obj)/pie.elf: $(obj)/pie_stage2.o $(obj)/pie.lds + $(call if_changed,ld) + +# Create binary data for the kernel +OBJCOPYFLAGS_pie.bin += -O binary + +$(obj)/pie.bin: $(obj)/pie.elf + $(call if_changed,objcopy) + +# Import the data into the kernel +ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) +OBJCOPYFLAGS_pie.bin.o += -B $(ARCH) -I binary -O elf32-bigarm +else +OBJCOPYFLAGS_pie.bin.o += -B $(ARCH) -I binary -O elf32-littlearm +endif + +$(obj)/pie.al.c: $(obj)/pie.elf + @$(OBJDUMP) -h --section .text $^ | awk '{sub(/2\*\*/,"(1 << ",$$7);if ($$2 == ".text") print "unsigned int __pie_atmel_pm_align = "$$7");"}' > $@ + +$(obj)/pie.bin.o: $(obj)/pie.bin + $(call if_changed,objcopy) + +LDFLAGS_pie.bin.elf += --just-symbols=$(obj)/pie.syms -r +$(obj)/pie.bin.elf: $(obj)/pie.bin.o $(obj)/pie.al.o $(obj)/pie.syms + $(call if_changed,ld) + diff --git a/arch/arm/pie/lib/empty.c b/arch/arm/pie/lib/empty.c new file mode 100644 index 000000000000..9a6d54956379 --- /dev/null +++ b/arch/arm/pie/lib/empty.c @@ -0,0 +1,15 @@ +void __div0(void) +{ +}; + +void __aeabi_unwind_cpp_pr0(void) +{ +}; + +void __aeabi_unwind_cpp_pr1(void) +{ +}; + +void __aeabi_unwind_cpp_pr2(void) +{ +}; diff --git a/arch/arm/pie/pie.c b/arch/arm/pie/pie.c new file mode 100644 index 000000000000..b32aa4cb11a8 --- /dev/null +++ b/arch/arm/pie/pie.c @@ -0,0 +1,97 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct pie_chunk { + struct gen_pool *pool; + unsigned long addr; + unsigned int align_offset; + size_t sz; +}; + +struct pie_chunk *__pie_load_data(struct gen_pool *pool, void *code_start, + void *code_end, unsigned int align) +{ + struct pie_chunk *chunk; + unsigned long offset; + int ret; + size_t code_sz; + unsigned long base; + phys_addr_t pbase; + + chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); + if (!chunk) { + ret = -ENOMEM; + goto err; + } + + code_sz = code_end - code_start; + chunk->pool = pool; + chunk->sz = code_sz; + + base = gen_pool_alloc(pool, chunk->sz + align); + if (!base) { + ret = -ENOMEM; + goto err_free; + } + + pbase = gen_pool_virt_to_phys(pool, base); + chunk->addr = (unsigned long)__arm_ioremap_exec(pbase, code_sz, false); + if (!chunk->addr) { + ret = -ENOMEM; + goto err_remap; + } + + chunk->align_offset = chunk->addr % align; + chunk->addr += chunk->align_offset; + + memcpy((char *)chunk->addr, code_start, code_sz); + flush_icache_range(chunk->addr, chunk->addr + code_sz); + + offset = gen_pool_virt_to_phys(pool, chunk->addr); + + return chunk; + +err_remap: + gen_pool_free(chunk->pool, chunk->addr, chunk->sz); + +err_free: + kfree(chunk); +err: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(__pie_load_data); + +phys_addr_t pie_to_phys(struct pie_chunk *chunk, unsigned long addr) +{ + return gen_pool_virt_to_phys(chunk->pool, addr); +} +EXPORT_SYMBOL_GPL(pie_to_phys); + +void __iomem *__kern_to_pie(struct pie_chunk *chunk, void *ptr) +{ + uintptr_t offset = (uintptr_t)ptr; + + if (offset >= chunk->sz) + return NULL; + else + return (void *)(chunk->addr + offset); +} +EXPORT_SYMBOL_GPL(__kern_to_pie); + +void pie_free(struct pie_chunk *chunk) +{ + gen_pool_free(chunk->pool, chunk->addr, chunk->sz); + kfree(chunk); +} +EXPORT_SYMBOL_GPL(pie_free); diff --git a/arch/arm/pie/pie.lds.S b/arch/arm/pie/pie.lds.S new file mode 100644 index 000000000000..e640c93f5bc8 --- /dev/null +++ b/arch/arm/pie/pie.lds.S @@ -0,0 +1,40 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +#include + +SECTIONS +{ + /* Don't need unwind tables */ + /DISCARD/ : { + *(.ARM.exidx*) + *(.ARM.extab*) + *(.comment) + } + + . = 0x0; + + ____pie_PIE_NAME_start : { + VMLINUX_SYMBOL(__pie_PIE_NAME_start) = .; + } + + .text : { + . = ALIGN(4); + KEEP(*(.text)) + } + + .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } + . = ALIGN(4); + + .data : { + *(SORT_BY_ALIGNMENT(.data*)) + . = ALIGN(4); + + *(SORT_BY_ALIGNMENT(.bss*)) + . = ALIGN(4); + } + + ____pie_PIE_NAME_end : { + VMLINUX_SYMBOL(__pie_PIE_NAME_end) = .; + } +} diff --git a/include/linux/pie.h b/include/linux/pie.h new file mode 100644 index 000000000000..cec58384dda2 --- /dev/null +++ b/include/linux/pie.h @@ -0,0 +1,146 @@ +/* + * Copyright 2013 Texas Instruments, Inc. + * Russ Dill + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _LINUX_PIE_H +#define _LINUX_PIE_H + +#include +#include + +#include +#include + +struct gen_pool; +struct pie_chunk; + +#ifdef CONFIG_PIE + +/** + * __pie_load_data - load and fixup PIE code from kernel data + * @pool: pool to allocate memory from and copy code into + * @start: virtual start address in kernel of chunk specific code + * @end: virtual end address in kernel of chunk specific code + * @phys: %true to fixup to physical address of destination, %false to + * fixup to virtual address of destination + * + * Returns 0 on success, -EERROR otherwise + */ +struct pie_chunk *__pie_load_data(struct gen_pool *pool, void *start, void *end, + unsigned int align); + +/** + * pie_to_phys - translate a virtual PIE address into a physical one + * @chunk: identifier returned by pie_load_sections + * @addr: virtual address within pie chunk + * + * Returns physical address on success, -1 otherwise + */ +phys_addr_t pie_to_phys(struct pie_chunk *chunk, unsigned long addr); + +void __iomem *__kern_to_pie(struct pie_chunk *chunk, void *ptr); + +/** + * pie_free - free the pool space used by an pie chunk + * @chunk: identifier returned by pie_load_sections + */ +void pie_free(struct pie_chunk *chunk); + +#define __pie_load_sections(pool, name, folder) ({ \ + extern char _binary_##folder##_pie_bin_start[]; \ + extern char __pie_##name##_start[]; \ + extern char __pie_##name##_end[]; \ + extern unsigned int __pie_##name##_align; \ + char *start = _binary_##folder##_pie_bin_start + \ + (unsigned long)__pie_##name##_start; \ + char *end = _binary_##folder##_pie_bin_start + \ + (unsigned long)__pie_##name##_end; \ + __pie_load_data(pool, start, end, __pie_##name##_align); \ +}) + +/* + * Required for any symbol within an PIE section that is referenced by the + * kernel + */ +#define EXPORT_PIE_SYMBOL(sym) extern typeof(sym) sym __weak + +#else + +static inline struct pie_chunk *__pie_load_data(struct gen_pool *pool, + void *start, void *end, + unsigned int algin) +{ + return ERR_PTR(-EINVAL); +} + +static inline phys_addr_t pie_to_phys(struct pie_chunk *chunk, + unsigned long addr) +{ + return -1; +} + +static inline void __iomem *__kern_to_pie(struct pie_chunk *chunk, void *ptr) +{ + return NULL; +} + +static inline void pie_free(struct pie_chunk *chunk) +{ +} + +#define __pie_load_sections(pool, name, folder, phys) ({ ERR_PTR(-EINVAL); }) + +#endif + +/** + * pie_load_sections - load and fixup sections associated with the given name + * @pool: pool to allocate memory from and copy code into + * fixup to virtual address of destination + * @name: the name given to __pie() and __pie_data() when marking + * data and code + * + * Returns 0 on success, -EERROR otherwise + */ +#define pie_load_sections(pool, name, folder) ({ \ + __pie_load_sections(pool, name, folder); \ +}) + +/** + * kern_to_pie - convert a kernel symbol to the virtual address of where + * that symbol is loaded into the given PIE chunk. + * + * @chunk: identifier returned by pie_load_sections + * @p: symbol to convert + * + * Return type is the same as type passed + */ +#define kern_to_pie(chunk, p) ({ \ + void *__ptr = (void *)(p); \ + typeof(p) __result = (typeof(p))__kern_to_pie(chunk, __ptr); \ + __result; \ +}) + +/** + * kern_to_fn - convert a kernel function symbol to the virtual address of where + * that symbol is loaded into the given PIE chunk + * + * @chunk: identifier returned by pie_load_sections + * @funcp: function to convert + * + * Return type is the same as type passed + */ +#define fn_to_pie(chunk, funcp) ({ \ + uintptr_t __kern_addr, __pie_addr; \ + \ + __kern_addr = (uintptr_t)funcp; \ + __pie_addr = kern_to_pie(chunk, __kern_addr); \ + \ + (typeof(&funcp))(__pie_addr); \ +}) + +#endif