Files
SDK_RK3288/kernel/lib/pie.c

143 lines
3.2 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.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/genalloc.h>
#include <linux/pie.h>
#include <asm/cacheflush.h>
struct pie_chunk {
struct gen_pool *pool;
unsigned long addr;
size_t sz;
};
extern char __pie_common_start[];
extern char __pie_common_end[];
extern char __pie_overlay_start[];
int __weak pie_arch_fill_tail(void *tail, void *common_start, void *common_end,
void *overlay_start, void *code_start, void *code_end,
void *rel_start, void *rel_end)
{
return 0;
}
int __weak pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail,
unsigned long offset)
{
return 0;
}
struct pie_chunk *__pie_load_data(struct gen_pool *pool, bool phys,
void *code_start, void *code_end,
void *rel_start, void *rel_end)
{
struct pie_chunk *chunk;
unsigned long offset;
int ret;
char *tail;
size_t common_sz;
size_t code_sz;
size_t tail_sz;
/* Calculate the tail size */
ret = pie_arch_fill_tail(NULL, __pie_common_start, __pie_common_end,
__pie_overlay_start, code_start, code_end,
rel_start, rel_end);
if (ret < 0)
goto err;
tail_sz = ret;
chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
if (!chunk) {
ret = -ENOMEM;
goto err;
}
common_sz = __pie_overlay_start - __pie_common_start;
code_sz = code_end - code_start;
chunk->pool = pool;
chunk->sz = common_sz + code_sz + tail_sz;
chunk->addr = gen_pool_alloc(pool, chunk->sz);
if (!chunk->addr) {
ret = -ENOMEM;
goto err_free;
}
/* Copy common code/data */
tail = (char *) chunk->addr;
memcpy(tail, __pie_common_start, common_sz);
tail += common_sz;
/* Copy chunk specific code/data */
memcpy(tail, code_start, code_sz);
tail += code_sz;
/* Fill in tail data */
ret = pie_arch_fill_tail(tail, __pie_common_start, __pie_common_end,
__pie_overlay_start, code_start, code_end,
rel_start, rel_end);
if (ret < 0)
goto err_alloc;
/* Calculate initial offset */
if (phys)
offset = gen_pool_virt_to_phys(pool, chunk->addr);
else
offset = chunk->addr;
/* Perform arch specific code fixups */
ret = pie_arch_fixup(chunk, (void *) chunk->addr, tail, offset);
if (ret < 0)
goto err_alloc;
flush_icache_range(chunk->addr, chunk->addr + chunk->sz);
return chunk;
err_alloc:
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;
offset -= (uintptr_t) __pie_common_start;
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);