2 * Dynamic Binary Instrumentation Module based on KProbes
3 * modules/driver/sspt/sspt_file.c
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.
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.
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.
19 * Copyright (C) Samsung Electronics, 2013
21 * 2013 Vyacheslav Cherkashin <v.cherkashin@samsung.com>
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>
35 static int calculation_hash_bits(int cnt)
38 for (bits = 1; cnt >>= 1; ++bits)
45 * @brief Create sspt_file struct
47 * @param dentry Dentry of file
48 * @param page_cnt Size of hash-table
49 * @return Pointer to the created sspt_file struct
51 struct sspt_file *sspt_file_create(struct dentry *dentry, int page_cnt)
54 struct sspt_file *obj = kmalloc(sizeof(*obj), GFP_ATOMIC);
59 INIT_LIST_HEAD(&obj->list);
66 obj->page_probes_hash_bits = calculation_hash_bits(page_cnt);
67 table_size = (1 << obj->page_probes_hash_bits);
69 obj->page_probes_table =
70 kmalloc(sizeof(*obj->page_probes_table)*table_size,
73 if (obj->page_probes_table == NULL)
76 for (i = 0; i < table_size; ++i)
77 INIT_HLIST_HEAD(&obj->page_probes_table[i]);
87 * @brief Remove sspt_file struct
89 * @param file remove object
92 void sspt_file_free(struct sspt_file *file)
94 struct hlist_head *head;
95 struct sspt_page *page;
96 int i, table_size = (1 << file->page_probes_hash_bits);
98 DECLARE_NODE_PTR_FOR_HLIST(p);
100 for (i = 0; i < table_size; ++i) {
101 head = &file->page_probes_table[i];
102 swap_hlist_for_each_entry_safe(page, p, n, head, hlist) {
103 hlist_del(&page->hlist);
104 sspt_page_free(page);
108 kfree(file->page_probes_table);
112 static void sspt_add_page(struct sspt_file *file, struct sspt_page *page)
115 hlist_add_head(&page->hlist,
116 &file->page_probes_table[hash_ptr(
117 (void *)page->offset,
118 file->page_probes_hash_bits)]);
121 static struct sspt_page *sspt_find_page(struct sspt_file *file,
122 unsigned long offset)
124 struct hlist_head *head;
125 struct sspt_page *page;
126 DECLARE_NODE_PTR_FOR_HLIST(node);
128 head = &file->page_probes_table[hash_ptr((void *)offset,
129 file->page_probes_hash_bits)];
130 swap_hlist_for_each_entry(page, node, head, hlist) {
131 if (page->offset == offset)
138 static struct sspt_page *sspt_find_page_or_new(struct sspt_file *file,
139 unsigned long offset)
141 struct sspt_page *page = sspt_find_page(file, offset);
144 page = sspt_page_create(offset);
146 sspt_add_page(file, page);
153 * @brief Get sspt_page from sspt_file
155 * @param file Pointer to the sspt_file struct
156 * @param page Page address
157 * @return Pointer to the sspt_page struct
159 struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
162 unsigned long offset;
164 if (file->vm_start > page || file->vm_end < page) {
165 /* TODO: or panic?! */
166 printk(KERN_INFO "ERROR: file_p[vm_start..vm_end] <> page: "
167 "file_p[vm_start=%lx, vm_end=%lx, "
168 "d_iname=%s] page=%lx\n",
169 file->vm_start, file->vm_end,
170 file->dentry->d_iname, page);
174 offset = page - file->vm_start;
176 return sspt_find_page(file, offset);
180 * @brief Add instruction pointer to sspt_file
182 * @param file Pointer to the sspt_file struct
183 * @param offset File offset
184 * @param args Function arguments
185 * @param ret_type Return type
188 void sspt_file_add_ip(struct sspt_file *file, unsigned long offset,
189 struct probe_info *probe_i)
191 struct sspt_page *page =
192 sspt_find_page_or_new(file, offset & PAGE_MASK);
194 /* FIXME: delete ip */
195 struct us_ip *ip = create_ip(offset, probe_i, page);
198 sspt_add_ip(page, ip);
201 void sspt_file_on_each_ip(struct sspt_file *file,
202 void (*func)(struct us_ip *, void *), void *data)
205 const int table_size = (1 << file->page_probes_hash_bits);
206 struct sspt_page *page;
207 struct hlist_head *head;
208 DECLARE_NODE_PTR_FOR_HLIST(node);
210 for (i = 0; i < table_size; ++i) {
211 head = &file->page_probes_table[i];
212 swap_hlist_for_each_entry(page, node, head, hlist)
213 sspt_page_on_each_ip(page, func, data);
218 * @brief Get sspt_page from sspt_file (look)
220 * @param file Pointer to the sspt_file struct
221 * @param offset_addr File offset
222 * @return Pointer to the sspt_page struct
224 struct sspt_page *sspt_get_page(struct sspt_file *file,
225 unsigned long offset_addr)
227 unsigned long offset = offset_addr & PAGE_MASK;
228 struct sspt_page *page = sspt_find_page_or_new(file, offset);
230 spin_lock(&page->lock);
236 * @brief Put sspt_page (unlook)
238 * @param file Pointer to the sspt_page struct
241 void sspt_put_page(struct sspt_page *page)
243 spin_unlock(&page->lock);
247 * @brief Check install sspt_file (legacy code, it is need remove)
249 * @param file Pointer to the sspt_file struct
254 int sspt_file_check_install_pages(struct sspt_file *file)
257 struct sspt_page *page;
258 struct hlist_head *head;
259 struct hlist_node *tmp;
260 DECLARE_NODE_PTR_FOR_HLIST(node);
262 table_size = (1 << file->page_probes_hash_bits);
263 for (i = 0; i < table_size; ++i) {
264 head = &file->page_probes_table[i];
265 swap_hlist_for_each_entry_safe(page, node, tmp, head, hlist) {
266 if (sspt_page_is_installed(page))
275 * @brief Install sspt_file
277 * @param file Pointer to the sspt_file struct
280 void sspt_file_install(struct sspt_file *file)
282 struct sspt_page *page = NULL;
283 struct hlist_head *head = NULL;
284 int i, table_size = (1 << file->page_probes_hash_bits);
285 unsigned long page_addr;
286 struct mm_struct *mm;
287 DECLARE_NODE_PTR_FOR_HLIST(node);
289 for (i = 0; i < table_size; ++i) {
290 head = &file->page_probes_table[i];
291 swap_hlist_for_each_entry_rcu(page, node, head, hlist) {
292 page_addr = file->vm_start + page->offset;
293 if (page_addr < file->vm_start ||
294 page_addr >= file->vm_end)
297 mm = page->file->proc->task->mm;
298 if (page_present(mm, page_addr))
299 sspt_register_page(page, file);
305 * @brief Uninstall sspt_file
307 * @param file Pointer to the sspt_file struct
308 * @param task Pointer to the task_stract struct
309 * @param flag Action for probes
312 int sspt_file_uninstall(struct sspt_file *file,
313 struct task_struct *task,
317 int table_size = (1 << file->page_probes_hash_bits);
318 struct sspt_page *page;
319 struct hlist_head *head;
320 struct hlist_node *tmp;
321 DECLARE_NODE_PTR_FOR_HLIST(node);
323 for (i = 0; i < table_size; ++i) {
324 head = &file->page_probes_table[i];
325 swap_hlist_for_each_entry_safe(page, node, tmp, head, hlist) {
326 err = sspt_unregister_page(page, flag, task);
328 printk(KERN_INFO "ERROR sspt_file_uninstall: "
335 if (flag != US_DISARM) {
345 * @brief Set mapping for sspt_file
347 * @param file Pointer to the sspt_file struct
348 * @param vma Pointer to the vm_area_struct struct
351 void sspt_file_set_mapping(struct sspt_file *file, struct vm_area_struct *vma)
353 if (file->loaded == 0) {
355 file->vm_start = vma->vm_start;
356 file->vm_end = vma->vm_end;