c0190dd1b554b13c6ccca45f92b0173b83cc558c
[firefly-linux-kernel-4.4.55.git] / lib / pie.c
1 /*
2  * Copyright 2013 Texas Instruments, Inc.
3  *      Russ Dill <russ.dill@ti.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/err.h>
13 #include <linux/io.h>
14 #include <linux/genalloc.h>
15 #include <linux/pie.h>
16 #include <asm/cacheflush.h>
17
18 struct pie_chunk {
19         struct gen_pool *pool;
20         unsigned long addr;
21         size_t sz;
22 };
23
24 extern char __pie_common_start[];
25 extern char __pie_common_end[];
26 extern char __pie_overlay_start[];
27
28 int __weak pie_arch_fill_tail(void *tail, void *common_start, void *common_end,
29                         void *overlay_start, void *code_start, void *code_end)
30 {
31         return 0;
32 }
33
34 int __weak pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail,
35                                                         unsigned long offset)
36 {
37         return 0;
38 }
39
40 struct pie_chunk *__pie_load_data(struct gen_pool *pool, void *code_start,
41                                         void *code_end, bool phys)
42 {
43         struct pie_chunk *chunk;
44         unsigned long offset;
45         int ret;
46         char *tail;
47         size_t common_sz;
48         size_t code_sz;
49         size_t tail_sz;
50
51         /* Calculate the tail size */
52         ret = pie_arch_fill_tail(NULL, __pie_common_start, __pie_common_end,
53                                 __pie_overlay_start, code_start, code_end);
54         if (ret < 0)
55                 goto err;
56         tail_sz = ret;
57
58         chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
59         if (!chunk) {
60                 ret = -ENOMEM;
61                 goto err;
62         }
63
64         common_sz = __pie_overlay_start - __pie_common_start;
65         code_sz = code_end - code_start;
66
67         chunk->pool = pool;
68         chunk->sz = common_sz + code_sz + tail_sz;
69
70         chunk->addr = gen_pool_alloc(pool, chunk->sz);
71         if (!chunk->addr) {
72                 ret = -ENOMEM;
73                 goto err_free;
74         }
75
76         /* Copy common code/data */
77         tail = (char *) chunk->addr;
78         memcpy(tail, __pie_common_start, common_sz);
79         tail += common_sz;
80
81         /* Copy chunk specific code/data */
82         memcpy(tail, code_start, code_sz);
83         tail += code_sz;
84
85         /* Fill in tail data */
86         ret = pie_arch_fill_tail(tail, __pie_common_start, __pie_common_end,
87                                 __pie_overlay_start, code_start, code_end);
88         if (ret < 0)
89                 goto err_alloc;
90
91         /* Calculate initial offset */
92         if (phys)
93                 offset = gen_pool_virt_to_phys(pool, chunk->addr);
94         else
95                 offset = chunk->addr;
96
97         /* Perform arch specific code fixups */
98         ret = pie_arch_fixup(chunk, (void *) chunk->addr, tail, offset);
99         if (ret < 0)
100                 goto err_alloc;
101
102         flush_icache_range(chunk->addr, chunk->addr + chunk->sz);
103
104         return chunk;
105
106 err_alloc:
107         gen_pool_free(chunk->pool, chunk->addr, chunk->sz);
108
109 err_free:
110         kfree(chunk);
111 err:
112         return ERR_PTR(ret);
113 }
114 EXPORT_SYMBOL_GPL(__pie_load_data);
115
116 phys_addr_t pie_to_phys(struct pie_chunk *chunk, unsigned long addr)
117 {
118         return gen_pool_virt_to_phys(chunk->pool, addr);
119 }
120 EXPORT_SYMBOL_GPL(pie_to_phys);
121
122 void __iomem *__kern_to_pie(struct pie_chunk *chunk, void *ptr)
123 {
124         uintptr_t offset = (uintptr_t) ptr;
125         offset -= (uintptr_t) __pie_common_start;
126         if (offset >= chunk->sz)
127                 return NULL;
128         else
129                 return (void *) (chunk->addr + offset);
130 }
131 EXPORT_SYMBOL_GPL(__kern_to_pie);
132
133 void pie_free(struct pie_chunk *chunk)
134 {
135         gen_pool_free(chunk->pool, chunk->addr, chunk->sz);
136         kfree(chunk);
137 }
138 EXPORT_SYMBOL_GPL(pie_free);