tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / kernel / swap / ksyms / no_ksyms.c
1 /**
2  * @file ksyms/no_ksyms.c
3  * @author Vyacheslav Cherkashin <v.cherkashin@samsung.com>
4  *
5  * @section LICENSE
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * @section COPYRIGHT
22  *
23  * Copyright (C) Samsung Electronics, 2013
24  *
25  * @section DESCRIPTION
26  *
27  * SWAP symbols searching implementation.
28  */
29
30 #include "ksyms.h"
31 #include "ksyms_init.h"
32 #include <linux/types.h>
33 #include <linux/vmalloc.h>
34 #include <linux/semaphore.h>
35 #include <linux/fs.h>
36 #include <linux/slab.h>
37 #include <linux/fcntl.h>
38
39 /**
40  * @def KSYMS_ERR
41  * Error message define.
42  */
43 #define KSYMS_ERR(format, args...) \
44         do { \
45                 char *f = __FILE__; \
46                 char *n = strrchr(f, '/'); \
47                 printk(KERN_INFO "%s:%u \'%s\' ERROR: " format "\n" ,  \
48                        (n) ? n+1 : f, __LINE__, __func__, ##args); \
49         } while (0)
50
51 /**
52  * @struct sys_map_item
53  * @brief System map list item info.
54  * @var sys_map_item::list
55  * List pointer.
56  * @var sys_map_item::addr
57  * Symbol address.
58  * @var sys_map_item::name
59  * Symbol name.
60  */
61 struct sys_map_item {
62         struct list_head list;
63
64         unsigned long addr;
65         char *name;
66 };
67
68 static char *sm_path;
69 module_param(sm_path, charp, 0);
70
71 /**
72  * @var smi_list
73  * List of sys_map_item.
74  */
75 LIST_HEAD(smi_list);
76 static struct file *file;
77
78 static int cnt_init_sm;
79
80 /**
81  * @var cnt_init_sm_lock
82  * System map items list lock.
83  */
84 DEFINE_SEMAPHORE(cnt_init_sm_lock);
85
86 static int file_open(void)
87 {
88         struct file *f = filp_open(sm_path, O_RDONLY, 0);
89
90         if (IS_ERR(f)) {
91                 KSYMS_ERR("cannot open file \'%s\'", sm_path);
92                 return PTR_ERR(f);
93         }
94
95         file = f;
96
97         return 0;
98 }
99
100 static void file_close(void)
101 {
102         if (file) {
103                 int ret = filp_close(file, NULL);
104                 file = NULL;
105
106                 if (ret) {
107                         KSYMS_ERR("while closing file \'%s\' err=%d",
108                                   sm_path, ret);
109                 }
110         }
111 }
112
113 static int file_check(void)
114 {
115         int ret = file_open();
116         if (ret == 0)
117                 file_close();
118
119         return ret;
120 }
121
122 static long file_size(struct file *file)
123 {
124         struct kstat st;
125         if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st))
126                 return -1;
127
128         if (!S_ISREG(st.mode))
129                 return -1;
130
131         if (st.size != (long)st.size)
132                 return -1;
133
134         return st.size;
135 }
136
137 static struct sys_map_item *create_smi(unsigned long addr, const char *name)
138 {
139         struct sys_map_item *smi = kmalloc(sizeof(*smi), GFP_KERNEL);
140
141         if (smi == NULL) {
142                 KSYMS_ERR("not enough memory");
143                 return NULL;
144         }
145
146         smi->name = kmalloc(strlen(name) + 1, GFP_KERNEL);
147         if (smi->name == NULL) {
148                 kfree(smi);
149                 KSYMS_ERR("not enough memory");
150                 return NULL;
151         }
152
153         INIT_LIST_HEAD(&smi->list);
154         smi->addr = addr;
155         strcpy(smi->name, name);
156
157         return smi;
158 }
159
160 static void free_smi(struct sys_map_item *smi)
161 {
162         kfree(smi->name);
163         kfree(smi);
164 }
165
166 static void add_smi(struct sys_map_item *smi)
167 {
168         list_add_tail(&smi->list, &smi_list);
169 }
170
171 static int is_endline(char c)
172 {
173         return c == '\n' || c == '\r' || c == '\0';
174 }
175
176 static int is_symbol_attr(char c)
177 {
178         return c == 't' || c == 'T';
179 }
180
181 static struct sys_map_item *get_sys_map_item(char *begin, char *end)
182 {
183         struct sys_map_item *smi = NULL;
184         int n, len = end - begin;
185         unsigned long addr;
186         char attr, name[128], *line;
187
188         line = kmalloc(len + 1, GFP_KERNEL);
189         memcpy(line, begin, len);
190         line[len] = '\0';
191
192         n = sscanf(line, "%lx %c %127s", &addr, &attr, name);
193         name[127] = '\0';
194
195         if (n != 3) {
196                 KSYMS_ERR("parsing line: \"%s\"", line);
197                 attr = '\0';
198         }
199
200         kfree(line);
201
202         if (is_symbol_attr(attr))
203                 smi = create_smi(addr, name);
204
205         return smi;
206 }
207
208
209 static void parsing(char *buf, int size)
210 {
211         struct sys_map_item *smi;
212         char *start, *end, *c;
213
214         start = buf;
215         end = buf + size;
216
217         for (c = start; c < end; ++c) {
218                 if (is_endline(*c)) {
219                         smi = get_sys_map_item(start, c);
220                         if (smi)
221                                 add_smi(smi);
222
223                         for (start = c; c < end; ++c) {
224                                 if (!is_endline(*c)) {
225                                         start = c;
226                                         break;
227                                 }
228                         }
229                 }
230         }
231 }
232
233 static int create_sys_map(void)
234 {
235         char *data;
236         long size;
237         int ret = file_open();
238
239         if (ret)
240                 return ret;
241
242         size = file_size(file);
243         if (size < 0) {
244                 KSYMS_ERR("cannot get file size");
245                 ret = size;
246                 goto close;
247         }
248
249         data = vmalloc(size);
250         if (data == NULL) {
251                 KSYMS_ERR("not enough memory");
252                 ret = -1;
253                 goto close;
254         }
255
256         if (kernel_read(file, 0, data, size) != size) {
257                 KSYMS_ERR("reading file %s", sm_path);
258                 ret = -1;
259                 goto free;
260         }
261
262         parsing(data, size);
263
264 free:
265         vfree(data);
266
267 close:
268         file_close();
269
270         return 0;
271 }
272
273 static void free_sys_map(void)
274 {
275         struct sys_map_item *smi, *n;
276         list_for_each_entry_safe(smi, n, &smi_list, list) {
277                 list_del(&smi->list);
278                 free_smi(smi);
279         }
280 }
281
282 /**
283  * @brief Generates symbols list.
284  *
285  * @return 0 on success.
286  */
287 int swap_get_ksyms(void)
288 {
289         int ret = 0;
290
291         down(&cnt_init_sm_lock);
292         if (cnt_init_sm == 0)
293                 ret = create_sys_map();
294
295         ++cnt_init_sm;
296         up(&cnt_init_sm_lock);
297
298         return ret;
299 }
300 EXPORT_SYMBOL_GPL(swap_get_ksyms);
301
302 /**
303  * @brief Frees symbols list.
304  *
305  * @return Void.
306  */
307 void swap_put_ksyms(void)
308 {
309         down(&cnt_init_sm_lock);
310         --cnt_init_sm;
311         if (cnt_init_sm == 0)
312                 free_sys_map();
313
314         if (cnt_init_sm < 0) {
315                 KSYMS_ERR("cnt_init_sm=%d", cnt_init_sm);
316                 cnt_init_sm = 0;
317         }
318
319         up(&cnt_init_sm_lock);
320 }
321 EXPORT_SYMBOL_GPL(swap_put_ksyms);
322
323 /**
324  * @brief Searches for symbol by its exact name.
325  *
326  * @param name Pointer the name string.
327  * @return Symbol's address.
328  */
329 unsigned long swap_ksyms(const char *name)
330 {
331         struct sys_map_item *smi;
332
333         list_for_each_entry(smi, &smi_list, list) {
334                 if (strcmp(name, smi->name) == 0)
335                         return smi->addr;
336         }
337
338         return 0;
339 }
340 EXPORT_SYMBOL_GPL(swap_ksyms);
341
342 /**
343  * @brief Searches for symbol by substring of its name.
344  *
345  * @param name Pointer to the name substring.
346  * @return Symbol's address.
347  */
348 unsigned long swap_ksyms_substr(const char *name)
349 {
350         struct sys_map_item *smi;
351         size_t len = strlen(name);
352
353         list_for_each_entry(smi, &smi_list, list) {
354                 if (strncmp(name, smi->name, len) == 0)
355                         return smi->addr;
356         }
357
358         return 0;
359 }
360 EXPORT_SYMBOL_GPL(swap_ksyms_substr);
361
362 /**
363  * @brief SWAP ksyms module initialization.
364  *
365  * @return 0 on success, negative error code on error.
366  */
367 int ksyms_init(void)
368 {
369         int ret = 0;
370
371         if (sm_path == NULL) {
372                 KSYMS_ERR("sm_path=NULL");
373                 return -EINVAL;
374         }
375
376         ret = file_check();
377         if (ret)
378                 return -EINVAL;
379
380         /* TODO: calling func 'swap_get_ksyms' in
381          * module used func 'swap_ksyms' */
382         swap_get_ksyms();
383
384         return 0;
385 }
386
387 /**
388  * @brief SWAP ksyms module deinitialization.
389  *
390  * @return Void.
391  */
392 void ksyms_exit(void)
393 {
394         down(&cnt_init_sm_lock);
395
396         if (cnt_init_sm > 0)
397                 free_sys_map();
398
399         up(&cnt_init_sm_lock);
400 }