Merge branch 'tizen_2.4' into tizen_2.4_dev
[kernel/swap-modules.git] / us_manager / sspt / sspt_file.c
1 /*
2  *  Dynamic Binary Instrumentation Module based on KProbes
3  *  modules/driver/sspt/sspt_file.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 "sspt.h"
26 #include "sspt_file.h"
27 #include "sspt_page.h"
28 #include "sspt_proc.h"
29 #include <linux/slab.h>
30 #include <linux/list.h>
31 #include <linux/hash.h>
32 #include <linux/sched.h>
33 #include <kprobe/swap_kprobes_deps.h>
34 #include <us_manager/probes/probes.h>
35 #include <us_manager/img/img_ip.h>
36
37 static int calculation_hash_bits(int cnt)
38 {
39         int bits;
40         for (bits = 1; cnt >>= 1; ++bits)
41                 ;
42
43         return bits;
44 }
45
46 /**
47  * @brief Create sspt_file struct
48  *
49  * @param dentry Dentry of file
50  * @param page_cnt Size of hash-table
51  * @return Pointer to the created sspt_file struct
52  */
53 struct sspt_file *sspt_file_create(struct dentry *dentry, int page_cnt)
54 {
55         int i, table_size;
56         struct sspt_file *obj = kmalloc(sizeof(*obj), GFP_ATOMIC);
57
58         if (obj == NULL)
59                 return NULL;
60
61         INIT_LIST_HEAD(&obj->list);
62         obj->proc = NULL;
63         obj->dentry = dentry;
64         obj->loaded = 0;
65         obj->vm_start = 0;
66         obj->vm_end = 0;
67
68         obj->page_probes_hash_bits = calculation_hash_bits(page_cnt);
69         table_size = (1 << obj->page_probes_hash_bits);
70
71         obj->page_probes_table =
72                         kmalloc(sizeof(*obj->page_probes_table)*table_size,
73                                 GFP_ATOMIC);
74
75         if (obj->page_probes_table == NULL)
76                 goto err;
77
78         for (i = 0; i < table_size; ++i)
79                 INIT_HLIST_HEAD(&obj->page_probes_table[i]);
80
81         return obj;
82
83 err:
84         kfree(obj);
85         return NULL;
86 }
87
88 /**
89  * @brief Remove sspt_file struct
90  *
91  * @param file remove object
92  * @return Void
93  */
94 void sspt_file_free(struct sspt_file *file)
95 {
96         struct hlist_head *head;
97         struct sspt_page *page;
98         int i, table_size = (1 << file->page_probes_hash_bits);
99         struct hlist_node *n;
100         DECLARE_NODE_PTR_FOR_HLIST(p);
101
102         for (i = 0; i < table_size; ++i) {
103                 head = &file->page_probes_table[i];
104                 swap_hlist_for_each_entry_safe(page, p, n, head, hlist) {
105                         hlist_del(&page->hlist);
106                         sspt_page_free(page);
107                 }
108         }
109
110         kfree(file->page_probes_table);
111         kfree(file);
112 }
113
114 static void sspt_add_page(struct sspt_file *file, struct sspt_page *page)
115 {
116         page->file = file;
117         hlist_add_head(&page->hlist,
118                        &file->page_probes_table[hash_ptr(
119                                        (void *)page->offset,
120                                        file->page_probes_hash_bits)]);
121 }
122
123 static struct sspt_page *sspt_find_page(struct sspt_file *file,
124                                         unsigned long offset)
125 {
126         struct hlist_head *head;
127         struct sspt_page *page;
128         DECLARE_NODE_PTR_FOR_HLIST(node);
129
130         head = &file->page_probes_table[hash_ptr((void *)offset,
131                                                  file->page_probes_hash_bits)];
132         swap_hlist_for_each_entry(page, node, head, hlist) {
133                 if (page->offset == offset)
134                         return page;
135         }
136
137         return NULL;
138 }
139
140 static struct sspt_page *sspt_find_page_or_new(struct sspt_file *file,
141                                                unsigned long offset)
142 {
143         struct sspt_page *page = sspt_find_page(file, offset);
144
145         if (page == NULL) {
146                 page = sspt_page_create(offset);
147                 if (page)
148                         sspt_add_page(file, page);
149         }
150
151         return page;
152 }
153
154 /**
155  * @brief Get sspt_page from sspt_file
156  *
157  * @param file Pointer to the sspt_file struct
158  * @param page Page address
159  * @return Pointer to the sspt_page struct
160  */
161 struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
162                                         unsigned long page)
163 {
164         unsigned long offset;
165
166         if (file->vm_start > page || file->vm_end < page) {
167                 /* TODO: or panic?! */
168                 printk(KERN_INFO "ERROR: file_p[vm_start..vm_end] <> page: "
169                        "file_p[vm_start=%lx, vm_end=%lx, "
170                        "d_iname=%s] page=%lx\n",
171                        file->vm_start, file->vm_end,
172                        file->dentry->d_iname, page);
173                 return NULL;
174         }
175
176         offset = page - file->vm_start;
177
178         return sspt_find_page(file, offset);
179 }
180
181 /**
182  * @brief Add instruction pointer to sspt_file
183  *
184  * @param file Pointer to the sspt_file struct
185  * @param offset File offset
186  * @param args Function arguments
187  * @param ret_type Return type
188  * @return Void
189  */
190 void sspt_file_add_ip(struct sspt_file *file, struct img_ip *img_ip)
191 {
192         unsigned long offset = 0;
193         struct sspt_page *page = NULL;
194         struct us_ip *ip = NULL;
195
196         offset = img_ip->addr & PAGE_MASK;
197         page = sspt_find_page_or_new(file, offset);
198
199         /* FIXME: delete ip */
200         ip = create_ip(img_ip);
201         if (!ip)
202                 return;
203
204         sspt_add_ip(page, ip);
205         probe_info_init(ip->desc->type, ip);
206 }
207
208 void sspt_file_on_each_ip(struct sspt_file *file,
209                           void (*func)(struct us_ip *, void *), void *data)
210 {
211         int i;
212         const int table_size = (1 << file->page_probes_hash_bits);
213         struct sspt_page *page;
214         struct hlist_head *head;
215         DECLARE_NODE_PTR_FOR_HLIST(node);
216
217         for (i = 0; i < table_size; ++i) {
218                 head = &file->page_probes_table[i];
219                 swap_hlist_for_each_entry(page, node, head, hlist)
220                         sspt_page_on_each_ip(page, func, data);
221         }
222 }
223
224 /**
225  * @brief Get sspt_page from sspt_file (look)
226  *
227  * @param file Pointer to the sspt_file struct
228  * @param offset_addr File offset
229  * @return Pointer to the sspt_page struct
230  */
231 struct sspt_page *sspt_get_page(struct sspt_file *file,
232                                 unsigned long offset_addr)
233 {
234         unsigned long offset = offset_addr & PAGE_MASK;
235         struct sspt_page *page = sspt_find_page_or_new(file, offset);
236
237         spin_lock(&page->lock);
238
239         return page;
240 }
241
242 /**
243  * @brief Put sspt_page (unlook)
244  *
245  * @param file Pointer to the sspt_page struct
246  * @return void
247  */
248 void sspt_put_page(struct sspt_page *page)
249 {
250         spin_unlock(&page->lock);
251 }
252
253 /**
254  * @brief Check install sspt_file (legacy code, it is need remove)
255  *
256  * @param file Pointer to the sspt_file struct
257  * @return
258  *       - 0 - false
259  *       - 1 - true
260  */
261 int sspt_file_check_install_pages(struct sspt_file *file)
262 {
263         int i, table_size;
264         struct sspt_page *page;
265         struct hlist_head *head;
266         struct hlist_node *tmp;
267         DECLARE_NODE_PTR_FOR_HLIST(node);
268
269         table_size = (1 << file->page_probes_hash_bits);
270         for (i = 0; i < table_size; ++i) {
271                 head = &file->page_probes_table[i];
272                 swap_hlist_for_each_entry_safe(page, node, tmp, head, hlist) {
273                         if (sspt_page_is_installed(page))
274                                 return 1;
275                 }
276         }
277
278         return 0;
279 }
280
281 /**
282  * @brief Install sspt_file
283  *
284  * @param file Pointer to the sspt_file struct
285  * @return Void
286  */
287 void sspt_file_install(struct sspt_file *file)
288 {
289         struct sspt_page *page = NULL;
290         struct hlist_head *head = NULL;
291         int i, table_size = (1 << file->page_probes_hash_bits);
292         unsigned long page_addr;
293         struct mm_struct *mm;
294         DECLARE_NODE_PTR_FOR_HLIST(node);
295
296         for (i = 0; i < table_size; ++i) {
297                 head = &file->page_probes_table[i];
298                 swap_hlist_for_each_entry_rcu(page, node, head, hlist) {
299                         page_addr = file->vm_start + page->offset;
300                         if (page_addr < file->vm_start ||
301                             page_addr >= file->vm_end)
302                                 continue;
303
304                         mm = page->file->proc->task->mm;
305                         if (page_present(mm, page_addr))
306                                 sspt_register_page(page, file);
307                 }
308         }
309 }
310
311 /**
312  * @brief Uninstall sspt_file
313  *
314  * @param file Pointer to the sspt_file struct
315  * @param task Pointer to the task_stract struct
316  * @param flag Action for probes
317  * @return Void
318  */
319 int sspt_file_uninstall(struct sspt_file *file,
320                         struct task_struct *task,
321                         enum US_FLAGS flag)
322 {
323         int i, err = 0;
324         int table_size = (1 << file->page_probes_hash_bits);
325         struct sspt_page *page;
326         struct hlist_head *head;
327         struct hlist_node *tmp;
328         DECLARE_NODE_PTR_FOR_HLIST(node);
329
330         for (i = 0; i < table_size; ++i) {
331                 head = &file->page_probes_table[i];
332                 swap_hlist_for_each_entry_safe(page, node, tmp, head, hlist) {
333                         err = sspt_unregister_page(page, flag, task);
334                         if (err != 0) {
335                                 printk(KERN_INFO "ERROR sspt_file_uninstall: "
336                                        "err=%d\n", err);
337                                 return err;
338                         }
339                 }
340         }
341
342         if (flag != US_DISARM) {
343                 file->loaded = 0;
344                 file->vm_start = 0;
345                 file->vm_end = 0;
346         }
347
348         return err;
349 }
350
351 /**
352  * @brief Set mapping for sspt_file
353  *
354  * @param file Pointer to the sspt_file struct
355  * @param vma Pointer to the vm_area_struct struct
356  * @return Void
357  */
358 void sspt_file_set_mapping(struct sspt_file *file, struct vm_area_struct *vma)
359 {
360         if (file->loaded == 0) {
361                 file->loaded = 1;
362                 file->vm_start = vma->vm_start;
363                 file->vm_end = vma->vm_end;
364         }
365 }