mmc: dw_mmc: check card present before starting request
[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                         void *rel_start, void *rel_end)
31 {
32         return 0;
33 }
34
35 int __weak pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail,
36                                                         unsigned long offset)
37 {
38         return 0;
39 }
40
41 struct pie_chunk *__pie_load_data(struct gen_pool *pool, bool phys,
42                 void *code_start, void *code_end,
43                 void *rel_start, void *rel_end)
44 {
45         struct pie_chunk *chunk;
46         unsigned long offset;
47         int ret;
48         char *tail;
49         size_t common_sz;
50         size_t code_sz;
51         size_t tail_sz;
52
53         /* Calculate the tail size */
54         ret = pie_arch_fill_tail(NULL, __pie_common_start, __pie_common_end,
55                                 __pie_overlay_start, code_start, code_end,
56                                 rel_start, rel_end);
57         if (ret < 0)
58                 goto err;
59         tail_sz = ret;
60
61         chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
62         if (!chunk) {
63                 ret = -ENOMEM;
64                 goto err;
65         }
66
67         common_sz = __pie_overlay_start - __pie_common_start;
68         code_sz = code_end - code_start;
69
70         chunk->pool = pool;
71         chunk->sz = common_sz + code_sz + tail_sz;
72
73         chunk->addr = gen_pool_alloc(pool, chunk->sz);
74         if (!chunk->addr) {
75                 ret = -ENOMEM;
76                 goto err_free;
77         }
78
79         /* Copy common code/data */
80         tail = (char *) chunk->addr;
81         memcpy(tail, __pie_common_start, common_sz);
82         tail += common_sz;
83
84         /* Copy chunk specific code/data */
85         memcpy(tail, code_start, code_sz);
86         tail += code_sz;
87
88         /* Fill in tail data */
89         ret = pie_arch_fill_tail(tail, __pie_common_start, __pie_common_end,
90                                 __pie_overlay_start, code_start, code_end,
91                                 rel_start, rel_end);
92         if (ret < 0)
93                 goto err_alloc;
94
95         /* Calculate initial offset */
96         if (phys)
97                 offset = gen_pool_virt_to_phys(pool, chunk->addr);
98         else
99                 offset = chunk->addr;
100
101         /* Perform arch specific code fixups */
102         ret = pie_arch_fixup(chunk, (void *) chunk->addr, tail, offset);
103         if (ret < 0)
104                 goto err_alloc;
105
106         flush_icache_range(chunk->addr, chunk->addr + chunk->sz);
107
108         return chunk;
109
110 err_alloc:
111         gen_pool_free(chunk->pool, chunk->addr, chunk->sz);
112
113 err_free:
114         kfree(chunk);
115 err:
116         return ERR_PTR(ret);
117 }
118 EXPORT_SYMBOL_GPL(__pie_load_data);
119
120 phys_addr_t pie_to_phys(struct pie_chunk *chunk, unsigned long addr)
121 {
122         return gen_pool_virt_to_phys(chunk->pool, addr);
123 }
124 EXPORT_SYMBOL_GPL(pie_to_phys);
125
126 void __iomem *__kern_to_pie(struct pie_chunk *chunk, void *ptr)
127 {
128         uintptr_t offset = (uintptr_t) ptr;
129         offset -= (uintptr_t) __pie_common_start;
130         if (offset >= chunk->sz)
131                 return NULL;
132         else
133                 return (void *) (chunk->addr + offset);
134 }
135 EXPORT_SYMBOL_GPL(__kern_to_pie);
136
137 void pie_free(struct pie_chunk *chunk)
138 {
139         gen_pool_free(chunk->pool, chunk->addr, chunk->sz);
140         kfree(chunk);
141 }
142 EXPORT_SYMBOL_GPL(pie_free);