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