2 * Kernel Probes (KProbes)
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) IBM Corporation, 2002, 2004
23 * Dynamic Binary Instrumentation Module based on KProbes
24 * modules/kprobe/dbi_insn_slots.c
26 * This program is free software; you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published by
28 * the Free Software Foundation; either version 2 of the License, or
29 * (at your option) any later version.
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
36 * You should have received a copy of the GNU General Public License
37 * along with this program; if not, write to the Free Software
38 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 * Copyright (C) Samsung Electronics, 2006-2012
42 * 2008-2009 Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
43 * Probes initial implementation; Support x86/ARM/MIPS for both user and kernel spaces.
44 * 2010 Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts
45 * 2012 Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
48 #include "dbi_insn_slots.h"
49 #include "dbi_uprobes.h"
50 #include "dbi_kdebug.h"
52 #include <linux/hash.h>
53 #include <linux/mman.h>
54 #include <linux/hugetlb.h>
56 #include <linux/slab.h>
57 #include <linux/spinlock.h>
59 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
60 extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
61 unsigned long len, unsigned long prot,
62 unsigned long flags, unsigned long pgoff, unsigned long *populate);
63 #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
64 extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
65 unsigned long len, unsigned long prot,
66 unsigned long flags, unsigned long pgoff);
67 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
69 struct hlist_head kprobe_insn_pages;
70 struct hlist_head uprobe_insn_pages;
74 unsigned long first_available;
75 unsigned long count_available;
82 struct kprobe_insn_page
84 struct hlist_node hlist;
87 struct task_struct *task;
90 static void chunk_init(struct chunk *chunk, void *data, size_t size, size_t size_block)
95 spin_lock_init(&chunk->lock);
96 chunk->data = (unsigned long *)data;
97 chunk->first_available = 0;
98 chunk->count_available = size / size_block;
99 chunk->size = chunk->count_available;
101 chunk->index = kmalloc(sizeof(*chunk->index)*chunk->count_available, GFP_ATOMIC);
104 for (i = 0; i != chunk->count_available; ++p) {
109 static void chunk_uninit(struct chunk *chunk)
114 static void* chunk_allocate(struct chunk *chunk, size_t size_block)
118 if (!chunk->count_available) {
122 spin_lock(&chunk->lock);
123 ret = chunk->data + chunk->first_available*size_block;
124 chunk->first_available = chunk->index[chunk->first_available];
125 --chunk->count_available;
126 spin_unlock(&chunk->lock);
131 static void chunk_deallocate(struct chunk *chunk, void *p, size_t size_block)
133 unsigned long idx = ((unsigned long *)p - chunk->data)/size_block;
135 spin_lock(&chunk->lock);
136 chunk->index[idx] = chunk->first_available;
137 chunk->first_available = idx;
138 ++chunk->count_available;
139 spin_unlock(&chunk->lock);
142 static inline int chunk_check_ptr(struct chunk *chunk, void *p, size_t size)
144 if (( chunk->data <= (unsigned long *)p) &&
145 ((chunk->data + size/sizeof(chunk->data)) > (unsigned long *)p)) {
152 static inline int chunk_free(struct chunk *chunk)
154 return (chunk->count_available == chunk->size);
157 static unsigned long alloc_user_pages(struct task_struct *task, unsigned long len, unsigned long prot, unsigned long flags, int atomic)
159 unsigned long ret = 0;
160 struct task_struct *otask = current;
161 struct mm_struct *mm;
163 mm = atomic ? task->active_mm : get_task_mm (task);
166 if (!down_write_trylock(&mm->mmap_sem)) {
169 up_read(&mm->mmap_sem);
170 down_write(&mm->mmap_sem);
175 // FIXME: its seems to be bad decision to replace 'current' pointer temporarily
176 current_thread_info()->task = task;
177 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
178 ret = do_mmap_pgoff(NULL, 0, len, prot, flags, 0, 0);
179 #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
180 ret = do_mmap_pgoff(NULL, 0, len, prot, flags, 0);
181 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
183 current_thread_info()->task = otask;
185 downgrade_write (&mm->mmap_sem);
189 printk("proc %d has no mm", task->tgid);
195 static void *page_new(struct task_struct *task, int atomic)
198 return (void *)alloc_user_pages(task, PAGE_SIZE,
199 PROT_EXEC|PROT_READ|PROT_WRITE,
200 MAP_ANONYMOUS|MAP_PRIVATE/*MAP_SHARED*/, atomic);
202 return kmalloc(PAGE_SIZE, GFP_ATOMIC);
206 static void page_free(void *data, struct task_struct *task)
209 //E. G.: This code provides kernel dump because of rescheduling while atomic.
210 //As workaround, this code was commented. In this case we will have memory leaks
211 //for instrumented process, but instrumentation process should functionate correctly.
212 //Planned that good solution for this problem will be done during redesigning KProbe
213 //for improving supportability and performance.
215 mm = get_task_mm (task);
217 down_write (&mm->mmap_sem);
218 do_munmap(mm, (unsigned long)(data), PAGE_SIZE);
219 up_write (&mm->mmap_sem);
223 // FIXME: implement the removal of memory for task
229 static inline size_t slot_size(struct task_struct *task)
232 return UPROBES_TRAMP_LEN;
234 return KPROBES_TRAMP_LEN;
238 static struct kprobe_insn_page *kip_new(struct task_struct *task, int atomic)
241 struct kprobe_insn_page *kip;
243 kip = kmalloc(sizeof(*kip), GFP_ATOMIC);
248 data = page_new(task, atomic);
254 chunk_init(&kip->chunk, data, PAGE_SIZE/sizeof(unsigned long), slot_size(task));
260 static void kip_free(struct kprobe_insn_page * kip)
262 chunk_uninit(&kip->chunk);
263 page_free(kip->chunk.data, kip->task);
268 * get_us_insn_slot() - Find a slot on an executable page for an instruction.
269 * We allocate an executable page if there's no room on existing ones.
271 kprobe_opcode_t *get_insn_slot(struct task_struct *task, int atomic)
273 kprobe_opcode_t * free_slot;
274 struct kprobe_insn_page *kip;
275 struct hlist_node *pos;
276 struct hlist_head *page_list = task ? &uprobe_insn_pages : &kprobe_insn_pages;
278 swap_hlist_for_each_entry_rcu(kip, pos, page_list, hlist) {
279 if (!task || (kip->task->tgid == task->tgid)) {
280 free_slot = chunk_allocate(&kip->chunk, slot_size(task));
281 if (free_slot == NULL) {
289 kip = kip_new(task, atomic);
293 INIT_HLIST_NODE (&kip->hlist);
294 hlist_add_head_rcu(&kip->hlist, page_list);
296 return chunk_allocate(&kip->chunk, slot_size(task));
299 void free_insn_slot(struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot)
301 struct kprobe_insn_page *kip;
302 struct hlist_node *pos;
304 swap_hlist_for_each_entry_rcu(kip, pos, page_list, hlist) {
305 if (!(!task || (kip->task->tgid == task->tgid)))
308 if (!chunk_check_ptr(&kip->chunk, slot, PAGE_SIZE))
311 chunk_deallocate(&kip->chunk, slot, slot_size(task));
313 if (chunk_free(&kip->chunk)) {
314 hlist_del_rcu(&kip->hlist);
321 panic("free_insn_slot: slot=%p is not data base\n", slot);
325 static struct kprobe *get_kprobe_by_insn_slot_arm(kprobe_opcode_t *addr, pid_t tgid)
327 struct hlist_head *head;
328 struct hlist_node *node;
329 struct kprobe *p, *retVal = NULL;
331 //TODO: test - two processes invokes instrumented function
332 head = &uprobe_insn_slot_table[hash_ptr (addr, KPROBE_HASH_BITS)];
333 swap_hlist_for_each_entry_rcu (p, node, head, is_hlist_arm) {
334 if (p->ainsn.insn == addr && tgid == p->tgid) {
340 DBPRINTF ("get_kprobe: probe %p", retVal);
344 static struct kprobe *get_kprobe_by_insn_slot_thumb(kprobe_opcode_t *addr, pid_t tgid)
346 struct hlist_head *head;
347 struct hlist_node *node;
348 struct kprobe *p, *retVal = NULL;
350 //TODO: test - two processes invokes instrumented function
351 head = &uprobe_insn_slot_table[hash_ptr (addr, KPROBE_HASH_BITS)];
352 swap_hlist_for_each_entry_rcu (p, node, head, is_hlist_thumb) {
353 if (p->ainsn.insn == addr && tgid == p->tgid) {
359 DBPRINTF ("get_kprobe: probe %p", retVal);
363 struct kprobe *get_kprobe_by_insn_slot(kprobe_opcode_t *addr, pid_t tgid, struct pt_regs *regs)
365 struct kprobe *p = NULL;
367 if (!thumb_mode(regs)) {
368 p = get_kprobe_by_insn_slot_arm(addr - UPROBES_TRAMP_RET_BREAK_IDX, tgid);
370 p = get_kprobe_by_insn_slot_thumb((kprobe_opcode_t *)((unsigned long)addr - 0x1a), tgid);
375 #else /* CONFIG_ARM */
376 struct kprobe *get_kprobe_by_insn_slot (void *addr, int tgid, struct task_struct *ctask)
378 struct hlist_head *head;
379 struct hlist_node *node;
380 struct kprobe *p, *retVal = NULL;
382 //TODO: test - two processes invokes instrumented function
383 head = &uprobe_insn_slot_table[hash_ptr (addr, KPROBE_HASH_BITS)];
384 hlist_for_each_entry_rcu (p, node, head, is_hlist) {
385 if (p->ainsn.insn == addr && tgid == p->tgid) {
391 DBPRINTF ("get_kprobe: probe %p", retVal);
394 #endif /* CONFIG_ARM */