Merge branch 'dev' into kernel
[kernel/swap-modules.git] / driver / 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_file.h"
26 #include "sspt_page.h"
27 #include <linux/slab.h>
28 #include <linux/list.h>
29 #include <linux/hash.h>
30 #include <dbi_kprobes_deps.h>
31
32 static int calculation_hash_bits(int cnt)
33 {
34         int bits;
35         for (bits = 1; cnt >>= 1; ++bits);
36
37         return bits;
38 }
39
40 struct sspt_file *sspt_file_create(char *name, struct dentry *dentry, int page_cnt)
41 {
42         struct sspt_file *obj = kmalloc(sizeof(*obj), GFP_ATOMIC);
43
44         if (obj) {
45                 int i, table_size;
46                 INIT_LIST_HEAD(&obj->list);
47                 obj->procs = NULL;
48                 obj->name = name;
49                 obj->dentry = dentry;
50                 obj->loaded = 0;
51                 obj->vm_start = 0;
52                 obj->vm_end = 0;
53
54                 obj->page_probes_hash_bits = calculation_hash_bits(page_cnt);//PAGE_PROBES_HASH_BITS;
55                 table_size = (1 << obj->page_probes_hash_bits);
56
57                 obj->page_probes_table = kmalloc(sizeof(*obj->page_probes_table)*table_size, GFP_ATOMIC);
58
59                 for (i = 0; i < table_size; ++i) {
60                         INIT_HLIST_HEAD(&obj->page_probes_table[i]);
61                 }
62         }
63
64         return obj;
65 }
66
67 void sspt_file_free(struct sspt_file *file)
68 {
69         struct hlist_node *p, *n;
70         struct hlist_head *head;
71         struct sspt_page *page;
72         int i, table_size = (1 << file->page_probes_hash_bits);
73
74         for (i = 0; i < table_size; ++i) {
75                 head = &file->page_probes_table[i];
76                 swap_hlist_for_each_entry_safe(page, p, n, head, hlist) {
77                         hlist_del(&page->hlist);
78                         sspt_page_free(page);
79                 }
80         }
81
82         kfree(file->page_probes_table);
83         kfree(file);
84 }
85
86 static void sspt_add_page(struct sspt_file *file, struct sspt_page *page)
87 {
88         page->file = file;
89         hlist_add_head(&page->hlist, &file->page_probes_table[hash_ptr((void *)page->offset,
90                                 file->page_probes_hash_bits)]);
91 }
92
93 struct sspt_file *sspt_file_copy(const struct sspt_file *file)
94 {
95         struct sspt_file *file_out;
96
97         if (file == NULL) {
98                 printk("### WARNING: file_p == NULL\n");
99                 return NULL;
100         }
101
102         file_out = kmalloc(sizeof(*file_out), GFP_ATOMIC);
103         if (file_out) {
104                 struct sspt_page *page = NULL;
105                 struct hlist_node *node = NULL;
106                 struct hlist_head *head = NULL;
107                 int i, table_size;
108                 INIT_LIST_HEAD(&file_out->list);
109                 file_out->procs = NULL;
110                 file_out->dentry = file->dentry;
111                 file_out->name = file->name;
112                 file_out->loaded = 0;
113                 file_out->vm_start = 0;
114                 file_out->vm_end = 0;
115
116                 file_out->page_probes_hash_bits = file->page_probes_hash_bits;
117                 table_size = (1 << file_out->page_probes_hash_bits);
118
119                 file_out->page_probes_table =
120                                 kmalloc(sizeof(*file_out->page_probes_table)*table_size, GFP_ATOMIC);
121
122                 for (i = 0; i < table_size; ++i) {
123                         INIT_HLIST_HEAD(&file_out->page_probes_table[i]);
124                 }
125
126                 // copy pages
127                 for (i = 0; i < table_size; ++i) {
128                         head = &file->page_probes_table[i];
129                         swap_hlist_for_each_entry(page, node, head, hlist) {
130                                 sspt_add_page(file_out, sspt_page_copy(page));
131                         }
132                 }
133         }
134
135         return file_out;
136 }
137
138 static struct sspt_page *sspt_find_page(struct sspt_file *file, unsigned long offset)
139 {
140         struct hlist_node *node;
141         struct hlist_head *head;
142         struct sspt_page *page;
143
144         head = &file->page_probes_table[hash_ptr((void *)offset, file->page_probes_hash_bits)];
145         swap_hlist_for_each_entry(page, node, head, hlist) {
146                 if (page->offset == offset) {
147                         return page;
148                 }
149         }
150
151         return NULL;
152 }
153
154 static struct sspt_page *sspt_find_page_or_new(struct sspt_file *file, unsigned long offset)
155 {
156         struct sspt_page *page = sspt_find_page(file, offset);
157
158         if (page == NULL) {
159                 page = sspt_page_create(offset);
160                 sspt_add_page(file, page);
161         }
162
163         return page;
164 }
165
166 struct sspt_page *sspt_find_page_mapped(struct sspt_file *file, unsigned long page)
167 {
168         unsigned long offset;
169
170         if (file->vm_start > page || file->vm_end < page) {
171                 // TODO: or panic?!
172                 printk("ERROR: file_p[vm_start..vm_end] <> page: file_p[vm_start=%lx, vm_end=%lx, path=%s, d_iname=%s] page=%lx\n",
173                                 file->vm_start, file->vm_end, file->name, file->dentry->d_iname, page);
174                 return NULL;
175         }
176
177         offset = page - file->vm_start;
178
179         return sspt_find_page(file, offset);
180 }
181
182 void sspt_file_add_ip(struct sspt_file *file, struct ip_data *ip_d)
183 {
184         unsigned long offset = ip_d->offset & PAGE_MASK;
185         struct sspt_page *page = sspt_find_page_or_new(file, offset);
186
187         // FIXME: delete ip
188         struct us_ip *ip = create_ip_by_ip_data(ip_d);
189
190         sspt_add_ip(page, ip);
191 }
192
193 struct sspt_page *sspt_get_page(struct sspt_file *file, unsigned long offset_addr)
194 {
195         unsigned long offset = offset_addr & PAGE_MASK;
196         struct sspt_page *page = sspt_find_page_or_new(file, offset);
197
198         spin_lock(&page->lock);
199
200         return page;
201 }
202
203 void sspt_put_page(struct sspt_page *page)
204 {
205         spin_unlock(&page->lock);
206 }