1 /*
2 * Copyright (C) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <linux/slab.h>
20 #include <linux/vmalloc.h>
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <asm/io.h>
24 #include <linux/uaccess.h>
25 #include <linux/version.h>
26 #include <linux/mm.h>
27 #include <linux/memblock.h>
28
29 #include "hi_osal.h"
30 #include "hi_module.h"
31 #include "securec.h"
32
33 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
34 #ifndef CONFIG_64BIT
35 #include <mach/io.h>
36 #endif
37 #endif
38
39 unsigned int g_kmalloc_used[HI_ID_BUTT] = {0};
40 unsigned int g_vmalloc_used[HI_ID_BUTT] = {0};
41
osal_get_kmalloc_used(unsigned int module_id)42 unsigned int osal_get_kmalloc_used(unsigned int module_id)
43 {
44 if (module_id >= HI_ID_BUTT) {
45 return 0;
46 }
47
48 return g_kmalloc_used[module_id];
49 }
50
osal_get_vmalloc_used(unsigned int module_id)51 unsigned int osal_get_vmalloc_used(unsigned int module_id)
52 {
53 if (module_id >= HI_ID_BUTT) {
54 return 0;
55 }
56
57 return g_vmalloc_used[module_id];
58 }
59
osal_convert_gfp_flag(unsigned int osal_gfp_flag)60 static unsigned int osal_convert_gfp_flag(unsigned int osal_gfp_flag)
61 {
62 unsigned int gfp_flag;
63
64 switch (osal_gfp_flag) {
65 case OSAL_GFP_KERNEL:
66 gfp_flag = GFP_KERNEL;
67 break;
68
69 case OSAL_GFP_ATOMIC:
70 gfp_flag = GFP_ATOMIC;
71 break;
72
73 case OSAL_GFP_DMA:
74 gfp_flag = GFP_DMA;
75 break;
76
77 default:
78 gfp_flag = GFP_KERNEL;
79 break;
80 }
81
82 return gfp_flag;
83 }
84
osal_kmalloc(unsigned int module_id,unsigned long size,unsigned int osal_gfp_flag)85 void *osal_kmalloc(unsigned int module_id, unsigned long size, unsigned int osal_gfp_flag)
86 {
87 unsigned int gfp_flag;
88
89 if (module_id >= HI_ID_BUTT) {
90 printk("osal_kmalloc module id(%ud) is wrong\n", module_id);
91 osal_dump_stack();
92 return NULL;
93 }
94
95 g_kmalloc_used[module_id]++;
96 gfp_flag = osal_convert_gfp_flag(osal_gfp_flag);
97 return kmalloc(size, gfp_flag);
98 }
99 EXPORT_SYMBOL(osal_kmalloc);
100
osal_kfree(unsigned int module_id,const void * addr)101 void osal_kfree(unsigned int module_id, const void *addr)
102 {
103 if (module_id >= HI_ID_BUTT) {
104 printk("osal_kfree module id(%ud) is wrong\n", module_id);
105 osal_dump_stack();
106 return;
107 }
108
109 if (addr != NULL) {
110 g_kmalloc_used[module_id]--;
111 kfree(addr);
112 }
113 return;
114 }
115 EXPORT_SYMBOL(osal_kfree);
116
osal_vmalloc(unsigned int module_id,unsigned long size)117 void *osal_vmalloc(unsigned int module_id, unsigned long size)
118 {
119 if (module_id >= HI_ID_BUTT) {
120 printk("osal_vmalloc module id(%ud) is wrong\n", module_id);
121 osal_dump_stack();
122 return NULL;
123 }
124
125 g_vmalloc_used[module_id]++;
126
127 return vmalloc(size);
128 }
129
130 EXPORT_SYMBOL(osal_vmalloc);
131
osal_vfree(unsigned int module_id,const void * addr)132 void osal_vfree(unsigned int module_id, const void *addr)
133 {
134 if (module_id >= HI_ID_BUTT) {
135 printk("osal_vfree module id(%ud) is wrong\n", module_id);
136 osal_dump_stack();
137 return;
138 }
139
140 if (addr != NULL) {
141 g_vmalloc_used[module_id]--;
142 vfree(addr);
143 }
144 return;
145 }
146
147 EXPORT_SYMBOL(osal_vfree);
148
osal_ioremap(unsigned long phys_addr,unsigned long size)149 void *osal_ioremap(unsigned long phys_addr, unsigned long size)
150 {
151 return ioremap(phys_addr, size);
152 }
153 EXPORT_SYMBOL(osal_ioremap);
154
osal_ioremap_nocache(unsigned long phys_addr,unsigned long size)155 void *osal_ioremap_nocache(unsigned long phys_addr, unsigned long size)
156 {
157 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
158 return ioremap(phys_addr, size);
159 #else
160 return ioremap_nocache(phys_addr, size);
161 #endif
162 }
163 EXPORT_SYMBOL(osal_ioremap_nocache);
164
osal_ioremap_cached(unsigned long phys_addr,unsigned long size)165 void *osal_ioremap_cached(unsigned long phys_addr, unsigned long size)
166 {
167 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
168 return ioremap_cached(phys_addr, size);
169 #else
170 return ioremap_cache(phys_addr, size);
171 #endif
172 }
173 EXPORT_SYMBOL(osal_ioremap_cached);
174
osal_iounmap(volatile void * addr)175 void osal_iounmap(volatile void *addr)
176 {
177 iounmap(addr);
178 }
179 EXPORT_SYMBOL(osal_iounmap);
180
osal_copy_from_user(void * to,const void * from,unsigned long n)181 unsigned long osal_copy_from_user(void *to, const void *from, unsigned long n)
182 {
183 return copy_from_user(to, from, n);
184 }
185 EXPORT_SYMBOL(osal_copy_from_user);
186
osal_copy_to_user(void * to,const void * from,unsigned long n)187 unsigned long osal_copy_to_user(void *to, const void *from, unsigned long n)
188 {
189 return copy_to_user(to, from, n);
190 }
191 EXPORT_SYMBOL(osal_copy_to_user);
192
osal_access_ok(int type,const void * addr,unsigned long size)193 int osal_access_ok(int type, const void *addr, unsigned long size)
194 {
195 uintptr_t uaddr = (uintptr_t)addr;
196 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
197 return access_ok(uaddr, size);
198 #else
199 return access_ok(type, uaddr, size);
200 #endif
201 }
202 EXPORT_SYMBOL(osal_access_ok);
203
osal_phys_to_virt(unsigned long addr)204 void *osal_phys_to_virt(unsigned long addr)
205 {
206 return phys_to_virt(addr);
207 }
208 EXPORT_SYMBOL(osal_phys_to_virt);
209
osal_virt_to_phys(const void * virt_addr)210 unsigned long osal_virt_to_phys(const void *virt_addr)
211 {
212 return virt_to_phys(virt_addr);
213 }
214 EXPORT_SYMBOL(osal_virt_to_phys);
215
216 /*
217 * Maps @size from @phys_addr into contiguous kernel virtual space
218 * Note:this function only support VM_MAP with PAGE_KERNEL flag
219 * */
osal_blockmem_vmap(unsigned long phys_addr,unsigned long size)220 void *osal_blockmem_vmap(unsigned long phys_addr, unsigned long size)
221 {
222 int ret = 0;
223 unsigned int i = 0;
224 unsigned int page_count;
225 struct page **pages = NULL;
226 void *vaddr = NULL;
227
228 if ((phys_addr == 0) || (size == 0)) {
229 printk("invalid vmap address: 0x%lX or size:%lu\n", phys_addr, size);
230 return NULL;
231 }
232
233 page_count = (size + PAGE_SIZE - 1) / PAGE_SIZE;
234 pages = vmalloc(page_count * sizeof(struct page *));
235 if (!pages) {
236 printk("vmap malloc pages failed\n");
237 return NULL;
238 }
239
240 ret = memset_s(pages, page_count * sizeof(struct page *), 0, page_count * sizeof(struct page *));
241 if (ret != 0) {
242 return NULL;
243 }
244
245 for (i = 0; i < page_count; i++) {
246 pages[i] = phys_to_page(phys_addr + i * PAGE_SIZE);
247 }
248
249 vaddr = vmap(pages, page_count, VM_MAP, PAGE_KERNEL);
250 if (!vaddr) {
251 printk("vmap failed phys_addr:0x%lX, size:%lu\n", phys_addr, size);
252 }
253
254 vfree(pages);
255 pages = NULL;
256
257 return vaddr;
258 }
259 EXPORT_SYMBOL(osal_blockmem_vmap);
260
261 /*
262 * Free the virtually contiguous memory area starting at @virt_addr
263 * which was created from the phys_addr passed to osal_vunmap()
264 * Must not be called in interrupt context.
265 * */
osal_blockmem_vunmap(const void * virt_addr)266 void osal_blockmem_vunmap(const void *virt_addr)
267 {
268 if (!virt_addr) {
269 printk("vumap failed: virt_addr is NULL\n");
270 return;
271 }
272
273 vunmap(virt_addr);
274 }
275 EXPORT_SYMBOL(osal_blockmem_vunmap);
276
277 /*
278 * Free the reserved memory which has been defined in product
279 **/
osal_blockmem_free(unsigned long phys_addr,unsigned long size)280 void osal_blockmem_free(unsigned long phys_addr, unsigned long size)
281 {
282 unsigned int pfn_start;
283 unsigned int pfn_end;
284
285 if ((phys_addr == 0) || (size == 0)) {
286 printk("Free block memory failed: phys_addr 0x%lX,size %lu\n",
287 phys_addr, size);
288 return;
289 }
290
291 pfn_start = __phys_to_pfn(phys_addr);
292 pfn_end = __phys_to_pfn(phys_addr + size);
293
294 for (; pfn_start < pfn_end; pfn_start++) {
295 struct page *page = pfn_to_page(pfn_start);
296 ClearPageReserved(page);
297 init_page_count(page);
298 __free_page(page);
299 adjust_managed_page_count(page, 1); /* 1 block mem page count */
300 }
301
302 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)
303 #ifndef CONFIG_ARCH_DISCARD_MEMBLOCK
304 (void)memblock_free(phys_addr, size);
305 #endif
306 #endif
307 return;
308 }
309 EXPORT_SYMBOL(osal_blockmem_free);
310