linux-uconsole/include/linux/pie.h
Russ Dill 2ecab0b339 PIE: Support embedding position independent executables
This commit adds support for embedding PIEs into the kernel, loading them
into genalloc sections, performing necessary relocations, and running code
from them. This allows platforms that need to run code from SRAM, such
an during suspend/resume, to develop that code in C instead of assembly.

Functions and data for each PIE should be grouped into sections with the
__pie(<group>) and __pie_data(<group>) macros respectively. Any symbols or
functions that are to be accessed from outside the PIE should be marked with
EXPORT_PIE_SYMBOL(<sym>). For example:

static struct ddr_timings xyz_timings __pie_data(platformxyz) = {
	[...]
};

void __pie(platformxyz) xyz_ddr_on(void *addr)
{
	[...]
}
EXPORT_PIE_SYMBOL(xyz_ddr_on);

While the kernel can access exported symbols from the PIE, the PIE cannot
access symbols from the kernel, but can access data from the kernel and
call functions in the kernel so long as addresses are passed into the PIE.

PIEs are loaded from the kernel into a genalloc pool with pie_load_sections.
pie_load_sections allocates space within the pool, copies the neccesary
code/data, and performs any necessary relocations. A chunk identifier is
returned for removing the PIE from the pool, and for translating symbols.

Because the PIEs are dynamically relocated, special accessors must be used
to access PIE symbols from kernel code:

- kern_to_pie(chunk, ptr):   Translate a PIE symbol to the virtual address
                             it is loaded into within the pool.

- fn_to_pie(chunk, ptr):     Same as above, but for function pointers.

- sram_to_phys(chunk, addr): Translate a virtual address within a loaded PIE
                             to a physical address.

Loading a PIE involves three main steps. First a set of common functions to
cover built-ins emitted by gcc (memcpy, memmove, etc) is copied into the pool.
Then the actual PIE code and data is copied into the pool. Because the PIE
code is contained within an overlay with other PIEs, offsets to the common
functions are maintained. Finally, relocations are performed as necessary.

Signed-off-by: Russ Dill <Russ.Dill@ti.com>
2013-11-21 13:39:21 +08:00

196 lines
5.8 KiB
C

/*
* Copyright 2013 Texas Instruments, Inc.
* Russ Dill <russ.dill@ti.com>
*
* 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 <linux/kernel.h>
#include <linux/err.h>
#include <asm/fncpy.h>
#include <asm/bug.h>
struct gen_pool;
struct pie_chunk;
/**
* pie_arch_fixup - arch specific fixups of copied PIE code
* @chunk: identifier to be used with kern_to_pie/pie_to_phys
* @base: virtual address of start of copied PIE section
* @tail: virtual address of tail data in copied PIE
* @offset: offset to apply to relocation entries.
*
* When this code is done executing, it should be possible to jump to code
* so long as it is located at the given offset.
*/
extern int pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail,
unsigned long offset);
/**
* pie_arch_fill_tail - arch specific tail information for copied PIE
* @tail: virtual address of tail data in copied PIE to be filled
* @common_start: virtual address of common code within kernel data
* @common_end: virtual end address of common code within kernel data
* @overlay_start: virtual address of first overlay within kernel data
* @code_start: virtual address of this overlay within kernel data
* @code_end: virtual end address of this overlay within kernel data
*
* Fill tail data with data necessary to for pie_arch_fixup to perform
* relocations. If tail is NULL, do not update data, but still calculate
* the number of bytes required.
*
* Returns number of bytes required/used for tail on success, -EERROR otherwise.
*/
extern int pie_arch_fill_tail(void *tail, void *common_start, void *common_end,
void *overlay_start, void *code_start, void *code_end);
#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
*/
extern struct pie_chunk *__pie_load_data(struct gen_pool *pool,
void *start, void *end, bool phys);
/**
* 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
*/
extern phys_addr_t pie_to_phys(struct pie_chunk *chunk, unsigned long addr);
extern 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
*/
extern void pie_free(struct pie_chunk *chunk);
#define __pie_load_sections(pool, name, phys) ({ \
extern char __pie_##name##_start[]; \
extern char __pie_##name##_end[]; \
\
__pie_load_data(pool, __pie_##name##_start, \
__pie_##name##_end, phys); \
})
/*
* Required for any symbol within an PIE section that is referenced by the
* kernel
*/
#define EXPORT_PIE_SYMBOL(sym) extern typeof(sym) sym __weak
/* For marking data and functions that should be part of a PIE */
#define __pie(name) __attribute__ ((__section__(".pie." #name ".text")))
#define __pie_data(name) __attribute__ ((__section__(".pie." #name ".data")))
#else
static inline struct pie_chunk *__pie_load_data(struct gen_pool *pool,
void *start, void *end, bool phys)
{
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, phys) ({ ERR_PTR(-EINVAL); })
#define EXPORT_PIE_SYMBOL(sym)
#define __pie(name)
#define __pie_data(name)
#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) ({ \
__pie_load_sections(pool, name, false); \
})
/**
* pie_load_sections_phys - load and fixup sections associated with the given
* name for execution with the MMU off
*
* @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_phys(pool, name) ({ \
__pie_load_sections(pool, name, true); \
})
/**
* 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
* @p: 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 = fnptr_to_addr(funcp); \
__pie_addr = kern_to_pie(chunk, __kern_addr); \
\
fnptr_translate(funcp, __pie_addr); \
})
#endif